Crosshair Option + Doodad Editor crash fix
* The level scroll logic was getting a null pointer crash if you open a doodad rather than a level file. * Add a crosshair option to the level editor, configurable in the Game Settings window.
This commit is contained in:
parent
8ca411a0ae
commit
0ec259b171
|
@ -20,6 +20,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||
"git.kirsle.net/apps/doodle/pkg/sound"
|
||||
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/render/sdl"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
|
@ -62,6 +63,11 @@ func main() {
|
|||
log.Error("Error loading user settings (defaults will be used): %s", err)
|
||||
}
|
||||
|
||||
// Set default user settings.
|
||||
if usercfg.Current.CrosshairColor == render.Invisible {
|
||||
usercfg.Current.CrosshairColor = balance.DefaultCrosshairColor
|
||||
}
|
||||
|
||||
app.Version = fmt.Sprintf("%s build %s%s. Built on %s",
|
||||
branding.Version,
|
||||
Build,
|
||||
|
|
|
@ -187,6 +187,8 @@ var (
|
|||
ButtonBabyBlue = style.DefaultButton
|
||||
ButtonPink = style.DefaultButton
|
||||
ButtonLightRed = style.DefaultButton
|
||||
|
||||
DefaultCrosshairColor = render.RGBA(0, 153, 255, 255)
|
||||
)
|
||||
|
||||
// Customize the various button styles.
|
||||
|
|
|
@ -69,6 +69,7 @@ func New(debug bool, engine render.Engine) *Doodle {
|
|||
// Make the render engine globally available. TODO: for wasm/ToBitmap
|
||||
shmem.CurrentRenderEngine = engine
|
||||
shmem.Flash = d.Flash
|
||||
shmem.FlashError = d.FlashError
|
||||
shmem.Prompt = d.Prompt
|
||||
|
||||
if debug {
|
||||
|
@ -240,6 +241,8 @@ func (d *Doodle) MakeSettingsWindow(supervisor *ui.Supervisor) *ui.Window {
|
|||
DebugCollision: &DebugCollision,
|
||||
HorizontalToolbars: &usercfg.Current.HorizontalToolbars,
|
||||
EnableFeatures: &usercfg.Current.EnableFeatures,
|
||||
CrosshairSize: &usercfg.Current.CrosshairSize,
|
||||
CrosshairColor: &usercfg.Current.CrosshairColor,
|
||||
}
|
||||
return windows.MakeSettingsWindow(d.width, d.height, cfg)
|
||||
}
|
||||
|
|
|
@ -351,6 +351,9 @@ func (u *EditorUI) Present(e render.Engine) {
|
|||
|
||||
u.screen.Present(e, render.Origin)
|
||||
|
||||
// Draw the crosshair over the level canvas, but not over UI popups.
|
||||
uix.DrawCrosshair(e, u.Canvas, usercfg.Current.CrosshairColor, usercfg.Current.CrosshairSize)
|
||||
|
||||
// Draw any windows being managed by Supervisor.
|
||||
u.Supervisor.Present(e)
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ var (
|
|||
OfflineMode bool
|
||||
|
||||
// Globally available Flash() function so we can emit text to the Doodle UI.
|
||||
Flash func(string, ...interface{})
|
||||
Flash func(string, ...interface{})
|
||||
FlashError func(string, ...interface{})
|
||||
|
||||
// Globally available Prompt() function.
|
||||
Prompt func(string, func(string))
|
||||
|
|
|
@ -116,6 +116,11 @@ func (w *Canvas) loopConstrainScroll() error {
|
|||
return errors.New("NoLimitScroll enabled")
|
||||
}
|
||||
|
||||
// Levels only.
|
||||
if w.level == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
capped bool
|
||||
maxWidth = w.level.MaxWidth
|
||||
|
|
70
pkg/uix/crosshair.go
Normal file
70
pkg/uix/crosshair.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package uix
|
||||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
/*
|
||||
Functions for the Crosshair feature of the game.
|
||||
|
||||
NOT dependent on Canvas!
|
||||
*/
|
||||
|
||||
type Crosshair struct {
|
||||
LengthPct float64 // between 0 and 1
|
||||
Widget ui.Widget
|
||||
}
|
||||
|
||||
func NewCrosshair(child ui.Widget, length float64) *Crosshair {
|
||||
return &Crosshair{
|
||||
LengthPct: length,
|
||||
Widget: child,
|
||||
}
|
||||
}
|
||||
|
||||
// DrawCrosshair renders a crosshair on the screen. It appears while the mouse cursor is
|
||||
// over the child widget and draws within the bounds of the child widget.
|
||||
//
|
||||
// The lengthPct is an integer ranged 0 to 100 to be a percentage length of the crosshair.
|
||||
// A value of zero will not draw anything and just return.
|
||||
func DrawCrosshair(e render.Engine, child ui.Widget, color render.Color, lengthPct int) {
|
||||
// Get our window boundaries based on our widget.
|
||||
var (
|
||||
Position = ui.AbsolutePosition(child)
|
||||
Size = child.Size()
|
||||
Cursor = shmem.Cursor
|
||||
VertLine = []render.Point{
|
||||
{X: Cursor.X, Y: Position.Y},
|
||||
{X: Cursor.X, Y: Position.Y + Size.H},
|
||||
}
|
||||
HozLine = []render.Point{
|
||||
{X: Position.X, Y: Cursor.Y},
|
||||
{X: Position.X + Size.W, Y: Cursor.Y},
|
||||
}
|
||||
)
|
||||
|
||||
if lengthPct > 100 {
|
||||
lengthPct = 100
|
||||
} else if lengthPct <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Mouse outside our box.
|
||||
if Cursor.X < Position.X || Cursor.X > Position.X+Size.W ||
|
||||
Cursor.Y < Position.Y || Cursor.Y > Position.Y+Size.H {
|
||||
return
|
||||
}
|
||||
|
||||
e.DrawLine(
|
||||
color,
|
||||
VertLine[0],
|
||||
VertLine[1],
|
||||
)
|
||||
e.DrawLine(
|
||||
color,
|
||||
HozLine[0],
|
||||
HozLine[1],
|
||||
)
|
||||
}
|
|
@ -18,6 +18,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/userdir"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
||||
// Settings are the available game settings.
|
||||
|
@ -30,6 +31,8 @@ type Settings struct {
|
|||
// Configurable settings (pkg/windows/settings.go)
|
||||
HorizontalToolbars bool `json:",omitempty"`
|
||||
EnableFeatures bool `json:",omitempty"`
|
||||
CrosshairSize int `json:",omitempty"`
|
||||
CrosshairColor render.Color
|
||||
|
||||
// Secret boolprops from balance/boolprops.go
|
||||
ShowHiddenDoodads bool `json:",omitempty"`
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package windows
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/native"
|
||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
||||
"git.kirsle.net/apps/doodle/pkg/userdir"
|
||||
"git.kirsle.net/go/render"
|
||||
|
@ -24,6 +26,8 @@ type Settings struct {
|
|||
DebugCollision *bool
|
||||
HorizontalToolbars *bool
|
||||
EnableFeatures *bool
|
||||
CrosshairSize *int
|
||||
CrosshairColor *render.Color
|
||||
|
||||
// Configuration options.
|
||||
SceneName string // name of scene which called this window
|
||||
|
@ -106,11 +110,14 @@ func (c Settings) makeOptionsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.F
|
|||
return nil
|
||||
}
|
||||
|
||||
var inputBoxWidth = 120
|
||||
rows := []struct {
|
||||
Header string
|
||||
Text string
|
||||
Boolean *bool
|
||||
Integer *int
|
||||
TextVariable *string
|
||||
Color *render.Color
|
||||
PadY int
|
||||
PadX int
|
||||
name string // for special cases
|
||||
|
@ -128,6 +135,16 @@ func (c Settings) makeOptionsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.F
|
|||
PadX: 4,
|
||||
name: "toolbars",
|
||||
},
|
||||
{
|
||||
Integer: c.CrosshairSize,
|
||||
Text: "Editor: Crosshair size (0 to disable):",
|
||||
PadX: 4,
|
||||
},
|
||||
{
|
||||
Color: c.CrosshairColor,
|
||||
Text: "Editor: Crosshair color:",
|
||||
PadX: 4,
|
||||
},
|
||||
{
|
||||
Header: "Debug Options (temporary)",
|
||||
},
|
||||
|
@ -196,6 +213,14 @@ func (c Settings) makeOptionsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.F
|
|||
PadX: row.PadX,
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
// Reserve indented space where the checkbox would have gone.
|
||||
spacer := ui.NewFrame("Spacer")
|
||||
spacer.Resize(render.NewRect(9, 9)) // TODO: ugly UI hack ;)
|
||||
frame.Pack(spacer, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: row.PadX,
|
||||
})
|
||||
}
|
||||
|
||||
// Any leftover Text gets packed to the left.
|
||||
|
@ -212,6 +237,94 @@ func (c Settings) makeOptionsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.F
|
|||
Side: ui.W,
|
||||
})
|
||||
}
|
||||
|
||||
// Int variables draw as a button to prompt for new value.
|
||||
// In future: TextVariable works here too.
|
||||
if row.Integer != nil {
|
||||
varButton := ui.NewButton("VarButton", ui.NewLabel(ui.Label{
|
||||
IntVariable: row.Integer,
|
||||
Font: ui.MenuFont,
|
||||
}))
|
||||
varButton.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
shmem.Prompt(row.Text+" ", func(answer string) {
|
||||
if answer == "" {
|
||||
return
|
||||
}
|
||||
|
||||
a, err := strconv.Atoi(answer)
|
||||
if err != nil {
|
||||
shmem.FlashError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if a < 0 {
|
||||
a = 0
|
||||
} else if a > 100 {
|
||||
a = 100
|
||||
}
|
||||
|
||||
*row.Integer = a
|
||||
shmem.Flash("Crosshair size set to %d%% (WIP)", a)
|
||||
|
||||
// call onClick to save change to disk now
|
||||
onClick(ed)
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
varButton.Compute(c.Engine)
|
||||
varButton.Resize(render.Rect{
|
||||
W: inputBoxWidth,
|
||||
H: varButton.Size().H,
|
||||
})
|
||||
|
||||
c.Supervisor.Add(varButton)
|
||||
frame.Pack(varButton, ui.Pack{
|
||||
Side: ui.E,
|
||||
PadX: row.PadX,
|
||||
})
|
||||
}
|
||||
|
||||
// Color picker button.
|
||||
if row.Color != nil {
|
||||
btn := ui.NewButton("ColorBtn", ui.NewFrame(""))
|
||||
style := style.DefaultButton
|
||||
style.Background = *row.Color
|
||||
style.HoverBackground = style.Background.Lighten(20)
|
||||
btn.SetStyle(&style)
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
shmem.Prompt("Enter color in hexadecimal notation: ", func(answer string) {
|
||||
if answer == "" {
|
||||
return
|
||||
}
|
||||
|
||||
color, err := render.HexColor(answer)
|
||||
if err != nil {
|
||||
shmem.FlashError("Invalid color value: %s", err)
|
||||
}
|
||||
|
||||
*row.Color = color
|
||||
style.Background = color
|
||||
style.HoverBackground = style.Background.Lighten(20)
|
||||
|
||||
// call onClick to save change to disk now
|
||||
onClick(ed)
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
btn.Compute(c.Engine)
|
||||
btn.Resize(render.Rect{
|
||||
W: inputBoxWidth,
|
||||
H: 20, // TODO
|
||||
})
|
||||
|
||||
c.Supervisor.Add(btn)
|
||||
frame.Pack(btn, ui.Pack{
|
||||
Side: ui.E,
|
||||
PadX: row.PadX,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Button toolbar.
|
||||
|
|
Loading…
Reference in New Issue
Block a user