diff --git a/cmd/doodad/commands/convert.go b/cmd/doodad/commands/convert.go index c6bcc29..d73b655 100644 --- a/cmd/doodad/commands/convert.go +++ b/cmd/doodad/commands/convert.go @@ -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 diff --git a/pkg/doodads/doodad.go b/pkg/doodads/doodad.go index 3ea8301..4803432 100644 --- a/pkg/doodads/doodad.go +++ b/pkg/doodads/doodad.go @@ -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 diff --git a/pkg/doodads/drawing.go b/pkg/doodads/drawing.go index e8a6312..f57a282 100644 --- a/pkg/doodads/drawing.go +++ b/pkg/doodads/drawing.go @@ -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, diff --git a/pkg/doodads/fmt_zipfile.go b/pkg/doodads/fmt_zipfile.go index bc3b39b..c8f5142 100644 --- a/pkg/doodads/fmt_zipfile.go +++ b/pkg/doodads/fmt_zipfile.go @@ -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 } diff --git a/pkg/editor_scene.go b/pkg/editor_scene.go index 4a5f145..e5c4510 100644 --- a/pkg/editor_scene.go +++ b/pkg/editor_scene.go @@ -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? diff --git a/pkg/level/types.go b/pkg/level/types.go index cf23529..6e40690 100644 --- a/pkg/level/types.go +++ b/pkg/level/types.go @@ -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), diff --git a/pkg/native/username.go b/pkg/native/username.go new file mode 100644 index 0000000..85d9298 --- /dev/null +++ b/pkg/native/username.go @@ -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") +} diff --git a/pkg/play_scene.go b/pkg/play_scene.go index 9dd8acd..7827fea 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -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) { diff --git a/pkg/uix/actor.go b/pkg/uix/actor.go index 19c9801..e9bfa3c 100644 --- a/pkg/uix/actor.go +++ b/pkg/uix/actor.go @@ -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. diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index 32dd80e..cee6321 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -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 diff --git a/pkg/uix/canvas_actors.go b/pkg/uix/canvas_actors.go index 3250403..bae6f97 100644 --- a/pkg/uix/canvas_actors.go +++ b/pkg/uix/canvas_actors.go @@ -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 } diff --git a/pkg/uix/canvas_debug.go b/pkg/uix/canvas_debug.go new file mode 100644 index 0000000..1268164 --- /dev/null +++ b/pkg/uix/canvas_debug.go @@ -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...) +} diff --git a/pkg/uix/canvas_present.go b/pkg/uix/canvas_present.go index 8f6ad5d..cc742fa 100644 --- a/pkg/uix/canvas_present.go +++ b/pkg/uix/canvas_present.go @@ -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 diff --git a/pkg/windows/doodad_dropper.go b/pkg/windows/doodad_dropper.go index 5c3baea..74049ed 100644 --- a/pkg/windows/doodad_dropper.go +++ b/pkg/windows/doodad_dropper.go @@ -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