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
|
@ -18,8 +18,8 @@ if (color === 'white') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupAnimations(color) {
|
function setupAnimations(color) {
|
||||||
let left = color === 'blue' ? 'blu-wl' : color+'-wl',
|
let left = color === 'blue' ? 'blu-wl' : color + '-wl',
|
||||||
right = color === 'blue' ? 'blu-wr' : color+'-wr',
|
right = color === 'blue' ? 'blu-wr' : color + '-wr',
|
||||||
leftFrames = [left + '1', left + '2', left + '3', left + '4'],
|
leftFrames = [left + '1', left + '2', left + '3', left + '4'],
|
||||||
rightFrames = [right + '1', right + '2', right + '3', right + '4'];
|
rightFrames = [right + '1', right + '2', right + '3', right + '4'];
|
||||||
|
|
||||||
|
@ -71,7 +71,8 @@ function main() {
|
||||||
myPt = Self.Position();
|
myPt = Self.Position();
|
||||||
|
|
||||||
// If the player is within aggro range, move towards.
|
// 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";
|
direction = playerPt.X < myPt.X ? "left" : "right";
|
||||||
followPlayer = true;
|
followPlayer = true;
|
||||||
|
|
||||||
|
@ -134,11 +135,16 @@ function playerControls() {
|
||||||
// will be hostile towards the player). Boring players will not be chased after and
|
// will be hostile towards the player). Boring players will not be chased after and
|
||||||
// the Azulian will not harm them if they make contact.
|
// the Azulian will not harm them if they make contact.
|
||||||
function isPlayerFood(actor) {
|
function isPlayerFood(actor) {
|
||||||
// Not a player or is invulnerable.
|
// Not a player or is invulnerable, or Peaceful difficulty.
|
||||||
if (!actor.IsPlayer() || actor.Invulnerable()) {
|
if (!actor.IsPlayer() || actor.Invulnerable() || Level.Difficulty < 0) {
|
||||||
return false;
|
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.
|
// Azulians are friendly to Thieves and other Azulians.
|
||||||
if (actor.Doodad().Filename === "thief.doodad" || actor.Doodad().Title.indexOf("Azulian") > -1) {
|
if (actor.Doodad().Filename === "thief.doodad" || actor.Doodad().Title.indexOf("Azulian") > -1) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -72,7 +72,7 @@ function main() {
|
||||||
// Scan for the player character and dive.
|
// Scan for the player character and dive.
|
||||||
try {
|
try {
|
||||||
AI_ScanForPlayer()
|
AI_ScanForPlayer()
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
console.error("Error in AI_ScanForPlayer: %s", e);
|
console.error("Error in AI_ScanForPlayer: %s", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ function main() {
|
||||||
|
|
||||||
// If diving, exit - don't edit animation.
|
// If diving, exit - don't edit animation.
|
||||||
if (state === states.diving) {
|
if (state === states.diving) {
|
||||||
Self.ShowLayerNamed("dive-"+direction);
|
Self.ShowLayerNamed("dive-" + direction);
|
||||||
lastDirection = direction;
|
lastDirection = direction;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,11 @@ function main() {
|
||||||
// It's not hostile towards characters that can fly (having
|
// It's not hostile towards characters that can fly (having
|
||||||
// no gravity).
|
// no gravity).
|
||||||
function AI_ScanForPlayer() {
|
function AI_ScanForPlayer() {
|
||||||
|
// If Peaceful difficulty, do not attack.
|
||||||
|
if (Level.Difficulty < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let stepY = 12, // number of pixels to skip
|
let stepY = 12, // number of pixels to skip
|
||||||
stepX = stepY,
|
stepX = stepY,
|
||||||
limit = stepX * 20, // furthest we'll scan
|
limit = stepX * 20, // furthest we'll scan
|
||||||
|
@ -198,7 +203,7 @@ function player() {
|
||||||
} else {
|
} else {
|
||||||
// Hover in place.
|
// Hover in place.
|
||||||
if (!Self.IsAnimating()) {
|
if (!Self.IsAnimating()) {
|
||||||
Self.PlayAnimation("fly-"+direction);
|
Self.PlayAnimation("fly-" + direction);
|
||||||
}
|
}
|
||||||
diving = false;
|
diving = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ func New(debug bool, engine render.Engine) *Doodle {
|
||||||
shmem.Flash = d.Flash
|
shmem.Flash = d.Flash
|
||||||
shmem.FlashError = d.FlashError
|
shmem.FlashError = d.FlashError
|
||||||
shmem.Prompt = d.Prompt
|
shmem.Prompt = d.Prompt
|
||||||
|
shmem.PromptPre = d.PromptPre
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
log.Logger.Config.Level = golog.DebugLevel
|
log.Logger.Config.Level = golog.DebugLevel
|
||||||
|
|
|
@ -25,3 +25,21 @@ const (
|
||||||
ScreenWidthMedium = 800
|
ScreenWidthMedium = 800
|
||||||
ScreenWidthLarge = 1000
|
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/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ type Base struct {
|
||||||
type Level struct {
|
type Level struct {
|
||||||
Base
|
Base
|
||||||
Password string `json:"passwd"`
|
Password string `json:"passwd"`
|
||||||
|
Difficulty enum.Difficulty `json:"difficulty"`
|
||||||
|
|
||||||
// Chunked pixel data.
|
// Chunked pixel data.
|
||||||
Chunker *Chunker `json:"chunks"`
|
Chunker *Chunker `json:"chunks"`
|
||||||
|
|
|
@ -5,7 +5,6 @@ package native
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/render/sdl"
|
"git.kirsle.net/go/render/sdl"
|
||||||
sdl2 "github.com/veandco/go-sdl2/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
|
return nil, err
|
||||||
}
|
}
|
||||||
defer surface.Free()
|
defer surface.Free()
|
||||||
log.Error("surf fmt: %+v", surface.Format)
|
|
||||||
|
|
||||||
// Convert the Surface into a pixelformat that supports the .At(x,y)
|
// Convert the Surface into a pixelformat that supports the .At(x,y)
|
||||||
// function properly, as the one we got above is "Not implemented"
|
// 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
|
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.
|
// Shell implements the developer console in-game.
|
||||||
type Shell struct {
|
type Shell struct {
|
||||||
parent *Doodle
|
parent *Doodle
|
||||||
|
|
|
@ -28,6 +28,7 @@ var (
|
||||||
|
|
||||||
// Globally available Prompt() function.
|
// Globally available Prompt() function.
|
||||||
Prompt func(string, func(string))
|
Prompt func(string, func(string))
|
||||||
|
PromptPre func(string, string, func(string))
|
||||||
|
|
||||||
// Ajax file cache for WASM use.
|
// Ajax file cache for WASM use.
|
||||||
AjaxCache map[string][]byte
|
AjaxCache map[string][]byte
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package uix
|
package uix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/keybind"
|
"git.kirsle.net/apps/doodle/pkg/keybind"
|
||||||
"git.kirsle.net/apps/doodle/pkg/level"
|
"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 {
|
if w.level != nil && addHistory {
|
||||||
w.level.UndoHistory.AddStroke(w.currentStroke)
|
w.level.UndoHistory.AddStroke(w.currentStroke)
|
||||||
} else if w.doodad != nil && addHistory {
|
} 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)
|
w.doodad.UndoHistory.AddStroke(w.currentStroke)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ func (w *Canvas) UndoStroke() bool {
|
||||||
if w.level != nil {
|
if w.level != nil {
|
||||||
undoer = w.level.UndoHistory
|
undoer = w.level.UndoHistory
|
||||||
} else if w.doodad != nil {
|
} 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
|
undoer = w.doodad.UndoHistory
|
||||||
} else {
|
} else {
|
||||||
log.Error("Canvas.UndoStroke: no Level or Doodad currently available to the canvas")
|
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
|
SelectValue interface{} // Selectbox default choice
|
||||||
Color *render.Color // Color
|
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.
|
// Tooltip to add to a form control.
|
||||||
// Checkbox only for now.
|
// Checkbox only for now.
|
||||||
Tooltip ui.Tooltip // config for the tooltip only
|
Tooltip ui.Tooltip // config for the tooltip only
|
||||||
|
@ -83,6 +87,7 @@ type Field struct {
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Value interface{}
|
Value interface{}
|
||||||
Label string
|
Label string
|
||||||
|
Separator bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -278,6 +283,22 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
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 {
|
if row.OnClick != nil {
|
||||||
row.OnClick()
|
row.OnClick()
|
||||||
}
|
}
|
||||||
|
@ -325,6 +346,10 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
||||||
|
|
||||||
if row.Options != nil {
|
if row.Options != nil {
|
||||||
for _, option := range row.Options {
|
for _, option := range row.Options {
|
||||||
|
if option.Separator {
|
||||||
|
btn.AddSeparator()
|
||||||
|
continue
|
||||||
|
}
|
||||||
btn.AddItem(option.Label, option.Value, func() {})
|
btn.AddItem(option.Label, option.Value, func() {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,6 +367,12 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
||||||
return nil
|
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)
|
btn.Supervise(form.Supervisor)
|
||||||
form.Supervisor.Add(btn)
|
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
|
// MakeSelfAPI generates the `Self` object for the scripting API in
|
||||||
|
|
|
@ -4,10 +4,13 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"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/level"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"git.kirsle.net/apps/doodle/pkg/modal"
|
"git.kirsle.net/apps/doodle/pkg/modal"
|
||||||
"git.kirsle.net/apps/doodle/pkg/native"
|
"git.kirsle.net/apps/doodle/pkg/native"
|
||||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
"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/apps/doodle/pkg/wallpaper"
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/ui"
|
"git.kirsle.net/go/ui"
|
||||||
|
@ -90,7 +93,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Given a level to edit?
|
// Given a level to edit?
|
||||||
if config.EditLevel != nil {
|
if !isNewLevel {
|
||||||
newPageType = config.EditLevel.PageType.String()
|
newPageType = config.EditLevel.PageType.String()
|
||||||
newWallpaper = config.EditLevel.Wallpaper
|
newWallpaper = config.EditLevel.Wallpaper
|
||||||
paletteName = textCurrentPalette
|
paletteName = textCurrentPalette
|
||||||
|
@ -105,205 +108,99 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
* Frame for selecting Page Type
|
* Frame for selecting Page Type
|
||||||
******************/
|
******************/
|
||||||
|
|
||||||
typeFrame := ui.NewFrame("Page Type Options Frame")
|
// Selected "Page Type" property.
|
||||||
frame.Pack(typeFrame, ui.Pack{
|
var pageType = level.Bounded
|
||||||
Side: ui.N,
|
if !isNewLevel {
|
||||||
FillX: true,
|
pageType = config.EditLevel.PageType
|
||||||
})
|
|
||||||
|
|
||||||
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},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typeBtn := ui.NewSelectBox("Type Select", ui.Label{
|
form := magicform.Form{
|
||||||
Font: ui.MenuFont,
|
Supervisor: config.Supervisor,
|
||||||
})
|
Engine: config.Engine,
|
||||||
typeFrame.Pack(typeBtn, ui.Pack{
|
Vertical: true,
|
||||||
Side: ui.W,
|
LabelWidth: 120,
|
||||||
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,
|
|
||||||
PadY: 2,
|
PadY: 2,
|
||||||
})
|
}
|
||||||
|
fields := []magicform.Field{
|
||||||
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
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
label: "Width:",
|
Label: "Page type:",
|
||||||
number: &config.EditLevel.MaxWidth,
|
Font: balance.UIFont,
|
||||||
|
Options: []magicform.Option{
|
||||||
|
{
|
||||||
|
Label: "Bounded",
|
||||||
|
Value: level.Bounded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Height:",
|
Label: "Bounded",
|
||||||
number: &config.EditLevel.MaxHeight,
|
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")
|
var selectedWallpaper = "notebook.png"
|
||||||
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.
|
|
||||||
if config.EditLevel != nil {
|
if config.EditLevel != nil {
|
||||||
wallBtn.SetValue(config.EditLevel.Wallpaper)
|
selectedWallpaper = config.EditLevel.Wallpaper
|
||||||
}
|
}
|
||||||
|
|
||||||
wallBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
fields = append(fields, []magicform.Field{
|
||||||
if selection, ok := wallBtn.GetValue(); ok {
|
{
|
||||||
if filename, ok := selection.Value.(string); ok {
|
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?
|
// Picking the Custom option?
|
||||||
if filename == balance.CustomWallpaperFilename {
|
if filename == balance.CustomWallpaperFilename {
|
||||||
filename, err := native.OpenFile("Choose a custom wallpaper:", "*.png *.jpg *.gif")
|
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)
|
b64data, err := wallpaper.FileToB64(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
shmem.Flash("Error loading wallpaper: %s", err)
|
shmem.Flash("Error loading wallpaper: %s", err)
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If editing a level, apply the update straight away.
|
// If editing a level, apply the update straight away.
|
||||||
|
@ -329,7 +226,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
newWallpaperB64 = b64data
|
newWallpaperB64 = b64data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if pageType, ok := level.PageTypeFromString(newPageType); ok {
|
if pageType, ok := level.PageTypeFromString(newPageType); ok {
|
||||||
|
@ -337,12 +234,9 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
newWallpaper = filename
|
newWallpaper = filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
return nil
|
},
|
||||||
})
|
}...)
|
||||||
|
|
||||||
wallBtn.Supervise(config.Supervisor)
|
|
||||||
config.Supervisor.Add(wallBtn)
|
|
||||||
|
|
||||||
/******************
|
/******************
|
||||||
* Frame for picking a default color palette.
|
* Frame for picking a default color palette.
|
||||||
|
@ -350,141 +244,122 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
|
|
||||||
// For new level or --experimental only.
|
// For new level or --experimental only.
|
||||||
if config.EditLevel == nil || balance.Feature.ChangePalette {
|
if config.EditLevel == nil || balance.Feature.ChangePalette {
|
||||||
palFrame := ui.NewFrame("Palette Frame")
|
var (
|
||||||
frame.Pack(palFrame, ui.Pack{
|
palettes = []magicform.Option{}
|
||||||
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,
|
|
||||||
})
|
|
||||||
|
|
||||||
if config.EditLevel != nil {
|
if config.EditLevel != nil {
|
||||||
palBtn.AddItem(paletteName, paletteName, func() {})
|
palettes = append(palettes, []magicform.Option{
|
||||||
palBtn.AddSeparator()
|
{
|
||||||
|
Label: paletteName, // "Keep current palette"
|
||||||
|
Value: paletteName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Separator: true,
|
||||||
|
},
|
||||||
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, palName := range level.DefaultPaletteNames {
|
for _, palName := range level.DefaultPaletteNames {
|
||||||
palName := palName
|
palettes = append(palettes, magicform.Option{
|
||||||
palBtn.AddItem(palName, palName, func() {})
|
Label: palName,
|
||||||
}
|
Value: palName,
|
||||||
|
|
||||||
palBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
|
||||||
if val, ok := palBtn.GetValue(); ok {
|
|
||||||
val2, _ := val.Value.(string)
|
|
||||||
paletteName = val2
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
config.Supervisor.Add(palBtn)
|
// Add form fields.
|
||||||
palBtn.Supervise(config.Supervisor)
|
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 {
|
if config.EditLevel != nil {
|
||||||
label3 := ui.NewLabel(ui.Label{
|
fields = append(fields, []magicform.Field{
|
||||||
Text: "Metadata",
|
{
|
||||||
|
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,
|
Font: balance.LabelFont,
|
||||||
})
|
},
|
||||||
frame.Pack(label3, ui.Pack{
|
{
|
||||||
Side: ui.N,
|
Label: "Title:",
|
||||||
FillX: true,
|
Font: balance.UIFont,
|
||||||
})
|
TextVariable: &config.EditLevel.Title,
|
||||||
|
PromptUser: func(answer string) {
|
||||||
type metadataObj struct {
|
config.EditLevel.Title = answer
|
||||||
Label string
|
},
|
||||||
Binding *string
|
},
|
||||||
Update func(string)
|
{
|
||||||
}
|
Label: "Author:",
|
||||||
var metaRows = []metadataObj{
|
Font: balance.UIFont,
|
||||||
{"Title:", &config.EditLevel.Title, func(v string) { config.EditLevel.Title = v }},
|
TextVariable: &config.EditLevel.Author,
|
||||||
{"Author:", &config.EditLevel.Author, func(v string) { config.EditLevel.Author = v }},
|
PromptUser: func(answer string) {
|
||||||
|
config.EditLevel.Author = answer
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mr := range metaRows {
|
// The confirm/cancel buttons.
|
||||||
mr := mr
|
var okLabel = "Ok"
|
||||||
mrFrame := ui.NewFrame("Metadata " + mr.Label + "Frame")
|
if config.EditLevel == nil {
|
||||||
frame.Pack(mrFrame, ui.Pack{
|
okLabel = "Continue"
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
})
|
fields = append(fields, []magicform.Field{
|
||||||
return nil
|
{
|
||||||
})
|
Buttons: []magicform.Field{
|
||||||
config.Supervisor.Add(mrButton)
|
{
|
||||||
mrFrame.Pack(mrButton, ui.Pack{
|
ButtonStyle: &balance.ButtonPrimary,
|
||||||
Side: ui.W,
|
Label: okLabel,
|
||||||
Expand: true,
|
Font: balance.UIFont,
|
||||||
PadX: 2,
|
OnClick: func() {
|
||||||
})
|
// Is it a NEW level?
|
||||||
}
|
if config.EditLevel == nil {
|
||||||
}
|
|
||||||
|
|
||||||
/******************
|
|
||||||
* 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 {
|
|
||||||
shmem.Flash("Create new map with %s page type and %s wallpaper", newPageType, newWallpaper)
|
shmem.Flash("Create new map with %s page type and %s wallpaper", newPageType, newWallpaper)
|
||||||
pageType, ok := level.PageTypeFromString(newPageType)
|
pageType, ok := level.PageTypeFromString(newPageType)
|
||||||
if !ok {
|
if !ok {
|
||||||
shmem.Flash("Invalid Page Type '%s'", newPageType)
|
shmem.Flash("Invalid Page Type '%s'", newPageType)
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lvl := level.New()
|
lvl := level.New()
|
||||||
|
@ -502,17 +377,11 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
} else {
|
} else {
|
||||||
shmem.FlashError("OnCreateNewLevel not attached")
|
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?
|
// 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 {
|
if paletteName != textCurrentPalette {
|
||||||
modal.Confirm(
|
modal.Confirm(
|
||||||
"Are you sure you want to change the level palette?\n" +
|
"Are you sure you want to change the level palette?\n" +
|
||||||
|
@ -526,30 +395,25 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
config.OnReload()
|
config.OnReload()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnCancel()
|
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.
|
// Creates the "New Doodad" frame.
|
||||||
|
|
|
@ -52,6 +52,10 @@ func NewDoodadDropper(config DoodadDropper) *ui.Window {
|
||||||
// Load all the doodads, skip hidden ones.
|
// Load all the doodads, skip hidden ones.
|
||||||
var items []*doodads.Doodad
|
var items []*doodads.Doodad
|
||||||
for _, filename := range doodadsAvailable {
|
for _, filename := range doodadsAvailable {
|
||||||
|
if filename == "_autosave.doodad" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
doodad, err := doodads.LoadFile(filename)
|
doodad, err := doodads.LoadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err.Error())
|
log.Error(err.Error())
|
||||||
|
|
|
@ -103,7 +103,7 @@ func NewTextToolWindow(cfg TextTool) *ui.Window {
|
||||||
Font: balance.LabelFont,
|
Font: balance.LabelFont,
|
||||||
TextVariable: ¤tText,
|
TextVariable: ¤tText,
|
||||||
OnClick: func() {
|
OnClick: func() {
|
||||||
shmem.Prompt("Enter new message: ", func(answer string) {
|
shmem.PromptPre("Enter new message: ", currentText, func(answer string) {
|
||||||
if answer != "" {
|
if answer != "" {
|
||||||
currentText = answer
|
currentText = answer
|
||||||
if cfg.OnChangeSettings != nil {
|
if cfg.OnChangeSettings != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user