From a10a09a8183d01ea935b22a91cf82f733a1be25b Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Mon, 2 Jan 2023 12:36:12 -0800 Subject: [PATCH] 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`) --- Changes.md | 12 + pkg/balance/cheats.go | 38 ++++ pkg/balance/theme.go | 3 + pkg/branding/branding.go | 2 +- pkg/cheats.go | 37 +++ pkg/commands.go | 21 +- pkg/doodle.go | 3 + pkg/play_scene.go | 13 +- pkg/play_scene_menubar.go | 21 +- pkg/play_scene_touch.go | 4 +- pkg/shell.go | 15 ++ pkg/uix/magic-form/magic_form.go | 8 +- pkg/usercfg/usercfg.go | 7 +- pkg/windows/cheats_menu.go | 372 +++++++++++++++++++++++++++++++ pkg/windows/settings.go | 47 +++- 15 files changed, 580 insertions(+), 23 deletions(-) create mode 100644 pkg/windows/cheats_menu.go diff --git a/Changes.md b/Changes.md index 5cf1187..f8f9f05 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,17 @@ # 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) This release brings a handful of minor new features to the game. diff --git a/pkg/balance/cheats.go b/pkg/balance/cheats.go index 38827f4..fb12527 100644 --- a/pkg/balance/cheats.go +++ b/pkg/balance/cheats.go @@ -1,5 +1,7 @@ package balance +import magicform "git.kirsle.net/SketchyMaze/doodle/pkg/uix/magic-form" + // Store a copy of the PlayerCharacterDoodad original value. var playerCharacterDefault string @@ -50,3 +52,39 @@ var CheatActors = map[string]string{ "megaton weight": "anvil", "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", + }, +} diff --git a/pkg/balance/theme.go b/pkg/balance/theme.go index 0239cbe..9054ca2 100644 --- a/pkg/balance/theme.go +++ b/pkg/balance/theme.go @@ -227,6 +227,9 @@ var ( DoodadDropperCols = 6 // rows/columns of buttons DoodadDropperRows = 3 + // CheatsMenu window settings. + CheatsMenuBackground = render.RGBA(0, 153, 153, 255) + // Button styles, customized in init(). ButtonPrimary = style.DefaultButton ButtonDanger = style.DefaultButton diff --git a/pkg/branding/branding.go b/pkg/branding/branding.go index 6492910..ea1551b 100644 --- a/pkg/branding/branding.go +++ b/pkg/branding/branding.go @@ -9,7 +9,7 @@ import ( const ( AppName = "Sketchy Maze" Summary = "A drawing-based maze game" - Version = "0.13.1" + Version = "0.13.2" Website = "https://www.sketchymaze.com" Copyright = "2022 Noah Petherbridge" Byline = "a game by Noah Petherbridge." diff --git a/pkg/cheats.go b/pkg/cheats.go index 6397495..4e0bd69 100644 --- a/pkg/cheats.go +++ b/pkg/cheats.go @@ -7,11 +7,48 @@ import ( "git.kirsle.net/SketchyMaze/doodle/pkg/balance" "git.kirsle.net/SketchyMaze/doodle/pkg/modal" "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 // 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 // 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 diff --git a/pkg/commands.go b/pkg/commands.go index 4fa3731..44356fd 100644 --- a/pkg/commands.go +++ b/pkg/commands.go @@ -315,13 +315,14 @@ func (c Command) BoolProp(d *Doodle) error { } if len(c.Args) != 2 { - return errors.New("Usage: boolProp [true or false]") + return errors.New("Usage: boolProp [true, false, flip]") } var ( name = c.Args[0] value = c.Args[1] truthy = value[0] == 't' || value[0] == 'T' || value[0] == '1' + flip = value == "flip" ok = true ) @@ -331,16 +332,28 @@ func (c Command) BoolProp(d *Doodle) error { d.Debug = truthy case "DebugOverlay": case "DO": - DebugOverlay = truthy + if flip { + DebugOverlay = !DebugOverlay + } else { + DebugOverlay = truthy + } case "DebugCollision": case "DC": - DebugCollision = truthy + if flip { + DebugCollision = !DebugCollision + } else { + DebugCollision = truthy + } default: ok = false } if ok { - d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy)) + if flip { + d.Flash("Toggled boolProp %s", name) + } else { + d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy)) + } } else { // Try the global boolProps in balance package. if err := balance.BoolProp(name, truthy); err != nil { diff --git a/pkg/doodle.go b/pkg/doodle.go index c794697..5c7fd0a 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -255,6 +255,9 @@ func (d *Doodle) MakeSettingsWindow(supervisor *ui.Supervisor) *ui.Window { OnApply: func() { }, + OnOpenCheatsWindow: func() *ui.Window { + return d.MakeCheatsWindow(supervisor) + }, // Boolean checkbox bindings DebugOverlay: &DebugOverlay, diff --git a/pkg/play_scene.go b/pkg/play_scene.go index f8274ae..e26429b 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -54,7 +54,7 @@ type PlayScene struct { cheated bool // user has entered a cheat code while playing // UI widgets. - supervisor *ui.Supervisor + Supervisor *ui.Supervisor screen *ui.Frame // A window sized invisible frame to position UI elements. menubar *ui.MenuBar editButton *ui.Button @@ -87,6 +87,9 @@ type PlayScene struct { invenItems []string // item list invenDoodads map[string]*uix.Canvas + // Cheats window + cheatsWindow *ui.Window + // Elapsed Time frame. timerFrame *ui.Frame timerPerfectImage *ui.Image @@ -109,7 +112,7 @@ func (s *PlayScene) Name() string { func (s *PlayScene) Setup(d *Doodle) error { s.d = d s.scripting = scripting.NewSupervisor() - s.supervisor = ui.NewSupervisor() + s.Supervisor = ui.NewSupervisor() // Show the loading screen. loadscreen.ShowWithProgress() @@ -167,7 +170,7 @@ func (s *PlayScene) setupAsync(d *Doodle) error { s.EditLevel() return nil }) - s.supervisor.Add(s.editButton) + s.Supervisor.Add(s.editButton) // Set up the inventory HUD. s.setupInventoryHud() @@ -751,7 +754,7 @@ func (s *PlayScene) Loop(d *Doodle, ev *event.State) error { // Update the timer. s.timerLabel.Text = savegame.FormatDuration(time.Since(s.startTime)) - s.supervisor.Loop(ev) + s.Supervisor.Loop(ev) // Has the window been resized? if ev.WindowResized || s.drawing.Point().IsZero() { @@ -845,7 +848,7 @@ func (s *PlayScene) Draw(d *Doodle) error { s.DrawTouchable() // Let Supervisor draw menus - s.supervisor.Present(d.Engine) + s.Supervisor.Present(d.Engine) return nil } diff --git a/pkg/play_scene_menubar.go b/pkg/play_scene_menubar.go index 2f53c42..3b8ffd7 100644 --- a/pkg/play_scene_menubar.go +++ b/pkg/play_scene_menubar.go @@ -20,7 +20,7 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar { // TODO: de-duplicate code from MainScene if u.winLevelPacks == nil { u.winLevelPacks = windows.NewLevelPackWindow(windows.LevelPack{ - Supervisor: u.supervisor, + Supervisor: u.Supervisor, Engine: d.Engine, OnPlayLevel: func(lp levelpack.LevelPack, which levelpack.Level) { @@ -66,7 +66,7 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar { levelMenu.AddSeparator() levelMenu.AddItemAccel("New viewport", "v", func() { pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{ - Supervisor: u.supervisor, + Supervisor: u.Supervisor, Engine: u.d.Engine, Level: u.Level, 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) return menu diff --git a/pkg/play_scene_touch.go b/pkg/play_scene_touch.go index 0dd8333..8b435c0 100644 --- a/pkg/play_scene_touch.go +++ b/pkg/play_scene_touch.go @@ -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 // clicking on the menus doesn't make the character move or jump. - if cursor.Inside(s.menubar.Rect()) || s.supervisor.GetModal() != nil || - s.supervisor.IsPointInWindow(cursor) { + if cursor.Inside(s.menubar.Rect()) || s.Supervisor.GetModal() != nil || + s.Supervisor.IsPointInWindow(cursor) { return } diff --git a/pkg/shell.go b/pkg/shell.go index 72b1c98..8859858 100644 --- a/pkg/shell.go +++ b/pkg/shell.go @@ -2,6 +2,7 @@ package doodle import ( "bytes" + "errors" "fmt" "strings" @@ -44,6 +45,20 @@ func (d *Doodle) PromptPre(question string, prefilled string, callback func(stri 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. type Shell struct { parent *Doodle diff --git a/pkg/uix/magic-form/magic_form.go b/pkg/uix/magic-form/magic_form.go index c016a27..a10ebc7 100644 --- a/pkg/uix/magic-form/magic_form.go +++ b/pkg/uix/magic-form/magic_form.go @@ -152,9 +152,15 @@ func (form Form) Create(into *ui.Frame, fields []Field) { btn.Compute(form.Engine) 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{ Side: ui.W, - PadX: 4, + PadX: 2, PadY: 2, }) } diff --git a/pkg/usercfg/usercfg.go b/pkg/usercfg/usercfg.go index 39af65d..55cf56a 100644 --- a/pkg/usercfg/usercfg.go +++ b/pkg/usercfg/usercfg.go @@ -3,9 +3,9 @@ Package usercfg has functions around the user's Game Settings. Other places in the codebase to look for its related functionality: -- pkg/windows/settings.go: the Settings Window is the UI owner of - this feature, it adjusts the usercfg.Current struct and Saves the - changes to disk. + - pkg/windows/settings.go: the Settings Window is the UI owner of + this feature, it adjusts the usercfg.Current struct and Saves the + changes to disk. */ package usercfg @@ -45,6 +45,7 @@ type Settings struct { ShowHiddenDoodads bool `json:",omitempty"` WriteLockOverride bool `json:",omitempty"` JSONIndent bool `json:",omitempty"` + EnableCheatsMenu bool `json:",omitempty"` // Bookkeeping. UpdatedAt time.Time diff --git a/pkg/windows/cheats_menu.go b/pkg/windows/cheats_menu.go new file mode 100644 index 0000000..3910c50 --- /dev/null +++ b/pkg/windows/cheats_menu.go @@ -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 +} diff --git a/pkg/windows/settings.go b/pkg/windows/settings.go index bb0958c..b15ae54 100644 --- a/pkg/windows/settings.go +++ b/pkg/windows/settings.go @@ -7,6 +7,7 @@ import ( "git.kirsle.net/SketchyMaze/doodle/pkg/gamepad" "git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/native" + "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/SketchyMaze/doodle/pkg/userdir" @@ -32,9 +33,13 @@ type Settings struct { ControllerStyle *int // Configuration options. - SceneName string // name of scene which called this window - ActiveTab string // specify the tab to open - OnApply func() + SceneName string // name of scene which called this window + ActiveTab string // specify the tab to open + 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. @@ -452,6 +457,42 @@ func (c Settings) makeExperimentalTab(tabFrame *ui.TabFrame, Width, Height int) Label: "Restart the game for changes to take effect.", 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