Doodad CLI Tool Features; Write Lock and Hidden
* The `doodad` CLI tool got a lot of new commands: * `doodad show` to verbosely print details about Levels and Doodads. * `edit-level` and `edit-doodad` to update details about Levels and Doodads, such as their Title, Author, page type and size, etc. * Doodads gain a `Hidden bool` that hides them from the palette in Editor Mode. The player character (Blue Azulian) is Hidden. * Add some boolProps to the balance/ package and made a dynamic system to easily configure these with the in-game dev console. * Command: `boolProp list` returns available balance.boolProps * `boolProp <name>` returns the current value. * `boolProp <name> <true or false>` sets the value. * The new boolProps are: * showAllDoodads: enable Hidden doodads on the palette UI (NOTE: reload the editor to take effect) * writeLockOverride: edit files that are write locked anyway * prettyJSON: pretty-format the JSON files saved by the game.
This commit is contained in:
parent
6476a67faf
commit
fd649b7ab1
124
cmd/doodad/commands/edit_doodad.go
Normal file
124
cmd/doodad/commands/edit_doodad.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// EditDoodad allows writing doodad metadata.
|
||||
var EditDoodad cli.Command
|
||||
|
||||
func init() {
|
||||
EditDoodad = cli.Command{
|
||||
Name: "edit-doodad",
|
||||
Usage: "update metadata for a Doodad file",
|
||||
ArgsUsage: "<filename.doodad>",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: "set the doodad title",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "author",
|
||||
Usage: "set the doodad author",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "hide",
|
||||
Usage: "Hide the doodad from the palette",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "unhide",
|
||||
Usage: "Unhide the doodad from the palette",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "lock",
|
||||
Usage: "write-lock the level file",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "unlock",
|
||||
Usage: "remove the write-lock on the level file",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return cli.NewExitError(
|
||||
"Usage: doodad edit-doodad <filename.doodad>",
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
var filenames = c.Args()
|
||||
for _, filename := range filenames {
|
||||
if err := editDoodad(c, filename); err != nil {
|
||||
log.Error("%s: %s", filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func editDoodad(c *cli.Context, filename string) error {
|
||||
var modified bool
|
||||
|
||||
dd, err := doodads.LoadJSON(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to load %s: %s", filename, err)
|
||||
}
|
||||
|
||||
log.Info("File: %s", filename)
|
||||
|
||||
/***************************
|
||||
* Update level properties *
|
||||
***************************/
|
||||
|
||||
if c.String("title") != "" {
|
||||
dd.Title = c.String("title")
|
||||
log.Info("Set title: %s", dd.Title)
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.String("author") != "" {
|
||||
dd.Author = c.String("author")
|
||||
log.Info("Set author: %s", dd.Author)
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.Bool("hide") {
|
||||
dd.Hidden = true
|
||||
log.Info("Marked doodad Hidden")
|
||||
modified = true
|
||||
} else if c.Bool("unhide") {
|
||||
dd.Hidden = false
|
||||
log.Info("Doodad is no longer Hidden")
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.Bool("lock") {
|
||||
dd.Locked = true
|
||||
log.Info("Write lock enabled.")
|
||||
modified = true
|
||||
} else if c.Bool("unlock") {
|
||||
dd.Locked = false
|
||||
log.Info("Write lock disabled.")
|
||||
modified = true
|
||||
}
|
||||
|
||||
/******************************
|
||||
* Save level changes to disk *
|
||||
******************************/
|
||||
|
||||
if modified {
|
||||
if err := dd.WriteFile(filename); err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Write error: %s", err), 1)
|
||||
}
|
||||
} else {
|
||||
log.Warn("Note: No changes made to level")
|
||||
}
|
||||
|
||||
return showDoodad(c, filename)
|
||||
}
|
156
cmd/doodad/commands/edit_level.go
Normal file
156
cmd/doodad/commands/edit_level.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// EditLevel allows writing level metadata.
|
||||
var EditLevel cli.Command
|
||||
|
||||
func init() {
|
||||
EditLevel = cli.Command{
|
||||
Name: "edit-level",
|
||||
Usage: "update metadata for a Level file",
|
||||
ArgsUsage: "<filename.level>",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: "set the level title",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "author",
|
||||
Usage: "set the level author",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "set the level password",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "type",
|
||||
Usage: "set the page type. One of: Unbounded, Bounded, NoNegativeSpace, Bordered",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "max-size",
|
||||
Usage: "set the page max size (WxH format, like 2550x3300)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "wallpaper",
|
||||
Usage: "set the wallpaper filename",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "lock",
|
||||
Usage: "write-lock the level file",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "unlock",
|
||||
Usage: "remove the write-lock on the level file",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return cli.NewExitError(
|
||||
"Usage: doodad edit-level <filename.level>",
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
var filenames = c.Args()
|
||||
for _, filename := range filenames {
|
||||
if err := editLevel(c, filename); err != nil {
|
||||
log.Error("%s: %s", filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func editLevel(c *cli.Context, filename string) error {
|
||||
var modified bool
|
||||
|
||||
lvl, err := level.LoadJSON(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to load %s: %s", filename, err)
|
||||
}
|
||||
|
||||
log.Info("File: %s", filename)
|
||||
|
||||
/***************************
|
||||
* Update level properties *
|
||||
***************************/
|
||||
|
||||
if c.String("title") != "" {
|
||||
lvl.Title = c.String("title")
|
||||
log.Info("Set title: %s", lvl.Title)
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.String("author") != "" {
|
||||
lvl.Author = c.String("author")
|
||||
log.Info("Set author: %s", lvl.Author)
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.String("password") != "" {
|
||||
lvl.Password = c.String("password")
|
||||
log.Info("Updated level password")
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.String("max-size") != "" {
|
||||
w, h, err := render.ParseResolution(c.String("max-size"))
|
||||
if err != nil {
|
||||
log.Error("-max-size: %s", err)
|
||||
} else {
|
||||
lvl.MaxWidth = int64(w)
|
||||
lvl.MaxHeight = int64(h)
|
||||
modified = true
|
||||
}
|
||||
}
|
||||
|
||||
if c.Bool("lock") {
|
||||
lvl.Locked = true
|
||||
log.Info("Write lock enabled.")
|
||||
modified = true
|
||||
} else if c.Bool("unlock") {
|
||||
lvl.Locked = false
|
||||
log.Info("Write lock disabled.")
|
||||
modified = true
|
||||
}
|
||||
|
||||
if c.String("type") != "" {
|
||||
if pageType, ok := level.PageTypeFromString(c.String("type")); ok {
|
||||
lvl.PageType = pageType
|
||||
log.Info("Page Type set to %s", pageType)
|
||||
modified = true
|
||||
} else {
|
||||
log.Error("Invalid -type value. Should be like Unbounded, Bounded, NoNegativeSpace, Bordered")
|
||||
}
|
||||
}
|
||||
|
||||
if c.String("wallpaper") != "" {
|
||||
lvl.Wallpaper = c.String("wallpaper")
|
||||
log.Info("Set wallpaper: %s", c.String("wallpaper"))
|
||||
modified = true
|
||||
}
|
||||
|
||||
/******************************
|
||||
* Save level changes to disk *
|
||||
******************************/
|
||||
|
||||
if modified {
|
||||
if err := lvl.WriteFile(filename); err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Write error: %s", err), 1)
|
||||
}
|
||||
} else {
|
||||
log.Warn("Note: No changes made to level")
|
||||
}
|
||||
|
||||
return showLevel(c, filename)
|
||||
}
|
224
cmd/doodad/commands/show.go
Normal file
224
cmd/doodad/commands/show.go
Normal file
|
@ -0,0 +1,224 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Show information about a Level or Doodad file.
|
||||
var Show cli.Command
|
||||
|
||||
func init() {
|
||||
Show = cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show information about a level or doodad file",
|
||||
ArgsUsage: "<.level or .doodad>",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "actors",
|
||||
Usage: "print verbose actor data in Level files",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "chunks",
|
||||
Usage: "print verbose data about all the pixel chunks in a file",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "script",
|
||||
Usage: "print the script from a doodad file and exit",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "verbose, v",
|
||||
Usage: "print verbose output (all verbose flags enabled)",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return cli.NewExitError(
|
||||
"Usage: doodad show <.level .doodad ...>",
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
filenames := c.Args()
|
||||
for _, filename := range filenames {
|
||||
switch strings.ToLower(filepath.Ext(filename)) {
|
||||
case enum.LevelExt:
|
||||
if err := showLevel(c, filename); err != nil {
|
||||
log.Error(err.Error())
|
||||
return cli.NewExitError("Error", 1)
|
||||
}
|
||||
case enum.DoodadExt:
|
||||
if err := showDoodad(c, filename); err != nil {
|
||||
log.Error(err.Error())
|
||||
return cli.NewExitError("Error", 1)
|
||||
}
|
||||
default:
|
||||
log.Error("File %s: not a level or doodad", filename)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// showLevel shows data about a level file.
|
||||
func showLevel(c *cli.Context, filename string) error {
|
||||
lvl, err := level.LoadJSON(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("===== Level: %s =====\n", filename)
|
||||
|
||||
fmt.Println("Headers:")
|
||||
fmt.Printf(" File version: %d\n", lvl.Version)
|
||||
fmt.Printf(" Game version: %s\n", lvl.GameVersion)
|
||||
fmt.Printf(" Level title: %s\n", lvl.Title)
|
||||
fmt.Printf(" Author: %s\n", lvl.Author)
|
||||
fmt.Printf(" Password: %s\n", lvl.Password)
|
||||
fmt.Printf(" Locked: %+v\n", lvl.Locked)
|
||||
fmt.Println("")
|
||||
|
||||
showPalette(lvl.Palette)
|
||||
|
||||
fmt.Println("Level Settings:")
|
||||
fmt.Printf(" Page type: %s\n", lvl.PageType.String())
|
||||
fmt.Printf(" Max size: %dx%d\n", lvl.MaxWidth, lvl.MaxHeight)
|
||||
fmt.Printf(" Wallpaper: %s\n", lvl.Wallpaper)
|
||||
fmt.Println("")
|
||||
|
||||
// Print the actor information.
|
||||
fmt.Println("Actors:")
|
||||
fmt.Printf(" Level contains %d actors\n", len(lvl.Actors))
|
||||
if c.Bool("actors") || c.Bool("verbose") {
|
||||
fmt.Println(" List of Actors:")
|
||||
for id, actor := range lvl.Actors {
|
||||
fmt.Printf(" - Name: %s\n", actor.Filename)
|
||||
fmt.Printf(" UUID: %s\n", id)
|
||||
fmt.Printf(" At: %s\n", actor.Point)
|
||||
if c.Bool("links") {
|
||||
for _, link := range actor.Links {
|
||||
if other, ok := lvl.Actors[link]; ok {
|
||||
fmt.Printf(" Link: %s (%s)\n", link, other.Filename)
|
||||
} else {
|
||||
fmt.Printf(" Link: %s (**UNRESOLVED**)", link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("")
|
||||
} else {
|
||||
fmt.Print(" Use -actors or -verbose to serialize Actors\n\n")
|
||||
}
|
||||
|
||||
// Serialize chunk information.
|
||||
showChunker(c, lvl.Chunker)
|
||||
|
||||
fmt.Println("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func showDoodad(c *cli.Context, filename string) error {
|
||||
dd, err := doodads.LoadJSON(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool("script") {
|
||||
fmt.Printf("// %s.js\n", filename)
|
||||
fmt.Println(strings.TrimSpace(dd.Script))
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("===== Doodad: %s =====\n", filename)
|
||||
|
||||
fmt.Println("Headers:")
|
||||
fmt.Printf(" File version: %d\n", dd.Version)
|
||||
fmt.Printf(" Game version: %s\n", dd.GameVersion)
|
||||
fmt.Printf(" Doodad title: %s\n", dd.Title)
|
||||
fmt.Printf(" Author: %s\n", dd.Author)
|
||||
fmt.Printf(" Locked: %+v\n", dd.Locked)
|
||||
fmt.Printf(" Hidden: %+v\n", dd.Hidden)
|
||||
fmt.Printf(" Script size: %d bytes\n", len(dd.Script))
|
||||
fmt.Println("")
|
||||
|
||||
if len(dd.Tags) > 0 {
|
||||
fmt.Println("Tags:")
|
||||
for k, v := range dd.Tags {
|
||||
fmt.Printf(" %s: %s\n", k, v)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
showPalette(dd.Palette)
|
||||
|
||||
for i, layer := range dd.Layers {
|
||||
fmt.Printf("Layer %d: %s\n", i, layer.Name)
|
||||
showChunker(c, layer.Chunker)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func showPalette(pal *level.Palette) {
|
||||
fmt.Println("Palette:")
|
||||
for _, sw := range pal.Swatches {
|
||||
fmt.Printf(" - Swatch name: %s\n", sw.Name)
|
||||
fmt.Printf(" Attributes: %s\n", sw.Attributes())
|
||||
fmt.Printf(" Color: %s\n", sw.Color.ToHex())
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func showChunker(c *cli.Context, ch *level.Chunker) {
|
||||
var worldSize = ch.WorldSize()
|
||||
var width = worldSize.W - worldSize.X
|
||||
var height = worldSize.H - worldSize.Y
|
||||
fmt.Println("Chunks:")
|
||||
fmt.Printf(" Pixels Per Chunk: %d^2\n", ch.Size)
|
||||
fmt.Printf(" Number Generated: %d\n", len(ch.Chunks))
|
||||
fmt.Printf(" Coordinate Range: (%d,%d) ... (%d,%d)\n",
|
||||
worldSize.X,
|
||||
worldSize.Y,
|
||||
worldSize.W,
|
||||
worldSize.H,
|
||||
)
|
||||
fmt.Printf(" World Dimensions: %dx%d\n", width, height)
|
||||
|
||||
// Verbose chunk information.
|
||||
if c.Bool("chunks") || c.Bool("verbose") {
|
||||
fmt.Println(" Chunk Details:")
|
||||
for point, chunk := range ch.Chunks {
|
||||
fmt.Printf(" - Coord: %s\n", point)
|
||||
fmt.Printf(" Type: %s\n", chunkTypeToName(chunk.Type))
|
||||
fmt.Printf(" Range: (%d,%d) ... (%d,%d)\n",
|
||||
int(point.X)*ch.Size,
|
||||
int(point.Y)*ch.Size,
|
||||
(int(point.X)*ch.Size)+ch.Size,
|
||||
(int(point.Y)*ch.Size)+ch.Size,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(" Use -chunks or -verbose to serialize Chunks")
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func chunkTypeToName(v int) string {
|
||||
switch v {
|
||||
case level.MapType:
|
||||
return "map"
|
||||
case level.GridType:
|
||||
return "grid"
|
||||
default:
|
||||
return fmt.Sprintf("type %d", v)
|
||||
}
|
||||
}
|
|
@ -52,6 +52,9 @@ func main() {
|
|||
|
||||
app.Commands = []cli.Command{
|
||||
commands.Convert,
|
||||
commands.Show,
|
||||
commands.EditLevel,
|
||||
commands.EditDoodad,
|
||||
commands.InstallScript,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,36 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var regexpResolution = regexp.MustCompile(`^(\d+)x(\d+)$`)
|
||||
|
||||
// ParseResolution turns a resolution string like "1024x768" and returns the
|
||||
// width and height values.
|
||||
func ParseResolution(resi string) (int, int, error) {
|
||||
m := regexpResolution.FindStringSubmatch(resi)
|
||||
if m == nil {
|
||||
return 0, 0, fmt.Errorf("invalid resolution format, should be %s",
|
||||
regexpResolution.String(),
|
||||
)
|
||||
}
|
||||
|
||||
width, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
height, err := strconv.Atoi(m[2])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return width, height, nil
|
||||
}
|
||||
|
||||
// TrimBox helps with Engine.Copy() to trim a destination box so that it
|
||||
// won't overflow with the parent container.
|
||||
func TrimBox(src, dst *Rect, p Point, S Rect, thickness int32) {
|
||||
|
|
60
pkg/balance/boolprops.go
Normal file
60
pkg/balance/boolprops.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package balance
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Fun bool props to wreak havoc in the game.
|
||||
var (
|
||||
// Force show hidden doodads in the palette in Editor Mode.
|
||||
ShowHiddenDoodads bool
|
||||
|
||||
// Force ability to edit Locked levels and doodads.
|
||||
WriteLockOverride bool
|
||||
|
||||
// Pretty-print JSON files when writing.
|
||||
JSONIndent bool
|
||||
)
|
||||
|
||||
// Human friendly names for the boolProps. Not necessarily the long descriptive
|
||||
// variable names above.
|
||||
var props = map[string]*bool{
|
||||
"showAllDoodads": &ShowHiddenDoodads,
|
||||
"writeLockOverride": &WriteLockOverride,
|
||||
"prettyJSON": &JSONIndent,
|
||||
|
||||
// WARNING: SLOW!
|
||||
"disableChunkTextureCache": &DisableChunkTextureCache,
|
||||
}
|
||||
|
||||
// GetBoolProp reads the current value of a boolProp.
|
||||
// Special value "list" will error out with a list of available props.
|
||||
func GetBoolProp(name string) (bool, error) {
|
||||
if name == "list" {
|
||||
var keys []string
|
||||
for k := range props {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return false, fmt.Errorf(
|
||||
"Boolprops: %s",
|
||||
strings.Join(keys, ", "),
|
||||
)
|
||||
}
|
||||
if prop, ok := props[name]; ok {
|
||||
return *prop, nil
|
||||
}
|
||||
return false, errors.New("no such boolProp")
|
||||
}
|
||||
|
||||
// BoolProp allows easily setting a boolProp by name.
|
||||
func BoolProp(name string, v bool) error {
|
||||
if prop, ok := props[name]; ok {
|
||||
*prop = v
|
||||
return nil
|
||||
}
|
||||
return errors.New("no such boolProp")
|
||||
}
|
|
@ -34,9 +34,6 @@ var (
|
|||
// Set to a color other than Invisible to force the uix.Canvas to color ALL
|
||||
// Stroke pixels in this color.
|
||||
DebugCanvasStrokeColor = render.Invisible
|
||||
|
||||
// Pretty-print JSON files when writing.
|
||||
JSONIndent = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
@ -201,8 +202,19 @@ func (c Command) Quit() error {
|
|||
|
||||
// BoolProp command sets available boolean variables.
|
||||
func (c Command) BoolProp(d *Doodle) error {
|
||||
if len(c.Args) == 1 {
|
||||
// Showing the value of a boolProp. Only supported for those defined
|
||||
// in balance/boolprops.go
|
||||
value, err := balance.GetBoolProp(c.Args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Flash("%s: %+v", c.Args[0], value)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Args) != 2 {
|
||||
return errors.New("Usage: boolProp <name> <true or false>")
|
||||
return errors.New("Usage: boolProp <name> [true or false]")
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -229,7 +241,12 @@ func (c Command) BoolProp(d *Doodle) error {
|
|||
if ok {
|
||||
d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy))
|
||||
} else {
|
||||
d.Flash("Unknown boolProp name %s", name)
|
||||
// Try the global boolProps in balance package.
|
||||
if err := balance.BoolProp(name, truthy); err != nil {
|
||||
d.Flash("%s", err)
|
||||
} else {
|
||||
d.Flash("%s: %+v", name, truthy)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
type Doodad struct {
|
||||
level.Base
|
||||
Filename string `json:"-"` // used internally, not saved in json
|
||||
Hidden bool `json:"hidden,omitempty"`
|
||||
Palette *level.Palette `json:"palette"`
|
||||
Script string `json:"script"`
|
||||
Layers []Layer `json:"layers"`
|
||||
|
|
|
@ -7,13 +7,17 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
)
|
||||
|
||||
// ToJSON serializes the doodad as JSON.
|
||||
func (d *Doodad) ToJSON() ([]byte, error) {
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
encoder := json.NewEncoder(out)
|
||||
if balance.JSONIndent {
|
||||
encoder.SetIndent("", "\t")
|
||||
}
|
||||
err := encoder.Encode(d)
|
||||
return out.Bytes(), err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"git.kirsle.net/apps/doodle/lib/events"
|
||||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
|
@ -86,6 +87,18 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Write locked level?
|
||||
if s.Level != nil && s.Level.Locked {
|
||||
if balance.WriteLockOverride {
|
||||
d.Flash("Note: write lock has been overridden")
|
||||
} else {
|
||||
d.Flash("That level is write-protected and cannot be viewed in the editor.")
|
||||
s.Level = nil
|
||||
s.UI.Canvas.ClearActors()
|
||||
s.filename = ""
|
||||
}
|
||||
}
|
||||
|
||||
// No level?
|
||||
if s.Level == nil {
|
||||
log.Debug("EditorScene.Setup: initializing a new Level")
|
||||
|
@ -96,7 +109,7 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
|||
s.UI.Canvas.Scrollable = true
|
||||
}
|
||||
case enum.DoodadDrawing:
|
||||
// No Doodad?
|
||||
// Getting a doodad from file?
|
||||
if s.filename != "" && s.OpenFile {
|
||||
log.Debug("EditorScene.Setup: Loading doodad from filename at %s", s.filename)
|
||||
if err := s.LoadDoodad(s.filename); err != nil {
|
||||
|
@ -104,6 +117,17 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Write locked doodad?
|
||||
if s.Doodad != nil && s.Doodad.Locked {
|
||||
if balance.WriteLockOverride {
|
||||
d.Flash("Note: write lock has been overridden")
|
||||
} else {
|
||||
d.Flash("That doodad is write-protected and cannot be viewed in the editor.")
|
||||
s.Doodad = nil
|
||||
s.filename = ""
|
||||
}
|
||||
}
|
||||
|
||||
// No Doodad?
|
||||
if s.Doodad == nil {
|
||||
log.Debug("EditorScene.Setup: initializing a new Doodad")
|
||||
|
|
|
@ -243,7 +243,7 @@ func (u *EditorUI) Loop(ev *events.State) error {
|
|||
)
|
||||
|
||||
// Statusbar filename label.
|
||||
filename := "untitled.map"
|
||||
filename := "untitled.level"
|
||||
fileType := "Level"
|
||||
if u.Scene.filename != "" {
|
||||
filename = u.Scene.filename
|
||||
|
|
|
@ -126,14 +126,33 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
|
|||
var buttonSize = (paletteWidth - window.BoxThickness(2)) / int32(perRow)
|
||||
u.doodadButtonSize = buttonSize
|
||||
|
||||
// Load all the doodads, skip hidden ones.
|
||||
var items []*doodads.Doodad
|
||||
for _, filename := range doodadsAvailable {
|
||||
doodad, err := doodads.LoadFile(filename)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
doodad = doodads.New(balance.DoodadSize)
|
||||
}
|
||||
|
||||
// Skip hidden doodads.
|
||||
if doodad.Hidden && !balance.ShowHiddenDoodads {
|
||||
log.Info("skip %s: hidden doodad", filename)
|
||||
continue
|
||||
}
|
||||
|
||||
doodad.Filename = filename
|
||||
items = append(items, doodad)
|
||||
}
|
||||
|
||||
// Draw the doodad buttons in a grid `perRow` buttons wide.
|
||||
var (
|
||||
row *ui.Frame
|
||||
rowCount int // for labeling the ui.Frame for each row
|
||||
btnRows = []*ui.Frame{} // Collect the row frames for the buttons.
|
||||
)
|
||||
for i, filename := range doodadsAvailable {
|
||||
filename := filename
|
||||
for i, doodad := range items {
|
||||
doodad := doodad
|
||||
|
||||
if row == nil || i%perRow == 0 {
|
||||
rowCount++
|
||||
|
@ -146,18 +165,12 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
|
|||
})
|
||||
}
|
||||
|
||||
doodad, err := doodads.LoadFile(filename)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
doodad = doodads.New(balance.DoodadSize)
|
||||
}
|
||||
|
||||
can := uix.NewCanvas(int(buttonSize), true)
|
||||
can.Name = filename
|
||||
can.Name = doodad.Title
|
||||
can.SetBackground(render.White)
|
||||
can.LoadDoodad(doodad)
|
||||
|
||||
btn := ui.NewButton(filename, can)
|
||||
btn := ui.NewButton(doodad.Title, can)
|
||||
btn.Resize(render.NewRect(
|
||||
buttonSize-2, // TODO: without the -2 the button border
|
||||
buttonSize-2, // rests on top of the window border.
|
||||
|
|
|
@ -2,6 +2,7 @@ package level
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
)
|
||||
|
@ -44,6 +45,32 @@ func (s Swatch) String() string {
|
|||
return s.Name
|
||||
}
|
||||
|
||||
// Attributes returns a comma-separated list of attributes as a string on
|
||||
// this swatch. This is for debugging and the `doodad show` CLI command to
|
||||
// summarize the swatch and shouldn't be used for game logic.
|
||||
func (s *Swatch) Attributes() string {
|
||||
var result string
|
||||
|
||||
if s.Solid {
|
||||
result += "solid,"
|
||||
}
|
||||
if s.Fire {
|
||||
result += "fire,"
|
||||
}
|
||||
if s.Water {
|
||||
result += "water,"
|
||||
}
|
||||
if s.isSparse {
|
||||
result += "sparse,"
|
||||
}
|
||||
|
||||
if result == "" {
|
||||
result = "none,"
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(result, ",")
|
||||
}
|
||||
|
||||
// IsSparse returns whether this Swatch is sparse (has only a palette index) and
|
||||
// requires inflation.
|
||||
func (s *Swatch) IsSparse() bool {
|
||||
|
|
|
@ -21,16 +21,16 @@ type Base struct {
|
|||
GameVersion string `json:"gameVersion" msgpack:"1"` // Game version that created the level.
|
||||
Title string `json:"title" msgpack:"2"`
|
||||
Author string `json:"author" msgpack:"3"`
|
||||
Locked bool `json:"locked" msgpack:"4"`
|
||||
|
||||
// Every drawing type is able to embed other files inside of itself.
|
||||
Files FileSystem `json:"files" msgpack:"4"`
|
||||
Files FileSystem `json:"files" msgpack:"5"`
|
||||
}
|
||||
|
||||
// Level is the container format for Doodle map drawings.
|
||||
type Level struct {
|
||||
Base
|
||||
Password string `json:"passwd" msgpack:"10"`
|
||||
Locked bool `json:"locked" msgpack:"11"`
|
||||
|
||||
// Chunked pixel data.
|
||||
Chunker *Chunker `json:"chunks" msgpack:"12"`
|
||||
|
|
|
@ -31,6 +31,11 @@ func (w *Canvas) InstallActors(actors level.ActorMap) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ClearActors removes all the actors from the Canvas.
|
||||
func (w *Canvas) ClearActors() {
|
||||
w.actors = []*Actor{}
|
||||
}
|
||||
|
||||
// SetScriptSupervisor assigns the Canvas scripting supervisor to enable
|
||||
// interaction with actor scripts.
|
||||
func (w *Canvas) SetScriptSupervisor(s *scripting.Supervisor) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user