doodle/pkg/windows/cheats_menu.go
Noah Petherbridge a10a09a818 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`)
2023-01-02 12:36:12 -08:00

373 lines
8.1 KiB
Go

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
}