Remove MsgPack, Fix doodad display on top/left edges

* Clean up unused msgpack code for levels and doodads
* Fix the cosmetic bug where actors in your level would display wrongly
  when scrolling off the top/left edges of the screen: they used to
  anchor at their own 0,0 coordinate and crop their width/height leading
  to a 'scrolling' effect that didn't happen on the right/bottom edges.
pull/84/head
Noah 2022-01-09 13:16:29 -08:00
parent cbd7816fdf
commit 9e4f34864d
15 changed files with 54 additions and 267 deletions

View File

@ -58,6 +58,12 @@ Miscellaneous changes:
user levels _now_.
* For the doodads JavaScript API: `time.Since()` is now available (from
the Go standard library)
* Fix a cosmetic bug where doodads scrolling off the top or left edges
of the level were being drawn incorrectly.
* Fix the Editor's status bar where it shows your cursor position
relative to the level and absolute to the app window to show the
correct values of each (they were reversed before).
* The developer shell now has a chatbot in it powered by RiveScript.
## v0.10.0 (Dec 30 2021)

View File

@ -6,7 +6,7 @@
< begin
// Bot Variables
! var name = Boy
! var name = Aiden
! var age = 15
! var sex = male
! var location = Michigan

4
go.mod
View File

@ -19,12 +19,8 @@ require (
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e // indirect
github.com/urfave/cli/v2 v2.3.0
github.com/veandco/go-sdl2 v0.4.10
github.com/vmihailenco/msgpack v4.0.4+incompatible
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)

14
go.sum
View File

@ -122,7 +122,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -137,7 +136,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -197,10 +195,8 @@ github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDS
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -274,8 +270,6 @@ github.com/veandco/go-sdl2 v0.4.1/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2
github.com/veandco/go-sdl2 v0.4.8/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
github.com/veandco/go-sdl2 v0.4.10 h1:8QoD2bhWl7SbQDflIAUYWfl9Vq+mT8/boJFAUzAScgY=
github.com/veandco/go-sdl2 v0.4.10/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -382,8 +376,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d h1:62NvYBuaanGXR2ZOfwDFkhhl6X1DUgf8qg3GuQvxZsE=
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -527,7 +519,6 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@ -557,7 +548,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -632,12 +622,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -20,7 +20,7 @@ type Doodad struct {
Tags map[string]string `json:"data"` // arbitrary key/value data storage
// Undo history, temporary live data not persisted to the level file.
UndoHistory *drawtool.History `json:"-" msgpack:"-"`
UndoHistory *drawtool.History `json:"-"`
}
// Layer holds a layer of drawing data for a Doodad.

View File

@ -282,10 +282,10 @@ func (u *EditorUI) Loop(ev *event.State) error {
// Update status bar labels.
{
u.StatusMouseText = fmt.Sprintf("Rel:(%d,%d) Abs:(%s)",
u.StatusMouseText = fmt.Sprintf("Rel:(%s) Abs:(%d,%d)",
*u.Scene.debWorldIndex,
ev.CursorX,
ev.CursorY,
*u.Scene.debWorldIndex,
)
u.StatusPaletteText = fmt.Sprintf("%s Tool",
u.Canvas.Tool,

View File

@ -12,7 +12,6 @@ import (
"git.kirsle.net/apps/doodle/pkg/shmem"
"git.kirsle.net/go/render"
"github.com/google/uuid"
"github.com/vmihailenco/msgpack"
)
// Types of chunks.
@ -42,9 +41,9 @@ type Chunk struct {
// JSONChunk holds a lightweight (interface-free) copy of the Chunk for
// unmarshalling JSON files from disk.
type JSONChunk struct {
Type int `json:"type" msgpack:"0"`
Data json.RawMessage `json:"data" msgpack:"-"`
BinData interface{} `json:"-" msgpack:"1"`
Type int `json:"type"`
Data json.RawMessage `json:"data"`
BinData interface{} `json:"-"`
}
// Accessor provides a high-level API to interact with absolute pixel coordinates
@ -59,9 +58,6 @@ type Accessor interface {
Len() int
MarshalJSON() ([]byte, error)
UnmarshalJSON([]byte) error
// MarshalMsgpack() ([]byte, error)
// UnmarshalMsgpack([]byte) error
// Serialize() interface{}
}
// NewChunk creates a new chunk.
@ -312,66 +308,3 @@ func (c *Chunk) UnmarshalJSON(b []byte) error {
return fmt.Errorf("Chunk.UnmarshalJSON: unsupported chunk type '%d'", c.Type)
}
}
func (c *Chunk) EncodeMsgpack(enc *msgpack.Encoder) error {
data := c.Accessor
generic := &JSONChunk{
Type: c.Type,
BinData: data,
}
return enc.Encode(generic)
}
func (c *Chunk) DecodeMsgpack(dec *msgpack.Decoder) error {
generic := &JSONChunk{}
err := dec.Decode(generic)
if err != nil {
return fmt.Errorf("Chunk.DecodeMsgpack: %s", err)
}
switch c.Type {
case MapType:
c.Accessor = generic.BinData.(MapAccessor)
default:
return fmt.Errorf("Chunk.DecodeMsgpack: unsupported chunk type '%d'", c.Type)
}
return nil
}
// // MarshalMsgpack writes the chunk to msgpack format.
// func (c *Chunk) MarshalMsgpack() ([]byte, error) {
// // data, err := c.Accessor.MarshalMsgpack()
// // if err != nil {
// // return []byte{}, err
// // }
// data := c.Accessor
//
// generic := &JSONChunk{
// Type: c.Type,
// BinData: data,
// }
// b, err := msgpack.Marshal(generic)
// return b, err
// }
//
// // UnmarshalMsgpack loads the chunk from msgpack format.
// func (c *Chunk) UnmarshalMsgpack(b []byte) error {
// // Parse it generically so we can hand off the inner "data" object to the
// // right accessor for unmarshalling.
// generic := &JSONChunk{}
// err := msgpack.Unmarshal(b, generic)
// if err != nil {
// return fmt.Errorf("Chunk.UnmarshalMsgpack: failed to unmarshal into generic JSONChunk type: %s", err)
// }
//
// switch c.Type {
// case MapType:
// c.Accessor = NewMapAccessor()
// return c.Accessor.UnmarshalMsgpack(generic.Data)
// default:
// return fmt.Errorf("Chunk.UnmarshalMsgpack: unsupported chunk type '%d'", c.Type)
// }
// }

View File

@ -6,7 +6,6 @@ import (
"fmt"
"git.kirsle.net/go/render"
"github.com/vmihailenco/msgpack"
)
// MapAccessor implements a chunk accessor by using a map of points to their
@ -128,65 +127,3 @@ func (a MapAccessor) UnmarshalJSON(b []byte) error {
return nil
}
// // MarshalMsgpack serializes for msgpack.
// func (a MapAccessor) MarshalMsgpack() ([]byte, error) {
// dict := map[string]int{}
// for point, sw := range a {
// dict[point.String()] = sw.Index()
// }
// return msgpack.Marshal(dict)
// }
//
// // Serialize converts the chunk accessor to a map for serialization.
// func (a MapAccessor) Serialize() interface{} {
// dict := map[string]int{}
// for point, sw := range a {
// dict[point.String()] = sw.Index()
// }
// return dict
// }
//
// // UnmarshalMsgpack decodes from msgpack format.
// func (a MapAccessor) UnmarshalMsgpack(b []byte) error {
// var dict map[string]int
// err := msgpack.Unmarshal(b, &dict)
// if err != nil {
// return err
// }
//
// for coord, index := range dict {
// point, err := render.ParsePoint(coord)
// if err != nil {
// return fmt.Errorf("MapAccessor.UnmarshalJSON: %s", err)
// }
// a[point] = NewSparseSwatch(index)
// }
//
// return nil
// }
func (a MapAccessor) EncodeMsgpack(enc *msgpack.Encoder) error {
dict := map[string]int{}
for point, sw := range a {
dict[point.String()] = sw.Index()
}
return enc.Encode(dict)
}
func (a MapAccessor) DecodeMsgpack(dec *msgpack.Decoder) error {
v, err := dec.DecodeMap()
if err != nil {
return fmt.Errorf("MapAccessor.DecodeMsgpack: %s", err)
}
dict := v.(map[string]int)
for coord, index := range dict {
point, err := render.ParsePoint(coord)
if err != nil {
return fmt.Errorf("MapAccessor.UnmarshalJSON: %s", err)
}
a[point] = NewSparseSwatch(index)
}
return nil
}

View File

@ -7,7 +7,6 @@ import (
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
"github.com/vmihailenco/msgpack"
)
// Chunker is the data structure that manages the chunks of a level, and
@ -336,14 +335,3 @@ func (c ChunkMap) MarshalJSON() ([]byte, error) {
out, err := json.Marshal(dict)
return out, err
}
// MarshalMsgpack to convert the chunk map to binary.
func (c ChunkMap) MarshalMsgpack() ([]byte, error) {
dict := map[string]*Chunk{}
for point, chunk := range c {
dict[point.String()] = chunk
}
out, err := msgpack.Marshal(dict)
return out, err
}

View File

@ -1,67 +0,0 @@
package level
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"git.kirsle.net/apps/doodle/pkg/filesystem"
"github.com/vmihailenco/msgpack"
)
// ToBinary serializes the level to binary format.
func (m *Level) ToBinary() ([]byte, error) {
header := filesystem.MakeHeader(filesystem.BinLevelType)
out := bytes.NewBuffer(header)
encoder := msgpack.NewEncoder(out)
err := encoder.Encode(m)
return out.Bytes(), err
}
// WriteBinary writes a level to binary format on disk.
func (m *Level) WriteBinary(filename string) error {
bin, err := m.ToBinary()
if err != nil {
return fmt.Errorf("Level.WriteBinary: encode error: %s", err)
}
err = ioutil.WriteFile(filename, bin, 0755)
if err != nil {
return fmt.Errorf("Level.WriteBinary: WriteFile error: %s", err)
}
return nil
}
// LoadBinary loads a map from binary file on disk.
func LoadBinary(filename string) (*Level, error) {
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fh.Close()
// Read and verify the file header from the binary format.
err = filesystem.ReadHeader(filesystem.BinLevelType, fh)
if err != nil {
return nil, err
}
// Decode the file from disk.
m := New()
decoder := msgpack.NewDecoder(fh)
err = decoder.Decode(&m)
if err != nil {
return m, fmt.Errorf("level.LoadBinary: decode error: %s", err)
}
// Fill in defaults.
if m.Wallpaper == "" {
m.Wallpaper = DefaultWallpaper
}
// Inflate the chunk metadata to map the pixels to their palette indexes.
m.Inflate()
return m, err
}

View File

@ -77,14 +77,7 @@ func LoadFile(filename string) (*Level, error) {
return FromJSON(filename, jsonData)
}
// Try the binary format.
if level, err := LoadBinary(filename); err == nil {
return level, nil
} else {
log.Warn(err.Error())
}
// Then the JSON format.
// Load as JSON.
if level, err := LoadJSON(filename); err == nil {
return level, nil
} else {

View File

@ -17,39 +17,39 @@ var (
// Base provides the common struct keys that are shared between Levels and
// Doodads.
type Base struct {
Version int `json:"version" msgpack:"0"` // File format version spec.
GameVersion string `json:"gameVersion" msgpack:"1"` // Game version that created the level.
Title string `json:"title" msgpack:"2"`
Author string `json:"author" msgpack:"3"`
Locked bool `json:"locked" msgpack:"4"`
Version int `json:"version"` // File format version spec.
GameVersion string `json:"gameVersion"` // Game version that created the level.
Title string `json:"title"`
Author string `json:"author"`
Locked bool `json:"locked"`
// Every drawing type is able to embed other files inside of itself.
Files FileSystem `json:"files" msgpack:"5"`
Files FileSystem `json:"files"`
}
// Level is the container format for Doodle map drawings.
type Level struct {
Base
Password string `json:"passwd" msgpack:"10"`
Password string `json:"passwd"`
// Chunked pixel data.
Chunker *Chunker `json:"chunks" msgpack:"12"`
Chunker *Chunker `json:"chunks"`
// The Palette holds the unique "colors" used in this map file, and their
// properties (solid, fire, slippery, etc.)
Palette *Palette `json:"palette" msgpack:"13"`
Palette *Palette `json:"palette"`
// Page boundaries and wallpaper settings.
PageType PageType `json:"pageType" msgpack:"14"`
MaxWidth int64 `json:"boundedWidth" msgpack:"15"` // only if bounded or bordered
MaxHeight int64 `json:"boundedHeight" msgpack:"16"`
Wallpaper string `json:"wallpaper" msgpack:"17"`
PageType PageType `json:"pageType"`
MaxWidth int64 `json:"boundedWidth"` // only if bounded or bordered
MaxHeight int64 `json:"boundedHeight"`
Wallpaper string `json:"wallpaper"`
// Actors keep a list of the doodad instances in this map.
Actors ActorMap `json:"actors" msgpack:"18"`
Actors ActorMap `json:"actors"`
// Undo history, temporary live data not persisted to the level file.
UndoHistory *drawtool.History `json:"-" msgpack:"-"`
UndoHistory *drawtool.History `json:"-"`
}
// New creates a blank level object with all its members initialized.

View File

@ -24,6 +24,11 @@ type Canvas struct {
ui.Frame
Palette *level.Palette
// Parent Canvas widget, e.g. for Actors inside of a Level so they can
// find the parent canvas and see where they are drawing in relation to
// it (to handle top/left edge cropping on scroll)
parent *Canvas
// Editable and Scrollable go hand in hand and, if you initialize a
// NewCanvas() with editable=true, they are both enabled.
Editable bool // Clicking will edit pixels of this canvas.

View File

@ -28,6 +28,7 @@ func (w *Canvas) InstallActors(actors level.ActorMap) error {
// Create the "live" Actor to exist in the world, and set its world
// position to the Point defined in the level data.
liveActor := NewActor(id, actor, doodad)
liveActor.Canvas.parent = w
liveActor.MoveTo(actor.Point)
w.actors = append(w.actors, liveActor)
@ -183,7 +184,6 @@ 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 // TODO
resizeTo.H -= delta
}

View File

@ -30,6 +30,15 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
H: S.H - w.BoxThickness(2),
})
// If we are an Actor canvas as part of a Level, get the absolute position of
// the parent (Level) canvas so we can compare where the Actor is drawn on-screen
// and detect if we are at the Top or Left edges of the parent, to crop and adjust
// the texture accordingly.
var ParentPosition render.Point
if w.parent != nil {
ParentPosition = ui.AbsolutePosition(w.parent)
}
// Draw the wallpaper.
if w.wallpaper.Valid() {
err := w.PresentWallpaper(e, p)
@ -88,7 +97,7 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// Zoom in the texture.
var (
texSize = tex.Size()
texSizeOrig = tex.Size()
texSizeOrig = texSize
)
if w.Zoom != 0 {
@ -136,14 +145,14 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// - 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 {
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 {
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
@ -160,18 +169,19 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
// - 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
if dst.X < p.X {
//
// 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 := p.X - dst.X
delta := texSizeOrig.W - src.W
dst.X = p.X + w.BoxThickness(1)
dst.W -= delta
src.X += delta
}
if dst.Y < p.Y {
delta := p.Y - dst.Y
if dst.Y == ParentPosition.Y+w.BoxThickness(1) {
delta := texSizeOrig.H - src.H
dst.Y = p.Y + w.BoxThickness(1)
dst.H -= delta
src.Y += delta
}