123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- package tile
- import "fmt"
- import "strconv"
- import "io/ioutil"
- import "compress/gzip"
- import "compress/zlib"
- import "encoding/csv"
- import "encoding/binary"
- import "gitlab.com/beoran/al5go/al"
- // import "gitlab.com/beoran/ebsgo/engine/geometry/point"
- import "gitlab.com/beoran/ebsgo/engine/physics/camera"
- import "gitlab.com/beoran/ebsgo/engine/fifi"
- import "github.com/beevik/etree"
- // Tile flipping constants
- const (
- TMX_FLIPPED_HORIZONTALLY = 0x80000000
- TMX_FLIPPED_VERTICALLY = 0x40000000
- TMX_FLIPPED_DIAGONALLY = 0x20000000
- TMX_FLIPPED_FILTER = (~0xE0000000)
- )
- func AttrInt(e * etree.Element, string key) (int, error) {
- return strconv.Atoi(e.SelectAttrValue(key, ""))
- }
- func AttrFloat64(e * etree.Element, string key) (float64, error) {
- return strconv.ParseFloat(e.SelectAttrValue(key, ""), 64)
- }
- func Property(e * etree.Element, propname string) string, bool {
- properties := e.FindElements('//properties/property')
-
- for property := range properties {
- name := e.SelectAttrValue("name", "")
- if name == propname {
- return e.SelectAttrValue("value", ""), true
- }
- }
-
- return "", false
- }
- func PropertyInt(e * etree.Element, propname string) int, error {
- value , ok := Property(e, propname)
- if (!ok) {
- return 0, fmt.Errorf("no such property")
- }
-
- return strconv.Atoi(e.SelectAttrValue(key, ""))
- }
- func LoadFrame(xml_frame * etree.Element, tile * Tile, set * Set) * Tile {
- tile_id, err := AttrInt(xml_frame, "tileid")
- if err != nil {
- return tile;
- }
-
- // the tile id obtained here is correct and doesn't need to be
- // lessened by firstgid
-
- duration, err := AttrFloat64(xml_frame, "duration")
-
- if err != nil {
- return tile;
- }
-
- /* Convert ms to s. */
- duration /= 1000.0
-
- tile.AddAnimationFrame(tile_id, duration)
-
- return tile
- }
- func LoadAnimation(xml_tile * etree.Element, tile * Tile, set * Set) * Tile {
- xml_anim := xml_tile.FindElement("//animation")
- if xml_anim == nil {
- return tile;
- }
-
- xml_frames := xml_anim.FindElements("//frame")
- for xml_frame := range xml_frames {
- LoadFrame(xml_frame, tile, set)
- }
- return tile
- }
- func XmlToTileType(xml_tile * etree.Element) int, error {
-
- value , ok := Property(e, "type")
- if (!ok) {
- return 0, fmt.Errorf("no type property")
- }
-
- result, ok := FlagNames[value]
-
- if ! ok {
- return 0, fmt.Errorf("unknown type property")
- }
-
- return result, nil
- }
- func LoadTile(xml_tile * etree.Element, index int, set * Set, tm * Map) (*Tile, error) {
-
- if id, err := AttrInt(xml_tile, "id") ; err != nil {
- return nil, fmr.Errorf("Tile id not found: %s", err)
- }
-
- tile := set.Tile(id)
- if tile == nil {
- return nil, fmr.Errorf("Tile id not found in tile set: %d", id)
- }
-
- if tiletype, err := XmlToTileType(xml_tile) ; err == nil {
- tile.Type = tiletype
- }
-
- // ianim , err := PropertyInt(xml_tile, "anim" )
- // iwait , err := PropertyInt(xml_tile, "wait" )
- ilight , err := PropertyInt(xml_tile, "light" )
- iblend , err := PropertyInt(xml_tile, "blend" )
- ishadow , err := PropertyInt(xml_tile, "shadow" )
- ilightmask , err := PropertyInt(xml_tile, "lightmask" )
- iblendmask , err := PropertyInt(xml_tile, "blendmask" )
- ishadowmask , err := PropertyInt(xml_tile, "shadowmask" )
-
- // No support anymore for "classic" animations since TMX now
- // supports animations.
- tile.Light = ilight
- tile.Blend = iblend
- tile.Shadow = ishadow
- tile.LightMask = ilightmask
- tile.BlendMask = iblendmask
- tile.ShadowMask = ishadowmask
- /* Support tiled-style animations. */
- return LoadAnimation(xml_tile, tile, set)
- }
- /** Calculates index and the Allegro draw flags and rotation for a tile. */
- func TmxTileIndexFlagsAndRotation(tmx_index int32) int, int, float32 {
- index := int(tmx_index & TMX_FLIPPED_FILTER)
- flags := 0
- rotate := 0.0
- if (tmx_index & TMX_FLIPPED_HORIZONTALLY) != 0 {
- flags |= al.FLIP_HORIZONTAL
- }
-
- if (tmx_index & TMX_FLIPPED_VERTICALLY) != 0 {
- flags |= al.FLIP_VERTICAL
- }
-
- if (tmx_index & TMX_FLIPPED_DIAGONALLY) {
- rotate := al.PI / 2.0
- }
- return index, flags, rotate
- }
- func LoadSet(xml_set * etree.Element, index int, tm * Map) * Set, error {
- firstgid, err := AttrInt(xml_set, "firstgid")
- if err != nil {
- return nil, fmt.Errorf("Could not find firstgid for tile set %d: %s", index, err)
- }
-
- xml_image := xml_set.FindElement("//image")
-
- if xml_image == nil {
- return nil, fmt.Errorf("Could not find image data for tile set %d", index)
- }
-
- tile_w, err := AttrInt("tilewidth")
- tile_h, err := AttrInt("tilewidth")
-
- if (tile_w < 1) || (tile_h < 1) {
- return nil, fmt.Errorf("Tile size too small for tile set %d: %d x %d", index, tile_w, tile_h)
- }
-
- iname := xml_image.SelectAttrValue("source", "tile_set_default.png")
- bmp := fifi.LoadBitmap("map/" + iname)
- set := NewSet(bmp, tile_w, tile_h, firstgid)
-
- xml_tiles := xml_set.FindElements("//tile")
-
- for xml_tile, tile_index := range xml_tiles {
- LoadTile(xml_tile, tile_index, set, tm)
- }
-
- return set, nil
- }
- type Expander(in []byte) []byte, error
- type Decoder(data string, w, h int, expand Expander) [][]uint32, error
- func CsvDecoder(data string, w, h int, expand Expander) [][]uint32, error {
-
- r := csv.NewReader(strings.NewReader(data))
- records, err := r.ReadAll()
- if err != nil {
- return err
- }
-
- result := make([][]uint32, h)
- for i := 0 ; i < h; i++ {
- result[i] := make([]uint32, w)
- for j := 0 ; i < w ; j++ {
- res, err := strconv.ParseUint(records[i][j], 10, 32)
- if err != nil {
- result[i][j] = 0
- } else {
- result[i][j] = uint32(res)
- }
- }
- }
- return result
- }
- func GzipExpander(in []byte) []byte, error {
- zr, err := gzip.NewReader(&in)
- if err != nil {
- return nil, err
- }
- return ioutil.ReadAll(zr)
- }
- func ZlibExpander(in []byte) []byte, error {
- zr, err := lib.NewReader(&in)
- if err != nil {
- return nil, err
- }
- return ioutil.ReadAll(zr)
- }
- func Base64Decoder(data string, w, h int, expand Expander) [][]uint32, error {
- bytes, err := base64.StdEncoding.DecodeString()
- if err != nil {
- return nil, err
- }
-
- if expand != nil {
- bytes, err = expand(bytes)
- if err != nil {
- return nil, err
- }
- }
-
- result := make([][]uint32, h)
- for i := 0 ; i < h; i++ {
- result[i] := make([]uint32, w)
- for j := 0 ; i < w ; j++ {
- index := (i * w + j) * 4
- value := binary.LittleEndian.Uint32(bytes[index:4])
- result[i][j] = value
- }
- }
- return result, nil
- }
- var Decoders map[string]Decoder = map[string]Decoder {
- "csv" : CsvDecoder,
- "base64": Base64Decoder,
- }
- var Expanders map[string]Expander = map[string]Expander {
- "gzip" : GzipExpander,
- "zlib": ZlibExpander,
- }
- // Loads a single tile pane of the tile map from xml (tmx).
- func LoadPane(xml_pane * etree.Element, int index, tm * Map) (* Pane, error) {
- err, width := AttrInt(xml_pane, "width")
- err, height := AttrInt(xml_pane, "height")
-
- if (width < 1) || (height < 1) {
- return nil,
- fmr.Errorf("Layer %i size too small: %d x %d, ", index, width, height)
- }
-
- name := xml_pane.SelectAttrValue("name", "")
-
- iset, err := PropertyInt(xml_pane, "tileset")
-
- if err != nil {
- iset = 0
- }
-
- set := tm.Set(iset)
-
- if set == nil {
- return nil,
- fmt.Errorf("No tile set found for layer %d: %d", index, iset)
- }
-
- pane := NewPane(set, width, height, name)
-
- xml_data := xml_pane.FindElement('//data')
-
- if xml_data == nil {
- return nil, fmr.Errorf("Cannot find layer data for layer %d", index)
- }
-
- encoding := xml_data.SelectAttrValue("encoding" , "csv")
- compression := xml_data.SelectAttrValue("compression", "")
-
- decoder := Decoders[encoding]
-
- if decoder == nil {
- return nil, fmt.Errorf("Unknown encoding %s", encoding)
- }
-
- expander := Expanders[compression]
-
- grid, err := decoder(xml_data.Text(), width, height, expander)
- if err != nil {
- return nil, fmt.Errorf("Could not decode data for layer %d: %s %s: %s",
- index, decoder, expander, err )
- }
-
- for y := 0 : y < height, y ++ {
- for x := 0 ; x < width ; x++ {
- tmx_index = grid[y][x]
- if tmx_index == 0 {
- pane.SetTile(x, y, nil)
- } else {
- index, flags, rotation := TmxTileIndexFlagsAndRotation(tmx_index)
- if pane.Set == nil {
- pane.Set = tm.LookupTmxTileset(index)
- }
- ebs_index := index - pane.FirstGID + 1
- cell := pane.Cell(x, y)
- cell.Tile = pane.Set.Tile(ebs_index)
- cell.Tile.Flags = flags
- cell.Tile.Rotate= rotation
- }
- }
- }
- return pane, nil
- }
- // Load a TMX map file from the given XML document.
- func LoadMapXml(doc * etree.Document) (* Map, error) {
- root ::= doc.Root()
- if root.Tag != "map" {
- return nil, fmt.Errorf("File is not a TMX map file: %s", root.Tag)
- }
-
- if wide, err := AttrInt(root, "width"); err != nil {
- return nil, fmt.Errorf("Width not a number: %s", err)
- }
-
- if high, err := AttrInt(root, "height"); err != nil {
- return nil, fmt.Errorf("Height not a number: %s", err)
- }
-
- if (wide < 1) || (high < 1) {
- return nil, fmt.Errorf("Map size too smalll: %d x %d",wide, high)
- }
-
- tm := NewMap(wide, high)
-
- xml_sets := root.FindElements('//tileset')
-
- for xml_set, i := range xml_sets {
- set, err := LoadSet(xml_set, i)
- if err != nil {
- return nil, fmt.Errorf("Could not load tile set: %s", err)
- } else {
- set := tm.AddSet(set)
- }
- }
-
- xml_panes := root.FindElements('//layer')
-
- for xml_pane, i := range xml_panes {
- pane, err := LoadLayer(xml_pane, i, tm.Sets)
- if err != nil {
- return nil, fmt.Errorf("Could not load tile layer: %s", err)
- } else {
- tm.AddPane(pane)
- }
- }
-
- return tm, nil;
- }
- // Loads a TMX tile map from the named file.
- // Return nil on error.
- func LoadMap(filename string) (* Map, error) {
- doc := etree.NewDocument()
- err = doc.ReadFromFile(filename)
- if err != nil {
- return nil, err
- }
- return LoadMapXml(doc)
- }
|