123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- package tile
- import "gitlab.com/beoran/ebsgo/monolog"
- import "fmt"
- import "strings"
- import "bytes"
- import "strconv"
- import "io/ioutil"
- import "compress/gzip"
- import "compress/zlib"
- import "encoding/binary"
- import "encoding/base64"
- 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 = (^uint32(0xE0000000))
- )
- func AttrInt(e * etree.Element, key string) (int, error) {
- return strconv.Atoi(e.SelectAttrValue(key, ""))
- }
- func AttrFloat64(e * etree.Element, key string) (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 := property.SelectAttrValue("name", "")
- if name == propname {
- return property.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(value)
- }
- 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
-
- frame := tile.AddAnimationFrame(tile_id, duration)
-
- monolog.Log("TILEIO", "Frame loaded: %d %f", frame.Index, frame.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)
- }
-
- monolog.Log("TILEIO", "Loaded animation: %d", len(tile.Frames))
- return tile
- }
- func XmlToTileType(xml_tile * etree.Element) (uint, error) {
-
- value , ok := Property(xml_tile, "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) {
-
- id, err := AttrInt(xml_tile, "id")
-
- if err != nil {
- return nil, fmt.Errorf("Tile id not found: %s", err)
- }
-
- tile := set.Tile(id)
- monolog.Log("TILEIO", "Got tile: %d <- %d", tile.Active, tile.Index)
-
-
- if tile == nil {
- return nil, fmt.Errorf("Tile id not found in tile set: %d", id)
- }
-
- value , ok := Property(xml_tile, "type")
- if (ok) {
- parts := strings.Split(value, ",")
- for _, part := range parts {
- clean := strings.Trim(part, "")
- tile.SetProperty(clean)
- }
- }
-
- // 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. */
- LoadAnimation(xml_tile, tile, set)
- monolog.Log("TILEIO", "Loaded tile: (%d x %d)", tile.SheetX(tile.Tileset), tile.SheetY(tile.Tileset))
- return tile, nil
- }
- /** Calculates index and the Allegro draw flags and rotation for a tile. */
- func TmxTileIndexFlagsAndRotation(tmx_index uint32) (int, int, float32) {
- index := int(tmx_index & TMX_FLIPPED_FILTER)
- flags := int(0)
- rotate := float32(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) != 0 {
- rotate = float32(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(xml_set, "tilewidth")
- tile_h, err := AttrInt(xml_set, "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)
-
- if bmp == nil {
- return nil, fmt.Errorf("Could not load tile set sheet: %s", iname)
- }
-
- set := NewSet(bmp, tile_w, tile_h, firstgid)
- monolog.Log("TILEIO", "Loaded tile set: (%d x %d) (%d x %d) %d", set.TileW, set.TileH, set.W, set.H, set.FirstGID)
-
- xml_tiles := xml_set.FindElements("//tile")
-
- for tile_index, xml_tile := range xml_tiles {
- LoadTile(xml_tile, tile_index, set, tm)
- }
-
- set.Recalculate()
-
- return set, nil
- }
- type Expander func(in []byte) ([]byte, error)
- type Decoder func(data string, w, h int, expand Expander) ([][]uint32, error)
- func CsvDecoder(data string, w, h int, expand Expander) ([][]uint32, error) {
- lines := strings.Split(data, "\n")
- x := 0
- y := 0
-
- result := make([][]uint32, h)
- for _, line := range lines {
- if line != "" {
- cells := strings.Split(line, ",")
- result[y] = make([]uint32, w)
- for x = 0 ; x < w ; x++ {
- value := uint32(0)
- if ( x >= len(cells) ) {
- break;
- }
- res, err := strconv.ParseUint(cells[x], 10, 32)
- if err == nil {
- value = uint32(res)
- }
- result[y][x] = value
- }
- y++
- }
- if y >= h {
- break;
- }
- }
- return result, nil
- }
- func GzipExpander(in []byte) ([]byte, error) {
- zr, err := gzip.NewReader(bytes.NewReader(in))
- if err != nil {
- return nil, err
- }
- return ioutil.ReadAll(zr)
- }
- func ZlibExpander(in []byte) ([]byte, error) {
- zr, err := zlib.NewReader(bytes.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(data)
- 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, index int, tm * Map) (* Pane, error) {
- width, err := AttrInt(xml_pane, "width")
- height, err := AttrInt(xml_pane, "height")
-
- if (width < 1) || (height < 1) {
- return nil,
- fmt.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, fmt.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;\nData:>%s<",
- index, encoding, compression, err, xml_data.Text())
- }
-
- monolog.Log("TILEIO", "Loaded grid size: (%d x %d) vs size (%d x %d)\n",len(grid), len(grid[0]), height, width)
-
- for y := 0 ; y < height ; y ++ {
- row := grid[y]
- for x := 0 ; x < width && x < len(row) ; x++ {
- tmx_index := row[x]
- if tmx_index == 0 {
- pane.SetTile(x, y, nil)
- } else {
- index, flags, rotation := TmxTileIndexFlagsAndRotation(tmx_index)
- if pane.Tileset == nil {
- pane.Tileset = tm.LookupTmxTileset(index)
- }
- ebs_index := index - pane.FirstGID() // + 1
- cell := pane.Cell(x, y)
- cell.Tile = pane.Tileset.Tile(ebs_index)
- cell.Flags = flags
- cell.Rotate = rotation
- }
- }
- }
-
- monolog.Log("TILEIO", "Loaded pane, initializing blends")
- pane.InitBlend(index)
-
-
- 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)
- }
-
- wide, err := AttrInt(root, "width")
-
- if err != nil {
- return nil, fmt.Errorf("Width not a number: %s", err)
- }
-
- high, err := AttrInt(root, "height");
- if 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 i, xml_set := range xml_sets {
- set, err := LoadSet(xml_set, i, tm)
- if err != nil {
- return nil, fmt.Errorf("Could not load tile set: %s", err)
- } else {
- _ = tm.AddSet(set)
- }
- }
-
- xml_panes := root.FindElements("//layer")
-
- for i, xml_pane := range xml_panes {
- pane, err := LoadPane(xml_pane, i, tm)
- 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)
- }
- func LoadMapFifi(relname string) (* Map, error) {
- realname := fifi.Map(relname)
- return LoadMap(realname)
- }
|