doodle/pkg/level/types.go
Noah Petherbridge a79601f983 D++ Default Author and Embedded Doodads Error
* Update native.DefaultAuthor to get the name registered from the user's JWT
  license in a way that avoids cyclic dependency errors.
* When plus_dpp.go#GetRegistration succeeds, it updates DefaultAuthor to the
  registered name. The main.go now gets and prints the registered owner to
  ensure this is populated on startup.
* Return correct ErrRegisteredFeature error when the FOSS version fails
  to load embedded doodads.
2024-04-18 22:31:11 -07:00

180 lines
4.7 KiB
Go

package level
import (
"archive/zip"
"encoding/json"
"fmt"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// Useful variables.
var (
DefaultWallpaper = "notebook.png"
)
// Base provides the common struct keys that are shared between Levels and
// Doodads.
type Base struct {
Version int `json:"version"` // File format version spec.
GameVersion string `json:"gameVersion"` // Game version that created the level.
Title string `json:"title"`
Author string `json:"author"`
Locked bool `json:"locked"`
// v2 level format: zip files with external chunks.
// (v0 was json text, v1 was gzip compressed json text).
// The game must load levels created using the previous
// formats, they will not have a Zipfile and will have
// Chunkers in memory from their (gz) json.
Zipfile *zip.Reader `json:"-"`
// Every drawing type is able to embed other files inside of itself.
Files *FileSystem `json:"files,omitempty"`
}
// Level is the container format for Doodle map drawings.
type Level struct {
Base
Password string `json:"passwd"`
UUID string `json:"uuid"` // unique level IDs, especially for the savegame.json
GameRule GameRule `json:"rules"`
// Chunked pixel data.
Chunker *Chunker `json:"chunks"`
// The Palette holds the unique "colors" used in this map file, and their
// properties (solid, fire, slippery, etc.)
Palette *Palette `json:"palette"`
// Page boundaries and wallpaper settings.
PageType PageType `json:"pageType"`
MaxWidth int64 `json:"boundedWidth"` // only if bounded or bordered
MaxHeight int64 `json:"boundedHeight"`
Wallpaper string `json:"wallpaper"`
// The last scrolled position in the editor.
ScrollPosition render.Point `json:"scroll"`
// Actors keep a list of the doodad instances in this map.
Actors ActorMap `json:"actors"`
// Publishing: attach any custom doodads the map uses on save.
SaveDoodads bool `json:"saveDoodads"`
SaveBuiltins bool `json:"saveBuiltins"`
// Signature for a level with embedded doodads to still play in free mode.
Signature []byte `json:"signature,omitempty"`
// Undo history, temporary live data not persisted to the level file.
UndoHistory *drawtool.History `json:"-"`
// Cache of loaded images (e.g. screenshots).
cacheImages map[string]*ui.Image
}
// GameRule
type GameRule struct {
Difficulty enum.Difficulty `json:"difficulty"`
Survival bool `json:"survival,omitempty"`
}
// New creates a blank level object with all its members initialized.
func New() *Level {
return &Level{
Base: Base{
Version: 1,
Title: "Untitled",
Author: native.DefaultAuthor,
Files: NewFileSystem(),
},
Chunker: NewChunker(balance.ChunkSize),
Palette: &Palette{},
Actors: ActorMap{},
PageType: NoNegativeSpace,
Wallpaper: DefaultWallpaper,
MaxWidth: 2550,
MaxHeight: 3300,
UndoHistory: drawtool.NewHistory(balance.UndoHistory),
}
}
// Teardown the level when the game is done with it. This frees up SDL2 cached
// texture chunks and reclaims memory in ways the Go garbage collector can not.
func (m *Level) Teardown() {
var (
chunks int
textures int
)
// Free any CACHED chunks' memory.
for chunk := range m.Chunker.IterCachedChunks() {
freed := chunk.Teardown()
chunks++
textures += freed
}
// Free any cached images (screenshots)
if m.cacheImages != nil {
for _, img := range m.cacheImages {
img.Destroy()
}
}
log.Debug("Teardown level (%s): Freed %d textures across %d level chunks", m.Title, textures, chunks)
}
// Pixel associates a coordinate with a palette index.
type Pixel struct {
X int `json:"x"`
Y int `json:"y"`
PaletteIndex int `json:"p"`
// Private runtime values.
Swatch *Swatch `json:"-"` // pointer to its swatch, for when rendered.
}
func (p Pixel) String() string {
return fmt.Sprintf("Pixel<%s '%s' (%d,%d)>", p.Swatch.Color, p.Swatch.Name, p.X, p.Y)
}
// Point returns the pixel's point.
func (p Pixel) Point() render.Point {
return render.Point{
X: p.X,
Y: p.Y,
}
}
// MarshalJSON serializes a Pixel compactly as a simple list.
func (p Pixel) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(
`[%d, %d, %d]`,
p.X, p.Y, p.PaletteIndex,
)), nil
}
// UnmarshalJSON loads a Pixel from JSON again.
func (p *Pixel) UnmarshalJSON(text []byte) error {
var triplet []int
err := json.Unmarshal(text, &triplet)
if err != nil {
return err
}
p.X = triplet[0]
p.Y = triplet[1]
if len(triplet) > 2 {
p.PaletteIndex = triplet[2]
}
return nil
}