Cheats Menu UI

* Added a Cheats Menu UI accessible from the Settings window's "Experimental"
  tab and from there you can enable the Cheats Menu from the "Help" screen of
  the gameplay mode.
* Commonly used cheats all have corresponding buttons to click on, especially
  helpful for touchscreen devices like the Pinephone where keyboard input
  doesn't always work reliably.
* The buttons in the Cheats Menu just automate entry of the cheat commands.
* `boolProp` command has a new `flip` option to toggle their value (e.g.
  `boolProp show-hidden-doodads flip`)
This commit is contained in:
Noah 2023-01-02 12:36:12 -08:00
parent 06dd30893c
commit a10a09a818
15 changed files with 580 additions and 23 deletions

View File

@ -1,5 +1,17 @@
# Changes # Changes
## v0.13.2 (TBD)
* In the level editor, you can now use the Pan Tool to access the actor
properties of doodads you've dropped into your level. Similar to the
Actor Tool, when you mouse-over an actor on your level it will highlight
in a grey box and a gear icon in the corner can be clicked to access
its properties. Making the properties available for the Pan Tool can
help with touchscreen devices, where it is difficult to touch the
properties button without accidentally dragging the actor elsewhere
on your level as might happen with the Actor Tool!
* Start distributing AppImage releases for GNU/Linux (64-bit and 32-bit)
## v0.13.1 (Oct 10 2022) ## v0.13.1 (Oct 10 2022)
This release brings a handful of minor new features to the game. This release brings a handful of minor new features to the game.

View File

@ -1,5 +1,7 @@
package balance package balance
import magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form"
// Store a copy of the PlayerCharacterDoodad original value. // Store a copy of the PlayerCharacterDoodad original value.
var playerCharacterDefault string var playerCharacterDefault string
@ -50,3 +52,39 @@ var CheatActors = map[string]string{
"megaton weight": "anvil", "megaton weight": "anvil",
"play as thief": "thief", "play as thief": "thief",
} }
// Options for the "Play as:" drop-down in the Cheat Menu window.
var CheatMenuActors = []magicform.Option{
{
Value: "",
Label: "Play as . . .",
},
{
Value: "boy.doodad",
Label: "Boy",
},
{
Value: "thief.doodad",
Label: "Thief",
},
{
Value: "azu-blu.doodad",
Label: "Azulian",
},
{
Value: "bird-red.doodad",
Label: "Bird",
},
{
Value: "crusher.doodad",
Label: "Crusher",
},
{
Value: "snake.doodad",
Label: "Snake",
},
{
Value: "anvil.doodad",
Label: "Anvil",
},
}

View File

@ -227,6 +227,9 @@ var (
DoodadDropperCols = 6 // rows/columns of buttons DoodadDropperCols = 6 // rows/columns of buttons
DoodadDropperRows = 3 DoodadDropperRows = 3
// CheatsMenu window settings.
CheatsMenuBackground = render.RGBA(0, 153, 153, 255)
// Button styles, customized in init(). // Button styles, customized in init().
ButtonPrimary = style.DefaultButton ButtonPrimary = style.DefaultButton
ButtonDanger = style.DefaultButton ButtonDanger = style.DefaultButton

View File

@ -9,7 +9,7 @@ import (
const ( const (
AppName = "Sketchy Maze" AppName = "Sketchy Maze"
Summary = "A drawing-based maze game" Summary = "A drawing-based maze game"
Version = "0.13.1" Version = "0.13.2"
Website = "https://www.sketchymaze.com" Website = "https://www.sketchymaze.com"
Copyright = "2022 Noah Petherbridge" Copyright = "2022 Noah Petherbridge"
Byline = "a game by Noah Petherbridge." Byline = "a game by Noah Petherbridge."

View File

@ -7,11 +7,48 @@ import (
"git.kirsle.net/SketchyMaze/doodle/pkg/balance" "git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/modal" "git.kirsle.net/SketchyMaze/doodle/pkg/modal"
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen" "git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
"git.kirsle.net/SketchyMaze/doodle/pkg/windows"
"git.kirsle.net/go/ui"
) )
// IsDefaultPlayerCharacter checks whether the DefaultPlayerCharacter doodad has // IsDefaultPlayerCharacter checks whether the DefaultPlayerCharacter doodad has
// been modified // been modified
// MakeCheatsWindow initializes the windows/cheats_menu.go window from anywhere you need it,
// binding all the variables in. If you pass a nil Supervisor, this function will attempt to
// find one based on your Scene and
func (d *Doodle) MakeCheatsWindow(supervisor *ui.Supervisor) *ui.Window {
// If not given a supervisor, try and find one.
if supervisor == nil {
if v, err := d.FindLikelySupervisor(); err != nil {
d.FlashError("Couldn't make cheats window: %s", err)
return nil
} else {
supervisor = v
}
}
cfg := windows.CheatsMenu{
Supervisor: supervisor,
Engine: d.Engine,
SceneName: func() string {
return d.Scene.Name()
},
RunCommand: func(command string) {
d.shell.Execute(command)
},
OnSetPlayerCharacter: func(doodad string) {
if scene, ok := d.Scene.(*PlayScene); ok {
scene.SetPlayerCharacter(doodad)
} else {
shmem.FlashError("This only works during Play Mode.")
}
},
}
return windows.MakeCheatsMenu(cfg)
}
// SetPlayerCharacter -- this is designed to be called in-game with the developer // SetPlayerCharacter -- this is designed to be called in-game with the developer
// console. Sets your player character to whatever doodad you want, not just the // console. Sets your player character to whatever doodad you want, not just the
// few that have cheat codes. If you set an invalid filename, you become the // few that have cheat codes. If you set an invalid filename, you become the

View File

@ -315,13 +315,14 @@ func (c Command) BoolProp(d *Doodle) error {
} }
if len(c.Args) != 2 { if len(c.Args) != 2 {
return errors.New("Usage: boolProp <name> [true or false]") return errors.New("Usage: boolProp <name> [true, false, flip]")
} }
var ( var (
name = c.Args[0] name = c.Args[0]
value = c.Args[1] value = c.Args[1]
truthy = value[0] == 't' || value[0] == 'T' || value[0] == '1' truthy = value[0] == 't' || value[0] == 'T' || value[0] == '1'
flip = value == "flip"
ok = true ok = true
) )
@ -331,16 +332,28 @@ func (c Command) BoolProp(d *Doodle) error {
d.Debug = truthy d.Debug = truthy
case "DebugOverlay": case "DebugOverlay":
case "DO": case "DO":
if flip {
DebugOverlay = !DebugOverlay
} else {
DebugOverlay = truthy DebugOverlay = truthy
}
case "DebugCollision": case "DebugCollision":
case "DC": case "DC":
if flip {
DebugCollision = !DebugCollision
} else {
DebugCollision = truthy DebugCollision = truthy
}
default: default:
ok = false ok = false
} }
if ok { if ok {
if flip {
d.Flash("Toggled boolProp %s", name)
} else {
d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy)) d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy))
}
} else { } else {
// Try the global boolProps in balance package. // Try the global boolProps in balance package.
if err := balance.BoolProp(name, truthy); err != nil { if err := balance.BoolProp(name, truthy); err != nil {

View File

@ -255,6 +255,9 @@ func (d *Doodle) MakeSettingsWindow(supervisor *ui.Supervisor) *ui.Window {
OnApply: func() { OnApply: func() {
}, },
OnOpenCheatsWindow: func() *ui.Window {
return d.MakeCheatsWindow(supervisor)
},
// Boolean checkbox bindings // Boolean checkbox bindings
DebugOverlay: &DebugOverlay, DebugOverlay: &DebugOverlay,

View File

@ -54,7 +54,7 @@ type PlayScene struct {
cheated bool // user has entered a cheat code while playing cheated bool // user has entered a cheat code while playing
// UI widgets. // UI widgets.
supervisor *ui.Supervisor Supervisor *ui.Supervisor
screen *ui.Frame // A window sized invisible frame to position UI elements. screen *ui.Frame // A window sized invisible frame to position UI elements.
menubar *ui.MenuBar menubar *ui.MenuBar
editButton *ui.Button editButton *ui.Button
@ -87,6 +87,9 @@ type PlayScene struct {
invenItems []string // item list invenItems []string // item list
invenDoodads map[string]*uix.Canvas invenDoodads map[string]*uix.Canvas
// Cheats window
cheatsWindow *ui.Window
// Elapsed Time frame. // Elapsed Time frame.
timerFrame *ui.Frame timerFrame *ui.Frame
timerPerfectImage *ui.Image timerPerfectImage *ui.Image
@ -109,7 +112,7 @@ func (s *PlayScene) Name() string {
func (s *PlayScene) Setup(d *Doodle) error { func (s *PlayScene) Setup(d *Doodle) error {
s.d = d s.d = d
s.scripting = scripting.NewSupervisor() s.scripting = scripting.NewSupervisor()
s.supervisor = ui.NewSupervisor() s.Supervisor = ui.NewSupervisor()
// Show the loading screen. // Show the loading screen.
loadscreen.ShowWithProgress() loadscreen.ShowWithProgress()
@ -167,7 +170,7 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
s.EditLevel() s.EditLevel()
return nil return nil
}) })
s.supervisor.Add(s.editButton) s.Supervisor.Add(s.editButton)
// Set up the inventory HUD. // Set up the inventory HUD.
s.setupInventoryHud() s.setupInventoryHud()
@ -751,7 +754,7 @@ func (s *PlayScene) Loop(d *Doodle, ev *event.State) error {
// Update the timer. // Update the timer.
s.timerLabel.Text = savegame.FormatDuration(time.Since(s.startTime)) s.timerLabel.Text = savegame.FormatDuration(time.Since(s.startTime))
s.supervisor.Loop(ev) s.Supervisor.Loop(ev)
// Has the window been resized? // Has the window been resized?
if ev.WindowResized || s.drawing.Point().IsZero() { if ev.WindowResized || s.drawing.Point().IsZero() {
@ -845,7 +848,7 @@ func (s *PlayScene) Draw(d *Doodle) error {
s.DrawTouchable() s.DrawTouchable()
// Let Supervisor draw menus // Let Supervisor draw menus
s.supervisor.Present(d.Engine) s.Supervisor.Present(d.Engine)
return nil return nil
} }

View File

@ -20,7 +20,7 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar {
// TODO: de-duplicate code from MainScene // TODO: de-duplicate code from MainScene
if u.winLevelPacks == nil { if u.winLevelPacks == nil {
u.winLevelPacks = windows.NewLevelPackWindow(windows.LevelPack{ u.winLevelPacks = windows.NewLevelPackWindow(windows.LevelPack{
Supervisor: u.supervisor, Supervisor: u.Supervisor,
Engine: d.Engine, Engine: d.Engine,
OnPlayLevel: func(lp levelpack.LevelPack, which levelpack.Level) { OnPlayLevel: func(lp levelpack.LevelPack, which levelpack.Level) {
@ -66,7 +66,7 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar {
levelMenu.AddSeparator() levelMenu.AddSeparator()
levelMenu.AddItemAccel("New viewport", "v", func() { levelMenu.AddItemAccel("New viewport", "v", func() {
pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{ pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{
Supervisor: u.supervisor, Supervisor: u.Supervisor,
Engine: u.d.Engine, Engine: u.d.Engine,
Level: u.Level, Level: u.Level,
Event: u.d.event, Event: u.d.event,
@ -76,9 +76,22 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar {
}) })
} }
d.MakeHelpMenu(menu, u.supervisor) helpMenu := d.MakeHelpMenu(menu, u.Supervisor)
if usercfg.Current.EnableCheatsMenu {
helpMenu.AddSeparator()
helpMenu.AddItem("Cheats Menu", func() {
if u.cheatsWindow != nil {
u.cheatsWindow.Hide()
u.cheatsWindow.Destroy()
u.cheatsWindow = nil
}
menu.Supervise(u.supervisor) u.cheatsWindow = u.d.MakeCheatsWindow(u.Supervisor)
u.cheatsWindow.Show()
})
}
menu.Supervise(u.Supervisor)
menu.Compute(d.Engine) menu.Compute(d.Engine)
return menu return menu

View File

@ -30,8 +30,8 @@ func (s *PlayScene) LoopTouchable(ev *event.State) {
// Don't do any of this if the mouse is over the menu bar, so // Don't do any of this if the mouse is over the menu bar, so
// clicking on the menus doesn't make the character move or jump. // clicking on the menus doesn't make the character move or jump.
if cursor.Inside(s.menubar.Rect()) || s.supervisor.GetModal() != nil || if cursor.Inside(s.menubar.Rect()) || s.Supervisor.GetModal() != nil ||
s.supervisor.IsPointInWindow(cursor) { s.Supervisor.IsPointInWindow(cursor) {
return return
} }

View File

@ -2,6 +2,7 @@ package doodle
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"strings" "strings"
@ -44,6 +45,20 @@ func (d *Doodle) PromptPre(question string, prefilled string, callback func(stri
d.shell.Open = true d.shell.Open = true
} }
// FindLikelySupervisor will locate a most likely ui.Supervisor depending on the current Scene,
// if it understands the Scene and knows where it keeps its Supervisor.
func (d *Doodle) FindLikelySupervisor() (*ui.Supervisor, error) {
switch scene := d.Scene.(type) {
case *EditorScene:
return scene.UI.Supervisor, nil
case *PlayScene:
return scene.Supervisor, nil
case *MainScene:
return scene.Supervisor, nil
}
return nil, errors.New("couldn't find a Supervisor")
}
// Shell implements the developer console in-game. // Shell implements the developer console in-game.
type Shell struct { type Shell struct {
parent *Doodle parent *Doodle

View File

@ -152,9 +152,15 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
btn.Compute(form.Engine) btn.Compute(form.Engine)
form.Supervisor.Add(btn) form.Supervisor.Add(btn)
// Tooltip? TODO - make nicer.
if row.Tooltip.Text != "" || row.Tooltip.TextVariable != nil {
tt := ui.NewTooltip(btn, row.Tooltip)
tt.Supervise(form.Supervisor)
}
frame.Pack(btn, ui.Pack{ frame.Pack(btn, ui.Pack{
Side: ui.W, Side: ui.W,
PadX: 4, PadX: 2,
PadY: 2, PadY: 2,
}) })
} }

View File

@ -45,6 +45,7 @@ type Settings struct {
ShowHiddenDoodads bool `json:",omitempty"` ShowHiddenDoodads bool `json:",omitempty"`
WriteLockOverride bool `json:",omitempty"` WriteLockOverride bool `json:",omitempty"`
JSONIndent bool `json:",omitempty"` JSONIndent bool `json:",omitempty"`
EnableCheatsMenu bool `json:",omitempty"`
// Bookkeeping. // Bookkeeping.
UpdatedAt time.Time UpdatedAt time.Time

372
pkg/windows/cheats_menu.go Normal file
View File

@ -0,0 +1,372 @@
package windows
import (
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form"
"git.kirsle.net/SketchyMaze/doodle/pkg/usercfg"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// CheatsMenu window.
type CheatsMenu struct {
// Settings passed in by doodle
Supervisor *ui.Supervisor
Engine render.Engine
// SceneName: the caller will provide a fresh SceneName since
// the cheats window could span multiple scenes.
SceneName func() string
// Window wants to run a developer shell command (e.g. cheat codes).
RunCommand func(string)
OnSetPlayerCharacter func(string)
}
// MakeCheatsMenu initializes a settings window for any scene.
// The window width/height are the actual SDL2 window dimensions.
func MakeCheatsMenu(cfg CheatsMenu) *ui.Window {
var (
// Application window width/height to center our window
// _, h = shmem.CurrentRenderEngine.WindowSize()
)
win := NewCheatsWindow(cfg)
win.Compute(cfg.Engine)
win.Supervise(cfg.Supervisor)
// Center the window.
// size := win.Size()
win.MoveTo(render.Point{
X: 20,
Y: 40,
})
return win
}
// NewCheatsWindow initializes the window.
func NewCheatsWindow(cfg CheatsMenu) *ui.Window {
var (
Width = 200
Height = 300
)
window := ui.NewWindow("Cheats Menu")
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: Width,
Height: Height,
Background: balance.CheatsMenuBackground,
})
///////////
// Tab Bar
tabFrame := ui.NewTabFrame("Tab Frame")
tabFrame.SetBackground(balance.CheatsMenuBackground)
window.Pack(tabFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
// Make the tabs
cfg.makePlayModeTab(tabFrame, Width, Height)
cfg.makeMiscTab(tabFrame, Width, Height)
tabFrame.Supervise(cfg.Supervisor)
return window
}
// Cheats Menu "Play Mode" Tab
func (c CheatsMenu) makePlayModeTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
tab := tabFrame.AddTab("Gameplay", ui.NewLabel(ui.Label{
Text: "Gameplay",
Font: balance.TabFont,
}))
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
// Run a command on the developer shell.
run := func(command string) {
if c.RunCommand != nil {
c.RunCommand(command)
} else {
shmem.FlashError("CheatsMenu: RunCommand() handler not available")
}
}
// Dummy variable for the "play as" dropdown.
var playAs string
form := magicform.Form{
Supervisor: c.Supervisor,
Engine: c.Engine,
Vertical: true,
LabelWidth: 90,
PadY: 0,
PadX: 0,
}
form.Create(tab, []magicform.Field{
{
Label: "These cheats are available\n" +
"only during level gameplay.",
Font: balance.UIFont,
},
{
Label: "Play as:",
TextVariable: &playAs,
Options: balance.CheatMenuActors,
Font: balance.UIFont,
OnSelect: func(v interface{}) {
doodad := v.(string)
if c.OnSetPlayerCharacter != nil {
c.OnSetPlayerCharacter(doodad)
} else {
shmem.FlashError("OnSetPlayerCharacter(%s): handler not ready", doodad)
}
},
},
{
Buttons: []magicform.Field{
{
Label: "God Mode",
Font: balance.SmallFont,
ButtonStyle: &balance.ButtonDanger,
Tooltip: ui.Tooltip{
Text: "Makes you invulnerable to damage and fire.",
},
OnClick: func() {
run(balance.CheatGodMode)
},
},
{
Label: "Show hidden actors",
Font: balance.SmallFont,
OnClick: func() {
run(balance.CheatShowAllActors)
},
},
},
},
{
Label: "Inventory",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Give Keys",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "Get all four colored keys and\n99x small keys",
},
OnClick: func() {
run(balance.CheatGiveKeys)
},
},
{
Label: "Give Gems",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "Get 1x of each of the four Gemstones.",
},
OnClick: func() {
run(balance.CheatGiveGems)
},
},
{
Label: "Drop All",
Font: balance.SmallFont,
ButtonStyle: &balance.ButtonDanger,
Tooltip: ui.Tooltip{
Text: "Remove ALL items from your inventory.",
},
OnClick: func() {
run(balance.CheatDropItems)
},
},
},
},
{
Label: "Physics",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Antigravity",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "Allows free movement in four directions",
},
OnClick: func() {
run(balance.CheatAntigravity)
},
},
{
Label: "NoClip",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "Toggle physical collision\n" +
"checks with level and actors.",
},
OnClick: func() {
run(balance.CheatNoclip)
},
},
},
},
{
Buttons: []magicform.Field{
{
Label: "Skip this level",
Font: balance.SmallFont,
ButtonStyle: &balance.ButtonDanger,
Tooltip: ui.Tooltip{
Text: "Consider the current level a win.",
},
OnClick: func() {
run(balance.CheatSkipLevel)
},
},
},
},
})
return tab
}
// Cheats Menu "Misc" Tab
func (c CheatsMenu) makeMiscTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
tab := tabFrame.AddTab("Misc", ui.NewLabel(ui.Label{
Text: "Misc",
Font: balance.TabFont,
}))
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
// Run a command on the developer shell.
run := func(command string) {
if c.RunCommand != nil {
c.RunCommand(command)
} else {
shmem.FlashError("CheatsMenu: RunCommand() handler not available")
}
}
form := magicform.Form{
Supervisor: c.Supervisor,
Engine: c.Engine,
Vertical: true,
LabelWidth: 90,
PadY: 0,
PadX: 0,
}
form.Create(tab, []magicform.Field{
{
Label: "Enable cheats menu",
BoolVariable: &usercfg.Current.EnableCheatsMenu,
Tooltip: ui.Tooltip{
Text: "Enables a Help->Cheats Menu during gameplay.",
},
OnClick: func() {
saveGameSettings()
},
},
{
Label: "Level Editor",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Show hidden doodads",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "Enable hidden built-in doodads (such as Boy)\n" +
"to be used in the Level Editor.",
Edge: ui.Bottom,
},
OnClick: func() {
// Like `boolProp show-hidden-doodads true`
var bp = "show-hidden-doodads"
if v, err := balance.GetBoolProp(bp); err == nil {
v = !v
balance.BoolProp(bp, v)
if v {
shmem.Flash("Hidden doodads will appear when you next reload the level editor.")
} else {
shmem.Flash("Hidden doodads are again hidden from the level editor.")
}
}
},
},
},
},
{
Label: "Testing",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Load Screen",
Font: balance.SmallFont,
OnClick: func() {
run(balance.CheatDebugLoadScreen)
},
},
{
Label: "Wait Screen",
Font: balance.SmallFont,
OnClick: func() {
run(balance.CheatDebugWaitScreen)
},
},
},
},
{
Label: "Level Progression",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Unlock all levels",
Font: balance.SmallFont,
Tooltip: ui.Tooltip{
Text: "For this play session, any level may be opened\n" +
"from Story Mode regardless of the padlock icon.",
},
OnClick: func() {
run(balance.CheatUnlockLevels)
},
},
},
},
{
Label: "Debugging",
Font: balance.LabelFont,
},
{
Buttons: []magicform.Field{
{
Label: "Debug overlay",
Font: balance.SmallFont,
OnClick: func() {
run("boolprop DO flip")
},
},
{
Label: "Show hitboxes",
Font: balance.SmallFont,
OnClick: func() {
run("boolprop DC flip")
},
},
},
},
})
return tab
}

View File

@ -7,6 +7,7 @@ import (
"git.kirsle.net/SketchyMaze/doodle/pkg/gamepad" "git.kirsle.net/SketchyMaze/doodle/pkg/gamepad"
"git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/native" "git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form" magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form"
"git.kirsle.net/SketchyMaze/doodle/pkg/usercfg" "git.kirsle.net/SketchyMaze/doodle/pkg/usercfg"
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir" "git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
@ -35,6 +36,10 @@ type Settings struct {
SceneName string // name of scene which called this window SceneName string // name of scene which called this window
ActiveTab string // specify the tab to open ActiveTab string // specify the tab to open
OnApply func() OnApply func()
OnOpenCheatsWindow func() *ui.Window // user opens the Cheats Menu
// The Settings window owns the Cheats window to ensure only one opens at a time.
cheatsWindow *ui.Window
} }
// MakeSettingsWindow initializes a settings window for any scene. // MakeSettingsWindow initializes a settings window for any scene.
@ -452,6 +457,42 @@ func (c Settings) makeExperimentalTab(tabFrame *ui.TabFrame, Width, Height int)
Label: "Restart the game for changes to take effect.", Label: "Restart the game for changes to take effect.",
Font: balance.UIFont, Font: balance.UIFont,
}, },
{
Label: "Cheat Codes",
Font: balance.LabelFont,
},
{
Label: "The ` (or ~) key will open a developer console into which you\n" +
"can enter cheat codes (see your guidebook). For touch-only\n" +
"devices, most of the useful cheats may be toggled via the\n" +
"Cheats Menu which you can launch by clicking below:",
Font: balance.UIFont,
},
{
Buttons: []magicform.Field{
{
ButtonStyle: &balance.ButtonPrimary,
Label: "Open Cheats Window",
Font: balance.UIFont.Update(render.Text{
PadY: 0,
}),
OnClick: func() {
if c.OnOpenCheatsWindow != nil {
if c.cheatsWindow != nil {
c.cheatsWindow.Hide()
c.cheatsWindow.Destroy()
c.cheatsWindow = nil
}
c.cheatsWindow = c.OnOpenCheatsWindow()
c.cheatsWindow.Show()
} else {
shmem.FlashError("OnOpenCheatsWindow not handled")
}
},
},
},
},
}) })
return tab return tab