Chunker size to uint8 and Rectangular Doodads #84

Merged
kirsle merged 3 commits from file-format-optimization into master 2023-02-18 05:49:49 +00:00
14 changed files with 173 additions and 87 deletions
Showing only changes of commit 31097881ff - Show all commits

View File

@ -15,6 +15,7 @@ import (
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads" "git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
"git.kirsle.net/SketchyMaze/doodle/pkg/level" "git.kirsle.net/SketchyMaze/doodle/pkg/level"
"git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/go/render" "git.kirsle.net/go/render"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/image/bmp" "golang.org/x/image/bmp"
@ -104,7 +105,6 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
// Read the source images. Ensure they all have the same boundaries. // Read the source images. Ensure they all have the same boundaries.
var ( var (
imageBounds image.Point imageBounds image.Point
chunkSize uint8 // the square shape for the Doodad chunk size
width int // dimensions of the incoming image width int // dimensions of the incoming image
height int height int
images []image.Image images []image.Image
@ -131,11 +131,8 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
// Validate all images are the same size. // Validate all images are the same size.
if i == 0 { if i == 0 {
imageBounds = imageSize imageBounds = imageSize
if imageSize.X > imageSize.Y {
width = imageSize.X width = imageSize.X
} else {
height = imageSize.Y height = imageSize.Y
}
} else if imageSize != imageBounds { } else if imageSize != imageBounds {
return cli.Exit("your source images are not all the same dimensions", 1) return cli.Exit("your source images are not all the same dimensions", 1)
} }
@ -152,17 +149,18 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
// Generate the output drawing file. // Generate the output drawing file.
switch strings.ToLower(filepath.Ext(outputFile)) { switch strings.ToLower(filepath.Ext(outputFile)) {
case extDoodad: case extDoodad:
log.Info("Output is a Doodad file (chunk size %d): %s", chunkSize, outputFile)
doodad := doodads.New(width, height) doodad := doodads.New(width, height)
doodad.GameVersion = branding.Version doodad.GameVersion = branding.Version
doodad.Title = c.String("title") doodad.Title = c.String("title")
if doodad.Title == "" { if doodad.Title == "" {
doodad.Title = "Converted Doodad" doodad.Title = "Converted Doodad"
} }
doodad.Author = os.Getenv("USER") doodad.Author = native.DefaultAuthor()
// Write the first layer and gather its palette. // Write the first layer and gather its palette.
log.Info("Converting first layer to drawing and getting the palette") log.Info("Converting first layer to drawing and getting the palette")
var chunkSize = doodad.ChunkSize8()
log.Info("Output is a Doodad file (%dx%d): %s", width, height, outputFile)
palette, layer0 := imageToChunker(images[0], chroma, nil, chunkSize) palette, layer0 := imageToChunker(images[0], chroma, nil, chunkSize)
doodad.Palette = palette doodad.Palette = palette
doodad.Layers[0].Chunker = layer0 doodad.Layers[0].Chunker = layer0
@ -197,7 +195,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
if lvl.Title == "" { if lvl.Title == "" {
lvl.Title = "Converted Level" lvl.Title = "Converted Level"
} }
lvl.Author = os.Getenv("USER") lvl.Author = native.DefaultAuthor()
palette, chunker := imageToChunker(images[0], chroma, nil, lvl.Chunker.Size) palette, chunker := imageToChunker(images[0], chroma, nil, lvl.Chunker.Size)
lvl.Palette = palette lvl.Palette = palette
lvl.Chunker = chunker lvl.Chunker = chunker

View File

@ -42,23 +42,29 @@ You can give it one or two values for dimensions:
func New(dimensions ...int) *Doodad { func New(dimensions ...int) *Doodad {
var ( var (
// Defaults // Defaults
size = balance.DoodadSize size int
chunkSize = balance.ChunkSize chunkSize uint8
width int width int
height int height int
) )
switch len(dimensions) { switch len(dimensions) {
case 1: case 1:
size = dimensions[0] width, height = dimensions[0], dimensions[0]
case 2: case 2:
width, height = dimensions[0], dimensions[1] width, height = dimensions[0], dimensions[1]
} }
// If no width/height, make it a classic square doodad. // Set the desired chunkSize to be the largest dimension.
if width+height == 0 { if width > height {
width = size size = width
height = size } 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 // Pick an optimal chunk size - if our doodad size

View File

@ -1,7 +1,6 @@
package doodads package doodads
import ( import (
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/go/render" "git.kirsle.net/go/render"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -25,7 +24,6 @@ func NewDrawing(id string, doodad *Doodad) *Drawing {
if id == "" { if id == "" {
id = uuid.Must(uuid.NewUUID()).String() id = uuid.Must(uuid.NewUUID()).String()
} }
log.Warn("NewDraging(%s): doodad.Rect=%s doodad.Size=%s", doodad.Title, doodad.Rect(), doodad.Size)
return &Drawing{ return &Drawing{
id: id, id: id,
Doodad: doodad, Doodad: doodad,

View File

@ -6,8 +6,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/go/render"
) )
// ToZipfile serializes the doodad into zipfile format. // ToZipfile serializes the doodad into zipfile format.
@ -63,7 +63,7 @@ func (d *Doodad) ToZipfile() ([]byte, error) {
// FromZipfile reads a doodad from zipfile format. // FromZipfile reads a doodad from zipfile format.
func FromZipfile(data []byte) (*Doodad, error) { func FromZipfile(data []byte) (*Doodad, error) {
var ( var (
doodad = New(balance.DoodadSize) doodad = New(0)
err = doodad.populateFromZipfile(data) err = doodad.populateFromZipfile(data)
) )
return doodad, err return doodad, err
@ -109,6 +109,13 @@ func (d *Doodad) populateFromZipfile(data []byte) error {
// Re-inflate data after saving a new zipfile. // Re-inflate data after saving a new zipfile.
d.Inflate() d.Inflate()
// If we are a legacy doodad and don't have a Size (width x height),
// set it from the chunk size.
if d.Size.IsZero() {
var size = d.ChunkSize()
d.Size = render.NewRect(size, size)
}
return err return err
} }

View File

@ -3,7 +3,6 @@ package doodle
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -19,6 +18,7 @@ import (
"git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/modal" "git.kirsle.net/SketchyMaze/doodle/pkg/modal"
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen" "git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/SketchyMaze/doodle/pkg/usercfg" "git.kirsle.net/SketchyMaze/doodle/pkg/usercfg"
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir" "git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
"git.kirsle.net/SketchyMaze/doodle/pkg/windows" "git.kirsle.net/SketchyMaze/doodle/pkg/windows"
@ -512,7 +512,7 @@ func (s *EditorScene) SaveLevel(filename string) error {
m.Title = "Alpha" m.Title = "Alpha"
} }
if m.Author == "" { if m.Author == "" {
m.Author = os.Getenv("USER") m.Author = native.DefaultAuthor()
} }
m.Palette = s.UI.Canvas.Palette m.Palette = s.UI.Canvas.Palette
@ -595,7 +595,7 @@ func (s *EditorScene) SaveDoodad(filename string) error {
d.Title = "Untitled Doodad" d.Title = "Untitled Doodad"
} }
if d.Author == "" { if d.Author == "" {
d.Author = os.Getenv("USER") d.Author = native.DefaultAuthor()
} }
// TODO: is this copying necessary? // TODO: is this copying necessary?

View File

@ -4,12 +4,12 @@ import (
"archive/zip" "archive/zip"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance" "git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool" "git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
"git.kirsle.net/SketchyMaze/doodle/pkg/enum" "git.kirsle.net/SketchyMaze/doodle/pkg/enum"
"git.kirsle.net/SketchyMaze/doodle/pkg/log" "git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/go/render" "git.kirsle.net/go/render"
) )
@ -83,7 +83,7 @@ func New() *Level {
Base: Base{ Base: Base{
Version: 1, Version: 1,
Title: "Untitled", Title: "Untitled",
Author: os.Getenv("USER"), Author: native.DefaultAuthor(),
Files: NewFileSystem(), Files: NewFileSystem(),
}, },
Chunker: NewChunker(balance.ChunkSize), Chunker: NewChunker(balance.ChunkSize),

29
pkg/native/username.go Normal file
View File

@ -0,0 +1,29 @@
package native
import (
"os"
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
)
var USER string = os.Getenv("USER")
/*
DefaultAuthor will return the local user's name to be the default Author
for levels and doodads they create.
If they have registered the game, use the name from their license JWT token.
Otherwise fall back to their native operating system user.
*/
func DefaultAuthor() string {
// Are we registered?
if license.IsRegistered() {
if reg, err := license.GetRegistration(); err == nil {
return reg.Name
}
}
// Return OS username
return os.Getenv("USER")
}

View File

@ -348,6 +348,11 @@ func (s *PlayScene) PlaceResizeCanvas() {
}) })
} }
// Canvas returns the main level canvas - useful to call from the debug console as `d.Scene.Canvas()`
func (s *PlayScene) Canvas() *uix.Canvas {
return s.drawing
}
// SetPlayerCharacter changes the doodad used for the player, by destroying the // SetPlayerCharacter changes the doodad used for the player, by destroying the
// current player character and making it from scratch. // current player character and making it from scratch.
func (s *PlayScene) SetPlayerCharacter(filename string) { func (s *PlayScene) SetPlayerCharacter(filename string) {

View File

@ -164,7 +164,7 @@ func (a *Actor) SetWet(v bool) {
// Size returns the size of the actor, from the underlying doodads.Drawing. // Size returns the size of the actor, from the underlying doodads.Drawing.
func (a *Actor) Size() render.Rect { func (a *Actor) Size() render.Rect {
return a.Drawing.Size() return a.Drawing.Doodad.Size
} }
// Velocity returns the actor's current velocity vector. // Velocity returns the actor's current velocity vector.

View File

@ -36,6 +36,11 @@ type Canvas struct {
Scrollable bool // Cursor keys will scroll the viewport of this canvas. Scrollable bool // Cursor keys will scroll the viewport of this canvas.
Zoom int // Zoom level on the canvas. Zoom int // Zoom level on the canvas.
// Set this if your Canvas is a small fixed size (e.g. in doodad dropper),
// so that doodads will crop their texture (if chunk size larger than your
// Canvas) as to not overflow the canvas bounds. Not needed for Level canvases.
CroppedSize bool
// Toogle for doodad canvases in the Level Editor to show their buttons. // Toogle for doodad canvases in the Level Editor to show their buttons.
ShowDoodadButtons bool ShowDoodadButtons bool
doodadButtonFrame ui.Widget // lazy init doodadButtonFrame ui.Widget // lazy init

View File

@ -220,7 +220,7 @@ func (w *Canvas) drawActors(e render.Engine, p render.Point) {
// Hitting the left edge. Cap the X coord and shrink the width. // Hitting the left edge. Cap the X coord and shrink the width.
delta := p.X - drawAt.X // positive number delta := p.X - drawAt.X // positive number
drawAt.X = p.X drawAt.X = p.X
// scrollTo.X -= delta // TODO scrollTo.X -= delta
resizeTo.W -= delta resizeTo.W -= delta
} }
@ -232,6 +232,7 @@ func (w *Canvas) drawActors(e render.Engine, p render.Point) {
// Hitting the top edge. Cap the Y coord and shrink the height. // Hitting the top edge. Cap the Y coord and shrink the height.
delta := p.Y - drawAt.Y delta := p.Y - drawAt.Y
drawAt.Y = p.Y drawAt.Y = p.Y
scrollTo.Y -= delta
resizeTo.H -= delta resizeTo.H -= delta
} }

26
pkg/uix/canvas_debug.go Normal file
View File

@ -0,0 +1,26 @@
package uix
import "strings"
// Some debugging functions for the Canvas reachable via dev console in-game.
// GetCanvasesByActorName searches a (level) canvas's installed actors and returns any of
// them having this Title or Filename, with filename being more precise.
func (c *Canvas) GetCanvasesByActorName(filename string) []*Canvas {
var (
byFilename = []*Canvas{}
byTitle = []*Canvas{}
lower = strings.ToLower(filename)
)
for _, a := range c.actors {
var doodad = a.Doodad()
if doodad.Filename == filename {
byFilename = append(byFilename, a.Canvas)
} else if strings.ToLower(doodad.Title) == lower {
byTitle = append(byTitle, a.Canvas)
}
}
return append(byFilename, byTitle...)
}

View File

@ -114,12 +114,16 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// into which it will render, cap the source width and height. // into which it will render, cap the source width and height.
// This is especially useful for Doodad buttons because the drawing // This is especially useful for Doodad buttons because the drawing
// is bigger than the button. // is bigger than the button.
if w.CroppedSize {
// NOTE: this is a concern mainly for the Doodad Dropper so that
// the doodads won't overflow the button size they appear in.
if src.W > S.W { if src.W > S.W {
src.W = S.W src.W = S.W
} }
if src.H > S.H { if src.H > S.H {
src.H = S.H src.H = S.H
} }
}
var size = int(chunk.Size) var size = int(chunk.Size)
dst := render.Rect{ dst := render.Rect{
@ -135,6 +139,10 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// TODO: all this shit is in TrimBox(), make it DRY // TODO: all this shit is in TrimBox(), make it DRY
// wtf? don't need all this code anymore??
_ = ParentPosition
/*
// If the destination width will cause it to overflow the widget // If the destination width will cause it to overflow the widget
// box, trim off the right edge of the destination rect. // box, trim off the right edge of the destination rect.
// //
@ -192,6 +200,8 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
dst.W = S.W - w.BoxThickness(1) dst.W = S.W - w.BoxThickness(1)
} }
*/
// When zooming OUT, make sure the source rect is at least the // When zooming OUT, make sure the source rect is at least the
// full size of the chunk texture; otherwise the ZoomMultiplies // full size of the chunk texture; otherwise the ZoomMultiplies
// above do correctly scale e.g. 128x128 to 64x64, but it only // above do correctly scale e.g. 128x128 to 64x64, but it only

View File

@ -262,6 +262,7 @@ func makeDoodadTab(config DoodadDropper, frame *ui.Frame, size render.Rect, cate
canvases = append(canvases, can) canvases = append(canvases, can)
btn := ui.NewButton(doodad.Title, can) btn := ui.NewButton(doodad.Title, can)
can.CroppedSize = true
btn.Resize(render.NewRect( btn.Resize(render.NewRect(
buttonSize-2, // TODO: without the -2 the button border buttonSize-2, // TODO: without the -2 the button border
buttonSize-2, // rests on top of the window border buttonSize-2, // rests on top of the window border