2019-07-07 06:28:11 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
2022-05-03 03:35:53 +00:00
|
|
|
"errors"
|
2019-07-07 06:28:11 +00:00
|
|
|
"fmt"
|
|
|
|
|
2022-09-24 22:17:25 +00:00
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
|
|
|
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
2019-12-31 02:13:28 +00:00
|
|
|
"git.kirsle.net/go/render"
|
2020-11-15 23:20:15 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
2019-07-07 06:28:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// EditLevel allows writing level metadata.
|
2020-06-05 06:11:03 +00:00
|
|
|
var EditLevel *cli.Command
|
2019-07-07 06:28:11 +00:00
|
|
|
|
|
|
|
func init() {
|
2020-06-05 06:11:03 +00:00
|
|
|
EditLevel = &cli.Command{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "edit-level",
|
|
|
|
Usage: "update metadata for a Level file",
|
|
|
|
ArgsUsage: "<filename.level>",
|
|
|
|
Flags: []cli.Flag{
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "quiet",
|
|
|
|
Aliases: []string{"q"},
|
|
|
|
Usage: "limit output (don't show doodad data at the end)",
|
2019-07-07 06:50:38 +00:00
|
|
|
},
|
2022-05-03 03:35:53 +00:00
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "output",
|
|
|
|
Aliases: []string{"o"},
|
|
|
|
Usage: "write to a different output file than the input (especially for --resize)",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "title",
|
|
|
|
Usage: "set the level title",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "author",
|
|
|
|
Usage: "set the level author",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "password",
|
|
|
|
Usage: "set the level password",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "type",
|
|
|
|
Usage: "set the page type. One of: Unbounded, Bounded, NoNegativeSpace, Bordered",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "max-size",
|
2022-05-03 03:35:53 +00:00
|
|
|
Usage: "set the bounded level page max size (WxH format, like 2550x3300)",
|
|
|
|
},
|
|
|
|
&cli.IntFlag{
|
|
|
|
Name: "resize",
|
|
|
|
Usage: "change the chunk size, and re-encode the whole level into chunks of the new size",
|
2019-07-07 06:28:11 +00:00
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.StringFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "wallpaper",
|
|
|
|
Usage: "set the wallpaper filename",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.BoolFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "lock",
|
|
|
|
Usage: "write-lock the level file",
|
|
|
|
},
|
2020-06-05 06:11:03 +00:00
|
|
|
&cli.BoolFlag{
|
2019-07-07 06:28:11 +00:00
|
|
|
Name: "unlock",
|
|
|
|
Usage: "remove the write-lock on the level file",
|
|
|
|
},
|
2021-10-08 03:50:24 +00:00
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "remove-actor",
|
|
|
|
Usage: "Remove all instances of the actor from the level. Value is their filename or UUID.",
|
|
|
|
},
|
2022-04-30 03:34:59 +00:00
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "touch",
|
|
|
|
Usage: "simply load and re-save the level, to migrate it to a zipfile",
|
|
|
|
},
|
2019-07-07 06:28:11 +00:00
|
|
|
},
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
if c.NArg() < 1 {
|
2021-09-04 04:35:12 +00:00
|
|
|
return cli.Exit(
|
2019-07-07 06:28:11 +00:00
|
|
|
"Usage: doodad edit-level <filename.level>",
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-06-05 06:11:03 +00:00
|
|
|
var filenames = c.Args().Slice()
|
2019-07-07 06:28:11 +00:00
|
|
|
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)
|
|
|
|
|
2022-05-03 03:35:53 +00:00
|
|
|
// Migrating it to a different chunk size?
|
|
|
|
if c.Int("resize") > 0 {
|
|
|
|
return rechunkLevel(c, filename, lvl)
|
|
|
|
}
|
|
|
|
|
2019-07-07 06:28:11 +00:00
|
|
|
/***************************
|
|
|
|
* Update level properties *
|
|
|
|
***************************/
|
|
|
|
|
2022-04-30 03:34:59 +00:00
|
|
|
if c.Bool("touch") {
|
|
|
|
log.Info("Just touching and resaving the file")
|
|
|
|
modified = true
|
|
|
|
}
|
|
|
|
|
2019-07-07 06:28:11 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-10-08 03:50:24 +00:00
|
|
|
if c.String("remove-actor") != "" {
|
|
|
|
var (
|
|
|
|
match = c.String("remove-actor")
|
|
|
|
removeIDs = []string{}
|
|
|
|
)
|
|
|
|
|
|
|
|
for id, actor := range lvl.Actors {
|
|
|
|
if id == match || actor.Filename == match {
|
|
|
|
removeIDs = append(removeIDs, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(removeIDs) > 0 {
|
|
|
|
for _, id := range removeIDs {
|
|
|
|
delete(lvl.Actors, id)
|
|
|
|
}
|
|
|
|
log.Info("Removed %d instances of actor %s from the level.", len(removeIDs), match)
|
|
|
|
modified = true
|
|
|
|
} else {
|
|
|
|
log.Error("Did not find any actors like %s in the level.", match)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-07 06:28:11 +00:00
|
|
|
/******************************
|
|
|
|
* Save level changes to disk *
|
|
|
|
******************************/
|
|
|
|
|
|
|
|
if modified {
|
|
|
|
if err := lvl.WriteFile(filename); err != nil {
|
2021-09-04 04:35:12 +00:00
|
|
|
return cli.Exit(fmt.Sprintf("Write error: %s", err), 1)
|
2019-07-07 06:28:11 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Warn("Note: No changes made to level")
|
|
|
|
}
|
|
|
|
|
2019-07-07 06:50:38 +00:00
|
|
|
if c.Bool("quiet") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-07-07 06:28:11 +00:00
|
|
|
return showLevel(c, filename)
|
|
|
|
}
|
2022-05-03 03:35:53 +00:00
|
|
|
|
|
|
|
// doodad edit-level --resize CHUNK_SIZE
|
|
|
|
//
|
|
|
|
// Handles the deep operation of re-copying the old level into a new level
|
|
|
|
// at the new chunk size.
|
|
|
|
func rechunkLevel(c *cli.Context, filename string, lvl *level.Level) error {
|
|
|
|
var chunkSize = c.Int("resize")
|
|
|
|
log.Info("Resizing the level's chunk size.")
|
|
|
|
log.Info("Current chunk size: %d", lvl.Chunker.Size)
|
|
|
|
log.Info("Target chunk size: %d", chunkSize)
|
|
|
|
|
|
|
|
if output := c.String("output"); output != "" {
|
|
|
|
filename = output
|
|
|
|
log.Info("Output file will be: %s", filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
if chunkSize == lvl.Chunker.Size {
|
|
|
|
return errors.New("the level already has the target chunk size")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep the level's current Chunker, and set a new one.
|
|
|
|
var oldChunker = lvl.Chunker
|
|
|
|
lvl.Chunker = level.NewChunker(chunkSize)
|
|
|
|
|
|
|
|
// Iterate all the Pixels of the old chunker.
|
|
|
|
log.Info("Copying pixels from old chunker into new chunker (this may take a while)...")
|
|
|
|
for pixel := range oldChunker.IterPixels() {
|
|
|
|
lvl.Chunker.Set(
|
|
|
|
pixel.Point(),
|
|
|
|
pixel.Swatch,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("Writing new data to filename: %s", filename)
|
|
|
|
if err := lvl.WriteFile(filename); err != nil {
|
|
|
|
log.Error(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return showLevel(c, filename)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|