Finalize Non-square Doodads

* Fix display bug with rectangular doodads scrolling off screen.
* The default Author of new files will be your registration name, if available
  before using your $USER name.
This commit is contained in:
Noah 2023-02-17 21:09:11 -08:00
parent ddcad27485
commit 31097881ff
14 changed files with 173 additions and 87 deletions

View File

@ -15,6 +15,7 @@ import (
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/go/render"
"github.com/urfave/cli/v2"
"golang.org/x/image/bmp"
@ -104,8 +105,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
// Read the source images. Ensure they all have the same boundaries.
var (
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
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.
if i == 0 {
imageBounds = imageSize
if imageSize.X > imageSize.Y {
width = imageSize.X
} else {
height = imageSize.Y
}
width = imageSize.X
height = imageSize.Y
} else if imageSize != imageBounds {
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.
switch strings.ToLower(filepath.Ext(outputFile)) {
case extDoodad:
log.Info("Output is a Doodad file (chunk size %d): %s", chunkSize, outputFile)
doodad := doodads.New(width, height)
doodad.GameVersion = branding.Version
doodad.Title = c.String("title")
if doodad.Title == "" {
doodad.Title = "Converted Doodad"
}
doodad.Author = os.Getenv("USER")
doodad.Author = native.DefaultAuthor()
// Write the first layer and gather its 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)
doodad.Palette = palette
doodad.Layers[0].Chunker = layer0
@ -197,7 +195,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
if lvl.Title == "" {
lvl.Title = "Converted Level"
}
lvl.Author = os.Getenv("USER")
lvl.Author = native.DefaultAuthor()
palette, chunker := imageToChunker(images[0], chroma, nil, lvl.Chunker.Size)
lvl.Palette = palette
lvl.Chunker = chunker

View File

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

View File

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

View File

@ -6,8 +6,8 @@ import (
"encoding/json"
"fmt"
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
"git.kirsle.net/go/render"
)
// ToZipfile serializes the doodad into zipfile format.
@ -63,7 +63,7 @@ func (d *Doodad) ToZipfile() ([]byte, error) {
// FromZipfile reads a doodad from zipfile format.
func FromZipfile(data []byte) (*Doodad, error) {
var (
doodad = New(balance.DoodadSize)
doodad = New(0)
err = doodad.populateFromZipfile(data)
)
return doodad, err
@ -109,6 +109,13 @@ func (d *Doodad) populateFromZipfile(data []byte) error {
// Re-inflate data after saving a new zipfile.
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
}

View File

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

View File

@ -4,12 +4,12 @@ import (
"archive/zip"
"encoding/json"
"fmt"
"os"
"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"
)
@ -83,7 +83,7 @@ func New() *Level {
Base: Base{
Version: 1,
Title: "Untitled",
Author: os.Getenv("USER"),
Author: native.DefaultAuthor(),
Files: NewFileSystem(),
},
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
// current player character and making it from scratch.
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.
func (a *Actor) Size() render.Rect {
return a.Drawing.Size()
return a.Drawing.Doodad.Size
}
// 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.
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.
ShowDoodadButtons bool
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.
delta := p.X - drawAt.X // positive number
drawAt.X = p.X
// scrollTo.X -= delta // TODO
scrollTo.X -= 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.
delta := p.Y - drawAt.Y
drawAt.Y = p.Y
scrollTo.Y -= 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,11 +114,15 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// into which it will render, cap the source width and height.
// This is especially useful for Doodad buttons because the drawing
// is bigger than the button.
if src.W > S.W {
src.W = S.W
}
if src.H > S.H {
src.H = S.H
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 {
src.W = S.W
}
if src.H > S.H {
src.H = S.H
}
}
var size = int(chunk.Size)
@ -135,62 +139,68 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// TODO: all this shit is in TrimBox(), make it DRY
// If the destination width will cause it to overflow the widget
// box, trim off the right edge of the destination rect.
//
// Keep in mind we're dealing with chunks here, and a chunk is
// a small part of the image. Example:
// - Canvas is 800x600 (S.W=800 S.H=600)
// - Chunk wants to render at 790,0 width 100,100 or whatever
// dst={790, 0, 100, 100}
// - Chunk box would exceed 800px width (X=790 + W=100 == 890)
// - Find the delta how much it exceeds as negative (800 - 890 == -90)
// - Lower the Source and Dest rects by that delta size so they
// stay proportional and don't scale or anything dumb.
if dst.X+src.W > p.X+S.W+w.BoxThickness(1) {
// NOTE: delta is a negative number,
// so it will subtract from the width.
delta := (p.X + S.W - w.BoxThickness(1)) - (dst.W + dst.X)
src.W += delta
dst.W += delta
}
if dst.Y+src.H > p.Y+S.H+w.BoxThickness(1) {
// NOTE: delta is a negative number
delta := (p.Y + S.H - w.BoxThickness(1)) - (dst.H + dst.Y)
src.H += delta
dst.H += delta
}
// wtf? don't need all this code anymore??
_ = ParentPosition
/*
// The same for the top left edge, so the drawings don't overlap
// menu bars or left side toolbars.
// - Canvas was placed 80px from the left of the screen.
// Canvas.MoveTo(80, 0)
// - A texture wants to draw at 60, 0 which would cause it to
// overlap 20 pixels into the left toolbar. It needs to be cropped.
// - The delta is: p.X=80 - dst.X=60 == 20
// - Set destination X to p.X to constrain it there: 20
// - Subtract the delta from destination W so we don't scale it.
// - Add 20 to X of the source: the left edge of source is not visible
//
// Note: the +w.BoxThickness works around a bug if the Actor Canvas has
// a border on it (e.g. in the Actor/Link Tool mouse-over or debug setting)
if dst.X == ParentPosition.X+w.BoxThickness(1) {
// NOTE: delta is a positive number,
// so it will add to the destination coordinates.
delta := texSizeOrig.W - src.W
dst.X = p.X + w.BoxThickness(1)
src.X += delta
}
if dst.Y == ParentPosition.Y+w.BoxThickness(1) {
delta := texSizeOrig.H - src.H
dst.Y = p.Y + w.BoxThickness(1)
src.Y += delta
}
// If the destination width will cause it to overflow the widget
// box, trim off the right edge of the destination rect.
//
// Keep in mind we're dealing with chunks here, and a chunk is
// a small part of the image. Example:
// - Canvas is 800x600 (S.W=800 S.H=600)
// - Chunk wants to render at 790,0 width 100,100 or whatever
// dst={790, 0, 100, 100}
// - Chunk box would exceed 800px width (X=790 + W=100 == 890)
// - Find the delta how much it exceeds as negative (800 - 890 == -90)
// - Lower the Source and Dest rects by that delta size so they
// stay proportional and don't scale or anything dumb.
if dst.X+src.W > p.X+S.W+w.BoxThickness(1) {
// NOTE: delta is a negative number,
// so it will subtract from the width.
delta := (p.X + S.W - w.BoxThickness(1)) - (dst.W + dst.X)
src.W += delta
dst.W += delta
}
if dst.Y+src.H > p.Y+S.H+w.BoxThickness(1) {
// NOTE: delta is a negative number
delta := (p.Y + S.H - w.BoxThickness(1)) - (dst.H + dst.Y)
src.H += delta
dst.H += delta
}
// Trim the destination width so it doesn't overlap the Canvas border.
if dst.W >= S.W-w.BoxThickness(1) {
dst.W = S.W - w.BoxThickness(1)
}
// The same for the top left edge, so the drawings don't overlap
// menu bars or left side toolbars.
// - Canvas was placed 80px from the left of the screen.
// Canvas.MoveTo(80, 0)
// - A texture wants to draw at 60, 0 which would cause it to
// overlap 20 pixels into the left toolbar. It needs to be cropped.
// - The delta is: p.X=80 - dst.X=60 == 20
// - Set destination X to p.X to constrain it there: 20
// - Subtract the delta from destination W so we don't scale it.
// - Add 20 to X of the source: the left edge of source is not visible
//
// Note: the +w.BoxThickness works around a bug if the Actor Canvas has
// a border on it (e.g. in the Actor/Link Tool mouse-over or debug setting)
if dst.X == ParentPosition.X+w.BoxThickness(1) {
// NOTE: delta is a positive number,
// so it will add to the destination coordinates.
delta := texSizeOrig.W - src.W
dst.X = p.X + w.BoxThickness(1)
src.X += delta
}
if dst.Y == ParentPosition.Y+w.BoxThickness(1) {
delta := texSizeOrig.H - src.H
dst.Y = p.Y + w.BoxThickness(1)
src.Y += delta
}
// Trim the destination width so it doesn't overlap the Canvas border.
if 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
// full size of the chunk texture; otherwise the ZoomMultiplies

View File

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