Noah Petherbridge
f76ba6fbb7
* Add some encoding/decoding functions for binary msgpack format for levels and doodads. Currently it writes msgpack files that can be decoded and printed by Python (mp2json.py) but it can't re-read from the binary format. For now, levels will continue to write in JSON format. * Add filesystem abstraction functions to the balance/ package to search multiple paths to find Levels and Doodads, to make way for system-level doodads.
193 lines
4.5 KiB
Go
193 lines
4.5 KiB
Go
package level
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.kirsle.net/apps/doodle/lib/render"
|
|
"github.com/vmihailenco/msgpack"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// // MarshalMsgpack serializes for msgpack.
|
|
// func (a MapAccessor) MarshalMsgpack() ([]byte, error) {
|
|
// dict := map[string]int{}
|
|
// for point, sw := range a {
|
|
// dict[point.String()] = sw.Index()
|
|
// }
|
|
// return msgpack.Marshal(dict)
|
|
// }
|
|
//
|
|
// // Serialize converts the chunk accessor to a map for serialization.
|
|
// func (a MapAccessor) Serialize() interface{} {
|
|
// dict := map[string]int{}
|
|
// for point, sw := range a {
|
|
// dict[point.String()] = sw.Index()
|
|
// }
|
|
// return dict
|
|
// }
|
|
//
|
|
// // UnmarshalMsgpack decodes from msgpack format.
|
|
// func (a MapAccessor) UnmarshalMsgpack(b []byte) error {
|
|
// var dict map[string]int
|
|
// err := msgpack.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
|
|
// }
|
|
|
|
func (a MapAccessor) EncodeMsgpack(enc *msgpack.Encoder) error {
|
|
dict := map[string]int{}
|
|
for point, sw := range a {
|
|
dict[point.String()] = sw.Index()
|
|
}
|
|
return enc.Encode(dict)
|
|
}
|
|
|
|
func (a MapAccessor) DecodeMsgpack(dec *msgpack.Decoder) error {
|
|
v, err := dec.DecodeMap()
|
|
if err != nil {
|
|
return fmt.Errorf("MapAccessor.DecodeMsgpack: %s", err)
|
|
}
|
|
dict := v.(map[string]int)
|
|
|
|
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
|
|
}
|