io.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. package tile
  2. import "fmt"
  3. import "strconv"
  4. import "io/ioutil"
  5. import "compress/gzip"
  6. import "compress/zlib"
  7. import "encoding/csv"
  8. import "encoding/binary"
  9. import "gitlab.com/beoran/al5go/al"
  10. // import "gitlab.com/beoran/ebsgo/engine/geometry/point"
  11. import "gitlab.com/beoran/ebsgo/engine/physics/camera"
  12. import "gitlab.com/beoran/ebsgo/engine/fifi"
  13. import "github.com/beevik/etree"
  14. // Tile flipping constants
  15. const (
  16. TMX_FLIPPED_HORIZONTALLY = 0x80000000
  17. TMX_FLIPPED_VERTICALLY = 0x40000000
  18. TMX_FLIPPED_DIAGONALLY = 0x20000000
  19. TMX_FLIPPED_FILTER = (~0xE0000000)
  20. )
  21. func AttrInt(e * etree.Element, string key) (int, error) {
  22. return strconv.Atoi(e.SelectAttrValue(key, ""))
  23. }
  24. func AttrFloat64(e * etree.Element, string key) (float64, error) {
  25. return strconv.ParseFloat(e.SelectAttrValue(key, ""), 64)
  26. }
  27. func Property(e * etree.Element, propname string) string, bool {
  28. properties := e.FindElements('//properties/property')
  29. for property := range properties {
  30. name := e.SelectAttrValue("name", "")
  31. if name == propname {
  32. return e.SelectAttrValue("value", ""), true
  33. }
  34. }
  35. return "", false
  36. }
  37. func PropertyInt(e * etree.Element, propname string) int, error {
  38. value , ok := Property(e, propname)
  39. if (!ok) {
  40. return 0, fmt.Errorf("no such property")
  41. }
  42. return strconv.Atoi(e.SelectAttrValue(key, ""))
  43. }
  44. func LoadFrame(xml_frame * etree.Element, tile * Tile, set * Set) * Tile {
  45. tile_id, err := AttrInt(xml_frame, "tileid")
  46. if err != nil {
  47. return tile;
  48. }
  49. // the tile id obtained here is correct and doesn't need to be
  50. // lessened by firstgid
  51. duration, err := AttrFloat64(xml_frame, "duration")
  52. if err != nil {
  53. return tile;
  54. }
  55. /* Convert ms to s. */
  56. duration /= 1000.0
  57. tile.AddAnimationFrame(tile_id, duration)
  58. return tile
  59. }
  60. func LoadAnimation(xml_tile * etree.Element, tile * Tile, set * Set) * Tile {
  61. xml_anim := xml_tile.FindElement("//animation")
  62. if xml_anim == nil {
  63. return tile;
  64. }
  65. xml_frames := xml_anim.FindElements("//frame")
  66. for xml_frame := range xml_frames {
  67. LoadFrame(xml_frame, tile, set)
  68. }
  69. return tile
  70. }
  71. func XmlToTileType(xml_tile * etree.Element) int, error {
  72. value , ok := Property(e, "type")
  73. if (!ok) {
  74. return 0, fmt.Errorf("no type property")
  75. }
  76. result, ok := FlagNames[value]
  77. if ! ok {
  78. return 0, fmt.Errorf("unknown type property")
  79. }
  80. return result, nil
  81. }
  82. func LoadTile(xml_tile * etree.Element, index int, set * Set, tm * Map) (*Tile, error) {
  83. if id, err := AttrInt(xml_tile, "id") ; err != nil {
  84. return nil, fmr.Errorf("Tile id not found: %s", err)
  85. }
  86. tile := set.Tile(id)
  87. if tile == nil {
  88. return nil, fmr.Errorf("Tile id not found in tile set: %d", id)
  89. }
  90. if tiletype, err := XmlToTileType(xml_tile) ; err == nil {
  91. tile.Type = tiletype
  92. }
  93. // ianim , err := PropertyInt(xml_tile, "anim" )
  94. // iwait , err := PropertyInt(xml_tile, "wait" )
  95. ilight , err := PropertyInt(xml_tile, "light" )
  96. iblend , err := PropertyInt(xml_tile, "blend" )
  97. ishadow , err := PropertyInt(xml_tile, "shadow" )
  98. ilightmask , err := PropertyInt(xml_tile, "lightmask" )
  99. iblendmask , err := PropertyInt(xml_tile, "blendmask" )
  100. ishadowmask , err := PropertyInt(xml_tile, "shadowmask" )
  101. // No support anymore for "classic" animations since TMX now
  102. // supports animations.
  103. tile.Light = ilight
  104. tile.Blend = iblend
  105. tile.Shadow = ishadow
  106. tile.LightMask = ilightmask
  107. tile.BlendMask = iblendmask
  108. tile.ShadowMask = ishadowmask
  109. /* Support tiled-style animations. */
  110. return LoadAnimation(xml_tile, tile, set)
  111. }
  112. /** Calculates index and the Allegro draw flags and rotation for a tile. */
  113. func TmxTileIndexFlagsAndRotation(tmx_index int32) int, int, float32 {
  114. index := int(tmx_index & TMX_FLIPPED_FILTER)
  115. flags := 0
  116. rotate := 0.0
  117. if (tmx_index & TMX_FLIPPED_HORIZONTALLY) != 0 {
  118. flags |= al.FLIP_HORIZONTAL
  119. }
  120. if (tmx_index & TMX_FLIPPED_VERTICALLY) != 0 {
  121. flags |= al.FLIP_VERTICAL
  122. }
  123. if (tmx_index & TMX_FLIPPED_DIAGONALLY) {
  124. rotate := al.PI / 2.0
  125. }
  126. return index, flags, rotate
  127. }
  128. func LoadSet(xml_set * etree.Element, index int, tm * Map) * Set, error {
  129. firstgid, err := AttrInt(xml_set, "firstgid")
  130. if err != nil {
  131. return nil, fmt.Errorf("Could not find firstgid for tile set %d: %s", index, err)
  132. }
  133. xml_image := xml_set.FindElement("//image")
  134. if xml_image == nil {
  135. return nil, fmt.Errorf("Could not find image data for tile set %d", index)
  136. }
  137. tile_w, err := AttrInt("tilewidth")
  138. tile_h, err := AttrInt("tilewidth")
  139. if (tile_w < 1) || (tile_h < 1) {
  140. return nil, fmt.Errorf("Tile size too small for tile set %d: %d x %d", index, tile_w, tile_h)
  141. }
  142. iname := xml_image.SelectAttrValue("source", "tile_set_default.png")
  143. bmp := fifi.LoadBitmap("map/" + iname)
  144. set := NewSet(bmp, tile_w, tile_h, firstgid)
  145. xml_tiles := xml_set.FindElements("//tile")
  146. for xml_tile, tile_index := range xml_tiles {
  147. LoadTile(xml_tile, tile_index, set, tm)
  148. }
  149. return set, nil
  150. }
  151. type Expander(in []byte) []byte, error
  152. type Decoder(data string, w, h int, expand Expander) [][]uint32, error
  153. func CsvDecoder(data string, w, h int, expand Expander) [][]uint32, error {
  154. r := csv.NewReader(strings.NewReader(data))
  155. records, err := r.ReadAll()
  156. if err != nil {
  157. return err
  158. }
  159. result := make([][]uint32, h)
  160. for i := 0 ; i < h; i++ {
  161. result[i] := make([]uint32, w)
  162. for j := 0 ; i < w ; j++ {
  163. res, err := strconv.ParseUint(records[i][j], 10, 32)
  164. if err != nil {
  165. result[i][j] = 0
  166. } else {
  167. result[i][j] = uint32(res)
  168. }
  169. }
  170. }
  171. return result
  172. }
  173. func GzipExpander(in []byte) []byte, error {
  174. zr, err := gzip.NewReader(&in)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return ioutil.ReadAll(zr)
  179. }
  180. func ZlibExpander(in []byte) []byte, error {
  181. zr, err := lib.NewReader(&in)
  182. if err != nil {
  183. return nil, err
  184. }
  185. return ioutil.ReadAll(zr)
  186. }
  187. func Base64Decoder(data string, w, h int, expand Expander) [][]uint32, error {
  188. bytes, err := base64.StdEncoding.DecodeString()
  189. if err != nil {
  190. return nil, err
  191. }
  192. if expand != nil {
  193. bytes, err = expand(bytes)
  194. if err != nil {
  195. return nil, err
  196. }
  197. }
  198. result := make([][]uint32, h)
  199. for i := 0 ; i < h; i++ {
  200. result[i] := make([]uint32, w)
  201. for j := 0 ; i < w ; j++ {
  202. index := (i * w + j) * 4
  203. value := binary.LittleEndian.Uint32(bytes[index:4])
  204. result[i][j] = value
  205. }
  206. }
  207. return result, nil
  208. }
  209. var Decoders map[string]Decoder = map[string]Decoder {
  210. "csv" : CsvDecoder,
  211. "base64": Base64Decoder,
  212. }
  213. var Expanders map[string]Expander = map[string]Expander {
  214. "gzip" : GzipExpander,
  215. "zlib": ZlibExpander,
  216. }
  217. // Loads a single tile pane of the tile map from xml (tmx).
  218. func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {
  219. err, width := AttrInt(xml_pane, "width")
  220. err, height := AttrInt(xml_pane, "height")
  221. if (width < 1) || (height < 1) {
  222. return nil,
  223. fmr.Errorf("Layer %i size too small: %d x %d, ", index, width, height)
  224. }
  225. name := xml_pane.SelectAttrValue("name", "")
  226. iset, err := PropertyInt(xml_pane, "tileset")
  227. if err != nil {
  228. iset = 0
  229. }
  230. set := tm.Set(iset)
  231. if set == nil {
  232. return nil,
  233. fmt.Errorf("No tile set found for layer %d: %d", index, iset)
  234. }
  235. pane := NewPane(set, width, height, name)
  236. xml_data := xml_pane.FindElement('//data')
  237. if xml_data == nil {
  238. return nil, fmr.Errorf("Cannot find layer data for layer %d", index)
  239. }
  240. encoding := xml_data.SelectAttrValue("encoding" , "csv")
  241. compression := xml_data.SelectAttrValue("compression", "")
  242. decoder := Decoders[encoding]
  243. if decoder == nil {
  244. return nil, fmt.Errorf("Unknown encoding %s", encoding)
  245. }
  246. expander := Expanders[compression]
  247. grid, err := decoder(xml_data.Text(), width, height, expander)
  248. if err != nil {
  249. return nil, fmt.Errorf("Could not decode data for layer %d: %s %s: %s",
  250. index, decoder, expander, err )
  251. }
  252. for y := 0 : y < height, y ++ {
  253. for x := 0 ; x < width ; x++ {
  254. tmx_index = grid[y][x]
  255. if tmx_index == 0 {
  256. pane.SetTile(x, y, nil)
  257. } else {
  258. index, flags, rotation := TmxTileIndexFlagsAndRotation(tmx_index)
  259. if pane.Set == nil {
  260. pane.Set = tm.LookupTmxTileset(index)
  261. }
  262. ebs_index := index - pane.FirstGID + 1
  263. cell := pane.Cell(x, y)
  264. cell.Tile = pane.Set.Tile(ebs_index)
  265. cell.Tile.Flags = flags
  266. cell.Tile.Rotate= rotation
  267. }
  268. }
  269. }
  270. return pane, nil
  271. }
  272. // Load a TMX map file from the given XML document.
  273. func LoadMapXml(doc * etree.Document) (* Map, error) {
  274. root ::= doc.Root()
  275. if root.Tag != "map" {
  276. return nil, fmt.Errorf("File is not a TMX map file: %s", root.Tag)
  277. }
  278. if wide, err := AttrInt(root, "width"); err != nil {
  279. return nil, fmt.Errorf("Width not a number: %s", err)
  280. }
  281. if high, err := AttrInt(root, "height"); err != nil {
  282. return nil, fmt.Errorf("Height not a number: %s", err)
  283. }
  284. if (wide < 1) || (high < 1) {
  285. return nil, fmt.Errorf("Map size too smalll: %d x %d",wide, high)
  286. }
  287. tm := NewMap(wide, high)
  288. xml_sets := root.FindElements('//tileset')
  289. for xml_set, i := range xml_sets {
  290. set, err := LoadSet(xml_set, i)
  291. if err != nil {
  292. return nil, fmt.Errorf("Could not load tile set: %s", err)
  293. } else {
  294. set := tm.AddSet(set)
  295. }
  296. }
  297. xml_panes := root.FindElements('//layer')
  298. for xml_pane, i := range xml_panes {
  299. pane, err := LoadLayer(xml_pane, i, tm.Sets)
  300. if err != nil {
  301. return nil, fmt.Errorf("Could not load tile layer: %s", err)
  302. } else {
  303. tm.AddPane(pane)
  304. }
  305. }
  306. return tm, nil;
  307. }
  308. // Loads a TMX tile map from the named file.
  309. // Return nil on error.
  310. func LoadMap(filename string) (* Map, error) {
  311. doc := etree.NewDocument()
  312. err = doc.ReadFromFile(filename)
  313. if err != nil {
  314. return nil, err
  315. }
  316. return LoadMapXml(doc)
  317. }