WIP: Chunker size to uint8 and Rectangular Doodads
Convert the Chunker size to a uint8 so chunk sizes are limited to 255px. This means that inside of a chunk, uint8's can track the relative pixel coordinates and result in a great memory savings since all of these uint8's are currently 64-bits wide apiece. WIP on rectangular shaped doodads: * You can create such a doodad in the editor and draw it normally. * It doesn't draw the right size when dragged into your level however: - In uix.Actor.Size() it gets a rect of the doodad's square Chunker size, instead of getting the proper doodad.Size rect. - If you give it the doodad.Size rect, it draws the Canvas size correctly instead of a square - the full drawing appears and in gameplay its hitbox (assuming the same large rectangle size) works correctly in-game. - But, the doodad has scrolling issues when it gets to the top or left edge of the screen! This old gnarly bug has come back. For some reason square canvas doodads draw correctly but rectangular ones have the drawing scroll just a bit - how far it scrolls is proportional to how big the doodad is, with the Start Flag only scrolling a few pixels before it stops.
This commit is contained in:
parent
a10a09a818
commit
ddcad27485
13
bootstrap.py
13
bootstrap.py
|
@ -70,23 +70,22 @@ def main(fast=False):
|
||||||
|
|
||||||
def install_deps(fast):
|
def install_deps(fast):
|
||||||
"""Install system dependencies."""
|
"""Install system dependencies."""
|
||||||
if fast:
|
fast = " -y" if fast else ""
|
||||||
fast = " -y"
|
|
||||||
|
|
||||||
if shell("which rpm") == 0 and shell("which dnf") == 0:
|
if shell("which rpm") == 0 and shell("which dnf") == 0:
|
||||||
# Fedora-like.
|
# Fedora-like.
|
||||||
if shell("rpm -q {}".format(' '.join(dep_fedora))) != 0:
|
if shell("rpm -q {}".format(' '.join(dep_fedora))) != 0:
|
||||||
must_shell("sudo dnf install {}{}".format(' '.join(dep_fedora)), fast)
|
must_shell("sudo dnf install {}{}".format(' '.join(dep_fedora), fast))
|
||||||
elif shell("which brew") == 0:
|
elif shell("which brew") == 0:
|
||||||
# MacOS, as Catalina has an apt command now??
|
# MacOS, as Catalina has an apt command now??
|
||||||
must_shell("brew install {} {}".format(' '.join(dep_macos)), fast)
|
must_shell("brew install {} {}".format(' '.join(dep_macos), fast))
|
||||||
elif shell("which apt") == 0:
|
elif shell("which apt") == 0:
|
||||||
# Debian-like.
|
# Debian-like.
|
||||||
if shell("dpkg-query -l {}".format(' '.join(dep_debian))) != 0:
|
if shell("dpkg-query -l {}".format(' '.join(dep_debian))) != 0:
|
||||||
must_shell("sudo apt update && sudo apt install {}{}".format(' '.join(dep_debian)), fast)
|
must_shell("sudo apt update && sudo apt install {}{}".format(' '.join(dep_debian), fast))
|
||||||
elif shell("which pacman") == 0:
|
elif shell("which pacman") == 0:
|
||||||
# Arch-like.
|
# Arch-like.
|
||||||
must_shell("sudo pacman -S{} {}{}".format(fast, ' '.join(dep_arch)))
|
must_shell("sudo pacman -S{} {}".format(fast, ' '.join(dep_arch)))
|
||||||
else:
|
else:
|
||||||
print("Warning: didn't detect your package manager to install SDL2 and other dependencies")
|
print("Warning: didn't detect your package manager to install SDL2 and other dependencies")
|
||||||
|
|
||||||
|
@ -152,7 +151,7 @@ def must_shell(cmd):
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser("doodle bootstrap")
|
parser = argparse.ArgumentParser("doodle bootstrap")
|
||||||
parser.add_argument("fast", "f",
|
parser.add_argument("--fast", "-f",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Run the script non-interactively (yes to your package manager, git clone over https)",
|
help="Run the script non-interactively (yes to your package manager, git clone over https)",
|
||||||
)
|
)
|
||||||
|
|
|
@ -104,7 +104,9 @@ 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 int // the square shape for the Doodad chunk size
|
chunkSize uint8 // the square shape for the Doodad chunk size
|
||||||
|
width int // dimensions of the incoming image
|
||||||
|
height int
|
||||||
images []image.Image
|
images []image.Image
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -130,9 +132,9 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
imageBounds = imageSize
|
imageBounds = imageSize
|
||||||
if imageSize.X > imageSize.Y {
|
if imageSize.X > imageSize.Y {
|
||||||
chunkSize = imageSize.X
|
width = imageSize.X
|
||||||
} else {
|
} else {
|
||||||
chunkSize = 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)
|
||||||
|
@ -151,7 +153,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
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)
|
log.Info("Output is a Doodad file (chunk size %d): %s", chunkSize, outputFile)
|
||||||
doodad := doodads.New(chunkSize)
|
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 == "" {
|
||||||
|
@ -188,6 +190,9 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
|
|
||||||
lvl := level.New()
|
lvl := level.New()
|
||||||
lvl.GameVersion = branding.Version
|
lvl.GameVersion = branding.Version
|
||||||
|
lvl.MaxWidth = int64(width)
|
||||||
|
lvl.MaxHeight = int64(height)
|
||||||
|
lvl.PageType = level.Bounded
|
||||||
lvl.Title = c.String("title")
|
lvl.Title = c.String("title")
|
||||||
if lvl.Title == "" {
|
if lvl.Title == "" {
|
||||||
lvl.Title = "Converted Level"
|
lvl.Title = "Converted Level"
|
||||||
|
@ -288,7 +293,7 @@ func drawingToImage(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
||||||
//
|
//
|
||||||
// img: input image like a PNG
|
// img: input image like a PNG
|
||||||
// chroma: transparent color
|
// chroma: transparent color
|
||||||
func imageToChunker(img image.Image, chroma render.Color, palette *level.Palette, chunkSize int) (*level.Palette, *level.Chunker) {
|
func imageToChunker(img image.Image, chroma render.Color, palette *level.Palette, chunkSize uint8) (*level.Palette, *level.Chunker) {
|
||||||
var (
|
var (
|
||||||
chunker = level.NewChunker(chunkSize)
|
chunker = level.NewChunker(chunkSize)
|
||||||
bounds = img.Bounds()
|
bounds = img.Bounds()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||||
"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/go/render"
|
"git.kirsle.net/go/render"
|
||||||
|
@ -220,7 +221,13 @@ func editLevel(c *cli.Context, filename string) error {
|
||||||
// Handles the deep operation of re-copying the old level into a new level
|
// Handles the deep operation of re-copying the old level into a new level
|
||||||
// at the new chunk size.
|
// at the new chunk size.
|
||||||
func rechunkLevel(c *cli.Context, filename string, lvl *level.Level) error {
|
func rechunkLevel(c *cli.Context, filename string, lvl *level.Level) error {
|
||||||
var chunkSize = c.Int("resize")
|
var chunkSize = balance.ChunkSize
|
||||||
|
if v := c.Int("resize"); v != 0 {
|
||||||
|
if v > 255 {
|
||||||
|
return errors.New("chunk size must be a uint8 <= 255")
|
||||||
|
}
|
||||||
|
chunkSize = uint8(v)
|
||||||
|
}
|
||||||
log.Info("Resizing the level's chunk size.")
|
log.Info("Resizing the level's chunk size.")
|
||||||
log.Info("Current chunk size: %d", lvl.Chunker.Size)
|
log.Info("Current chunk size: %d", lvl.Chunker.Size)
|
||||||
log.Info("Target chunk size: %d", chunkSize)
|
log.Info("Target chunk size: %d", chunkSize)
|
||||||
|
|
|
@ -205,6 +205,7 @@ func showDoodad(c *cli.Context, filename string) error {
|
||||||
fmt.Printf(" Game version: %s\n", dd.GameVersion)
|
fmt.Printf(" Game version: %s\n", dd.GameVersion)
|
||||||
fmt.Printf(" Doodad title: %s\n", dd.Title)
|
fmt.Printf(" Doodad title: %s\n", dd.Title)
|
||||||
fmt.Printf(" Author: %s\n", dd.Author)
|
fmt.Printf(" Author: %s\n", dd.Author)
|
||||||
|
fmt.Printf(" Dimensions: %s\n", dd.Size)
|
||||||
fmt.Printf(" Hitbox: %s\n", dd.Hitbox)
|
fmt.Printf(" Hitbox: %s\n", dd.Hitbox)
|
||||||
fmt.Printf(" Locked: %+v\n", dd.Locked)
|
fmt.Printf(" Locked: %+v\n", dd.Locked)
|
||||||
fmt.Printf(" Hidden: %+v\n", dd.Hidden)
|
fmt.Printf(" Hidden: %+v\n", dd.Hidden)
|
||||||
|
@ -256,9 +257,12 @@ func showPalette(pal *level.Palette) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func showChunker(c *cli.Context, ch *level.Chunker) {
|
func showChunker(c *cli.Context, ch *level.Chunker) {
|
||||||
var worldSize = ch.WorldSize()
|
var (
|
||||||
var width = worldSize.W - worldSize.X
|
worldSize = ch.WorldSize()
|
||||||
var height = worldSize.H - worldSize.Y
|
chunkSize = int(ch.Size)
|
||||||
|
width = worldSize.W - worldSize.X
|
||||||
|
height = worldSize.H - worldSize.Y
|
||||||
|
)
|
||||||
fmt.Println("Chunks:")
|
fmt.Println("Chunks:")
|
||||||
fmt.Printf(" Pixels Per Chunk: %d^2\n", ch.Size)
|
fmt.Printf(" Pixels Per Chunk: %d^2\n", ch.Size)
|
||||||
fmt.Printf(" Number Generated: %d\n", len(ch.Chunks))
|
fmt.Printf(" Number Generated: %d\n", len(ch.Chunks))
|
||||||
|
@ -277,10 +281,10 @@ func showChunker(c *cli.Context, ch *level.Chunker) {
|
||||||
fmt.Printf(" - Coord: %s\n", point)
|
fmt.Printf(" - Coord: %s\n", point)
|
||||||
fmt.Printf(" Type: %s\n", chunkTypeToName(chunk.Type))
|
fmt.Printf(" Type: %s\n", chunkTypeToName(chunk.Type))
|
||||||
fmt.Printf(" Range: (%d,%d) ... (%d,%d)\n",
|
fmt.Printf(" Range: (%d,%d) ... (%d,%d)\n",
|
||||||
int(point.X)*ch.Size,
|
int(point.X)*chunkSize,
|
||||||
int(point.Y)*ch.Size,
|
int(point.Y)*chunkSize,
|
||||||
(int(point.X)*ch.Size)+ch.Size,
|
(int(point.X)*chunkSize)+chunkSize,
|
||||||
(int(point.Y)*ch.Size)+ch.Size,
|
(int(point.Y)*chunkSize)+chunkSize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -55,7 +55,7 @@ var (
|
||||||
FollowPlayerFirstTicks uint64 = 60
|
FollowPlayerFirstTicks uint64 = 60
|
||||||
|
|
||||||
// Default chunk size for canvases.
|
// Default chunk size for canvases.
|
||||||
ChunkSize = 128
|
ChunkSize uint8 = 128
|
||||||
|
|
||||||
// Default size for a new Doodad.
|
// Default size for a new Doodad.
|
||||||
DoodadSize = 100
|
DoodadSize = 100
|
||||||
|
|
|
@ -14,6 +14,7 @@ type Doodad struct {
|
||||||
Filename string `json:"-"` // used internally, not saved in json
|
Filename string `json:"-"` // used internally, not saved in json
|
||||||
Hidden bool `json:"hidden,omitempty"`
|
Hidden bool `json:"hidden,omitempty"`
|
||||||
Palette *level.Palette `json:"palette"`
|
Palette *level.Palette `json:"palette"`
|
||||||
|
Size render.Rect `json:"size"` // doodad dimensions
|
||||||
Script string `json:"script"`
|
Script string `json:"script"`
|
||||||
Hitbox render.Rect `json:"hitbox"`
|
Hitbox render.Rect `json:"hitbox"`
|
||||||
Layers []Layer `json:"layers"`
|
Layers []Layer `json:"layers"`
|
||||||
|
@ -30,10 +31,41 @@ type Layer struct {
|
||||||
Chunker *level.Chunker `json:"chunks"`
|
Chunker *level.Chunker `json:"chunks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Doodad.
|
/*
|
||||||
func New(size int) *Doodad {
|
New creates a new Doodad.
|
||||||
if size == 0 {
|
|
||||||
|
You can give it one or two values for dimensions:
|
||||||
|
|
||||||
|
- New(size int) creates a square doodad (classic)
|
||||||
|
- New(width, height int) lets you have a different width x height.
|
||||||
|
*/
|
||||||
|
func New(dimensions ...int) *Doodad {
|
||||||
|
var (
|
||||||
|
// Defaults
|
||||||
size = balance.DoodadSize
|
size = balance.DoodadSize
|
||||||
|
chunkSize = balance.ChunkSize
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
)
|
||||||
|
|
||||||
|
switch len(dimensions) {
|
||||||
|
case 1:
|
||||||
|
size = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick an optimal chunk size - if our doodad size
|
||||||
|
// is under 256 use only one chunk per layer by matching
|
||||||
|
// that size.
|
||||||
|
if size <= 255 {
|
||||||
|
chunkSize = uint8(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Doodad{
|
return &Doodad{
|
||||||
|
@ -41,11 +73,12 @@ func New(size int) *Doodad {
|
||||||
Version: 1,
|
Version: 1,
|
||||||
},
|
},
|
||||||
Palette: level.DefaultPalette(),
|
Palette: level.DefaultPalette(),
|
||||||
Hitbox: render.NewRect(size, size),
|
Hitbox: render.NewRect(width, height),
|
||||||
|
Size: render.NewRect(width, height),
|
||||||
Layers: []Layer{
|
Layers: []Layer{
|
||||||
{
|
{
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Chunker: level.NewChunker(size),
|
Chunker: level.NewChunker(chunkSize),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Tags: map[string]string{},
|
Tags: map[string]string{},
|
||||||
|
@ -59,7 +92,7 @@ func New(size int) *Doodad {
|
||||||
// is optional - pass nil and a new blank chunker is created.
|
// is optional - pass nil and a new blank chunker is created.
|
||||||
func (d *Doodad) AddLayer(name string, chunker *level.Chunker) Layer {
|
func (d *Doodad) AddLayer(name string, chunker *level.Chunker) Layer {
|
||||||
if chunker == nil {
|
if chunker == nil {
|
||||||
chunker = level.NewChunker(d.ChunkSize())
|
chunker = level.NewChunker(d.ChunkSize8())
|
||||||
}
|
}
|
||||||
|
|
||||||
layer := Layer{
|
layer := Layer{
|
||||||
|
@ -107,12 +140,17 @@ func (d *Doodad) Tag(name string) string {
|
||||||
|
|
||||||
// ChunkSize returns the chunk size of the Doodad's first layer.
|
// ChunkSize returns the chunk size of the Doodad's first layer.
|
||||||
func (d *Doodad) ChunkSize() int {
|
func (d *Doodad) ChunkSize() int {
|
||||||
|
return int(d.Layers[0].Chunker.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChunkSize8 returns the chunk size of the Doodad's first layer as its actual uint8 value.
|
||||||
|
func (d *Doodad) ChunkSize8() uint8 {
|
||||||
return d.Layers[0].Chunker.Size
|
return d.Layers[0].Chunker.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rect returns a rect of the ChunkSize for scaling a Canvas widget.
|
// Rect returns a rect of the ChunkSize for scaling a Canvas widget.
|
||||||
func (d *Doodad) Rect() render.Rect {
|
func (d *Doodad) Rect() render.Rect {
|
||||||
var size = d.ChunkSize()
|
var size = int(d.ChunkSize())
|
||||||
return render.Rect{
|
return render.Rect{
|
||||||
W: size,
|
W: size,
|
||||||
H: size,
|
H: size,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
@ -24,6 +25,7 @@ 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,
|
||||||
|
|
|
@ -3,7 +3,6 @@ package doodle
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -291,32 +290,16 @@ func (d *Doodle) NewMap() {
|
||||||
|
|
||||||
// NewDoodad loads a new Doodad in Edit Mode.
|
// NewDoodad loads a new Doodad in Edit Mode.
|
||||||
// If size is zero, it prompts the user to select a size or accept the default size.
|
// If size is zero, it prompts the user to select a size or accept the default size.
|
||||||
func (d *Doodle) NewDoodad(size int) {
|
func (d *Doodle) NewDoodad(width, height int) {
|
||||||
if size == 0 {
|
if width+height == 0 {
|
||||||
d.Prompt(fmt.Sprintf("Doodad size or %d>", balance.DoodadSize), func(answer string) {
|
width = balance.DoodadSize
|
||||||
size := balance.DoodadSize
|
height = width
|
||||||
if answer != "" {
|
|
||||||
i, err := strconv.Atoi(answer)
|
|
||||||
if err != nil {
|
|
||||||
d.FlashError("Error: Doodad size must be a number.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size = i
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse with the proper answer.
|
|
||||||
if size <= 0 {
|
|
||||||
d.FlashError("Error: Doodad size must be a positive number.")
|
|
||||||
}
|
|
||||||
d.NewDoodad(size)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Starting a new doodad")
|
log.Info("Starting a new doodad")
|
||||||
scene := &EditorScene{
|
scene := &EditorScene{
|
||||||
DrawingType: enum.DoodadDrawing,
|
DrawingType: enum.DoodadDrawing,
|
||||||
DoodadSize: size,
|
DoodadSize: render.NewRect(width, height),
|
||||||
}
|
}
|
||||||
d.Goto(scene)
|
d.Goto(scene)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ type EditorScene struct {
|
||||||
DrawingType enum.DrawingType
|
DrawingType enum.DrawingType
|
||||||
OpenFile bool
|
OpenFile bool
|
||||||
Filename string
|
Filename string
|
||||||
DoodadSize int
|
DoodadSize render.Rect
|
||||||
RememberScrollPosition render.Point // Play mode remembers it for us
|
RememberScrollPosition render.Point // Play mode remembers it for us
|
||||||
|
|
||||||
UI *EditorUI
|
UI *EditorUI
|
||||||
|
@ -195,7 +195,7 @@ func (s *EditorScene) setupAsync(d *Doodle) error {
|
||||||
// No Doodad?
|
// No Doodad?
|
||||||
if s.Doodad == nil {
|
if s.Doodad == nil {
|
||||||
log.Debug("EditorScene.Setup: initializing a new Doodad")
|
log.Debug("EditorScene.Setup: initializing a new Doodad")
|
||||||
s.Doodad = doodads.New(s.DoodadSize)
|
s.Doodad = doodads.New(s.DoodadSize.W, s.DoodadSize.H)
|
||||||
s.UI.Canvas.LoadDoodad(s.Doodad)
|
s.UI.Canvas.LoadDoodad(s.Doodad)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ func (s *EditorScene) setupAsync(d *Doodle) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: move inside the UI. Just an approximate position for now.
|
// TODO: move inside the UI. Just an approximate position for now.
|
||||||
s.UI.Canvas.Resize(render.NewRect(s.DoodadSize, s.DoodadSize))
|
s.UI.Canvas.Resize(s.DoodadSize)
|
||||||
s.UI.Canvas.ScrollTo(render.Origin)
|
s.UI.Canvas.ScrollTo(render.Origin)
|
||||||
s.UI.Canvas.Scrollable = false
|
s.UI.Canvas.Scrollable = false
|
||||||
s.UI.Workspace.Compute(d.Engine)
|
s.UI.Workspace.Compute(d.Engine)
|
||||||
|
@ -574,7 +574,7 @@ func (s *EditorScene) LoadDoodad(filename string) error {
|
||||||
|
|
||||||
s.DrawingType = enum.DoodadDrawing
|
s.DrawingType = enum.DoodadDrawing
|
||||||
s.Doodad = doodad
|
s.Doodad = doodad
|
||||||
s.DoodadSize = doodad.Layers[0].Chunker.Size
|
s.DoodadSize = doodad.Size
|
||||||
s.UI.Canvas.LoadDoodad(s.Doodad)
|
s.UI.Canvas.LoadDoodad(s.Doodad)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (u *EditorUI) startDragActor(doodad *doodads.Doodad, actor *level.Actor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size and scale this doodad according to the zoom level.
|
// Size and scale this doodad according to the zoom level.
|
||||||
size := doodad.Rect()
|
size := doodad.Size
|
||||||
size.W = u.Canvas.ZoomMultiply(size.W)
|
size.W = u.Canvas.ZoomMultiply(size.W)
|
||||||
size.H = u.Canvas.ZoomMultiply(size.H)
|
size.H = u.Canvas.ZoomMultiply(size.H)
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,7 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
||||||
// File menu
|
// File menu
|
||||||
fileMenu := menu.AddMenu("File")
|
fileMenu := menu.AddMenu("File")
|
||||||
fileMenu.AddItemAccel("New level", "Ctrl-N", u.Scene.MenuNewLevel)
|
fileMenu.AddItemAccel("New level", "Ctrl-N", u.Scene.MenuNewLevel)
|
||||||
fileMenu.AddItem("New doodad", func() {
|
fileMenu.AddItem("New doodad", u.Scene.MenuNewDoodad)
|
||||||
u.Scene.ConfirmUnload(func() {
|
|
||||||
// New doodad size with prompt.
|
|
||||||
d.NewDoodad(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
fileMenu.AddItemAccel("Save", "Ctrl-S", u.Scene.MenuSave(false))
|
fileMenu.AddItemAccel("Save", "Ctrl-S", u.Scene.MenuSave(false))
|
||||||
fileMenu.AddItemAccel("Save as...", "Shift-Ctrl-S", func() {
|
fileMenu.AddItemAccel("Save as...", "Shift-Ctrl-S", func() {
|
||||||
d.Prompt("Save as filename>", func(answer string) {
|
d.Prompt("Save as filename>", func(answer string) {
|
||||||
|
@ -285,6 +280,13 @@ func (s *EditorScene) MenuNewLevel() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *EditorScene) MenuNewDoodad() {
|
||||||
|
s.ConfirmUnload(func() {
|
||||||
|
// New doodad size with prompt.
|
||||||
|
s.d.GotoNewDoodadMenu()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// File->Open, or Ctrl-O
|
// File->Open, or Ctrl-O
|
||||||
func (s *EditorScene) MenuOpen() {
|
func (s *EditorScene) MenuOpen() {
|
||||||
s.ConfirmUnload(func() {
|
s.ConfirmUnload(func() {
|
||||||
|
|
|
@ -83,11 +83,11 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
||||||
if u.Canvas != nil && u.Canvas.Palette != nil {
|
if u.Canvas != nil && u.Canvas.Palette != nil {
|
||||||
for i, swatch := range u.Canvas.Palette.Swatches {
|
for i, swatch := range u.Canvas.Palette.Swatches {
|
||||||
swatch := swatch
|
swatch := swatch
|
||||||
var width = buttonSize
|
var width = uint8(buttonSize) // TODO: dangerous - buttonSize must be small
|
||||||
|
|
||||||
// Drawing buttons in two-column mode? (default right-side palette layout)
|
// Drawing buttons in two-column mode? (default right-side palette layout)
|
||||||
if twoColumn {
|
if twoColumn {
|
||||||
width = buttonSize / 2
|
width /= 2
|
||||||
if row == nil || i%2 == 0 {
|
if row == nil || i%2 == 0 {
|
||||||
row = ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
row = ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
||||||
frame.Pack(row, packConfig)
|
frame.Pack(row, packConfig)
|
||||||
|
@ -101,7 +101,8 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
||||||
var (
|
var (
|
||||||
colorbox = uix.NewCanvas(width, false)
|
colorbox = uix.NewCanvas(width, false)
|
||||||
chunker = level.NewChunker(width)
|
chunker = level.NewChunker(width)
|
||||||
size = render.NewRect(width, width)
|
iw = int(width)
|
||||||
|
size = render.NewRect(iw, iw)
|
||||||
)
|
)
|
||||||
chunker.SetRect(size, swatch)
|
chunker.SetRect(size, swatch)
|
||||||
colorbox.Resize(size)
|
colorbox.Resize(size)
|
||||||
|
|
|
@ -27,7 +27,7 @@ type Chunk struct {
|
||||||
|
|
||||||
// Values told to it from higher up, not stored in JSON.
|
// Values told to it from higher up, not stored in JSON.
|
||||||
Point render.Point
|
Point render.Point
|
||||||
Size int
|
Size uint8
|
||||||
|
|
||||||
// Texture cache properties so we don't redraw pixel-by-pixel every frame.
|
// Texture cache properties so we don't redraw pixel-by-pixel every frame.
|
||||||
uuid uuid.UUID
|
uuid uuid.UUID
|
||||||
|
@ -157,14 +157,17 @@ func (c *Chunk) generateTexture(mask render.Color) (render.Texturer, error) {
|
||||||
// want a cached bitmap image that only generates itself once, and
|
// want a cached bitmap image that only generates itself once, and
|
||||||
// again when marked dirty.
|
// again when marked dirty.
|
||||||
func (c *Chunk) ToBitmap(mask render.Color) image.Image {
|
func (c *Chunk) ToBitmap(mask render.Color) image.Image {
|
||||||
canvas := c.SizePositive()
|
var (
|
||||||
imgSize := image.Rectangle{
|
size = int(c.Size)
|
||||||
|
canvas = c.SizePositive()
|
||||||
|
imgSize = image.Rectangle{
|
||||||
Min: image.Point{},
|
Min: image.Point{},
|
||||||
Max: image.Point{
|
Max: image.Point{
|
||||||
X: c.Size,
|
X: size,
|
||||||
Y: c.Size,
|
Y: size,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if imgSize.Max.X == 0 {
|
if imgSize.Max.X == 0 {
|
||||||
imgSize.Max.X = int(canvas.W)
|
imgSize.Max.X = int(canvas.W)
|
||||||
|
@ -186,8 +189,8 @@ func (c *Chunk) ToBitmap(mask render.Color) image.Image {
|
||||||
// Pixel coordinate offset to map the Chunk World Position to the
|
// Pixel coordinate offset to map the Chunk World Position to the
|
||||||
// smaller image boundaries.
|
// smaller image boundaries.
|
||||||
pointOffset := render.Point{
|
pointOffset := render.Point{
|
||||||
X: c.Point.X * c.Size,
|
X: c.Point.X * size,
|
||||||
Y: c.Point.Y * c.Size,
|
Y: c.Point.Y * size,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blot all the pixels onto it.
|
// Blot all the pixels onto it.
|
||||||
|
|
|
@ -21,7 +21,7 @@ type Chunker struct {
|
||||||
// doodads use them for frames. When chunks are exported to
|
// doodads use them for frames. When chunks are exported to
|
||||||
// zipfile the Layer keeps them from overlapping.
|
// zipfile the Layer keeps them from overlapping.
|
||||||
Layer int `json:"-"` // internal use only
|
Layer int `json:"-"` // internal use only
|
||||||
Size int `json:"size"`
|
Size uint8 `json:"size"`
|
||||||
|
|
||||||
// A Zipfile reference for new-style levels and doodads which
|
// A Zipfile reference for new-style levels and doodads which
|
||||||
// keep their chunks in external parts of a zip file.
|
// keep their chunks in external parts of a zip file.
|
||||||
|
@ -51,7 +51,7 @@ type Chunker struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChunker creates a new chunk manager with a given chunk size.
|
// NewChunker creates a new chunk manager with a given chunk size.
|
||||||
func NewChunker(size int) *Chunker {
|
func NewChunker(size uint8) *Chunker {
|
||||||
return &Chunker{
|
return &Chunker{
|
||||||
Size: size,
|
Size: size,
|
||||||
Chunks: ChunkMap{},
|
Chunks: ChunkMap{},
|
||||||
|
@ -186,10 +186,13 @@ func (c *Chunker) IterCachedChunks() <-chan *Chunk {
|
||||||
func (c *Chunker) IterViewportChunks(viewport render.Rect) <-chan render.Point {
|
func (c *Chunker) IterViewportChunks(viewport render.Rect) <-chan render.Point {
|
||||||
pipe := make(chan render.Point)
|
pipe := make(chan render.Point)
|
||||||
go func() {
|
go func() {
|
||||||
sent := make(map[render.Point]interface{})
|
var (
|
||||||
|
sent = make(map[render.Point]interface{})
|
||||||
|
size = int(c.Size)
|
||||||
|
)
|
||||||
|
|
||||||
for x := viewport.X; x < viewport.W; x += (c.Size / 4) {
|
for x := viewport.X; x < viewport.W; x += (size / 4) {
|
||||||
for y := viewport.Y; y < viewport.H; y += (c.Size / 4) {
|
for y := viewport.Y; y < viewport.H; y += (size / 4) {
|
||||||
|
|
||||||
// Constrain this chunksize step to a point within the bounds
|
// Constrain this chunksize step to a point within the bounds
|
||||||
// of the viewport. This can yield partial chunks on the edges
|
// of the viewport. This can yield partial chunks on the edges
|
||||||
|
@ -243,12 +246,16 @@ func (c *Chunker) IterPixels() <-chan Pixel {
|
||||||
// manage: the lowest pixels from the lowest chunks to the highest pixels of
|
// manage: the lowest pixels from the lowest chunks to the highest pixels of
|
||||||
// the highest chunks.
|
// the highest chunks.
|
||||||
func (c *Chunker) WorldSize() render.Rect {
|
func (c *Chunker) WorldSize() render.Rect {
|
||||||
chunkLowest, chunkHighest := c.Bounds()
|
var (
|
||||||
|
size = int(c.Size)
|
||||||
|
chunkLowest, chunkHighest = c.Bounds()
|
||||||
|
)
|
||||||
|
|
||||||
return render.Rect{
|
return render.Rect{
|
||||||
X: chunkLowest.X * c.Size,
|
X: chunkLowest.X * size,
|
||||||
Y: chunkLowest.Y * c.Size,
|
Y: chunkLowest.Y * size,
|
||||||
W: (chunkHighest.X * c.Size) + (c.Size - 1),
|
W: (chunkHighest.X * size) + (size - 1),
|
||||||
H: (chunkHighest.Y * c.Size) + (c.Size - 1),
|
H: (chunkHighest.Y * size) + (size - 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ func GiantScreenshot(lvl *level.Level) (image.Image, error) {
|
||||||
// How big will our image be?
|
// How big will our image be?
|
||||||
var (
|
var (
|
||||||
size = lvl.Chunker.WorldSizePositive()
|
size = lvl.Chunker.WorldSizePositive()
|
||||||
chunkSize = lvl.Chunker.Size
|
chunkSize = int(lvl.Chunker.Size)
|
||||||
chunkLow, chunkHigh = lvl.Chunker.Bounds()
|
chunkLow, chunkHigh = lvl.Chunker.Bounds()
|
||||||
worldSize = render.Rect{
|
worldSize = render.Rect{
|
||||||
X: chunkLow.X,
|
X: chunkLow.X,
|
||||||
|
|
|
@ -24,6 +24,7 @@ on the MainScene or elsewhere as wanted.
|
||||||
type MenuScene struct {
|
type MenuScene struct {
|
||||||
// Configuration.
|
// Configuration.
|
||||||
StartupMenu string
|
StartupMenu string
|
||||||
|
NewDoodad bool
|
||||||
|
|
||||||
Supervisor *ui.Supervisor
|
Supervisor *ui.Supervisor
|
||||||
|
|
||||||
|
@ -60,6 +61,17 @@ func (d *Doodle) GotoNewMenu() {
|
||||||
d.Goto(scene)
|
d.Goto(scene)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GotoNewDoodadMenu loads the MenuScene and shows the "New" window,
|
||||||
|
// but selected on the Doodad tab by default.
|
||||||
|
func (d *Doodle) GotoNewDoodadMenu() {
|
||||||
|
log.Info("Loading the MenuScene to the New window")
|
||||||
|
scene := &MenuScene{
|
||||||
|
StartupMenu: "new",
|
||||||
|
NewDoodad: true,
|
||||||
|
}
|
||||||
|
d.Goto(scene)
|
||||||
|
}
|
||||||
|
|
||||||
// GotoLoadMenu loads the MenuScene and shows the "Load" window.
|
// GotoLoadMenu loads the MenuScene and shows the "Load" window.
|
||||||
func (d *Doodle) GotoLoadMenu() {
|
func (d *Doodle) GotoLoadMenu() {
|
||||||
log.Info("Loading the MenuScene to the Load window for Edit Mode")
|
log.Info("Loading the MenuScene to the Load window for Edit Mode")
|
||||||
|
@ -152,6 +164,7 @@ func (s *MenuScene) setupNewWindow(d *Doodle) error {
|
||||||
window := windows.NewAddEditLevel(windows.AddEditLevel{
|
window := windows.NewAddEditLevel(windows.AddEditLevel{
|
||||||
Supervisor: s.Supervisor,
|
Supervisor: s.Supervisor,
|
||||||
Engine: d.Engine,
|
Engine: d.Engine,
|
||||||
|
NewDoodad: s.NewDoodad,
|
||||||
OnChangePageTypeAndWallpaper: func(pageType level.PageType, wallpaper string) {
|
OnChangePageTypeAndWallpaper: func(pageType level.PageType, wallpaper string) {
|
||||||
log.Info("OnChangePageTypeAndWallpaper called: %+v, %+v", pageType, wallpaper)
|
log.Info("OnChangePageTypeAndWallpaper called: %+v, %+v", pageType, wallpaper)
|
||||||
s.canvas.Destroy() // clean up old textures
|
s.canvas.Destroy() // clean up old textures
|
||||||
|
@ -163,8 +176,8 @@ func (s *MenuScene) setupNewWindow(d *Doodle) error {
|
||||||
Level: lvl,
|
Level: lvl,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
OnCreateNewDoodad: func(size int) {
|
OnCreateNewDoodad: func(width, height int) {
|
||||||
d.NewDoodad(size)
|
d.NewDoodad(width, height)
|
||||||
},
|
},
|
||||||
OnCancel: func() {
|
OnCancel: func() {
|
||||||
d.Goto(&MainScene{})
|
d.Goto(&MainScene{})
|
||||||
|
|
|
@ -69,7 +69,7 @@ func (s *PlayScene) computeInventory() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas := uix.NewCanvas(doodad.ChunkSize(), false)
|
canvas := uix.NewCanvas(doodad.ChunkSize8(), false)
|
||||||
canvas.SetBackground(render.RGBA(1, 0, 0, 0))
|
canvas.SetBackground(render.RGBA(1, 0, 0, 0))
|
||||||
canvas.LoadDoodad(doodad)
|
canvas.LoadDoodad(doodad)
|
||||||
canvas.Resize(render.NewRect(
|
canvas.Resize(render.NewRect(
|
||||||
|
|
|
@ -471,10 +471,10 @@ func (s *PlayScene) installPlayerDoodad(filename string, spawn render.Point, cen
|
||||||
// Center the player within the box of the doodad, for the Start Flag especially.
|
// Center the player within the box of the doodad, for the Start Flag especially.
|
||||||
if !centerIn.IsZero() {
|
if !centerIn.IsZero() {
|
||||||
spawn = render.NewPoint(
|
spawn = render.NewPoint(
|
||||||
spawn.X+(centerIn.W/2)-(player.Layers[0].Chunker.Size/2),
|
spawn.X+(centerIn.W/2)-(player.ChunkSize()/2),
|
||||||
|
|
||||||
// Y: the bottom of the flag, 4 pixels from the floor.
|
// Y: the bottom of the flag, 4 pixels from the floor.
|
||||||
spawn.Y+centerIn.H-4-(player.Layers[0].Chunker.Size),
|
spawn.Y+centerIn.H-4-(player.ChunkSize()),
|
||||||
)
|
)
|
||||||
} else if spawn.IsZero() && !s.SpawnPoint.IsZero() {
|
} else if spawn.IsZero() && !s.SpawnPoint.IsZero() {
|
||||||
spawn = s.SpawnPoint
|
spawn = s.SpawnPoint
|
||||||
|
|
|
@ -67,8 +67,8 @@ func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor
|
||||||
id = uuid.Must(uuid.NewUUID()).String()
|
id = uuid.Must(uuid.NewUUID()).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
size := doodad.Layers[0].Chunker.Size
|
size := doodad.ChunkSize()
|
||||||
can := NewCanvas(int(size), false)
|
can := NewCanvas(uint8(size), false)
|
||||||
can.Name = id
|
can.Name = id
|
||||||
|
|
||||||
// TODO: if the Background is render.Invisible it gets defaulted to
|
// TODO: if the Background is render.Invisible it gets defaulted to
|
||||||
|
@ -76,7 +76,7 @@ func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor
|
||||||
can.SetBackground(render.RGBA(0, 0, 1, 0))
|
can.SetBackground(render.RGBA(0, 0, 1, 0))
|
||||||
|
|
||||||
can.LoadDoodad(doodad)
|
can.LoadDoodad(doodad)
|
||||||
can.Resize(render.NewRect(size, size))
|
can.Resize(doodad.Size)
|
||||||
|
|
||||||
actor := &Actor{
|
actor := &Actor{
|
||||||
Drawing: doodads.NewDrawing(id, doodad),
|
Drawing: doodads.NewDrawing(id, doodad),
|
||||||
|
|
|
@ -132,15 +132,17 @@ type Canvas struct {
|
||||||
|
|
||||||
// NewCanvas initializes a Canvas widget.
|
// NewCanvas initializes a Canvas widget.
|
||||||
//
|
//
|
||||||
|
// size is the Chunker size (uint8)
|
||||||
|
//
|
||||||
// If editable is true, Scrollable is also set to true, which means the arrow
|
// If editable is true, Scrollable is also set to true, which means the arrow
|
||||||
// keys will scroll the canvas viewport which is desirable in Edit Mode.
|
// keys will scroll the canvas viewport which is desirable in Edit Mode.
|
||||||
func NewCanvas(size int, editable bool) *Canvas {
|
func NewCanvas(size uint8, editable bool) *Canvas {
|
||||||
w := &Canvas{
|
w := &Canvas{
|
||||||
Editable: editable,
|
Editable: editable,
|
||||||
Scrollable: editable,
|
Scrollable: editable,
|
||||||
Palette: level.NewPalette(),
|
Palette: level.NewPalette(),
|
||||||
BrushSize: 1,
|
BrushSize: 1,
|
||||||
chunks: level.NewChunker(size),
|
chunks: level.NewChunker(uint8(size)),
|
||||||
actors: make([]*Actor, 0),
|
actors: make([]*Actor, 0),
|
||||||
wallpaper: &Wallpaper{},
|
wallpaper: &Wallpaper{},
|
||||||
|
|
||||||
|
@ -372,7 +374,7 @@ func (w *Canvas) ViewportRelative() render.Rect {
|
||||||
// levels under control.
|
// levels under control.
|
||||||
func (w *Canvas) LoadingViewport() render.Rect {
|
func (w *Canvas) LoadingViewport() render.Rect {
|
||||||
var (
|
var (
|
||||||
chunkSize int
|
chunkSize uint8
|
||||||
vp = w.Viewport()
|
vp = w.Viewport()
|
||||||
margin = balance.LoadingViewportMarginChunks
|
margin = balance.LoadingViewportMarginChunks
|
||||||
)
|
)
|
||||||
|
@ -381,17 +383,18 @@ func (w *Canvas) LoadingViewport() render.Rect {
|
||||||
if w.level != nil {
|
if w.level != nil {
|
||||||
chunkSize = w.level.Chunker.Size
|
chunkSize = w.level.Chunker.Size
|
||||||
} else if w.doodad != nil {
|
} else if w.doodad != nil {
|
||||||
chunkSize = w.doodad.ChunkSize()
|
chunkSize = w.doodad.ChunkSize8()
|
||||||
} else {
|
} else {
|
||||||
chunkSize = balance.ChunkSize
|
chunkSize = balance.ChunkSize
|
||||||
log.Error("Canvas.LoadingViewport: no drawing to get chunk size from, default to %d", chunkSize)
|
log.Error("Canvas.LoadingViewport: no drawing to get chunk size from, default to %d", chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size = int(chunkSize)
|
||||||
return render.Rect{
|
return render.Rect{
|
||||||
X: vp.X - chunkSize*margin.X,
|
X: vp.X - size*margin.X,
|
||||||
Y: vp.Y - chunkSize*margin.Y,
|
Y: vp.Y - size*margin.Y,
|
||||||
W: vp.W + chunkSize*margin.X,
|
W: vp.W + size*margin.X,
|
||||||
H: vp.H + chunkSize*margin.Y,
|
H: vp.H + size*margin.Y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 / 2 // TODO
|
// scrollTo.X -= delta // TODO
|
||||||
resizeTo.W -= delta
|
resizeTo.W -= delta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,9 +121,10 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
|
||||||
src.H = S.H
|
src.H = S.H
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size = int(chunk.Size)
|
||||||
dst := render.Rect{
|
dst := render.Rect{
|
||||||
X: p.X + w.Scroll.X + w.BoxThickness(1) + w.ZoomMultiply(coord.X*chunk.Size),
|
X: p.X + w.Scroll.X + w.BoxThickness(1) + w.ZoomMultiply(coord.X*size),
|
||||||
Y: p.Y + w.Scroll.Y + w.BoxThickness(1) + w.ZoomMultiply(coord.Y*chunk.Size),
|
Y: p.Y + w.Scroll.Y + w.BoxThickness(1) + w.ZoomMultiply(coord.Y*size),
|
||||||
|
|
||||||
// src.W and src.H will be AT MOST the full width and height of
|
// src.W and src.H will be AT MOST the full width and height of
|
||||||
// a Canvas widget. Subtract the scroll offset to keep it bounded
|
// a Canvas widget. Subtract the scroll offset to keep it bounded
|
||||||
|
|
|
@ -398,6 +398,14 @@ func (form Form) Create(into *ui.Frame, fields []Field) {
|
||||||
if row.OnSelect != nil {
|
if row.OnSelect != nil {
|
||||||
row.OnSelect(selection.Value)
|
row.OnSelect(selection.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update bound variables.
|
||||||
|
if v, ok := selection.Value.(int); ok && row.IntVariable != nil {
|
||||||
|
*row.IntVariable = v
|
||||||
|
}
|
||||||
|
if v, ok := selection.Value.(string); ok && row.TextVariable != nil {
|
||||||
|
*row.TextVariable = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,10 +26,13 @@ type AddEditLevel struct {
|
||||||
// Editing settings for an existing level?
|
// Editing settings for an existing level?
|
||||||
EditLevel *level.Level
|
EditLevel *level.Level
|
||||||
|
|
||||||
|
// Show the "New Doodad" tab by default?
|
||||||
|
NewDoodad bool
|
||||||
|
|
||||||
// Callback functions.
|
// Callback functions.
|
||||||
OnChangePageTypeAndWallpaper func(pageType level.PageType, wallpaper string)
|
OnChangePageTypeAndWallpaper func(pageType level.PageType, wallpaper string)
|
||||||
OnCreateNewLevel func(*level.Level)
|
OnCreateNewLevel func(*level.Level)
|
||||||
OnCreateNewDoodad func(size int)
|
OnCreateNewDoodad func(width, height int)
|
||||||
OnReload func()
|
OnReload func()
|
||||||
OnCancel func()
|
OnCancel func()
|
||||||
}
|
}
|
||||||
|
@ -74,6 +77,11 @@ func NewAddEditLevel(config AddEditLevel) *ui.Window {
|
||||||
|
|
||||||
tabframe.Supervise(config.Supervisor)
|
tabframe.Supervise(config.Supervisor)
|
||||||
|
|
||||||
|
// Show the doodad tab?
|
||||||
|
if config.NewDoodad {
|
||||||
|
tabframe.SetTab("doodad")
|
||||||
|
}
|
||||||
|
|
||||||
window.Hide()
|
window.Hide()
|
||||||
return window
|
return window
|
||||||
}
|
}
|
||||||
|
@ -389,7 +397,8 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) {
|
||||||
func (config AddEditLevel) setupDoodadFrame(tf *ui.TabFrame) {
|
func (config AddEditLevel) setupDoodadFrame(tf *ui.TabFrame) {
|
||||||
// Default options.
|
// Default options.
|
||||||
var (
|
var (
|
||||||
doodadSize = 64
|
doodadWidth = 64
|
||||||
|
doodadHeight = doodadWidth
|
||||||
)
|
)
|
||||||
|
|
||||||
frame := tf.AddTab("doodad", ui.NewLabel(ui.Label{
|
frame := tf.AddTab("doodad", ui.NewLabel(ui.Label{
|
||||||
|
@ -401,110 +410,89 @@ func (config AddEditLevel) setupDoodadFrame(tf *ui.TabFrame) {
|
||||||
* Frame for selecting Page Type
|
* Frame for selecting Page Type
|
||||||
******************/
|
******************/
|
||||||
|
|
||||||
typeFrame := ui.NewFrame("Doodad Options Frame")
|
var sizeOptions = []magicform.Option{
|
||||||
frame.Pack(typeFrame, ui.Pack{
|
{Label: "32", Value: 32},
|
||||||
Side: ui.N,
|
{Label: "64", Value: 64},
|
||||||
FillX: true,
|
{Label: "96", Value: 96},
|
||||||
})
|
{Label: "128", Value: 128},
|
||||||
|
{Label: "200", Value: 200},
|
||||||
label1 := ui.NewLabel(ui.Label{
|
{Label: "256", Value: 256},
|
||||||
Text: "Doodad sprite size (square):",
|
{Label: "Custom...", Value: 0},
|
||||||
Font: balance.LabelFont,
|
|
||||||
})
|
|
||||||
typeFrame.Pack(label1, ui.Pack{
|
|
||||||
Side: ui.W,
|
|
||||||
})
|
|
||||||
|
|
||||||
// A selectbox to suggest some sizes or let the user enter a custom.
|
|
||||||
sizeBtn := ui.NewSelectBox("Size Select", ui.Label{
|
|
||||||
Font: ui.MenuFont,
|
|
||||||
})
|
|
||||||
typeFrame.Pack(sizeBtn, ui.Pack{
|
|
||||||
Side: ui.W,
|
|
||||||
Expand: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, row := range []struct {
|
|
||||||
Name string
|
|
||||||
Value int
|
|
||||||
}{
|
|
||||||
{"32", 32},
|
|
||||||
{"64", 64},
|
|
||||||
{"96", 96},
|
|
||||||
{"128", 128},
|
|
||||||
{"200", 200},
|
|
||||||
{"256", 256},
|
|
||||||
{"Custom...", 0},
|
|
||||||
} {
|
|
||||||
row := row
|
|
||||||
sizeBtn.AddItem(row.Name, row.Value, func() {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sizeBtn.SetValue(doodadSize)
|
form := magicform.Form{
|
||||||
sizeBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
Supervisor: config.Supervisor,
|
||||||
if selection, ok := sizeBtn.GetValue(); ok {
|
Engine: config.Engine,
|
||||||
if size, ok := selection.Value.(int); ok {
|
Vertical: true,
|
||||||
if size == 0 {
|
LabelWidth: 90,
|
||||||
shmem.Prompt("Enter a custom size for the doodad width and height: ", func(answer string) {
|
}
|
||||||
|
form.Create(frame, []magicform.Field{
|
||||||
|
{
|
||||||
|
Label: "Width:",
|
||||||
|
Font: balance.LabelFont,
|
||||||
|
Type: magicform.Selectbox,
|
||||||
|
IntVariable: &doodadWidth,
|
||||||
|
Options: sizeOptions,
|
||||||
|
OnSelect: func(v interface{}) {
|
||||||
|
if v.(int) == 0 {
|
||||||
|
shmem.Prompt("Enter a custom size for the doodad width: ", func(answer string) {
|
||||||
if a, err := strconv.Atoi(answer); err == nil && a > 0 {
|
if a, err := strconv.Atoi(answer); err == nil && a > 0 {
|
||||||
doodadSize = a
|
doodadWidth = a
|
||||||
} else {
|
} else {
|
||||||
shmem.FlashError("Doodad size should be a number greater than zero.")
|
shmem.FlashError("Doodad size should be a number greater than zero.")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Label: "Height:",
|
||||||
|
Font: balance.LabelFont,
|
||||||
|
Type: magicform.Selectbox,
|
||||||
|
IntVariable: &doodadHeight,
|
||||||
|
Options: sizeOptions,
|
||||||
|
OnSelect: func(v interface{}) {
|
||||||
|
if v.(int) == 0 {
|
||||||
|
shmem.Prompt("Enter a custom size for the doodad height: ", func(answer string) {
|
||||||
|
if a, err := strconv.Atoi(answer); err == nil && a > 0 {
|
||||||
|
doodadHeight = a
|
||||||
} else {
|
} else {
|
||||||
doodadSize = size
|
shmem.FlashError("Doodad size should be a number greater than zero.")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
sizeBtn.Supervise(config.Supervisor)
|
},
|
||||||
config.Supervisor.Add(sizeBtn)
|
},
|
||||||
|
{
|
||||||
/******************
|
Buttons: []magicform.Field{
|
||||||
* Confirm/cancel buttons.
|
{
|
||||||
******************/
|
Label: "Continue",
|
||||||
|
Font: balance.UIFont,
|
||||||
bottomFrame := ui.NewFrame("Button Frame")
|
ButtonStyle: &balance.ButtonPrimary,
|
||||||
frame.Pack(bottomFrame, ui.Pack{
|
OnClick: func() {
|
||||||
Side: ui.N,
|
|
||||||
FillX: true,
|
|
||||||
PadY: 8,
|
|
||||||
})
|
|
||||||
|
|
||||||
var buttons = []struct {
|
|
||||||
Label string
|
|
||||||
F func(ui.EventData) error
|
|
||||||
}{
|
|
||||||
{"Continue", func(ed ui.EventData) error {
|
|
||||||
if config.OnCreateNewDoodad != nil {
|
if config.OnCreateNewDoodad != nil {
|
||||||
config.OnCreateNewDoodad(doodadSize)
|
config.OnCreateNewDoodad(doodadWidth, doodadHeight)
|
||||||
} else {
|
} else {
|
||||||
shmem.FlashError("OnCreateNewDoodad not attached")
|
shmem.FlashError("OnCreateNewDoodad not attached")
|
||||||
}
|
}
|
||||||
return nil
|
},
|
||||||
}},
|
},
|
||||||
|
{
|
||||||
{"Cancel", func(ed ui.EventData) error {
|
Label: "Cancel",
|
||||||
|
Font: balance.UIFont,
|
||||||
|
ButtonStyle: &balance.ButtonPrimary,
|
||||||
|
OnClick: func() {
|
||||||
|
if config.OnCancel != nil {
|
||||||
config.OnCancel()
|
config.OnCancel()
|
||||||
return nil
|
} else {
|
||||||
}},
|
shmem.FlashError("OnCancel not attached")
|
||||||
}
|
}
|
||||||
for _, t := range buttons {
|
},
|
||||||
btn := ui.NewButton(t.Label, ui.NewLabel(ui.Label{
|
},
|
||||||
Text: t.Label,
|
},
|
||||||
Font: balance.MenuFont,
|
},
|
||||||
}))
|
|
||||||
btn.Handle(ui.Click, t.F)
|
|
||||||
config.Supervisor.Add(btn)
|
|
||||||
bottomFrame.Pack(btn, ui.Pack{
|
|
||||||
Side: ui.W,
|
|
||||||
PadX: 4,
|
|
||||||
PadY: 8,
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates the Game Rules frame for existing level (set difficulty, etc.)
|
// Creates the Game Rules frame for existing level (set difficulty, etc.)
|
||||||
|
|
|
@ -253,7 +253,7 @@ func makeDoodadTab(config DoodadDropper, frame *ui.Frame, size render.Rect, cate
|
||||||
lastColumn = 0
|
lastColumn = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
can := uix.NewCanvas(int(buttonSize), true)
|
can := uix.NewCanvas(uint8(buttonSize), true) // TODO: dangerous - buttonSize must be small
|
||||||
can.Name = doodad.Title
|
can.Name = doodad.Title
|
||||||
can.SetBackground(balance.DoodadButtonBackground)
|
can.SetBackground(balance.DoodadButtonBackground)
|
||||||
can.LoadDoodad(doodad)
|
can.LoadDoodad(doodad)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user