package doodads import ( "git.kirsle.net/SketchyMaze/doodle/pkg/balance" "git.kirsle.net/SketchyMaze/doodle/pkg/drawtool" "git.kirsle.net/SketchyMaze/doodle/pkg/level" "git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/go/render" ) // Doodad is a reusable component for Levels that have scripts and graphics. type Doodad struct { level.Base Filename string `json:"-"` // used internally, not saved in json Hidden bool `json:"hidden,omitempty"` Palette *level.Palette `json:"palette"` Size render.Rect `json:"size"` // doodad dimensions Script string `json:"script"` Hitbox render.Rect `json:"hitbox"` Layers []Layer `json:"layers"` Tags map[string]string `json:"data"` // arbitrary key/value data storage Options map[string]*Option `json:"options"` // runtime options for a doodad // Undo history, temporary live data not persisted to the level file. UndoHistory *drawtool.History `json:"-"` } // Layer holds a layer of drawing data for a Doodad. type Layer struct { Name string `json:"name"` Chunker *level.Chunker `json:"chunks"` } /* New creates a new Doodad. You can give it one or two values for dimensions: - New(size int) creates a square doodad (classic) - New(width, height int) lets you have a different width x height. */ func New(dimensions ...int) *Doodad { var ( // Defaults size int chunkSize uint8 width int height int ) switch len(dimensions) { case 1: width, height = dimensions[0], dimensions[0] case 2: width, height = dimensions[0], dimensions[1] } // Set the desired chunkSize to be the largest dimension. if width > height { size = width } else { size = height } // If no size at all, fall back on the default. if size == 0 { size = int(balance.ChunkSize) } // Pick an optimal chunk size - if our doodad size // is under 256 use only one chunk per layer by matching // that size. if size <= 255 { chunkSize = uint8(size) } else { chunkSize = balance.ChunkSize } return &Doodad{ Base: level.Base{ Version: 1, }, Palette: level.DefaultPalette(), Hitbox: render.NewRect(width, height), Size: render.NewRect(width, height), Layers: []Layer{ { Name: "main", Chunker: level.NewChunker(chunkSize), }, }, Tags: map[string]string{}, Options: map[string]*Option{}, UndoHistory: drawtool.NewHistory(balance.UndoHistory), } } // AddLayer adds a new layer to the doodad. Call this rather than appending // your own layer so it points the Zipfile and layer number in. The chunker // is optional - pass nil and a new blank chunker is created. func (d *Doodad) AddLayer(name string, chunker *level.Chunker) Layer { if chunker == nil { chunker = level.NewChunker(d.ChunkSize8()) } layer := Layer{ Name: name, Chunker: chunker, } layer.Chunker.Layer = len(d.Layers) d.Layers = append(d.Layers, layer) d.Inflate() return layer } // Teardown cleans up texture cache memory when the doodad is no longer needed by the game. func (d *Doodad) Teardown() { var ( chunks int textures int ) for _, layer := range d.Layers { for coord := range layer.Chunker.IterChunks() { if chunk, ok := layer.Chunker.GetChunk(coord); ok { freed := chunk.Teardown() chunks++ textures += freed } } } // Debug log if any textures were actually freed. if textures > 0 { log.Debug("Teardown doodad (%s): Freed %d textures across %d chunks", d.Title, textures, chunks) } } // Tag gets a value from the doodad's tags. func (d *Doodad) Tag(name string) string { if v, ok := d.Tags[name]; ok { return v } log.Warn("Doodad(%s).Tag(%s): tag not defined", d.Title, name) return "" } // ChunkSize returns the chunk size of the Doodad's first layer. func (d *Doodad) ChunkSize() int { return int(d.Layers[0].Chunker.Size) } // ChunkSize8 returns the chunk size of the Doodad's first layer as its actual uint8 value. func (d *Doodad) ChunkSize8() uint8 { return d.Layers[0].Chunker.Size } // Rect returns a rect of the ChunkSize for scaling a Canvas widget. func (d *Doodad) Rect() render.Rect { var size = int(d.ChunkSize()) return render.Rect{ W: size, H: size, } } // Inflate attaches the pixels to their swatches after loading from disk. func (d *Doodad) Inflate() { d.Palette.Inflate() for i, layer := range d.Layers { layer.Chunker.Layer = i layer.Chunker.Inflate(d.Palette) } }