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:
parent
06dd30893c
commit
a10a09a818
12
Changes.md
12
Changes.md
|
@ -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.
|
||||||
|
|
|
@ -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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
pkg/shell.go
15
pkg/shell.go
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ Package usercfg has functions around the user's Game Settings.
|
||||||
|
|
||||||
Other places in the codebase to look for its related functionality:
|
Other places in the codebase to look for its related functionality:
|
||||||
|
|
||||||
- pkg/windows/settings.go: the Settings Window is the UI owner of
|
- pkg/windows/settings.go: the Settings Window is the UI owner of
|
||||||
this feature, it adjusts the usercfg.Current struct and Saves the
|
this feature, it adjusts the usercfg.Current struct and Saves the
|
||||||
changes to disk.
|
changes to disk.
|
||||||
*/
|
*/
|
||||||
|
@ -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
372
pkg/windows/cheats_menu.go
Normal 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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user