doodle/pkg/level/publishing/publishing.go

121 lines
3.2 KiB
Go

/*
Package publishing contains functionality for "publishing" a Level, which
involves the writing and reading of custom doodads embedded inside
the levels.
Free tiers of the game will not read or write embedded doodads into
levels.
*/
package publishing
import (
"errors"
"fmt"
"sort"
"strings"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
)
/*
Level "Publishing" functions, involving the writing and reading of embedded
doodads within level files.
*/
// Publish writes a published level file, with embedded doodads included.
func Publish(lvl *level.Level) error {
// Not embedding doodads?
if !lvl.SaveDoodads {
if removed := lvl.DeleteFiles(balance.EmbeddedDoodadsBasePath); removed > 0 {
log.Info("Note: removed %d attached doodads because SaveDoodads is false", removed)
}
return nil
}
// Registered games only.
if !license.IsRegistered() {
return errors.New("only registered versions of the game can attach doodads to levels")
}
// Get and embed the doodads.
builtins, customs := GetUsedDoodadNames(lvl)
var names = map[string]interface{}{}
if lvl.SaveBuiltins {
log.Debug("including builtins: %+v", builtins)
customs = append(customs, builtins...)
}
for _, filename := range customs {
log.Debug("Embed filename: %s", filename)
names[filename] = nil
doodad, err := doodads.LoadFromEmbeddable(filename, lvl)
if err != nil {
return fmt.Errorf("couldn't load doodad %s: %s", filename, err)
}
bin, err := doodad.Serialize()
if err != nil {
return fmt.Errorf("couldn't serialize doodad %s: %s", filename, err)
}
// Embed it.
lvl.SetFile(balance.EmbeddedDoodadsBasePath+filename, bin)
}
// Trim any doodads not currently in the level.
for _, filename := range lvl.ListFilesAt(balance.EmbeddedDoodadsBasePath) {
basename := strings.TrimPrefix(filename, balance.EmbeddedDoodadsBasePath)
if _, ok := names[basename]; !ok {
log.Debug("Remove embedded doodad %s (cleanup)", basename)
lvl.DeleteFile(filename)
}
}
return nil
}
// GetUsedDoodadNames returns the lists of doodad filenames in use in a level,
// bucketed by built-in or custom user doodads.
func GetUsedDoodadNames(lvl *level.Level) (builtin []string, custom []string) {
// Collect all the doodad names in use in this level.
unique := map[string]interface{}{}
names := []string{}
if lvl != nil {
for _, actor := range lvl.Actors {
if _, ok := unique[actor.Filename]; ok {
continue
}
unique[actor.Filename] = nil
names = append(names, actor.Filename)
}
}
// Identify which of the doodads are built-ins.
// builtin = []string{}
builtinMap := map[string]interface{}{}
// custom := []string{}
if builtins, err := doodads.ListBuiltin(); err == nil {
for _, filename := range builtins {
if _, ok := unique[filename]; ok {
builtin = append(builtin, filename)
builtinMap[filename] = nil
}
}
}
for _, name := range names {
if _, ok := builtinMap[name]; ok {
continue
}
custom = append(custom, name)
}
sort.Strings(builtin)
sort.Strings(custom)
return builtin, custom
}