doodle/pkg/level/chunk_map.go
Noah Petherbridge 81cb3bd617 1st Round of Doodad Sprites + Improve Doodad Tool
* Improve the `doodad convert` command to convert a series of input
  images into multiple Frames of a Doodad:
  `doodad convert frame1.png frame2.png frameN.png output.doodad`
* Add the initial round of dev-asset sprites for the default Doodads:
  * Button, Button-TypeB and Sticky Button
  * Red, Blue, Green and Yellow Locked Doors and Keys
  * Electric Door
  * Trapdoor Down
* Add dev-assets/palette.json that defines our default doodad color
  palette. Eventually the JSON will be used by the `doodad` tool to give
  the layers meaningful names.
2019-04-17 00:02:41 -07:00

130 lines
2.9 KiB
Go

package level
import (
"encoding/json"
"errors"
"fmt"
"git.kirsle.net/apps/doodle/lib/render"
)
// MapAccessor implements a chunk accessor by using a map of points to their
// palette indexes. This is the simplest accessor and is best for sparse chunks.
type MapAccessor map[render.Point]*Swatch
// NewMapAccessor initializes a MapAccessor.
func NewMapAccessor() MapAccessor {
return MapAccessor{}
}
// Inflate the sparse swatches from their palette indexes.
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 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,
len(pal.Swatches),
)
}
a[point] = pal.Swatches[swatch.paletteIndex]
}
}
return nil
}
// Len returns the current size of the map, or number of pixels registered.
func (a MapAccessor) Len() int {
return len(a)
}
// IterViewport returns a channel to loop over pixels in the viewport.
func (a MapAccessor) IterViewport(viewport render.Rect) <-chan Pixel {
pipe := make(chan Pixel)
go func() {
for px := range a.Iter() {
if px.Point().Inside(viewport) {
pipe <- px
}
}
close(pipe)
}()
return pipe
}
// Iter returns a channel to loop over all points in this chunk.
func (a MapAccessor) Iter() <-chan Pixel {
pipe := make(chan Pixel)
go func() {
for point, swatch := range a {
pipe <- Pixel{
X: point.X,
Y: point.Y,
Swatch: swatch,
}
}
close(pipe)
}()
return pipe
}
// Get a pixel from the map.
func (a MapAccessor) Get(p render.Point) (*Swatch, error) {
pixel, ok := a[p]
if !ok {
return nil, errors.New("no pixel")
}
return pixel, nil
}
// Set a pixel on the map.
func (a MapAccessor) Set(p render.Point, sw *Swatch) error {
a[p] = sw
return nil
}
// Delete a pixel from the map.
func (a MapAccessor) Delete(p render.Point) error {
if _, ok := a[p]; ok {
delete(a, p)
return nil
}
return errors.New("pixel was not there")
}
// MarshalJSON to convert the chunk map to JSON.
//
// When serialized, the key is the "X,Y" coordinate and the value is the
// swatch index of the Palette, rather than redundantly serializing out the
// Swatch object for every pixel.
func (a MapAccessor) MarshalJSON() ([]byte, error) {
dict := map[string]int{}
for point, sw := range a {
dict[point.String()] = sw.Index()
}
out, err := json.Marshal(dict)
return out, err
}
// UnmarshalJSON to convert the chunk map back from JSON.
func (a MapAccessor) UnmarshalJSON(b []byte) error {
var dict map[string]int
err := json.Unmarshal(b, &dict)
if err != nil {
return err
}
for coord, index := range dict {
point, err := render.ParsePoint(coord)
if err != nil {
return fmt.Errorf("MapAccessor.UnmarshalJSON: %s", err)
}
a[point] = NewSparseSwatch(index)
}
return nil
}