Actor Zoom + Experimental Settings GUI
Improvements to the Zoom feature: * Actor position and size within your level scales up and down appropriately. The canvas size of the actor is scaled and its canvas is told the Zoom number of the parent so it will render its own graphic scaled correctly too. Other features: * "Experimental" tab added to the Settings window as a UI version of the --experimental CLI option. The option saves persistently to disk. * The "Replace Palette" experimental feature now works better. Debating whether it's a useful feature to even have.
This commit is contained in:
parent
ecdfc46358
commit
0a8bce708e
|
@ -125,7 +125,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable feature flags?
|
// Enable feature flags?
|
||||||
if c.Bool("experimental") {
|
if c.Bool("experimental") || usercfg.Current.EnableFeatures {
|
||||||
balance.FeaturesOn()
|
balance.FeaturesOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,17 @@ package balance
|
||||||
|
|
||||||
// Feature Flags to turn on/off experimental content.
|
// Feature Flags to turn on/off experimental content.
|
||||||
var Feature = feature{
|
var Feature = feature{
|
||||||
|
/////////
|
||||||
|
// Experimental features that are off by default
|
||||||
Zoom: false, // enable the zoom in/out feature (very buggy rn)
|
Zoom: false, // enable the zoom in/out feature (very buggy rn)
|
||||||
CustomWallpaper: true, // attach custom wallpaper img to levels
|
|
||||||
ChangePalette: false, // reset your palette after level creation to a diff preset
|
ChangePalette: false, // reset your palette after level creation to a diff preset
|
||||||
|
|
||||||
|
/////////
|
||||||
|
// Fully activated features
|
||||||
|
|
||||||
|
// Attach custom wallpaper img to levels
|
||||||
|
CustomWallpaper: true,
|
||||||
|
|
||||||
// Allow embedded doodads in levels.
|
// Allow embedded doodads in levels.
|
||||||
EmbeddableDoodads: true,
|
EmbeddableDoodads: true,
|
||||||
}
|
}
|
||||||
|
@ -13,7 +20,6 @@ var Feature = feature{
|
||||||
// FeaturesOn turns on all feature flags, from CLI --experimental option.
|
// FeaturesOn turns on all feature flags, from CLI --experimental option.
|
||||||
func FeaturesOn() {
|
func FeaturesOn() {
|
||||||
Feature.Zoom = true
|
Feature.Zoom = true
|
||||||
Feature.CustomWallpaper = true
|
|
||||||
Feature.ChangePalette = true
|
Feature.ChangePalette = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -239,6 +239,7 @@ func (d *Doodle) MakeSettingsWindow(supervisor *ui.Supervisor) *ui.Window {
|
||||||
DebugOverlay: &DebugOverlay,
|
DebugOverlay: &DebugOverlay,
|
||||||
DebugCollision: &DebugCollision,
|
DebugCollision: &DebugCollision,
|
||||||
HorizontalToolbars: &usercfg.Current.HorizontalToolbars,
|
HorizontalToolbars: &usercfg.Current.HorizontalToolbars,
|
||||||
|
EnableFeatures: &usercfg.Current.EnableFeatures,
|
||||||
}
|
}
|
||||||
return windows.MakeSettingsWindow(d.width, d.height, cfg)
|
return windows.MakeSettingsWindow(d.width, d.height, cfg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,23 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the editor scene from scratch. Good nuclear option when you change the level's
|
||||||
|
// palette on-the-fly or some other sticky situation and want to reload the editor.
|
||||||
|
func (s *EditorScene) Reset() {
|
||||||
|
if s.Level != nil {
|
||||||
|
s.Level.Chunker.Redraw()
|
||||||
|
}
|
||||||
|
if s.Doodad != nil {
|
||||||
|
s.Doodad.Layers[s.ActiveLayer].Chunker.Redraw()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.d.Goto(&EditorScene{
|
||||||
|
Filename: s.Filename,
|
||||||
|
Level: s.Level,
|
||||||
|
Doodad: s.Doodad,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// setupAsync initializes trhe editor scene in the background,
|
// setupAsync initializes trhe editor scene in the background,
|
||||||
// underneath a loading screen.
|
// underneath a loading screen.
|
||||||
func (s *EditorScene) setupAsync(d *Doodle) error {
|
func (s *EditorScene) setupAsync(d *Doodle) error {
|
||||||
|
|
|
@ -347,6 +347,13 @@ func (u *EditorUI) Present(e render.Engine) {
|
||||||
if u.Supervisor.IsDragging() {
|
if u.Supervisor.IsDragging() {
|
||||||
if actor := u.DraggableActor; actor != nil {
|
if actor := u.DraggableActor; actor != nil {
|
||||||
var size = actor.canvas.Size()
|
var size = actor.canvas.Size()
|
||||||
|
|
||||||
|
// Scale the actor up or down by the zoom level of the Editor Canvas.
|
||||||
|
// TODO: didn't seem to make a difference
|
||||||
|
// size.W = u.Canvas.ZoomMultiply(size.W)
|
||||||
|
// size.H = u.Canvas.ZoomMultiply(size.H)
|
||||||
|
// actor.canvas.Zoom = u.Canvas.Zoom
|
||||||
|
|
||||||
actor.canvas.Present(u.d.Engine, render.NewPoint(
|
actor.canvas.Present(u.d.Engine, render.NewPoint(
|
||||||
u.cursor.X-(size.W/2),
|
u.cursor.X-(size.W/2),
|
||||||
u.cursor.Y-(size.H/2),
|
u.cursor.Y-(size.H/2),
|
||||||
|
@ -415,7 +422,7 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
// NOTE: The drag event begins at editor_ui_doodad.go when configuring the
|
// NOTE: The drag event begins at editor_ui_doodad.go when configuring the
|
||||||
// Doodad Palette buttons.
|
// Doodad Palette buttons.
|
||||||
drawing.Handle(ui.Drop, func(ed ui.EventData) error {
|
drawing.Handle(ui.Drop, func(ed ui.EventData) error {
|
||||||
log.Info("Drawing canvas has received a drop!")
|
// Editor Canvas's position relative to the window.
|
||||||
var P = ui.AbsolutePosition(drawing)
|
var P = ui.AbsolutePosition(drawing)
|
||||||
|
|
||||||
// Was it an actor from the Doodad Palette?
|
// Was it an actor from the Doodad Palette?
|
||||||
|
@ -446,6 +453,10 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Adjust the level position per the zoom factor.
|
||||||
|
position.X = drawing.ZoomDivide(position.X)
|
||||||
|
position.Y = drawing.ZoomDivide(position.Y)
|
||||||
|
|
||||||
// Was it an already existing actor to re-add to the map?
|
// Was it an already existing actor to re-add to the map?
|
||||||
if actor.actor != nil {
|
if actor.actor != nil {
|
||||||
actor.actor.Point = position
|
actor.actor.Point = position
|
||||||
|
|
|
@ -133,6 +133,10 @@ func (u *EditorUI) SetupPopups(d *Doodle) {
|
||||||
scene.Level.Wallpaper = wallpaper
|
scene.Level.Wallpaper = wallpaper
|
||||||
u.Canvas.LoadLevel(scene.Level)
|
u.Canvas.LoadLevel(scene.Level)
|
||||||
},
|
},
|
||||||
|
OnReload: func() {
|
||||||
|
log.Warn("RELOAD LEVEL")
|
||||||
|
scene.Reset()
|
||||||
|
},
|
||||||
OnCancel: func() {
|
OnCancel: func() {
|
||||||
u.levelSettingsWindow.Hide()
|
u.levelSettingsWindow.Hide()
|
||||||
},
|
},
|
||||||
|
|
|
@ -62,10 +62,6 @@ func LoadBinary(filename string) (*Level, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
||||||
m.Chunker.Inflate(m.Palette)
|
m.Inflate()
|
||||||
m.Actors.Inflate()
|
|
||||||
|
|
||||||
// Inflate the private instance values.
|
|
||||||
m.Palette.Inflate()
|
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,7 @@ func FromJSON(filename string, data []byte) (*Level, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
||||||
m.Chunker.Inflate(m.Palette)
|
m.Inflate()
|
||||||
m.Actors.Inflate()
|
|
||||||
|
|
||||||
// Inflate the private instance values.
|
|
||||||
m.Palette.Inflate()
|
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
24
pkg/level/inflate.go
Normal file
24
pkg/level/inflate.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package level
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inflate the level from its compressed JSON form.
|
||||||
|
|
||||||
|
This is called as part of the LoadJSON function when the level is read
|
||||||
|
from disk. In the JSON format, pixels in the level refer to the palette
|
||||||
|
by its index number.
|
||||||
|
|
||||||
|
This function calls the following:
|
||||||
|
|
||||||
|
* Chunker.Inflate(Palette) to update references to the level's pixels to point
|
||||||
|
to the Swatch entry.
|
||||||
|
* Actors.Inflate()
|
||||||
|
* Palette.Inflate() to load private instance values for the palette subsystem.
|
||||||
|
*/
|
||||||
|
func (l *Level) Inflate() {
|
||||||
|
// Inflate the chunk metadata to map the pixels to their palette indexes.
|
||||||
|
l.Chunker.Inflate(l.Palette)
|
||||||
|
l.Actors.Inflate()
|
||||||
|
|
||||||
|
// Inflate the private instance values.
|
||||||
|
l.Palette.Inflate()
|
||||||
|
}
|
|
@ -134,3 +134,28 @@ func (p *Palette) update() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplacePalette installs a new palette into your level.
|
||||||
|
// Your existing level colors, by index, are replaced by the incoming
|
||||||
|
// palette. If the new palette is smaller, extraneous indices are
|
||||||
|
// left alone.
|
||||||
|
func (l *Level) ReplacePalette(pal *Palette) {
|
||||||
|
for i, swatch := range pal.Swatches {
|
||||||
|
if i >= len(l.Palette.Swatches) {
|
||||||
|
l.Palette.Swatches = append(l.Palette.Swatches, swatch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ugly code, but can't just replace the swatch
|
||||||
|
// wholesale -- the inflated level data means existing
|
||||||
|
// pixels already have refs to their Swatch and they
|
||||||
|
// will keep those refs until you fully save and exit
|
||||||
|
// out of the editor.
|
||||||
|
l.Palette.Swatches[i].Name = swatch.Name
|
||||||
|
l.Palette.Swatches[i].Color = swatch.Color
|
||||||
|
l.Palette.Swatches[i].Pattern = swatch.Pattern
|
||||||
|
l.Palette.Swatches[i].Solid = swatch.Solid
|
||||||
|
l.Palette.Swatches[i].Fire = swatch.Fire
|
||||||
|
l.Palette.Swatches[i].Water = swatch.Water
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -116,8 +116,18 @@ func (w *Canvas) drawActors(e render.Engine, p render.Point) {
|
||||||
can = a.Canvas // Canvas widget that draws the actor
|
can = a.Canvas // Canvas widget that draws the actor
|
||||||
actorPoint = a.Position()
|
actorPoint = a.Position()
|
||||||
actorSize = a.Size()
|
actorSize = a.Size()
|
||||||
|
resizeTo = actorSize
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Adjust actor position and size by the zoom level.
|
||||||
|
actorPoint.X = w.ZoomMultiply(actorPoint.X)
|
||||||
|
actorPoint.Y = w.ZoomMultiply(actorPoint.Y)
|
||||||
|
resizeTo.W = w.ZoomMultiply(resizeTo.W)
|
||||||
|
resizeTo.H = w.ZoomMultiply(resizeTo.H)
|
||||||
|
|
||||||
|
// Tell the actor's canvas to copy our zoom level so it resizes its image too.
|
||||||
|
can.Zoom = w.Zoom
|
||||||
|
|
||||||
// Create a box of World Coordinates that this actor occupies. The
|
// Create a box of World Coordinates that this actor occupies. The
|
||||||
// Actor X,Y from level data is already a World Coordinate;
|
// Actor X,Y from level data is already a World Coordinate;
|
||||||
// accomodate for the size of the Actor.
|
// accomodate for the size of the Actor.
|
||||||
|
@ -137,7 +147,6 @@ func (w *Canvas) drawActors(e render.Engine, p render.Point) {
|
||||||
X: p.X + w.Scroll.X + actorPoint.X + w.BoxThickness(1),
|
X: p.X + w.Scroll.X + actorPoint.X + w.BoxThickness(1),
|
||||||
Y: p.Y + w.Scroll.Y + actorPoint.Y + w.BoxThickness(1),
|
Y: p.Y + w.Scroll.Y + actorPoint.Y + w.BoxThickness(1),
|
||||||
}
|
}
|
||||||
resizeTo := actorSize
|
|
||||||
|
|
||||||
// XXX TODO: when an Actor hits the left or top edge and shrinks,
|
// XXX TODO: when an Actor hits the left or top edge and shrinks,
|
||||||
// scrolling to offset that shrink is currently hard to solve.
|
// scrolling to offset that shrink is currently hard to solve.
|
||||||
|
|
|
@ -29,6 +29,7 @@ type Settings struct {
|
||||||
|
|
||||||
// Configurable settings (pkg/windows/settings.go)
|
// Configurable settings (pkg/windows/settings.go)
|
||||||
HorizontalToolbars bool `json:",omitempty"`
|
HorizontalToolbars bool `json:",omitempty"`
|
||||||
|
EnableFeatures bool `json:",omitempty"`
|
||||||
|
|
||||||
// Secret boolprops from balance/boolprops.go
|
// Secret boolprops from balance/boolprops.go
|
||||||
ShowHiddenDoodads bool `json:",omitempty"`
|
ShowHiddenDoodads bool `json:",omitempty"`
|
||||||
|
|
|
@ -22,6 +22,7 @@ type AddEditLevel struct {
|
||||||
// Callback functions.
|
// Callback functions.
|
||||||
OnChangePageTypeAndWallpaper func(pageType level.PageType, wallpaper string)
|
OnChangePageTypeAndWallpaper func(pageType level.PageType, wallpaper string)
|
||||||
OnCreateNewLevel func(*level.Level)
|
OnCreateNewLevel func(*level.Level)
|
||||||
|
OnReload func()
|
||||||
OnCancel func()
|
OnCancel func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +412,11 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
"if the new palette is smaller, some pixels may be\n" +
|
"if the new palette is smaller, some pixels may be\n" +
|
||||||
"lost from your level. OK to continue?",
|
"lost from your level. OK to continue?",
|
||||||
).WithTitle("Change Level Palette").Then(func() {
|
).WithTitle("Change Level Palette").Then(func() {
|
||||||
config.OnCancel()
|
// Install the new level palette.
|
||||||
|
config.EditLevel.ReplacePalette(level.DefaultPalettes[paletteName])
|
||||||
|
if config.OnReload != nil {
|
||||||
|
config.OnReload()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ type Settings struct {
|
||||||
DebugOverlay *bool
|
DebugOverlay *bool
|
||||||
DebugCollision *bool
|
DebugCollision *bool
|
||||||
HorizontalToolbars *bool
|
HorizontalToolbars *bool
|
||||||
|
EnableFeatures *bool
|
||||||
|
|
||||||
// Configuration options.
|
// Configuration options.
|
||||||
SceneName string // name of scene which called this window
|
SceneName string // name of scene which called this window
|
||||||
|
@ -71,13 +72,10 @@ func NewSettingsWindow(cfg Settings) *ui.Window {
|
||||||
FillX: true,
|
FillX: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
///////////
|
// Make the tabs
|
||||||
// Options (index) Tab
|
|
||||||
cfg.makeOptionsTab(tabFrame, Width, Height)
|
cfg.makeOptionsTab(tabFrame, Width, Height)
|
||||||
|
|
||||||
///////////
|
|
||||||
// Controls Tab
|
|
||||||
cfg.makeControlsTab(tabFrame, Width, Height)
|
cfg.makeControlsTab(tabFrame, Width, Height)
|
||||||
|
cfg.makeExperimentalTab(tabFrame, Width, Height)
|
||||||
|
|
||||||
tabFrame.Supervise(cfg.Supervisor)
|
tabFrame.Supervise(cfg.Supervisor)
|
||||||
|
|
||||||
|
@ -455,3 +453,129 @@ func (c Settings) makeControlsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Settings Window "Experimental" Tab
|
||||||
|
func (c Settings) makeExperimentalTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
|
||||||
|
tab := tabFrame.AddTab("Experimental", ui.NewLabel(ui.Label{
|
||||||
|
Text: "Experimental",
|
||||||
|
Font: balance.TabFont,
|
||||||
|
}))
|
||||||
|
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
|
||||||
|
|
||||||
|
// Common click handler for all settings,
|
||||||
|
// so we can write the updated info to disk.
|
||||||
|
onClick := func(ed ui.EventData) error {
|
||||||
|
saveGameSettings()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := []struct {
|
||||||
|
Header string
|
||||||
|
Text string
|
||||||
|
Boolean *bool
|
||||||
|
TextVariable *string
|
||||||
|
PadY int
|
||||||
|
PadX int
|
||||||
|
name string // for special cases
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Header: "Enable Experimental Features",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Text: "The setting below can enable experimental features in this\n" +
|
||||||
|
"game. These are features which are still in development and\n" +
|
||||||
|
"may have unstable or buggy behavior.",
|
||||||
|
PadY: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: "Zoom In/Out",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Text: "This adds Zoom options to the level editor. It has a few\n" +
|
||||||
|
"bugs around scrolling but may be useful already.",
|
||||||
|
PadY: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: "Replace Level Palette",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Text: "This adds an option to the Level Properties dialog to\n" +
|
||||||
|
"replace your level palette with one of the defaults,\n" +
|
||||||
|
"like on the New Level screen. It might not actually work.",
|
||||||
|
PadY: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Boolean: c.EnableFeatures,
|
||||||
|
Text: "Enable experimental features",
|
||||||
|
PadX: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Text: "Restart the game for changes to take effect.",
|
||||||
|
PadY: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
row := row
|
||||||
|
frame := ui.NewFrame("Frame")
|
||||||
|
tab.Pack(frame, ui.Pack{
|
||||||
|
Side: ui.N,
|
||||||
|
FillX: true,
|
||||||
|
PadY: row.PadY,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Headers get their own row to themselves.
|
||||||
|
if row.Header != "" {
|
||||||
|
label := ui.NewLabel(ui.Label{
|
||||||
|
Text: row.Header,
|
||||||
|
Font: balance.LabelFont,
|
||||||
|
})
|
||||||
|
frame.Pack(label, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
PadX: row.PadX,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkboxes get their own row.
|
||||||
|
if row.Boolean != nil {
|
||||||
|
cb := ui.NewCheckbox(row.Text, row.Boolean, ui.NewLabel(ui.Label{
|
||||||
|
Text: row.Text,
|
||||||
|
Font: balance.UIFont,
|
||||||
|
}))
|
||||||
|
cb.Handle(ui.Click, onClick)
|
||||||
|
cb.Supervise(c.Supervisor)
|
||||||
|
|
||||||
|
// Add warning to the toolbars option if the EditMode is currently active.
|
||||||
|
if row.name == "toolbars" && c.SceneName == "Edit" {
|
||||||
|
ui.NewTooltip(cb, ui.Tooltip{
|
||||||
|
Text: "Note: reload your level after changing this option.\n" +
|
||||||
|
"Playtesting and returning will do.",
|
||||||
|
Edge: ui.Top,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.Pack(cb, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
PadX: row.PadX,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any leftover Text gets packed to the left.
|
||||||
|
if row.Text != "" {
|
||||||
|
tf := ui.NewFrame("TextFrame")
|
||||||
|
label := ui.NewLabel(ui.Label{
|
||||||
|
Text: row.Text,
|
||||||
|
Font: balance.UIFont,
|
||||||
|
})
|
||||||
|
tf.Pack(label, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
})
|
||||||
|
frame.Pack(tf, ui.Pack{
|
||||||
|
Side: ui.W,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tab
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user