diff --git a/cmd/doodad/commands/convert.go b/cmd/doodad/commands/convert.go index 0cd4ef0..dd9bcae 100644 --- a/cmd/doodad/commands/convert.go +++ b/cmd/doodad/commands/convert.go @@ -35,11 +35,19 @@ func init() { Usage: "chroma key color for transparency on input image files", Value: "#ffffff", }, + cli.StringFlag{ + Name: "title, t", + Usage: "set the title of the level or doodad being created", + }, + cli.StringFlag{ + Name: "palette, p", + Usage: "use a palette JSON to define color swatch properties", + }, }, Action: func(c *cli.Context) error { - if c.NArg() != 2 { + if c.NArg() < 2 { return cli.NewExitError( - "Usage: doodad convert \n"+ + "Usage: doodad convert \n"+ " Image file types: png, bmp\n"+ " Drawing file types: level, doodad", 1, @@ -57,15 +65,15 @@ func init() { args := c.Args() var ( - inputFile = args[0] - inputType = strings.ToLower(filepath.Ext(inputFile)) - outputFile = args[1] + inputFiles = args[:len(args)-1] + inputType = strings.ToLower(filepath.Ext(inputFiles[0])) + outputFile = args[len(args)-1] outputType = strings.ToLower(filepath.Ext(outputFile)) ) if inputType == extPNG || inputType == extBMP { if outputType == extLevel || outputType == extDoodad { - if err := imageToDrawing(c, chroma, inputFile, outputFile); err != nil { + if err := imageToDrawing(c, chroma, inputFiles, outputFile); err != nil { return cli.NewExitError(err.Error(), 1) } return nil @@ -73,7 +81,7 @@ func init() { return cli.NewExitError("Image inputs can only output to Doodle drawings", 1) } else if inputType == extLevel || inputType == extDoodad { if outputType == extPNG || outputType == extBMP { - if err := drawingToImage(c, chroma, inputFile, outputFile); err != nil { + if err := drawingToImage(c, chroma, inputFiles, outputFile); err != nil { return cli.NewExitError(err.Error(), 1) } return nil @@ -86,29 +94,45 @@ func init() { } } -func imageToDrawing(c *cli.Context, chroma render.Color, inputFile, outputFile string) error { - reader, err := os.Open(inputFile) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - - img, format, err := image.Decode(reader) - log.Info("format: %s", format) - _ = img - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - - // Get the bounding box information of the source image. +func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, outputFile string) error { + // Read the source images. Ensure they all have the same boundaries. var ( - bounds = img.Bounds() - imageSize = bounds.Size() - chunkSize int // the square shape for Doodad chunk size + imageBounds image.Point + chunkSize int // the square shape for the Doodad chunk size + images []image.Image ) - if imageSize.X > imageSize.Y { - chunkSize = imageSize.X - } else { - chunkSize = imageSize.Y + + for i, filename := range inputFiles { + reader, err := os.Open(filename) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + + img, format, err := image.Decode(reader) + log.Info("Parsed image %d of %d. Format: %s", i+1, len(inputFiles), format) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + + // Get the bounding box information of the source image. + var ( + bounds = img.Bounds() + imageSize = bounds.Size() + ) + + // Validate all images are the same size. + if i == 0 { + imageBounds = imageSize + if imageSize.X > imageSize.Y { + chunkSize = imageSize.X + } else { + chunkSize = imageSize.Y + } + } else if imageSize != imageBounds { + return cli.NewExitError("your source images are not all the same dimensions", 1) + } + + images = append(images, img) } // Generate the output drawing file. @@ -117,9 +141,27 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFile, outputFile s log.Info("Output is a Doodad file (chunk size %d): %s", chunkSize, outputFile) doodad := doodads.New(chunkSize) doodad.GameVersion = doodle.Version - doodad.Title = "Converted Doodad" + doodad.Title = c.String("title") + if doodad.Title == "" { + doodad.Title = "Converted Doodad" + } doodad.Author = os.Getenv("USER") - doodad.Palette = imageToChunker(img, chroma, doodad.Layers[0].Chunker) + + // Write the first layer and gather its palette. + palette, layer0 := imageToChunker(images[0], chroma, chunkSize) + doodad.Palette = palette + doodad.Layers[0].Chunker = layer0 + + // Write any additional layers. + if len(images) > 1 { + for i, img := range images[1:] { + _, chunker := imageToChunker(img, chroma, chunkSize) + doodad.Layers = append(doodad.Layers, doodads.Layer{ + Name: fmt.Sprintf("layer-%d", i+1), + Chunker: chunker, + }) + } + } err := doodad.WriteJSON(outputFile) if err != nil { @@ -127,12 +169,20 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFile, outputFile s } case extLevel: log.Info("Output is a Level file: %s", outputFile) + if len(images) > 1 { + log.Warn("Notice: levels only support one layer so only your first image will be used") + } lvl := level.New() lvl.GameVersion = doodle.Version - lvl.Title = "Converted Level" + lvl.Title = c.String("title") + if lvl.Title == "" { + lvl.Title = "Converted Level" + } lvl.Author = os.Getenv("USER") - lvl.Palette = imageToChunker(img, chroma, lvl.Chunker) + palette, chunker := imageToChunker(images[0], chroma, lvl.Chunker.Size) + lvl.Palette = palette + lvl.Chunker = chunker err := lvl.WriteJSON(outputFile) if err != nil { @@ -145,9 +195,10 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFile, outputFile s return nil } -func drawingToImage(c *cli.Context, chroma render.Color, inputFile, outputFile string) error { +func drawingToImage(c *cli.Context, chroma render.Color, inputFiles []string, outputFile string) error { var palette *level.Palette var chunker *level.Chunker + inputFile := inputFiles[0] switch strings.ToLower(filepath.Ext(inputFile)) { case extLevel: @@ -224,9 +275,10 @@ func drawingToImage(c *cli.Context, chroma render.Color, inputFile, outputFile s // // img: input image like a PNG // chroma: transparent color -func imageToChunker(img image.Image, chroma render.Color, chunker *level.Chunker) *level.Palette { +func imageToChunker(img image.Image, chroma render.Color, chunkSize int) (*level.Palette, *level.Chunker) { var ( palette = level.NewPalette() + chunker = level.NewChunker(chunkSize) bounds = img.Bounds() ) @@ -265,6 +317,7 @@ func imageToChunker(img image.Image, chroma render.Color, chunker *level.Chunker for _, hex := range sortedColors { palette.Swatches = append(palette.Swatches, uniqueColor[hex]) } + palette.Inflate() - return palette + return palette, chunker } diff --git a/dev-assets/doodads/buttons/README.md b/dev-assets/doodads/buttons/README.md new file mode 100644 index 0000000..001f1bb --- /dev/null +++ b/dev-assets/doodads/buttons/README.md @@ -0,0 +1,12 @@ +# Button Doodads + +```bash +doodad convert -t "Sticky Button" sticky1.png sticky2.png sticky-button.doodad +doodad install-script sticky.js sticky-button.doodad + +doodad convert -t "Button" button1.png button2.png button.doodad +doodad install-script button.js button.doodad + +doodad convert -t "Button Type B" typeB1.png typeB2.png button-typeB.doodad +doodad install-script button.js button-typeB.doodad +``` diff --git a/dev-assets/doodads/buttons/button.js b/dev-assets/doodads/buttons/button.js new file mode 100644 index 0000000..f640ca0 --- /dev/null +++ b/dev-assets/doodads/buttons/button.js @@ -0,0 +1,8 @@ +function main() { + console.log("Sticky Button initialized!"); + + Events.OnCollide( function() { + console.log("Touched!"); + Self.Canvas.SetBackground(RGBA(255, 153, 0, 153)) + }) +} diff --git a/dev-assets/doodads/buttons/button1.png b/dev-assets/doodads/buttons/button1.png new file mode 100644 index 0000000..6736af8 Binary files /dev/null and b/dev-assets/doodads/buttons/button1.png differ diff --git a/dev-assets/doodads/buttons/button2.png b/dev-assets/doodads/buttons/button2.png new file mode 100644 index 0000000..67c3559 Binary files /dev/null and b/dev-assets/doodads/buttons/button2.png differ diff --git a/dev-assets/doodads/buttons/sticky.js b/dev-assets/doodads/buttons/sticky.js new file mode 100644 index 0000000..f640ca0 --- /dev/null +++ b/dev-assets/doodads/buttons/sticky.js @@ -0,0 +1,8 @@ +function main() { + console.log("Sticky Button initialized!"); + + Events.OnCollide( function() { + console.log("Touched!"); + Self.Canvas.SetBackground(RGBA(255, 153, 0, 153)) + }) +} diff --git a/dev-assets/doodads/buttons/sticky1.png b/dev-assets/doodads/buttons/sticky1.png new file mode 100644 index 0000000..dbd955b Binary files /dev/null and b/dev-assets/doodads/buttons/sticky1.png differ diff --git a/dev-assets/doodads/buttons/sticky2.png b/dev-assets/doodads/buttons/sticky2.png new file mode 100644 index 0000000..c9e556b Binary files /dev/null and b/dev-assets/doodads/buttons/sticky2.png differ diff --git a/dev-assets/doodads/buttons/typeB1.png b/dev-assets/doodads/buttons/typeB1.png new file mode 100644 index 0000000..ff2d2b4 Binary files /dev/null and b/dev-assets/doodads/buttons/typeB1.png differ diff --git a/dev-assets/doodads/buttons/typeB2.png b/dev-assets/doodads/buttons/typeB2.png new file mode 100644 index 0000000..5cfe20c Binary files /dev/null and b/dev-assets/doodads/buttons/typeB2.png differ diff --git a/dev-assets/doodads/doors/README.md b/dev-assets/doodads/doors/README.md new file mode 100644 index 0000000..8fc5bf2 --- /dev/null +++ b/dev-assets/doodads/doors/README.md @@ -0,0 +1,15 @@ +# Button Doodads + +```bash +doodad convert -t "Red Door" red1.png red2.png red-door.doodad +doodad convert -t "Blue Door" blue1.png blue2.png blue-door.doodad +doodad convert -t "Green Door" green1.png green2.png green-door.doodad +doodad convert -t "Yellow Door" yellow1.png yellow2.png yellow-door.doodad + +doodad convert -t "Red Key" red-key.png red-key.doodad +doodad convert -t "Blue Key" blue-key.png blue-key.doodad +doodad convert -t "Green Key" green-key.png green-key.doodad +doodad convert -t "Yellow Key" yellow-key.png yellow-key.doodad + +doodad convert -t "Electric Door" electric{1,2,3,4}.png electric-door.doodad +``` diff --git a/dev-assets/doodads/doors/blue-key.png b/dev-assets/doodads/doors/blue-key.png new file mode 100644 index 0000000..11e4355 Binary files /dev/null and b/dev-assets/doodads/doors/blue-key.png differ diff --git a/dev-assets/doodads/doors/blue1.png b/dev-assets/doodads/doors/blue1.png new file mode 100644 index 0000000..13ad51b Binary files /dev/null and b/dev-assets/doodads/doors/blue1.png differ diff --git a/dev-assets/doodads/doors/blue2.png b/dev-assets/doodads/doors/blue2.png new file mode 100644 index 0000000..1e5bcdb Binary files /dev/null and b/dev-assets/doodads/doors/blue2.png differ diff --git a/dev-assets/doodads/doors/electric1.png b/dev-assets/doodads/doors/electric1.png new file mode 100644 index 0000000..a9ca8a5 Binary files /dev/null and b/dev-assets/doodads/doors/electric1.png differ diff --git a/dev-assets/doodads/doors/electric2.png b/dev-assets/doodads/doors/electric2.png new file mode 100644 index 0000000..09f24b1 Binary files /dev/null and b/dev-assets/doodads/doors/electric2.png differ diff --git a/dev-assets/doodads/doors/electric3.png b/dev-assets/doodads/doors/electric3.png new file mode 100644 index 0000000..912d766 Binary files /dev/null and b/dev-assets/doodads/doors/electric3.png differ diff --git a/dev-assets/doodads/doors/electric4.png b/dev-assets/doodads/doors/electric4.png new file mode 100644 index 0000000..4519275 Binary files /dev/null and b/dev-assets/doodads/doors/electric4.png differ diff --git a/dev-assets/doodads/doors/green-key.png b/dev-assets/doodads/doors/green-key.png new file mode 100644 index 0000000..fe876aa Binary files /dev/null and b/dev-assets/doodads/doors/green-key.png differ diff --git a/dev-assets/doodads/doors/green1.png b/dev-assets/doodads/doors/green1.png new file mode 100644 index 0000000..e49c653 Binary files /dev/null and b/dev-assets/doodads/doors/green1.png differ diff --git a/dev-assets/doodads/doors/green2.png b/dev-assets/doodads/doors/green2.png new file mode 100644 index 0000000..9e229d0 Binary files /dev/null and b/dev-assets/doodads/doors/green2.png differ diff --git a/dev-assets/doodads/doors/red-key.png b/dev-assets/doodads/doors/red-key.png new file mode 100644 index 0000000..38db173 Binary files /dev/null and b/dev-assets/doodads/doors/red-key.png differ diff --git a/dev-assets/doodads/doors/red1.png b/dev-assets/doodads/doors/red1.png new file mode 100644 index 0000000..c963c2d Binary files /dev/null and b/dev-assets/doodads/doors/red1.png differ diff --git a/dev-assets/doodads/doors/red2.png b/dev-assets/doodads/doors/red2.png new file mode 100644 index 0000000..07dc65f Binary files /dev/null and b/dev-assets/doodads/doors/red2.png differ diff --git a/dev-assets/doodads/doors/yellow-key.png b/dev-assets/doodads/doors/yellow-key.png new file mode 100644 index 0000000..c83c738 Binary files /dev/null and b/dev-assets/doodads/doors/yellow-key.png differ diff --git a/dev-assets/doodads/doors/yellow1.png b/dev-assets/doodads/doors/yellow1.png new file mode 100644 index 0000000..b6a6e72 Binary files /dev/null and b/dev-assets/doodads/doors/yellow1.png differ diff --git a/dev-assets/doodads/doors/yellow2.png b/dev-assets/doodads/doors/yellow2.png new file mode 100644 index 0000000..a985563 Binary files /dev/null and b/dev-assets/doodads/doors/yellow2.png differ diff --git a/dev-assets/doodads/palette.json b/dev-assets/doodads/palette.json new file mode 100644 index 0000000..a2f9e19 --- /dev/null +++ b/dev-assets/doodads/palette.json @@ -0,0 +1,35 @@ +{ + "#000000": { + "name": "black" + }, + "#666666": { + "name": "dark-grey" + }, + "#999999": { + "name": "grey" + }, + "#CCCCCC": { + "name": "light-grey" + }, + "#FF0000": { + "name": "red" + }, + "#0099FF": { + "name": "light-blue" + }, + "#0000FF": { + "name": "blue" + }, + "#009900": { + "name": "green" + }, + "#999900": { + "name": "gold" + }, + "#4D391B": { + "name": "brown" + }, + "#8B652C": { + "name": "light-brown" + } +} diff --git a/dev-assets/doodads/trapdoors/README.md b/dev-assets/doodads/trapdoors/README.md new file mode 100644 index 0000000..02d30b2 --- /dev/null +++ b/dev-assets/doodads/trapdoors/README.md @@ -0,0 +1,5 @@ +# Button Doodads + +```bash +doodad convert -t "Trapdoor Down" down{1,2,3}.png trapdoor-down.doodad +``` diff --git a/dev-assets/doodads/trapdoors/down1.png b/dev-assets/doodads/trapdoors/down1.png new file mode 100644 index 0000000..4af2ba4 Binary files /dev/null and b/dev-assets/doodads/trapdoors/down1.png differ diff --git a/dev-assets/doodads/trapdoors/down2.png b/dev-assets/doodads/trapdoors/down2.png new file mode 100644 index 0000000..51a7c8c Binary files /dev/null and b/dev-assets/doodads/trapdoors/down2.png differ diff --git a/dev-assets/doodads/trapdoors/down3.png b/dev-assets/doodads/trapdoors/down3.png new file mode 100644 index 0000000..986e8b4 Binary files /dev/null and b/dev-assets/doodads/trapdoors/down3.png differ diff --git a/dev-assets/doodads/trapdoors/down4.png b/dev-assets/doodads/trapdoors/down4.png new file mode 100644 index 0000000..5e6e5f2 Binary files /dev/null and b/dev-assets/doodads/trapdoors/down4.png differ diff --git a/pkg/level/chunk_map.go b/pkg/level/chunk_map.go index a5b5def..abc0e7e 100644 --- a/pkg/level/chunk_map.go +++ b/pkg/level/chunk_map.go @@ -22,7 +22,7 @@ func (a MapAccessor) Inflate(pal *Palette) error { for point, swatch := range a { if swatch.IsSparse() { // Replace this with the correct swatch from the palette. - if len(pal.Swatches) < swatch.paletteIndex { + if swatch.paletteIndex >= len(pal.Swatches) { return fmt.Errorf("MapAccessor.Inflate: swatch for point %s has paletteIndex %d but palette has only %d colors", point, swatch.paletteIndex,