-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathtilepix.go
179 lines (151 loc) · 4.5 KB
/
tilepix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package tilepix
/*
_______ _ _ _____ _
|__ __(_) | | __ (_)
| | _| | ___| |__) |__ __
| | | | |/ _ \ ___/ \ \/ /
| | | | | __/ | | |> <
|_| |_|_|\___|_| |_/_/\_\
*/
import (
"encoding/xml"
"errors"
"io"
"net/http"
"os"
"path/filepath"
log "github.com/sirupsen/logrus"
)
const (
gidHorizontalFlip = 0x80000000
gidVerticalFlip = 0x40000000
gidDiagonalFlip = 0x20000000
gidFlip = gidHorizontalFlip | gidVerticalFlip | gidDiagonalFlip
)
// ObjectType is used to represent the types an object can be.
type ObjectType int
func (o ObjectType) String() string {
switch o {
case EllipseObj:
return "Ellipse"
case PolygonObj:
return "Polygon"
case PolylineObj:
return "Polyline"
case RectangleObj:
return "Rectangle"
case PointObj:
return "Point"
case TileObj:
return "Tile"
}
return "Unknown"
}
// These are the currently supported object types.
const (
EllipseObj ObjectType = iota
PolygonObj
PolylineObj
RectangleObj
PointObj
TileObj
)
// Errors which are returned from various places in the package.
var (
ErrUnknownEncoding = errors.New("tmx: invalid encoding scheme")
ErrUnknownCompression = errors.New("tmx: invalid compression method")
ErrInvalidDecodedDataLen = errors.New("tmx: invalid decoded data length")
ErrInvalidGID = errors.New("tmx: invalid GID")
ErrInvalidObjectType = errors.New("tmx: the object type requested does not match this object")
ErrInvalidPointsField = errors.New("tmx: invalid points string")
ErrInfiniteMap = errors.New("tmx: infinite maps are not currently supported")
)
var (
// NilTile is a tile with no tile set. Will be skipped over when drawing.
NilTile = &DecodedTile{Nil: true}
)
// GID is a global tile ID. Tiles can use GID or ID.
type GID uint32
// ID is a tile ID. Tiles can use GID or ID.
type ID uint32
// DataTile is a tile from a data object.
type DataTile struct {
GID GID `xml:"gid,attr"`
}
func osOpen(name string) (http.File, error) {
f, err := os.Open(name)
return f, err
}
// Read will read, decode and initialise a Tiled Map from a data reader.
// openFileFunc is used to retrieve tilesets and can be nil, in which case os.Open is used.
func Read(r io.Reader, dir string, openFileFunc func(name string) (http.File, error)) (*Map, error) {
log.Debug("Read: reading from io.Reader")
if openFileFunc == nil {
openFileFunc = osOpen
}
var m Map
if err := xml.NewDecoder(r).Decode(&m); err != nil {
log.WithError(err).Error("Read: could not decode to Map")
return nil, err
}
m.dir = dir
if m.Infinite {
log.WithError(ErrInfiniteMap).Error("Read: map has attribute 'infinite=true', not supported")
return nil, ErrInfiniteMap
}
log.WithField("Tileset count", len(m.Tilesets)).Debug("Read: checking for tileset sources")
for i, ts := range m.Tilesets {
if ts.Source != "" {
f, err := openFileFunc(filepath.Join(dir, ts.Source))
if err != nil {
log.WithError(err).Error("Read: could not read tileset source")
return nil, err
}
sourceTs, err := readTileset(f, dir)
_ = f.Close()
if err != nil {
log.WithError(err).Error("Read: could not read tileset source")
return nil, err
}
sourceTs.FirstGID = ts.FirstGID
m.Tilesets[i] = sourceTs
}
}
if err := m.decodeLayers(); err != nil {
log.WithError(err).Error("Read: could not decode layers")
return nil, err
}
m.setParents()
log.WithField("TileLayer count", len(m.TileLayers)).Debug("Read: processing layer tilesets")
for _, l := range m.TileLayers {
tileset, isEmpty, usesMultipleTilesets := getTileset(l)
if usesMultipleTilesets {
log.Debug("Read: multiple tilesets in use")
continue
}
l.Empty, l.Tileset = isEmpty, tileset
}
// Tiled calculates co-ordinates from the top-left, flipping the y co-ordinate means we match the standard
// bottom-left calculation.
log.WithField("Object layer count", len(m.ObjectGroups)).Debug("Read: processing object layers")
for _, og := range m.ObjectGroups {
og.flipY()
}
log.WithField("Tileset count", len(m.Tilesets)).Debug("Read: processing tilesets")
for _, ts := range m.Tilesets {
ts.setSprite()
}
return &m, nil
}
// ReadFile will read, decode and initialise a Tiled Map from a file path.
func ReadFile(filePath string) (*Map, error) {
log.WithField("Filepath", filePath).Debug("ReadFile: reading file")
f, err := os.Open(filePath)
if err != nil {
log.WithError(err).Error("ReadFile: could not open file")
return nil, err
}
defer f.Close()
dir := filepath.Dir(filePath)
return Read(f, dir, nil)
}