Exemplo n.º 1
0
 def __init__(self, mapReader):
     self.p = mapReader
     self.mMap = None
     self.mError = QString('')
     self.mReadingExternalTileset = False
     self.xml = QXmlStreamReader()
     self.mGidMapper = GidMapper()
Exemplo n.º 2
0
    def __init__(self):
        self.mLayerDataFormat = Map.LayerDataFormat.Base64Zlib
        self.mDtdEnabled = False
        self.mUseAbsolutePaths = False

        self.mMapDir = QDir()
        self.mGidMapper = GidMapper()
        self.mError = ''
Exemplo n.º 3
0
 def __init__(self, mapReader):
     self.p = mapReader
     self.mMap = None
     self.mError = QString('')
     self.mReadingExternalTileset = False
     self.xml = QXmlStreamReader()
     self.mGidMapper = GidMapper()
Exemplo n.º 4
0
 def __init__(self):
     self.mError = ''
     self.mGidMapper = GidMapper()
     self.mMap = None
     self.mMapDir = QDir()
     self.mReadingExternalTileset = False
Exemplo n.º 5
0
class VariantToMapConverter():
    def __init__(self):
        self.mError = ''
        self.mGidMapper = GidMapper()
        self.mMap = None
        self.mMapDir = QDir()
        self.mReadingExternalTileset = False

    # Using the MapReader context since the messages are the same
    def tr(self, sourceText, disambiguation='', n=-1):
        return QCoreApplication.translate('MapReader', sourceText,
                                          disambiguation, n)

    def trUtf8(self, sourceText, disambiguation='', n=-1):
        return QCoreApplication.translate('MapReader', sourceText,
                                          disambiguation, n)

    ##
    # Tries to convert the given \a variant to a Map instance. The \a mapDir
    # is necessary to resolve any relative references to external images.
    #
    # Returns 0 in case of an error. The error can be obstained using
    # self.errorString().
    ##
    def toMap(self, variant, mapDir):
        self.mGidMapper.clear()
        self.mMapDir = mapDir
        variantMap = variant
        orientationString = variantMap.get("orientation", '')
        orientation = orientationFromString(orientationString)
        if (orientation == Map.Orientation.Unknown):
            self.mError = self.tr("Unsupported map orientation: \"%s\"" %
                                  orientationString)
            return None

        staggerAxisString = variantMap.get("staggeraxis", '')
        staggerAxis = staggerAxisFromString(staggerAxisString)
        staggerIndexString = variantMap.get("staggerindex", '')
        staggerIndex = staggerIndexFromString(staggerIndexString)
        renderOrderString = variantMap.get("renderorder", '')
        renderOrder = renderOrderFromString(renderOrderString)
        nextObjectId = variantMap.get("nextobjectid", 0)
        map = Map(orientation, variantMap.get("width", 0),
                  variantMap.get("height", 0), variantMap.get("tilewidth", 0),
                  variantMap.get("tileheight", 0))
        map.setHexSideLength(variantMap.get("hexsidelength", 0))
        map.setStaggerAxis(staggerAxis)
        map.setStaggerIndex(staggerIndex)
        map.setRenderOrder(renderOrder)
        if (nextObjectId):
            map.setNextObjectId(nextObjectId)
        self.mMap = map
        map.setProperties(self.toProperties(variantMap.get("properties", {})))
        bgColor = variantMap.get("backgroundcolor", '')
        if (bgColor != '' and QColor.isValidColor(bgColor)):
            map.setBackgroundColor(QColor(bgColor))
        for tilesetVariant in variantMap.get("tilesets", []):
            tileset = self.__toTileset(tilesetVariant)
            if not tileset:
                return None

            map.addTileset(tileset)

        for layerVariant in variantMap.get("layers", []):
            layer = self.toLayer(layerVariant)
            if not layer:
                return None

            map.addLayer(layer)

        return map

    ##
    # Returns the last error, if any.
    ##
    def errorString(self):
        return self.mError

    def toProperties(self, variant):
        variantMap = variant
        properties = Properties()
        for it in variantMap.items():
            properties[it[0]] = it[1]
        return properties

    def toTileset(self, variant, directory):
        self.mMapDir = directory
        self.mReadingExternalTileset = True
        tileset = self.__toTileset(variant)
        self.mReadingExternalTileset = False
        return tileset

    def __toTileset(self, variant):
        variantMap = variant
        firstGid = variantMap.get("firstgid", 0)

        # Handle external tilesets
        sourceVariant = variantMap.get("source", '')
        if sourceVariant != '':
            source = resolvePath(self.mMapDir, sourceVariant)
            tileset, error = readTileset(source)
            if not tileset:
                self.mError = self.tr("Error while loading tileset '%s': %s" %
                                      (source, error))
            else:
                self.mGidMapper.insert(firstGid, tileset)
            return tileset

        name = variantMap.get("name", '')
        tileWidth = variantMap.get("tilewidth", 0)
        tileHeight = variantMap.get("tileheight", 0)
        spacing = variantMap.get("spacing", 0)
        margin = variantMap.get("margin", 0)
        tileOffset = variantMap.get("tileoffset", {})
        tileOffsetX = tileOffset.get("x", 0)
        tileOffsetY = tileOffset.get("y", 0)
        if (tileWidth <= 0 or tileHeight <= 0
                or (firstGid == 0 and not self.mReadingExternalTileset)):
            self.mError = self.tr(
                "Invalid tileset parameters for tileset '%s'" % name)
            return None

        tileset = Tileset.create(name, tileWidth, tileHeight, spacing, margin)
        tileset.setTileOffset(QPoint(tileOffsetX, tileOffsetY))
        trans = variantMap.get("transparentcolor", '')
        if (trans != '' and QColor.isValidColor(trans)):
            tileset.setTransparentColor(QColor(trans))
        imageVariant = variantMap.get("image", '')
        if imageVariant != '':
            imagePath = resolvePath(self.mMapDir, imageVariant)
            if (not tileset.loadFromImage(imagePath)):
                self.mError = self.tr("Error loading tileset image:\n'%s'" %
                                      imagePath)
                return None

        tileset.setProperties(
            self.toProperties(variantMap.get("properties", {})))

        # Read terrains
        terrainsVariantList = variantMap.get("terrains", [])
        for terrainMap in terrainsVariantList:
            tileset.addTerrain(terrainMap.get("name", ''),
                               terrainMap.get("tile", 0))

        # Read tile terrain and external image information
        tilesVariantMap = variantMap.get("tiles", {})
        for it in tilesVariantMap.items():
            ok = False
            tileIndex = Int(it[0])
            if (tileIndex < 0):
                self.mError = self.tr("Tileset tile index negative:\n'%d'" %
                                      tileIndex)

            if (tileIndex >= tileset.tileCount()):
                # Extend the tileset to fit the tile
                if (tileIndex >= len(tilesVariantMap)):
                    # If tiles are  defined this way, there should be an entry
                    # for each tile.
                    # Limit the index to number of entries to prevent running out
                    # of memory on malicious input.f
                    self.mError = self.tr(
                        "Tileset tile index too high:\n'%d'" % tileIndex)
                    return None

                for i in range(tileset.tileCount(), tileIndex + 1):
                    tileset.addTile(QPixmap())

            tile = tileset.tileAt(tileIndex)
            if (tile):
                tileVar = it[1]
                terrains = tileVar.get("terrain", [])
                if len(terrains) == 4:
                    for i in range(0, 4):
                        terrainId, ok = Int2(terrains[i])
                        if (ok and terrainId >= 0
                                and terrainId < tileset.terrainCount()):
                            tile.setCornerTerrainId(i, terrainId)

                probability, ok = Float2(tileVar.get("probability", 0.0))
                if (ok):
                    tile.setProbability(probability)
                imageVariant = tileVar.get("image", '')
                if imageVariant != '':
                    imagePath = resolvePath(self.mMapDir, imageVariant)
                    tileset.setTileImage(tileIndex, QPixmap(imagePath),
                                         imagePath)

                objectGroupVariant = tileVar.get("objectgroup", {})
                if len(objectGroupVariant) > 0:
                    tile.setObjectGroup(self.toObjectGroup(objectGroupVariant))
                frameList = tileVar.get("animation", [])
                lenFrames = len(frameList)
                if lenFrames > 0:
                    frames = QVector()
                    for i in range(lenFrames):
                        frames.append(Frame())
                    for i in range(lenFrames - 1, -1, -1):
                        frameVariantMap = frameList[i]
                        frame = frames[i]
                        frame.tileId = frameVariantMap.get("tileid", 0)
                        frame.duration = frameVariantMap.get("duration", 0)

                    tile.setFrames(frames)

        # Read tile properties
        propertiesVariantMap = variantMap.get("tileproperties", {})

        for it in propertiesVariantMap.items():
            tileIndex = Int(it[0])
            propertiesVar = it[1]
            if (tileIndex >= 0 and tileIndex < tileset.tileCount()):
                properties = self.toProperties(propertiesVar)
                tileset.tileAt(tileIndex).setProperties(properties)

        if not self.mReadingExternalTileset:
            self.mGidMapper.insert(firstGid, tileset)

        return tileset

    def toLayer(self, variant):
        variantMap = variant
        layer = None
        if (variantMap["type"] == "tilelayer"):
            layer = self.toTileLayer(variantMap)
        elif (variantMap["type"] == "objectgroup"):
            layer = self.toObjectGroup(variantMap)
        elif (variantMap["type"] == "imagelayer"):
            layer = self.toImageLayer(variantMap)
        if (layer):
            layer.setProperties(
                self.toProperties(variantMap.get("properties", {})))
            offset = QPointF(variantMap.get("offsetx", 0.0),
                             variantMap.get("offsety", 0.0))
            layer.setOffset(offset)

        return layer

    def toTileLayer(self, variantMap):
        name = variantMap.get("name", '')
        width = variantMap.get("width", 0)
        height = variantMap.get("height", 0)
        dataVariant = variantMap["data"]

        tileLayer = TileLayer(name, variantMap.get("x", 0),
                              variantMap.get("y", 0), width, height)
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        tileLayer.setOpacity(opacity)
        tileLayer.setVisible(visible)

        encoding = variantMap.get("encoding", '')
        compression = variantMap.get("compression", '')

        if encoding == '' or encoding == "csv":
            layerDataFormat = Map.LayerDataFormat.CSV
        elif (encoding == "base64"):
            if compression == '':
                layerDataFormat = Map.LayerDataFormat.Base64
            elif (compression == "gzip"):
                layerDataFormat = Map.LayerDataFormat.Base64Gzip
            elif (compression == "zlib"):
                layerDataFormat = Map.LayerDataFormat.Base64Zlib
            else:
                self.mError = self.tr("Compression method '%s' not supported" %
                                      compression)
                return None
        else:
            self.mError = self.tr("Unknown encoding: %1" % encoding)
            return None

        self.mMap.setLayerDataFormat(layerDataFormat)

        if layerDataFormat == Map.LayerDataFormat.XML or layerDataFormat == Map.LayerDataFormat.CSV:
            dataVariantList = dataVariant

            if len(dataVariantList) != width * height:
                self.mError = self.tr("Corrupt layer data for layer '%s'" %
                                      name)
                return None
            x = 0
            y = 0
            ok = False

            if (len(dataVariantList) != width * height):
                self.mError = self.tr("Corrupt layer data for layer '%s'" %
                                      name)
                return None
            for gidVariant in dataVariantList:
                gid, ok = Int2(gidVariant)
                if (not ok):
                    self.mError = self.tr(
                        "Unable to parse tile at (%d,%d) on layer '%s'" %
                        (x, y, tileLayer.name()))
                    return None

                cell = self.mGidMapper.gidToCell(gid, ok)
                tileLayer.setCell(x, y, cell)
                x += 1
                if (x >= tileLayer.width()):
                    x = 0
                    y += 1
        elif layerDataFormat==Map.LayerDataFormat.Base64 \
            or layerDataFormat==Map.LayerDataFormat.Base64Zlib \
            or layerDataFormat==Map.LayerDataFormat.Base64Gzip:
            data = QByteArray(dataVariant)
            error = self.mGidMapper.decodeLayerData(tileLayer, data,
                                                    layerDataFormat)

            if error == DecodeError.CorruptLayerData:
                self.mError = self.tr("Corrupt layer data for layer '%s'" %
                                      name)
                return None
            elif error == DecodeError.TileButNoTilesets:
                self.mError = self.tr("Tile used but no tilesets specified")
                return None
            elif error == DecodeError.InvalidTile:
                self.mError = self.tr("Invalid tile: %d" %
                                      self.mGidMapper.invalidTile())
                return None
            elif error == DecodeError.NoError:
                pass

        return tileLayer

    def toObjectGroup(self, variantMap):
        objectGroup = ObjectGroup(variantMap.get("name", ''),
                                  variantMap.get("x", 0),
                                  variantMap.get("y", 0),
                                  variantMap.get("width", 0),
                                  variantMap.get("height", 0))
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        objectGroup.setOpacity(opacity)
        objectGroup.setVisible(visible)
        objectGroup.setColor(variantMap.get("color", ''))
        drawOrderString = variantMap.get("draworder", '')
        if drawOrderString != '':
            objectGroup.setDrawOrder(drawOrderFromString(drawOrderString))
            if (objectGroup.drawOrder() == ObjectGroup.DrawOrder.UnknownOrder):
                self.mError = self.tr("Invalid draw order: %s" %
                                      drawOrderString)
                return None

        for objectVariant in variantMap.get("objects", []):
            objectVariantMap = objectVariant
            name = objectVariantMap.get("name", '')
            type = objectVariantMap.get("type", '')
            id = objectVariantMap.get("id", 0.0)
            gid = objectVariantMap.get("gid", 0.0)
            x = objectVariantMap.get("x", 0.0)
            y = objectVariantMap.get("y", 0.0)
            width = objectVariantMap.get("width", 0.0)
            height = objectVariantMap.get("height", 0.0)
            rotation = objectVariantMap.get("rotation", 0.0)
            pos = QPointF(x, y)
            size = QSizeF(width, height)
            object = MapObject(name, type, pos, size)
            object.setId(id)
            object.setRotation(rotation)
            if (gid):
                cell, ok = self.mGidMapper.gidToCell(gid)
                object.setCell(cell)
                if not object.cell().isEmpty():
                    tileSize = object.cell().tile.size()
                    if width == 0:
                        object.setWidth(tileSize.width())
                    if height == 0:
                        object.setHeight(tileSize.height())

            if (objectVariantMap.__contains__("visible")):
                object.setVisible(objectVariantMap.get("visible", True))
            object.setProperties(
                self.toProperties(objectVariantMap.get("properties", {})))
            objectGroup.addObject(object)
            polylineVariant = objectVariantMap.get("polyline", [])
            polygonVariant = objectVariantMap.get("polygon", [])
            if len(polygonVariant) > 0:
                object.setShape(MapObject.Polygon)
                object.setPolygon(self.toPolygon(polygonVariant))

            if len(polylineVariant) > 0:
                object.setShape(MapObject.Polyline)
                object.setPolygon(self.toPolygon(polylineVariant))

            if (objectVariantMap.__contains__("ellipse")):
                object.setShape(MapObject.Ellipse)

        return objectGroup

    def toImageLayer(self, variantMap):
        imageLayer = ImageLayer(variantMap.get("name", ''),
                                variantMap.get("x", 0), variantMap.get("y", 0),
                                variantMap.get("width", 0),
                                variantMap.get("height", 0))
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        imageLayer.setOpacity(opacity)
        imageLayer.setVisible(visible)
        trans = variantMap.get("transparentcolor", '')
        if (not trans.isEmpty() and QColor.isValidColor(trans)):
            imageLayer.setTransparentColor(QColor(trans))
        imageVariant = variantMap.get("image", '')
        if imageVariant != '':
            imagePath = resolvePath(self.mMapDir, imageVariant)
            if (not imageLayer.loadFromImage(QImage(imagePath), imagePath)):
                self.mError = self.tr("Error loading image:\n'%s'" % imagePath)
                return None

        return imageLayer

    def toPolygon(self, variant):
        polygon = QPolygonF()
        for pointVariant in variant:
            pointVariantMap = pointVariant
            pointX = pointVariantMap.get("x", 0.0)
            pointY = pointVariantMap.get("y", 0.0)
            polygon.append(QPointF(pointX, pointY))

        return polygon
Exemplo n.º 6
0
class MapReaderPrivate():
    def tr(self, sourceText, disambiguation='', n=-1):
        return QCoreApplication.translate('MapReader', sourceText,
                                          disambiguation, n)

    def trUtf8(self, sourceText, disambiguation='', n=-1):
        return QCoreApplication.translate('MapReader', sourceText,
                                          disambiguation, n)

    def __init__(self, mapReader):
        self.p = mapReader
        self.mMap = None
        self.mError = QString('')
        self.mReadingExternalTileset = False
        self.xml = QXmlStreamReader()
        self.mGidMapper = GidMapper()

    def readMap(self, device, path):
        self.mError = QString('')
        self.mPath = path
        map = None
        self.xml.setDevice(device)
        if (self.xml.readNextStartElement() and self.xml.name() == "map"):
            map = self.__readMap()
        else:
            self.xml.raiseError(self.tr("Not a map file."))

        self.mGidMapper.clear()
        return map

    def readTileset(self, device, path):
        self.mError = ''
        self.mPath = path
        tileset = None
        self.mReadingExternalTileset = True
        self.xml.setDevice(device)
        if (self.xml.readNextStartElement() and self.xml.name() == "tileset"):
            tileset = self.__readTileset()
        else:
            self.xml.raiseError(self.tr("Not a tileset file."))
        self.mReadingExternalTileset = False
        return tileset

    def openFile(self, file):
        if (not file.exists()):
            self.mError = self.tr("File not found: %s" % file.fileName())
            return False
        elif (not file.open(QFile.ReadOnly | QFile.Text)):
            self.mError = self.tr("Unable to read file: %s" % file.fileName())
            return False

        return True

    def errorString(self):
        if self.mError != '':
            return self.mError
        else:
            return self.tr("%d\n\nLine %d, column %s" %
                           (self.xml.lineNumber(), self.xml.columnNumber(),
                            self.xml.errorString()))

    def __readUnknownElement(self):
        qDebug("Unknown element (fixme): " + self.xml.name() + " at line " +
               self.xml.lineNumber() + ", column " + self.xml.columnNumber())
        self.xml.skipCurrentElement()

    def __readMap(self):
        atts = self.xml.attributes()
        mapWidth = Int(atts.value("width"))
        mapHeight = Int(atts.value("height"))
        tileWidth = Int(atts.value("tilewidth"))
        tileHeight = Int(atts.value("tileheight"))
        hexSideLength = Int(atts.value("hexsidelength"))
        orientationString = atts.value("orientation")
        orientation = orientationFromString(orientationString)
        if (orientation == Map.Orientation.Unknown):
            self.xml.raiseError(
                self.tr("Unsupported map orientation: \"%s\"" %
                        orientationString))

        staggerAxisString = atts.value("staggeraxis")
        staggerAxis = staggerAxisFromString(staggerAxisString)
        staggerIndexString = atts.value("staggerindex")
        staggerIndex = staggerIndexFromString(staggerIndexString)
        renderOrderString = atts.value("renderorder")
        renderOrder = renderOrderFromString(renderOrderString)
        nextObjectId = Int(atts.value("nextobjectid"))
        self.mMap = Map(orientation, mapWidth, mapHeight, tileWidth,
                        tileHeight)
        self.mMap.setHexSideLength(hexSideLength)
        self.mMap.setStaggerAxis(staggerAxis)
        self.mMap.setStaggerIndex(staggerIndex)
        self.mMap.setRenderOrder(renderOrder)
        if (nextObjectId):
            self.mMap.setNextObjectId(nextObjectId)

        bgColorString = atts.value("backgroundcolor")
        if len(bgColorString) > 0:
            self.mMap.setBackgroundColor(QColor(bgColorString))
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                self.mMap.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "tileset"):
                self.mMap.addTileset(self.__readTileset())
            elif (self.xml.name() == "layer"):
                self.mMap.addLayer(self.__readLayer())
            elif (self.xml.name() == "objectgroup"):
                self.mMap.addLayer(self.__readObjectGroup())
            elif (self.xml.name() == "imagelayer"):
                self.mMap.addLayer(self.__readImageLayer())
            else:
                self.__readUnknownElement()

        # Clean up in case of error
        if (self.xml.hasError()):
            self.mMap = None

        return self.mMap

    def __readTileset(self):
        atts = self.xml.attributes()
        source = atts.value("source")
        firstGid = Int(atts.value("firstgid"))
        tileset = None
        if source == '':  # Not an external tileset
            name = atts.value("name")
            tileWidth = Int(atts.value("tilewidth"))
            tileHeight = Int(atts.value("tileheight"))
            tileSpacing = Int(atts.value("spacing"))
            margin = Int(atts.value("margin"))
            if (tileWidth < 0 or tileHeight < 0
                    or (firstGid == 0 and not self.mReadingExternalTileset)):
                self.xml.raiseError(
                    self.tr("Invalid tileset parameters for tileset '%s'" %
                            name))
            else:
                tileset = Tileset.create(name, tileWidth, tileHeight,
                                         tileSpacing, margin)

                while (self.xml.readNextStartElement()):
                    if (self.xml.name() == "tile"):
                        self.__readTilesetTile(tileset)
                    elif (self.xml.name() == "tileoffset"):
                        oa = self.xml.attributes()
                        x = Int(oa.value("x"))
                        y = Int(oa.value("y"))
                        tileset.setTileOffset(QPoint(x, y))
                        self.xml.skipCurrentElement()
                    elif (self.xml.name() == "properties"):
                        tileset.mergeProperties(self.__readProperties())
                    elif (self.xml.name() == "image"):
                        if (tileWidth == 0 or tileHeight == 0):
                            self.xml.raiseError(
                                self.
                                tr("Invalid tileset parameters for tileset '%s'"
                                   % name))

                            tileset.clear()
                            break
                        else:
                            self.__readTilesetImage(tileset)
                    elif (self.xml.name() == "terraintypes"):
                        self.__readTilesetTerrainTypes(tileset)
                    else:
                        self.__readUnknownElement()
        else:  # External tileset
            absoluteSource = self.p.resolveReference(source, self.mPath)
            tileset, error = self.p.readExternalTileset(absoluteSource)
            if (not tileset):
                self.xml.raiseError(
                    self.tr("Error while loading tileset '%s': %s" %
                            (absoluteSource, error)))

            self.xml.skipCurrentElement()

        if (tileset and not self.mReadingExternalTileset):
            self.mGidMapper.insert(firstGid, tileset)
        return tileset

    def __readTilesetTile(self, tileset):
        atts = self.xml.attributes()
        id = Int(atts.value("id"))
        if (id < 0):
            self.xml.raiseError(self.tr("Invalid tile ID: %d" % id))
            return

        hasImage = tileset.imageSource() != ''
        if (hasImage and id >= tileset.tileCount()):
            self.xml.raiseError(
                self.tr("Tile ID does not exist in tileset image: %d" % id))
            return

        if (id > tileset.tileCount()):
            self.xml.raiseError(
                self.tr("Invalid (nonconsecutive) tile ID: %d" % id))
            return

        # For tilesets without image source, consecutive tile IDs are allowed (for
        # tiles with individual images)
        if (id == tileset.tileCount()):
            tileset.addTile(QPixmap())
        tile = tileset.tileAt(id)
        # Read tile quadrant terrain ids
        terrain = atts.value("terrain")
        if terrain != '':
            quadrants = terrain.split(",")
            if (len(quadrants) == 4):
                for i in range(4):
                    if quadrants[i] == '':
                        t = -1
                    else:
                        t = Int(quadrants[i])
                    tile.setCornerTerrainId(i, t)

        # Read tile probability
        probability = atts.value("probability")
        if probability != '':
            tile.setProbability(Float(probability))
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                tile.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "image"):
                source = self.xml.attributes().value("source")
                if source != '':
                    source = self.p.resolveReference(source, self.mPath)
                tileset.setTileImage(id, QPixmap.fromImage(self.__readImage()),
                                     source)
            elif (self.xml.name() == "objectgroup"):
                tile.setObjectGroup(self.__readObjectGroup())
            elif (self.xml.name() == "animation"):
                tile.setFrames(self.__readAnimationFrames())
            else:
                self.__readUnknownElement()

        # Temporary code to support TMW-style animation frame properties
        if (not tile.isAnimated() and tile.hasProperty("animation-frame0")):
            frames = QVector()
            i = 0
            while (i >= 0):
                frameName = "animation-frame" + str(i)
                delayName = "animation-delay" + str(i)
                if (tile.hasProperty(frameName)
                        and tile.hasProperty(delayName)):
                    frame = Frame()
                    frame.tileId = tile.property(frameName)
                    frame.duration = tile.property(delayName) * 10
                    frames.append(frame)
                else:
                    break
                i += 1

            tile.setFrames(frames)

    def __readTilesetImage(self, tileset):
        atts = self.xml.attributes()
        source = atts.value("source")
        trans = atts.value("trans")
        if len(trans) > 0:
            if (not trans.startswith('#')):
                trans = '#' + trans
            tileset.setTransparentColor(QColor(trans))

        if len(source) > 0:
            source = self.p.resolveReference(source, self.mPath)
        # Set the width that the tileset had when the map was saved
        width = Int(atts.value("width"))
        self.mGidMapper.setTilesetWidth(tileset, width)
        if (not tileset.loadFromImage(self.__readImage(), source)):
            self.xml.raiseError(
                self.tr("Error loading tileset image:\n'%s'" % source))

    def __readTilesetTerrainTypes(self, tileset):
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "terrain"):
                atts = self.xml.attributes()
                name = atts.value("name")
                tile = Int(atts.value("tile"))
                terrain = tileset.addTerrain(name, tile)
                while (self.xml.readNextStartElement()):
                    if (self.xml.name() == "properties"):
                        terrain.mergeProperties(self.__readProperties())
                    else:
                        self.__readUnknownElement()

            else:
                self.__readUnknownElement()

    def __readImage(self):
        atts = self.xml.attributes()
        source = atts.value("source")
        format = atts.value("format")
        if len(source) == 0:
            while (self.xml.readNextStartElement()):
                if (self.xml.name() == "data"):
                    atts = self.xml.attributes()
                    encoding = atts.value("encoding")
                    data = self.xml.readElementText().toLatin1()
                    if (encoding == "base64"):
                        data = QByteArray.fromBase64(data)

                    self.xml.skipCurrentElement()
                    return QImage.fromData(data, format.toLatin1())
                else:
                    self.__readUnknownElement()

        else:
            self.xml.skipCurrentElement()
            source = self.p.resolveReference(source, self.mPath)
            image = self.p.readExternalImage(source)
            if (image.isNull()):
                self.xml.raiseError(
                    self.tr("Error loading image:\n'%s'" % source))
            return image

        return QImage()

    def __readLayer(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        tileLayer = TileLayer(name, x, y, width, height)
        readLayerAttributes(tileLayer, atts)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                tileLayer.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "data"):
                self.__readLayerData(tileLayer)
            else:
                self.__readUnknownElement()

        return tileLayer

    def __readLayerData(self, tileLayer):
        atts = self.xml.attributes()
        encoding = atts.value("encoding")
        compression = atts.value("compression")
        layerDataFormat = 0
        if (encoding == ''):
            layerDataFormat = Map.LayerDataFormat.XML
        elif (encoding == "csv"):
            layerDataFormat = Map.LayerDataFormat.CSV
        elif (encoding == "base64"):
            if (compression == ''):
                layerDataFormat = Map.LayerDataFormat.Base64
            elif (compression == "gzip"):
                layerDataFormat = Map.LayerDataFormat.Base64Gzip
            elif (compression == "zlib"):
                layerDataFormat = Map.LayerDataFormat.Base64Zlib
            else:
                self.xml.raiseError(
                    self.tr("Compression method '%s' not supported" %
                            compression))
                return
        else:
            self.xml.raiseError(self.tr("Unknown encoding: %s" % encoding))
            return

        self.mMap.setLayerDataFormat(layerDataFormat)

        x = 0
        y = 0
        while (self.xml.readNext() != QXmlStreamReader.Invalid):
            if (self.xml.isEndElement()):
                break
            elif (self.xml.isStartElement()):
                if (self.xml.name() == "tile"):
                    if (y >= tileLayer.height()):
                        self.xml.raiseError(
                            self.tr("Too many <tile> elements"))
                        continue

                    atts = self.xml.attributes()
                    gid = Int(atts.value("gid"))
                    tileLayer.setCell(x, y, self.__cellForGid(gid))
                    x += 1
                    if (x >= tileLayer.width()):
                        x = 0
                        y += 1

                    self.xml.skipCurrentElement()
                else:
                    self.__readUnknownElement()
            elif (self.xml.isCharacters() and not self.xml.isWhitespace()):
                if (encoding == "base64"):
                    self.__decodeBinaryLayerData(tileLayer, self.xml.text(),
                                                 layerDataFormat)
                elif (encoding == "csv"):
                    self.__decodeCSVLayerData(tileLayer, self.xml.text())

    def __decodeBinaryLayerData(self, tileLayer, data, format):
        error = self.mGidMapper.decodeLayerData(tileLayer, data, format)

        if error == DecodeError.CorruptLayerData:
            self.xml.raiseError(
                self.tr("Corrupt layer data for layer '%s'" %
                        tileLayer.name()))
            return
        elif error == DecodeError.TileButNoTilesets:
            self.xml.raiseError(self.tr("Tile used but no tilesets specified"))
            return
        elif error == DecodeError.InvalidTile:
            self.xml.raiseError(
                self.tr("Invalid tile: %d" % self.mGidMapper.invalidTile()))
            return
        elif error == DecodeError.NoError:
            pass

    def __decodeCSVLayerData(self, tileLayer, text):
        trimText = text.strip()
        tiles = trimText.split(',')
        if (len(tiles) != tileLayer.width() * tileLayer.height()):
            self.xml.raiseError(
                self.tr("Corrupt layer data for layer '%s'" %
                        tileLayer.name()))
            return

        for y in range(tileLayer.height()):
            for x in range(tileLayer.width()):
                conversionOk = False
                gid, conversionOk = Int2(tiles[y * tileLayer.width() + x])
                if (not conversionOk):
                    self.xml.raiseError(
                        self.tr(
                            "Unable to parse tile at (%d,%d) on layer '%s'" %
                            (x + 1, y + 1, tileLayer.name())))
                    return

                tileLayer.setCell(x, y, self.__cellForGid(gid))

    ##
    # Returns the cell for the given global tile ID. Errors are raised with
    # the QXmlStreamReader.
    #
    # @param gid the global tile ID
    # @return the cell data associated with the given global tile ID, or an
    #         empty cell if not found
    ##
    def __cellForGid(self, gid):
        ok = False
        result, ok = self.mGidMapper.gidToCell(gid)
        if (not ok):
            if (self.mGidMapper.isEmpty()):
                self.xml.raiseError(
                    self.tr("Tile used but no tilesets specified"))
            else:
                self.xml.raiseError(self.tr("Invalid tile: %d" % gid))

        return result

    def __readImageLayer(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        imageLayer = ImageLayer(name, x, y, width, height)
        readLayerAttributes(imageLayer, atts)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "image"):
                self.__readImageLayerImage(imageLayer)
            elif (self.xml.name() == "properties"):
                imageLayer.mergeProperties(self.__readProperties())
            else:
                self.__readUnknownElement()

        return imageLayer

    def __readImageLayerImage(self, imageLayer):
        atts = self.xml.attributes()
        source = atts.value("source")
        trans = atts.value("trans")
        if trans != '':
            if (not trans.startswith('#')):
                trans = '#' + trans
            imageLayer.setTransparentColor(QColor(trans))

        source = self.p.resolveReference(source, self.mPath)
        imageLayerImage = self.p.readExternalImage(source)
        if (not imageLayer.loadFromImage(imageLayerImage, source)):
            self.xml.raiseError(
                self.tr("Error loading image layer image:\n'%s'" % source))
        self.xml.skipCurrentElement()

    def __readObjectGroup(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        objectGroup = ObjectGroup(name, x, y, width, height)
        readLayerAttributes(objectGroup, atts)
        color = atts.value("color")
        if color != '':
            objectGroup.setColor(color)
        if (atts.hasAttribute("draworder")):
            value = atts.value("draworder")
            drawOrder = drawOrderFromString(value)
            if (drawOrder == ObjectGroup.DrawOrder.UnknownOrder):
                #del objectGroup
                self.xml.raiseError(self.tr("Invalid draw order: %s" % value))
                return None

            objectGroup.setDrawOrder(drawOrder)

        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "object"):
                objectGroup.addObject(self.__readObject())
            elif (self.xml.name() == "properties"):
                objectGroup.mergeProperties(self.__readProperties())
            else:
                self.__readUnknownElement()

        return objectGroup

    def __readObject(self):
        atts = self.xml.attributes()
        id = Int(atts.value("id"))
        name = atts.value("name")
        gid = Int(atts.value("gid"))
        x = Float(atts.value("x"))
        y = Float(atts.value("y"))
        width = Float(atts.value("width"))
        height = Float(atts.value("height"))
        type = atts.value("type")
        visibleRef = atts.value("visible")
        pos = QPointF(x, y)
        size = QSizeF(width, height)
        object = MapObject(name, type, pos, size)
        object.setId(id)

        try:
            rotation = Float(atts.value("rotation"))
            ok = True
        except:
            ok = False
        if (ok):
            object.setRotation(rotation)
        if (gid):
            object.setCell(self.__cellForGid(gid))
            if (not object.cell().isEmpty()):
                tileSize = object.cell().tile.size()
                if (width == 0):
                    object.setWidth(tileSize.width())
                if (height == 0):
                    object.setHeight(tileSize.height())

        try:
            visible = int(visibleRef)
            ok = True
        except:
            ok = False
        if ok:
            object.setVisible(visible)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                object.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "polygon"):
                object.setPolygon(self.__readPolygon())
                object.setShape(MapObject.Polygon)
            elif (self.xml.name() == "polyline"):
                object.setPolygon(self.__readPolygon())
                object.setShape(MapObject.Polyline)
            elif (self.xml.name() == "ellipse"):
                self.xml.skipCurrentElement()
                object.setShape(MapObject.Ellipse)
            else:
                self.__readUnknownElement()

        return object

    def __readPolygon(self):
        atts = self.xml.attributes()
        points = atts.value("points")
        pointsList = list(filter(lambda x: x.strip() != '', points.split(' ')))
        polygon = QPolygonF()
        ok = True
        for point in pointsList:
            try:
                x, y = point.split(',')
            except:
                ok = False
                break

            x, ok = Float2(x)
            if (not ok):
                break

            y, ok = Float2(y)
            if (not ok):
                break
            polygon.append(QPointF(x, y))

        if (not ok):
            self.xml.raiseError(self.tr("Invalid points data for polygon"))
        self.xml.skipCurrentElement()
        return polygon

    def __readAnimationFrames(self):
        frames = QVector()
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "frame"):
                atts = self.xml.attributes()
                frame = Frame()
                frame.tileId = Int(atts.value("tileid"))
                frame.duration = Int(atts.value("duration"))
                frames.append(frame)
                self.xml.skipCurrentElement()
            else:
                self.__readUnknownElement()

        return frames

    def __readProperties(self):
        properties = Properties()
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "property"):
                self.__readProperty(properties)
            else:
                self.__readUnknownElement()

        return properties

    def __readProperty(self, properties):
        atts = self.xml.attributes()
        propertyName = atts.value("name")
        propertyValue = atts.value("value")
        while (self.xml.readNext() != QXmlStreamReader.Invalid):
            if (self.xml.isEndElement()):
                break
            elif (self.xml.isCharacters() and not self.xml.isWhitespace()):
                if (propertyValue.isEmpty()):
                    propertyValue = self.xml.text()
            elif (self.xml.isStartElement()):
                self.__readUnknownElement()

        properties.insert(propertyName, propertyValue)
Exemplo n.º 7
0
 def __init__(self):
     pass
     self.mMapDir = QDir()
     self.mGidMapper = GidMapper()
Exemplo n.º 8
0
class MapToVariantConverter():

    def __init__(self):
        pass
        self.mMapDir = QDir()
        self.mGidMapper = GidMapper()
        
    def toVariant(self, arg1, arg2):
        tp1 = type(arg1)
        tp2 = type(arg2)
        if tp1==Map and tp2==QDir:
            ##
            # Converts the given \s map to a QVariant. The \a mapDir is used to
            # construct relative paths to external resources.
            ##
            map, mapDir = arg1, arg2
            self.mMapDir = mapDir
            self.mGidMapper.clear()
            mapVariant = {}
            mapVariant["version"] = 1.0
            mapVariant["orientation"] = orientationToString(map.orientation())
            mapVariant["renderorder"] = renderOrderToString(map.renderOrder())
            mapVariant["width"] = map.width()
            mapVariant["height"] = map.height()
            mapVariant["tilewidth"] = map.tileWidth()
            mapVariant["tileheight"] = map.tileHeight()
            mapVariant["properties"] = self.__toVariant(map.properties())
            mapVariant["nextobjectid"] = map.nextObjectId()
            if (map.orientation() == Map.Orientation.Hexagonal) :
                mapVariant["hexsidelength"] = map.hexSideLength()
            
            if (map.orientation() == Map.Orientation.Hexagonal or map.orientation() == Map.Orientation.Staggered) :
                mapVariant["staggeraxis"] = staggerAxisToString(map.staggerAxis())
                mapVariant["staggerindex"] = staggerIndexToString(map.staggerIndex())
            
            bgColor = map.backgroundColor()
            if (bgColor.isValid()):
                mapVariant["backgroundcolor"] = bgColor.name()
            tilesetVariants = []
            firstGid = 1
            for tileset in map.tilesets():
                tilesetVariants.append(self.__toVariant(tileset, firstGid))
                self.mGidMapper.insert(firstGid, tileset)
                firstGid += tileset.tileCount()
            
            mapVariant["tilesets"] = tilesetVariants
            layerVariants = []
            for layer in map.layers():
                x = layer.layerType()
                if x==Layer.TileLayerType:
                    layerVariants.append(self.__toVariant(layer, map.layerDataFormat()))
                elif x==Layer.ObjectGroupType:
                    layerVariants.append(self.__toVariant(layer))
                elif x==Layer.ImageLayerType:
                    layerVariants.append(self.__toVariant(layer))
            
            mapVariant["layers"] = layerVariants
            return mapVariant
        elif tp1==Tileset and tp2==QDir:
            ##
            # Converts the given \s tileset to a QVariant. The \a directory is used to
            # construct relative paths to external resources.
            ##
            tileset, directory = arg1, arg2
            self.mMapDir = directory
            return self.__toVariant(tileset, 0)
        
    def __toVariant(self, *args):
        l = len(args)
        if l==1:
            arg = args[0]
            tp = type(arg)
            if tp == Properties:
                properties = arg
                variantMap = {}
                for it in properties:
                    variantMap[it[0]] = it[1]
                return variantMap
            elif tp == ObjectGroup:
                objectGroup = arg
                objectGroupVariant = {}
                objectGroupVariant["type"] = "objectgroup"
                if (objectGroup.color().isValid()):
                    objectGroupVariant["color"] = objectGroup.color().name()
                objectGroupVariant["draworder"] = drawOrderToString(objectGroup.drawOrder())
                self.addLayerAttributes(objectGroupVariant, objectGroup)
                objectVariants = []
                for object in objectGroup.objects():
                    objectVariant = {}
                    name = object.name()
                    _type = object.type()
                    objectVariant["properties"] = self.__toVariant(object.properties())
                    objectVariant["id"] = object.id()
                    objectVariant["name"] = name
                    objectVariant["type"] = _type
                    if (not object.cell().isEmpty()):
                        objectVariant["gid"] = self.mGidMapper.cellToGid(object.cell())
                    objectVariant["x"] = object.x()
                    objectVariant["y"] = object.y()
                    objectVariant["width"] = object.width()
                    objectVariant["height"] = object.height()
                    objectVariant["rotation"] = object.rotation()
                    objectVariant["visible"] = object.isVisible()
                    ## Polygons are stored in this format:
                    #
                    #   "polygon/polyline": [
                    #       { "x": 0, "y": 0 },
                    #       { "x": 1, "y": 1 },
                    #       ...
                    #   ]
                    ##
                    polygon = object.polygon()
                    if (not polygon.isEmpty()) :
                        pointVariants = []
                        for point in polygon:
                            pointVariant = {}
                            pointVariant["x"] = point.x()
                            pointVariant["y"] = point.y()
                            pointVariants.append(pointVariant)
                        
                        if (object.shape() == MapObject.Polygon):
                            objectVariant["polygon"] = pointVariants
                        else:
                            objectVariant["polyline"] = pointVariants
                    
                    if (object.shape() == MapObject.Ellipse):
                        objectVariant["ellipse"] = True
                    objectVariants.append(objectVariant)
                
                objectGroupVariant["objects"] = objectVariants
                return objectGroupVariant
            elif tp == ImageLayer:
                imageLayer = arg
                imageLayerVariant = {}
                imageLayerVariant["type"] = "imagelayer"
                self.addLayerAttributes(imageLayerVariant, imageLayer)
                rel = self.mMapDir.relativeFilePath(imageLayer.imageSource())
                imageLayerVariant["image"] = rel
                transColor = imageLayer.transparentColor()
                if (transColor.isValid()):
                    imageLayerVariant["transparentcolor"] = transColor.name()
                return imageLayerVariant
        elif l==2:
            arg1, arg2 = args
            tp1 = type(arg1)
            tp2 = type(arg2)
            if tp1==Tileset and tp2==int:
                tileset, firstGid = arg1, arg2
                tilesetVariant = {}
                
                if firstGid > 0:
                    tilesetVariant["firstgid"] = firstGid

                fileName = tileset.fileName()
                if fileName != '':
                    source = self.mMapDir.relativeFilePath(fileName)
                    tilesetVariant["source"] = source
                    # Tileset is external, so no need to write any of the stuff below
                    return tilesetVariant
                
                tilesetVariant["firstgid"] = firstGid
                tilesetVariant["name"] = tileset.name()
                tilesetVariant["tilewidth"] = tileset.tileWidth()
                tilesetVariant["tileheight"] = tileset.tileHeight()
                tilesetVariant["spacing"] = tileset.tileSpacing()
                tilesetVariant["margin"] = tileset.margin()
                tilesetVariant["tilecount"] = tileset.tileCount()
                tilesetVariant["properties"] = self.__toVariant(tileset.properties())
                offset = tileset.tileOffset()
                if (not offset.isNull()) :
                    tileOffset = {}
                    tileOffset["x"] = offset.x()
                    tileOffset["y"] = offset.y()
                    tilesetVariant["tileoffset"] = tileOffset
                
                # Write the image element
                imageSource = tileset.imageSource()
                if imageSource != '':
                    rel = self.mMapDir.relativeFilePath(tileset.imageSource())
                    tilesetVariant["image"] = rel
                    transColor = tileset.transparentColor()
                    if (transColor.isValid()):
                        tilesetVariant["transparentcolor"] = transColor.name()
                    tilesetVariant["imagewidth"] = tileset.imageWidth()
                    tilesetVariant["imageheight"] = tileset.imageHeight()
                
                ##
                # Write the properties, terrain, external image, object group and
                # animation for those tiles that have them.
                ##
                tilePropertiesVariant = {}
                tilesVariant = {}
                for i in range(0, tileset.tileCount()):
                    tile = tileset.tileAt(i)
                    properties = tile.properties()
                    if (not properties.isEmpty()):
                        tilePropertiesVariant[QString.number(i)] = self.__toVariant(properties)
                    tileVariant = {}
                    if (tile.terrain() != 0xFFFFFFFF) :
                        terrainIds = []
                        for j in range(0, 4):
                            terrainIds.append(tile.cornerTerrainId(j))
                        tileVariant["terrain"] = terrainIds
                    
                    if (tile.probability() != 1.0):
                        tileVariant["probability"] = tile.probability()
                    if tile.imageSource() != '':
                        rel = self.mMapDir.relativeFilePath(tile.imageSource())
                        tileVariant["image"] = rel
                    
                    if (tile.objectGroup()):
                        tileVariant["objectgroup"] = self.__toVariant(tile.objectGroup())
                    if (tile.isAnimated()) :
                        frameVariants = []
                        for frame in tile.frames():
                            frameVariant = {}
                            frameVariant["tileid"] = frame.tileId
                            frameVariant["duration"] = frame.duration
                            frameVariants.append(frameVariant)
                        
                        tileVariant["animation"] = frameVariants
                    
                    if len(tileVariant) > 0:
                        tilesVariant[QString.number(i)] = tileVariant
                
                if len(tilePropertiesVariant) > 0:
                    tilesetVariant["tileproperties"] = tilePropertiesVariant
                if len(tilesVariant) > 0:
                    tilesetVariant["tiles"] = tilesVariant
                # Write terrains
                if (tileset.terrainCount() > 0) :
                    terrainsVariant = []
                    for i in range(0, tileset.terrainCount()):
                        terrain = tileset.terrain(i)
                        properties = terrain.properties()
                        terrainVariant = {}
                        terrainVariant["name"] = terrain.name()
                        if (not properties.isEmpty()):
                            terrainVariant["properties"] = self.__toVariant(properties)
                        terrainVariant["tile"] = terrain.imageTileId()
                        terrainsVariant.append(terrainVariant)
                    
                    tilesetVariant["terrains"] = terrainsVariant
                
                return tilesetVariant
            elif tp1==TileLayer and tp2==Map.LayerDataFormat:
                tileLayer, format = arg1, arg2
                tileLayerVariant = {}
                tileLayerVariant["type"] = "tilelayer"
                self.addLayerAttributes(tileLayerVariant, tileLayer)
                
                if format == Map.LayerDataFormat.XML or format == Map.LayerDataFormat.CSV:
                    tileVariants = []
                    for y in range(tileLayer.height()):
                        for x in range(tileLayer.width()):
                            tileVariants.append(self.mGidMapper.cellToGid(tileLayer.cellAt(x, y)))
                    tileLayerVariant["data"] = tileVariants
                elif format in [Map.LayerDataFormat.Base64, Map.LayerDataFormat.Base64Zlib, Map.LayerDataFormat.Base64Gzip]:
                    tileLayerVariant["encoding"] = "base64"

                    if format == Map.LayerDataFormat.Base64Zlib:
                        tileLayerVariant["compression"] = "zlib"
                    elif format == Map.LayerDataFormat.Base64Gzip:
                        tileLayerVariant["compression"] = "gzip"

                    layerData = self.mGidMapper.encodeLayerData(tileLayer, format)
                    tileLayerVariant["data"] = layerData.data().decode()
                    
                return tileLayerVariant
                
    def addLayerAttributes(self, layerVariant, layer):
        layerVariant["name"] = layer.name()
        layerVariant["width"] = layer.width()
        layerVariant["height"] = layer.height()
        layerVariant["x"] = layer.x()
        layerVariant["y"] = layer.y()
        layerVariant["visible"] = layer.isVisible()
        layerVariant["opacity"] = layer.opacity()
        offset = layer.offset()
        if not offset.isNull():
            layerVariant["offsetx"] = offset.x()
            layerVariant["offsety"] = offset.y()

        properties = layer.properties()
        if (not properties.isEmpty()):
            layerVariant["properties"] = self.__toVariant(properties)
Exemplo n.º 9
0
    def read(self, fileName):
        file = QFile(fileName)
        if (not file.open (QIODevice.ReadOnly)):
            self.mError = self.tr("Could not open file for reading.")
            return None
        
        # default to values of the original flare alpha game.
        map = Map(Map.Orientation.Isometric, 256, 256, 64, 32)
        stream = QTextStream(file)
        line = QString()
        sectionName = QString()
        newsection = False
        path = QFileInfo(file).absolutePath()
        base = 10
        gidMapper = GidMapper()
        gid = 1
        tilelayer = None
        objectgroup = None
        mapobject = None
        tilesetsSectionFound = False
        headerSectionFound = False
        tilelayerSectionFound = False # tile layer or objects
        while (not stream.atEnd()):
            line = stream.readLine()
            if line == '':
                continue
            startsWith = line[0]
            if (startsWith == '['):
                sectionName = line[1:line.index(']')]
                newsection = True
                continue
            
            if (sectionName == "header"):
                headerSectionFound = True
                #get map properties
                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "width"):
                        map.setWidth(Int(value))
                    elif (key == "height"):
                        map.setHeight(Int(value))
                    elif (key == "tilewidth"):
                        map.setTileWidth(Int(value))
                    elif (key == "tileheight"):
                        map.setTileHeight(Int(value))
                    elif (key == "orientation"):
                        map.setOrientation(orientationFromString(value))
                    else:
                        map.setProperty(key, value)
                
            elif (sectionName == "tilesets"):
                tilesetsSectionFound = True
                epos = line.index('=')
                key = line[:epos].strip()
                value = line[epos + 1:].strip()
                if (key == "tileset"):
                    _list = value.split(',')
                    absoluteSource = _list[0]
                    if (QDir.isRelativePath(absoluteSource)):
                        absoluteSource = path + '/' + absoluteSource
                    tilesetwidth = 0
                    tilesetheight = 0
                    if len(_list) > 2:
                        tilesetwidth = Int(_list[1])
                        tilesetheight = Int(_list[2])
                    
                    tileset = Tileset.create(QFileInfo(absoluteSource).fileName(), tilesetwidth, tilesetheight)
                    ok = tileset.loadFromImage(absoluteSource)
                    if not ok:
                        self.mError = self.tr("Error loading tileset %s, which expands to %s. Path not found!"%(_list[0], absoluteSource))
                        return None
                    else :
                        if len(_list) > 4:
                            tileset.setTileOffset(QPoint(Int(_list[3]),Int(_list[4])))
                        gidMapper.insert(gid, tileset)
                        if len(_list) > 5:
                            gid += Int(_list[5])
                        else :
                            gid += tileset.tileCount()
                        
                        map.addTileset(tileset)

            elif (sectionName == "layer"):
                if (not tilesetsSectionFound):
                    self.mError = self.tr("No tilesets section found before layer section.")
                    return None
                
                tilelayerSectionFound = True
                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "type"):
                        tilelayer = TileLayer(value, 0, 0, map.width(),map.height())
                        map.addLayer(tilelayer)
                    elif (key == "format"):
                        if (value == "dec"):
                            base = 10
                        elif (value == "hex"):
                            base = 16
                        
                    elif (key == "data"):
                        for y in range(map.height()):
                            line = stream.readLine()
                            l = line.split(',')
                            for x in range(min(map.width(), len(l))):
                                ok = False
                                tileid = int(l[x], base)
                                c, ok = gidMapper.gidToCell(tileid)
                                if (not ok):
                                    self.mError += self.tr("Error mapping tile id %1.").arg(tileid)
                                    return None
                                
                                tilelayer.setCell(x, y, c)

                    else :
                        tilelayer.setProperty(key, value)

            else :
                if (newsection):
                    if (map.indexOfLayer(sectionName) == -1):
                        objectgroup = ObjectGroup(sectionName, 0,0,map.width(), map.height())
                        map.addLayer(objectgroup)
                    else :
                        objectgroup = map.layerAt(map.indexOfLayer(sectionName))
                    
                    mapobject = MapObject()
                    objectgroup.addObject(mapobject)
                    newsection = False
                
                if (not mapobject):
                    continue
                if (startsWith == '#'):
                    name = line[1].strip()
                    mapobject.setName(name)
                
                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "type"):
                        mapobject.setType(value)
                    elif (key == "location"):
                        loc = value.split(',')
                        x,y = 0.0, 0.0
                        w,h = 0, 0
                        if (map.orientation() == Map.Orthogonal):
                            x = loc[0].toFloat()*map.tileWidth()
                            y = loc[1].toFloat()*map.tileHeight()
                            if len(loc) > 3:
                                w = Int(loc[2])*map.tileWidth()
                                h = Int(loc[3])*map.tileHeight()
                            else :
                                w = map.tileWidth()
                                h = map.tileHeight()
                            
                        else :
                            x = loc[0].toFloat()*map.tileHeight()
                            y = loc[1].toFloat()*map.tileHeight()
                            if len(loc) > 3:
                                w = Int(loc[2])*map.tileHeight()
                                h = Int(loc[3])*map.tileHeight()
                            else :
                                w = h = map.tileHeight()

                        mapobject.setPosition(QPointF(x, y))
                        mapobject.setSize(w, h)
                    else :
                        mapobject.setProperty(key, value)


        if (not headerSectionFound or not tilesetsSectionFound or not tilelayerSectionFound):
            self.mError = self.tr("This seems to be no valid flare map. "
                        "A Flare map consists of at least a header "
                        "section, a tileset section and one tile layer.")
            return None
        
        return map
Exemplo n.º 10
0
class LuaPlugin(WritableMapFormat):
    def __init__(self):
        super().__init__()

        self.mMapDir = QDir()
        self.mError = ''
        self.mGidMapper = GidMapper()

    # MapWriterInterface
    def write(self, map, fileName):
        file = QSaveFile(fileName)
        if (not file.open(QIODevice.WriteOnly | QIODevice.Text)) :
            self.mError = self.tr("Could not open file for writing.")
            return False
        
        self.mMapDir = QDir(QFileInfo(fileName).path())
        writer = LuaTableWriter(file)
        writer.writeStartDocument()
        self.writeMap(writer, map)
        writer.writeEndDocument()
        if (file.error() != QFile.NoError) :
            self.mError = file.errorString()
            return False
        
        if (not file.commit()) :
            self.mError = file.errorString()
            return False
        return True
    
    def nameFilter(self):
        return self.tr("Lua files (*.lua)")
    
    def errorString(self):
        return self.mError

    def writeMap(self, writer, map):
        writer.writeStartReturnTable()
        writer.writeKeyAndValue("version", "1.1")
        writer.writeKeyAndValue("luaversion", "5.1")
        writer.writeKeyAndValue("tiledversion", QCoreApplication.applicationVersion())
        orientation = orientationToString(map.orientation())
        renderOrder = renderOrderToString(map.renderOrder())
        writer.writeKeyAndValue("orientation", orientation)
        writer.writeKeyAndValue("renderorder", renderOrder)
        writer.writeKeyAndValue("width", map.width())
        writer.writeKeyAndValue("height", map.height())
        writer.writeKeyAndValue("tilewidth", map.tileWidth())
        writer.writeKeyAndValue("tileheight", map.tileHeight())
        writer.writeKeyAndValue("nextobjectid", map.nextObjectId())
        if (map.orientation() == Map.Orientation.Hexagonal):
            writer.writeKeyAndValue("hexsidelength", map.hexSideLength())
        if (map.orientation() == Map.Orientation.Staggered or map.orientation() == Map.Orientation.Hexagonal) :
            writer.writeKeyAndValue("staggeraxis", staggerAxisToString(map.staggerAxis()))
            writer.writeKeyAndValue("staggerindex", staggerIndexToString(map.staggerIndex()))
        
        backgroundColor = map.backgroundColor()
        if (backgroundColor.isValid()) :
            # Example: backgroundcolor = { 255, 200, 100 }
            writer.writeStartTable("backgroundcolor")
            writer.setSuppressNewlines(True)
            writer.writeValue(backgroundColor.red())
            writer.writeValue(backgroundColor.green())
            writer.writeValue(backgroundColor.blue())
            if (backgroundColor.alpha() != 255):
                writer.writeValue(backgroundColor.alpha())
            writer.writeEndTable()
            writer.setSuppressNewlines(False)
        
        self.writeProperties(writer, map.properties())
        writer.writeStartTable("tilesets")
        self.mGidMapper.clear()
        firstGid = 1
        for tileset in map.tilesets():
            self.writeTileset(writer, tileset, firstGid)
            self.mGidMapper.insert(firstGid, tileset)
            firstGid += tileset.tileCount()
        
        writer.writeEndTable()
        writer.writeStartTable("layers")
        for layer in map.layers():
            x = layer.layerType()
            if x==Layer.TileLayerType:
                self.writeTileLayer(writer, layer, map.layerDataFormat())
            elif x==Layer.ObjectGroupType:
                self.writeObjectGroup(writer, layer)
            elif x==Layer.ImageLayerType:
                self.writeImageLayer(writer, layer)

        writer.writeEndTable()
        writer.writeEndTable()
    
    def writeProperties(self, writer, properties):
        writer.writeStartTable("properties")
        for it in properties:
            writer.writeQuotedKeyAndValue(it[0], it[1])
        writer.writeEndTable()

    def writeTileset(self, writer, tileset, firstGid):
        writer.writeStartTable()
        writer.writeKeyAndValue("name", tileset.name())
        writer.writeKeyAndValue("firstgid", firstGid)
        if tileset.fileName() != '':
            rel = self.mMapDir.relativeFilePath(tileset.fileName())
            writer.writeKeyAndValue("filename", rel)
        
        ## 
        # Include all tileset information even for external tilesets, since the
        # external reference is generally a .tsx file (in XML format).
        ##
        writer.writeKeyAndValue("tilewidth", tileset.tileWidth())
        writer.writeKeyAndValue("tileheight", tileset.tileHeight())
        writer.writeKeyAndValue("spacing", tileset.tileSpacing())
        writer.writeKeyAndValue("margin", tileset.margin())
        if tileset.imageSource() != '':
            rel = self.mMapDir.relativeFilePath(tileset.imageSource())
            writer.writeKeyAndValue("image", rel)
            writer.writeKeyAndValue("imagewidth", tileset.imageWidth())
            writer.writeKeyAndValue("imageheight", tileset.imageHeight())
        
        if (tileset.transparentColor().isValid()):
            writer.writeKeyAndValue("transparentcolor",tileset.transparentColor().name())

        offset = tileset.tileOffset()
        writer.writeStartTable("tileoffset")
        writer.writeKeyAndValue("x", offset.x())
        writer.writeKeyAndValue("y", offset.y())
        writer.writeEndTable()
        self.writeProperties(writer, tileset.properties())
        writer.writeStartTable("terrains")
        for i in range(tileset.terrainCount()):
            t = tileset.terrain(i)
            writer.writeStartTable()
            writer.writeKeyAndValue("name", t.name())
            writer.writeKeyAndValue("tile", t.imageTileId())
            self.writeProperties(writer, t.properties())
            writer.writeEndTable()

        writer.writeEndTable()
        writer.writeKeyAndValue("tilecount", tileset.tileCount())
        writer.writeStartTable("tiles")
        for i in range(0, tileset.tileCount()):
            tile = tileset.tileAt(i)
            # For brevity only write tiles with interesting properties
            if (not includeTile(tile)):
                continue
            writer.writeStartTable()
            writer.writeKeyAndValue("id", i)
            if (not tile.properties().isEmpty()):
                self.writeProperties(writer, tile.properties())
            if tile.imageSource() != '':
                src = self.mMapDir.relativeFilePath(tile.imageSource())
                tileSize = tile.size()
                writer.writeKeyAndValue("image", src)
                if (not tileSize.isNull()) :
                    writer.writeKeyAndValue("width", tileSize.width())
                    writer.writeKeyAndValue("height", tileSize.height())

            terrain = tile.terrain()
            if (terrain != 0xFFFFFFFF) :
                writer.writeStartTable("terrain")
                writer.setSuppressNewlines(True)
                for i in range(0, 4):
                    writer.writeValue(tile.cornerTerrainId(i))
                writer.writeEndTable()
                writer.setSuppressNewlines(False)
            
            if (tile.probability() != 1.0):
                writer.writeKeyAndValue("probability", tile.probability())
            objectGroup = tile.objectGroup()
            if objectGroup:
                self.writeObjectGroup(writer, objectGroup, "objectGroup")
            if (tile.isAnimated()) :
                frames = tile.frames()
                writer.writeStartTable("animation")
                for frame in frames:
                    writer.writeStartTable()
                    writer.writeKeyAndValue("tileid", QString.number(frame.tileId))
                    writer.writeKeyAndValue("duration", QString.number(frame.duration))
                    writer.writeEndTable()
                
                writer.writeEndTable() # animation
            
            writer.writeEndTable() # tile
        
        writer.writeEndTable() # tiles
        writer.writeEndTable() # tileset
    
    def writeTileLayer(self, writer, tileLayer, format):
        writer.writeStartTable()
        writer.writeKeyAndValue("type", "tilelayer")
        writer.writeKeyAndValue("name", tileLayer.name())
        writer.writeKeyAndValue("x", tileLayer.x())
        writer.writeKeyAndValue("y", tileLayer.y())
        writer.writeKeyAndValue("width", tileLayer.width())
        writer.writeKeyAndValue("height", tileLayer.height())
        writer.writeKeyAndValue("visible", tileLayer.isVisible())
        writer.writeKeyAndValue("opacity", tileLayer.opacity())
        offset = tileLayer.offset()
        writer.writeKeyAndValue("offsetx", offset.x())
        writer.writeKeyAndValue("offsety", offset.y())

        if format==Map.LayerDataFormat.XML or format==Map.LayerDataFormat.CSV:
            self.writeProperties(writer, tileLayer.properties())
            writer.writeKeyAndValue("encoding", "lua")
            writer.writeStartTable("data")
            for y in range(0, tileLayer.height()):
                if (y > 0):
                    writer.prepareNewLine()
                for x in range(0, tileLayer.width()):
                    writer.writeValue(self.mGidMapper.cellToGid(tileLayer.cellAt(x, y)))
        elif format==Map.LayerDataFormat.Base64 \
            or format==Map.LayerDataFormat.Base64Zlib \
            or format==Map.LayerDataFormat.Base64Gzip:
            writer.writeKeyAndValue("encoding", "base64")

            if format == Map.LayerDataFormat.Base64Zlib:
                writer.writeKeyAndValue("compression", "zlib")
            elif format == Map.LayerDataFormat.Base64Gzip:
                writer.writeKeyAndValue("compression", "gzip")

            layerData = self.mGidMapper.encodeLayerData(tileLayer, format)
            writer.writeKeyAndValue("data", layerData)

        writer.writeEndTable()
        writer.writeEndTable()
    
    def writeObjectGroup(self, writer, objectGroup, key=QByteArray()):
        if key=='':
            writer.writeStartTable()
        else:
            writer.writeStartTable(key)
        writer.writeKeyAndValue("type", "objectgroup")
        writer.writeKeyAndValue("name", objectGroup.name())
        writer.writeKeyAndValue("visible", objectGroup.isVisible())
        writer.writeKeyAndValue("opacity", objectGroup.opacity())
        
        offset = objectGroup.offset()
        writer.writeKeyAndValue("offsetx", offset.x())
        writer.writeKeyAndValue("offsety", offset.y())

        self.writeProperties(writer, objectGroup.properties())
        writer.writeStartTable("objects")
        for mapObject in objectGroup.objects():
            self.writeMapObject(writer, mapObject)
        writer.writeEndTable()
        writer.writeEndTable()
    
    def writeImageLayer(self, writer, imageLayer):
        writer.writeStartTable()
        writer.writeKeyAndValue("type", "imagelayer")
        writer.writeKeyAndValue("name", imageLayer.name())
        writer.writeKeyAndValue("x", imageLayer.x())
        writer.writeKeyAndValue("y", imageLayer.y())
        writer.writeKeyAndValue("visible", imageLayer.isVisible())
        writer.writeKeyAndValue("opacity", imageLayer.opacity())
        rel = self.mMapDir.relativeFilePath(imageLayer.imageSource())
        writer.writeKeyAndValue("image", rel)
        if (imageLayer.transparentColor().isValid()) :
            writer.writeKeyAndValue("transparentcolor",
                                    imageLayer.transparentColor().name())
        
        self.writeProperties(writer, imageLayer.properties())
        writer.writeEndTable()
    
    def writeMapObject(self, writer, mapObject):
        writer.writeStartTable()
        writer.writeKeyAndValue("id", mapObject.id())
        writer.writeKeyAndValue("name", mapObject.name())
        writer.writeKeyAndValue("type", mapObject.type())
        writer.writeKeyAndValue("shape", toString(mapObject.shape()))
        writer.writeKeyAndValue("x", mapObject.x())
        writer.writeKeyAndValue("y", mapObject.y())
        writer.writeKeyAndValue("width", mapObject.width())
        writer.writeKeyAndValue("height", mapObject.height())
        writer.writeKeyAndValue("rotation", mapObject.rotation())
        if (not mapObject.cell().isEmpty()):
            writer.writeKeyAndValue("gid", self.mGidMapper.cellToGid(mapObject.cell()))
        writer.writeKeyAndValue("visible", mapObject.isVisible())
        polygon = mapObject.polygon()
        if (not polygon.isEmpty()) :
            if (mapObject.shape() == MapObject.Polygon):
                writer.writeStartTable("polygon")
            else:
                writer.writeStartTable("polyline")
    #if defined(POLYGON_FORMAT_FULL)
            ## This format is the easiest to read and understand:
            #
            #  :
            #    { x = 1, y = 1 },
            #    { x = 2, y = 2 },
            #    { x = 3, y = 3 },
            #    ...
            #  }
            ##
            for point in polygon:
                writer.writeStartTable()
                writer.setSuppressNewlines(True)
                writer.writeKeyAndValue("x", point.x())
                writer.writeKeyAndValue("y", point.y())
                writer.writeEndTable()
                writer.setSuppressNewlines(False)
            
    #elif defined(POLYGON_FORMAT_PAIRS)
            ## This is an alternative that takes about 25% less memory.
            #
            #  :
            #    { 1, 1 },
            #    { 2, 2 },
            #    { 3, 3 },
            #    ...
            #  }
            ##
            for point in polygon:
                writer.writeStartTable()
                writer.setSuppressNewlines(True)
                writer.writeValue(point.x())
                writer.writeValue(point.y())
                writer.writeEndTable()
                writer.setSuppressNewlines(False)
            
    #elif defined(POLYGON_FORMAT_OPTIMAL)
            ## Writing it out in two tables, one for the x coordinates and one for
            # the y coordinates. It is a compromise between code readability and # performance. This takes the least amount of memory (60% less than
            # the first approach).
            #
            # x = { 1, 2, 3, ... }
            # y = { 1, 2, 3, ... }
            ##
            writer.writeStartTable("x")
            writer.setSuppressNewlines(True)
            for point in polygon:
                writer.writeValue(point.x())
            writer.writeEndTable()
            writer.setSuppressNewlines(False)
            writer.writeStartTable("y")
            writer.setSuppressNewlines(True)
            for point in polygon:
                writer.writeValue(point.y())
            writer.writeEndTable()
            writer.setSuppressNewlines(False)
    #endif
            writer.writeEndTable()
        
        self.writeProperties(writer, mapObject.properties())
        writer.writeEndTable()
Exemplo n.º 11
0
class MapWriterPrivate():

    def tr(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapWriter', sourceText, disambiguation, n)

    def trUtf8(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapWriter', sourceText, disambiguation, n)

    def __init__(self):
        self.mLayerDataFormat = Map.LayerDataFormat.Base64Zlib
        self.mDtdEnabled = False
        self.mUseAbsolutePaths = False

        self.mMapDir = QDir()
        self.mGidMapper = GidMapper()
        self.mError = ''

    def writeMap(self, map, device, path):
        self.mMapDir = QDir(path)
        self.mUseAbsolutePaths = path==''
        self.mLayerDataFormat = map.layerDataFormat()
        writer = createWriter(device)
        writer.writeStartDocument()
        if (self.mDtdEnabled):
            writer.writeDTD("<not DOCTYPE map SYSTEM \"http://mapeditor.org/dtd/1.0/map.dtd\">")

        self.__writeMap(writer, map)
        writer.writeEndDocument()
        del writer

    def writeTileset(self, tileset, device, path):
        self.mMapDir = QDir(path)
        self.mUseAbsolutePaths = path==''
        writer = createWriter(device)
        writer.writeStartDocument()
        if (self.mDtdEnabled):
            writer.writeDTD("<not DOCTYPE tileset SYSTEM \"http://mapeditor.org/dtd/1.0/map.dtd\">")

        self.__writeTileset(writer, tileset, 0)
        writer.writeEndDocument()
        del writer

    def openFile(self, file):
        if (not file.open(QIODevice.WriteOnly | QIODevice.Text)):
            self.mError = self.tr("Could not open file for writing.")
            return False

        return True

    def __writeMap(self, w, map):
        w.writeStartElement("map")
        orientation = orientationToString(map.orientation())
        renderOrder = renderOrderToString(map.renderOrder())
        w.writeAttribute("version", "1.0")
        w.writeAttribute("orientation", orientation)
        w.writeAttribute("renderorder", renderOrder)
        w.writeAttribute("width", str(map.width()))
        w.writeAttribute("height", str(map.height()))
        w.writeAttribute("tilewidth", str(map.tileWidth()))
        w.writeAttribute("tileheight", str(map.tileHeight()))
        if (map.orientation() == Map.Orientation.Hexagonal):
            w.writeAttribute("hexsidelength", str(map.hexSideLength()))

        if (map.orientation() == Map.Orientation.Staggered or map.orientation() == Map.Orientation.Hexagonal):
            w.writeAttribute("staggeraxis", staggerAxisToString(map.staggerAxis()))
            w.writeAttribute("staggerindex", staggerIndexToString(map.staggerIndex()))

        if (map.backgroundColor().isValid()):
            w.writeAttribute("backgroundcolor", map.backgroundColor().name())

        w.writeAttribute("nextobjectid", str(map.nextObjectId()))
        self.__writeProperties(w, map.properties())
        self.mGidMapper.clear()
        firstGid = 1
        for tileset in map.tilesets():
            self.__writeTileset(w, tileset, firstGid)
            self.mGidMapper.insert(firstGid, tileset)
            firstGid += tileset.tileCount()

        for layer in map.layers():
            type = layer.layerType()
            if (type == Layer.TileLayerType):
                self.__writeTileLayer(w, layer)
            elif (type == Layer.ObjectGroupType):
                self.__writeObjectGroup(w, layer)
            elif (type == Layer.ImageLayerType):
                self.__writeImageLayer(w, layer)

        w.writeEndElement()

    def __writeTileset(self, w, tileset, firstGid):
        w.writeStartElement("tileset")
        if (firstGid > 0):
            w.writeAttribute("firstgid", str(firstGid))
        fileName = tileset.fileName()
        if fileName != '':
            source = fileName
            if (not self.mUseAbsolutePaths):
                source = self.mMapDir.relativeFilePath(source)
            w.writeAttribute("source", source)
            # Tileset is external, so no need to write any of the stuff below
            w.writeEndElement()
            return

        w.writeAttribute("name", tileset.name())
        w.writeAttribute("tilewidth", str(tileset.tileWidth()))
        w.writeAttribute("tileheight", str(tileset.tileHeight()))
        tileSpacing = tileset.tileSpacing()
        margin = tileset.margin()
        if (tileSpacing != 0):
            w.writeAttribute("spacing",
                             str(tileSpacing))
        if (margin != 0):
            w.writeAttribute("margin", str(margin))
        
        w.writeAttribute("tilecount", str(tileset.tileCount()))
                     
        offset = tileset.tileOffset()
        if (not offset.isNull()):
            w.writeStartElement("tileoffset")
            w.writeAttribute("x", str(offset.x()))
            w.writeAttribute("y", str(offset.y()))
            w.writeEndElement()

        # Write the tileset properties
        self.__writeProperties(w, tileset.properties())
        # Write the image element
        imageSource = tileset.imageSource()
        if imageSource != '':
            w.writeStartElement("image")
            source = imageSource
            if (not self.mUseAbsolutePaths):
                source = self.mMapDir.relativeFilePath(source)
            w.writeAttribute("source", source)
            transColor = tileset.transparentColor()
            if (transColor.isValid()):
                w.writeAttribute("trans", transColor.name()[1])
            if (tileset.imageWidth() > 0):
                w.writeAttribute("width",
                                 str(tileset.imageWidth()))
            if (tileset.imageHeight() > 0):
                w.writeAttribute("height",
                                 str(tileset.imageHeight()))
            w.writeEndElement()

        # Write the terrain types
        if (tileset.terrainCount() > 0):
            w.writeStartElement("terraintypes")
            for i in range(tileset.terrainCount()):
                t = tileset.terrain(i)
                w.writeStartElement("terrain")
                w.writeAttribute("name", t.name())
                w.writeAttribute("tile", str(t.imageTileId()))
                self.__writeProperties(w, t.properties())
                w.writeEndElement()

            w.writeEndElement()

        # Write the properties for those tiles that have them
        for i in range(tileset.tileCount()):
            tile = tileset.tileAt(i)
            properties = tile.properties()
            terrain = tile.terrain()
            probability = tile.probability()
            objectGroup = tile.objectGroup()
            if (not properties.isEmpty() or terrain != 0xFFFFFFFF or probability != 1.0 or imageSource=='' or objectGroup or tile.isAnimated()):
                w.writeStartElement("tile")
                w.writeAttribute("id", str(i))
                if (terrain != 0xFFFFFFFF):
                    w.writeAttribute("terrain", makeTerrainAttribute(tile))
                if (probability != 1.0):
                    w.writeAttribute("probability", str(probability))
                if (not properties.isEmpty()):
                    self.__writeProperties(w, properties)
                if imageSource=='':
                    w.writeStartElement("image")
                    tileSize = tile.size()
                    if (not tileSize.isNull()):
                        w.writeAttribute("width",
                                         str(tileSize.width()))
                        w.writeAttribute("height",
                                         str(tileSize.height()))

                    if (tile.imageSource()==''):
                        w.writeAttribute("format",
                                         "png")
                        w.writeStartElement("data")
                        w.writeAttribute("encoding",
                                         "base64")
                        buffer = QBuffer()
                        tile.image().save(buffer, "png")
                        w.writeCharacters(buffer.data().toBase64())
                        w.writeEndElement() #
                    else:
                        source = tile.imageSource()
                        if (not self.mUseAbsolutePaths):
                            source = self.mMapDir.relativeFilePath(source)
                        w.writeAttribute("source", source)

                    w.writeEndElement() #

                if (objectGroup):
                    self.__writeObjectGroup(w, objectGroup)
                if (tile.isAnimated()):
                    frames = tile.frames()
                    w.writeStartElement("animation")
                    for frame in frames:
                        w.writeStartElement("frame")
                        w.writeAttribute("tileid", str(frame.tileId))
                        w.writeAttribute("duration", str(frame.duration))
                        w.writeEndElement() #

                    w.writeEndElement() #

                w.writeEndElement() #

        w.writeEndElement()

    def __writeTileLayer(self, w, tileLayer):
        w.writeStartElement("layer")
        self.__writeLayerAttributes(w, tileLayer)
        self.__writeProperties(w, tileLayer.properties())
        encoding = QString()
        compression = QString()
        if (self.mLayerDataFormat == Map.LayerDataFormat.Base64
                or self.mLayerDataFormat == Map.LayerDataFormat.Base64Gzip
                or self.mLayerDataFormat == Map.LayerDataFormat.Base64Zlib):
            encoding = "base64"
            if (self.mLayerDataFormat == Map.LayerDataFormat.Base64Gzip):
                compression = "gzip"
            elif (self.mLayerDataFormat == Map.LayerDataFormat.Base64Zlib):
                compression = "zlib"
        elif (self.mLayerDataFormat == Map.LayerDataFormat.CSV):
            encoding = "csv"
        w.writeStartElement("data")
        if encoding != '':
            w.writeAttribute("encoding", encoding)
        if compression != '':
            w.writeAttribute("compression", compression)
        if (self.mLayerDataFormat == Map.LayerDataFormat.XML):
            for y in range(tileLayer.height()):
                for x in range(tileLayer.width()):
                    gid = self.mGidMapper.cellToGid(tileLayer.cellAt(x, y))
                    w.writeStartElement("tile")
                    w.writeAttribute("gid", str(gid))
                    w.writeEndElement()
        elif (self.mLayerDataFormat == Map.LayerDataFormat.CSV):
            tileData = ''
            for y in range(tileLayer.height()):
                for x in range(tileLayer.width()):
                    gid = self.mGidMapper.cellToGid(tileLayer.cellAt(x, y))
                    tileData += str(gid)
                    if (x != tileLayer.width() - 1 or y != tileLayer.height() - 1):
                        tileData += ","

                tileData += "\n"

            w.writeCharacters("\n")
            w.writeCharacters(tileData)
        else:
            tileData = self.mGidMapper.encodeLayerData(tileLayer, self.mLayerDataFormat)
            
            w.writeCharacters("\n   ")
            w.writeCharacters(tileData.data().decode())
            w.writeCharacters("\n  ")

        w.writeEndElement() # </data>
        w.writeEndElement() # </layer>

    def __writeLayerAttributes(self, w, layer):
        if layer.name() != '':
            w.writeAttribute("name", layer.name())

        x = layer.x()
        y = layer.y()
        opacity = layer.opacity()
        if (x != 0):
            w.writeAttribute("x", str(x))
        if (y != 0):
            w.writeAttribute("y", str(y))
            
        if (layer.layerType() == Layer.TileLayerType):
            w.writeAttribute("width", str(layer.width()))
            w.writeAttribute("height", str(layer.height()))
            
        if (not layer.isVisible()):
            w.writeAttribute("visible", "0")
        if (opacity != 1.0):
            w.writeAttribute("opacity", str(opacity))
        
        offset = layer.offset()
        if (not offset.isNull()):
            w.writeAttribute("offsetx", str(offset.x()))
            w.writeAttribute("offsety", str(offset.y()))

    def __writeObjectGroup(self, w, objectGroup):
        w.writeStartElement("objectgroup")
        if (objectGroup.color().isValid()):
            w.writeAttribute("color", objectGroup.color().name())
        if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.TopDownOrder):
            w.writeAttribute("draworder", drawOrderToString(objectGroup.drawOrder()))

        self.__writeLayerAttributes(w, objectGroup)
        self.__writeProperties(w, objectGroup.properties())
        for mapObject in objectGroup.objects():
            self.__writeObject(w, mapObject)
        w.writeEndElement()

    def __writeObject(self, w, mapObject):
        w.writeStartElement("object")
        w.writeAttribute("id", str(mapObject.id()))
        name = mapObject.name()
        type = mapObject.type()
        if name != '':
            w.writeAttribute("name", name)
        if type != '':
            w.writeAttribute("type", type)
        if (not mapObject.cell().isEmpty()):
            gid = self.mGidMapper.cellToGid(mapObject.cell())
            w.writeAttribute("gid", str(gid))

        pos = mapObject.position()
        size = mapObject.size()
        
        w.writeAttribute("x", str(pos.x()))
        w.writeAttribute("y", str(pos.y()))
        if (size.width() != 0):
            w.writeAttribute("width", str(size.width()))
        if (size.height() != 0):
            w.writeAttribute("height", str(size.height()))
        rotation = mapObject.rotation()
        if (rotation != 0.0):
            w.writeAttribute("rotation", str(rotation))
        if (not mapObject.isVisible()):
            w.writeAttribute("visible"), "0"
        self.__writeProperties(w, mapObject.properties())
        polygon = mapObject.polygon()
        if (not polygon.isEmpty()):
            if (mapObject.shape() == MapObject.Polygon):
                w.writeStartElement("polygon")
            else:
                w.writeStartElement("polyline")
            points = ''
            for point in polygon:
                points += str(point.x()) + ',' + str(point.y()) + ' '

            points = points[:-1]
            w.writeAttribute("points", points)
            w.writeEndElement()

        if (mapObject.shape() == MapObject.Ellipse):
            w.writeEmptyElement("ellipse")
        w.writeEndElement()

    def __writeImageLayer(self, w, imageLayer):
        w.writeStartElement("imagelayer")
        self.__writeLayerAttributes(w, imageLayer)
        # Write the image element
        imageSource = imageLayer.imageSource()
        if imageSource != '':
            w.writeStartElement("image")
            source = imageSource
            if (not self.mUseAbsolutePaths):
                source = self.mMapDir.relativeFilePath(source)
            w.writeAttribute("source", source)
            transColor = imageLayer.transparentColor()
            if (transColor.isValid()):
                w.writeAttribute("trans", transColor.name()[1])
            w.writeEndElement()

        self.__writeProperties(w, imageLayer.properties())
        w.writeEndElement()

    def __writeProperties(self, w, properties):
        if (properties.isEmpty()):
            return
        w.writeStartElement("properties")
        for it in properties:
            w.writeStartElement("property")
            w.writeAttribute("name", it[0])
            value = str(it[1])
            if value.__contains__('\n'):
                w.writeCharacters(value)
            else:
                w.writeAttribute("value", value)

            w.writeEndElement()

        w.writeEndElement()
Exemplo n.º 12
0
    def read(self, fileName):
        file = QFile(fileName)
        if (not file.open(QIODevice.ReadOnly)):
            self.mError = self.tr("Could not open file for reading.")
            return None

        # default to values of the original flare alpha game.
        map = Map(Map.Orientation.Isometric, 256, 256, 64, 32)
        stream = QTextStream(file)
        line = QString()
        sectionName = QString()
        newsection = False
        path = QFileInfo(file).absolutePath()
        base = 10
        gidMapper = GidMapper()
        gid = 1
        tilelayer = None
        objectgroup = None
        mapobject = None
        tilesetsSectionFound = False
        headerSectionFound = False
        tilelayerSectionFound = False  # tile layer or objects
        while (not stream.atEnd()):
            line = stream.readLine()
            if line == '':
                continue
            startsWith = line[0]
            if (startsWith == '['):
                sectionName = line[1:line.index(']')]
                newsection = True
                continue

            if (sectionName == "header"):
                headerSectionFound = True
                #get map properties
                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "width"):
                        map.setWidth(Int(value))
                    elif (key == "height"):
                        map.setHeight(Int(value))
                    elif (key == "tilewidth"):
                        map.setTileWidth(Int(value))
                    elif (key == "tileheight"):
                        map.setTileHeight(Int(value))
                    elif (key == "orientation"):
                        map.setOrientation(orientationFromString(value))
                    else:
                        map.setProperty(key, value)

            elif (sectionName == "tilesets"):
                tilesetsSectionFound = True
                epos = line.index('=')
                key = line[:epos].strip()
                value = line[epos + 1:].strip()
                if (key == "tileset"):
                    _list = value.split(',')
                    absoluteSource = _list[0]
                    if (QDir.isRelativePath(absoluteSource)):
                        absoluteSource = path + '/' + absoluteSource
                    tilesetwidth = 0
                    tilesetheight = 0
                    if len(_list) > 2:
                        tilesetwidth = Int(_list[1])
                        tilesetheight = Int(_list[2])

                    tileset = Tileset.create(
                        QFileInfo(absoluteSource).fileName(), tilesetwidth,
                        tilesetheight)
                    ok = tileset.loadFromImage(absoluteSource)
                    if not ok:
                        self.mError = self.tr(
                            "Error loading tileset %s, which expands to %s. Path not found!"
                            % (_list[0], absoluteSource))
                        return None
                    else:
                        if len(_list) > 4:
                            tileset.setTileOffset(
                                QPoint(Int(_list[3]), Int(_list[4])))
                        gidMapper.insert(gid, tileset)
                        if len(_list) > 5:
                            gid += Int(_list[5])
                        else:
                            gid += tileset.tileCount()

                        map.addTileset(tileset)

            elif (sectionName == "layer"):
                if (not tilesetsSectionFound):
                    self.mError = self.tr(
                        "No tilesets section found before layer section.")
                    return None

                tilelayerSectionFound = True
                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "type"):
                        tilelayer = TileLayer(value, 0, 0, map.width(),
                                              map.height())
                        map.addLayer(tilelayer)
                    elif (key == "format"):
                        if (value == "dec"):
                            base = 10
                        elif (value == "hex"):
                            base = 16

                    elif (key == "data"):
                        for y in range(map.height()):
                            line = stream.readLine()
                            l = line.split(',')
                            for x in range(min(map.width(), len(l))):
                                ok = False
                                tileid = int(l[x], base)
                                c, ok = gidMapper.gidToCell(tileid)
                                if (not ok):
                                    self.mError += self.tr(
                                        "Error mapping tile id %1.").arg(
                                            tileid)
                                    return None

                                tilelayer.setCell(x, y, c)

                    else:
                        tilelayer.setProperty(key, value)

            else:
                if (newsection):
                    if (map.indexOfLayer(sectionName) == -1):
                        objectgroup = ObjectGroup(sectionName, 0, 0,
                                                  map.width(), map.height())
                        map.addLayer(objectgroup)
                    else:
                        objectgroup = map.layerAt(
                            map.indexOfLayer(sectionName))

                    mapobject = MapObject()
                    objectgroup.addObject(mapobject)
                    newsection = False

                if (not mapobject):
                    continue
                if (startsWith == '#'):
                    name = line[1].strip()
                    mapobject.setName(name)

                epos = line.index('=')
                if (epos != -1):
                    key = line[:epos].strip()
                    value = line[epos + 1:].strip()
                    if (key == "type"):
                        mapobject.setType(value)
                    elif (key == "location"):
                        loc = value.split(',')
                        x, y = 0.0, 0.0
                        w, h = 0, 0
                        if (map.orientation() == Map.Orthogonal):
                            x = loc[0].toFloat() * map.tileWidth()
                            y = loc[1].toFloat() * map.tileHeight()
                            if len(loc) > 3:
                                w = Int(loc[2]) * map.tileWidth()
                                h = Int(loc[3]) * map.tileHeight()
                            else:
                                w = map.tileWidth()
                                h = map.tileHeight()

                        else:
                            x = loc[0].toFloat() * map.tileHeight()
                            y = loc[1].toFloat() * map.tileHeight()
                            if len(loc) > 3:
                                w = Int(loc[2]) * map.tileHeight()
                                h = Int(loc[3]) * map.tileHeight()
                            else:
                                w = h = map.tileHeight()

                        mapobject.setPosition(QPointF(x, y))
                        mapobject.setSize(w, h)
                    else:
                        mapobject.setProperty(key, value)

        if (not headerSectionFound or not tilesetsSectionFound
                or not tilelayerSectionFound):
            self.mError = self.tr(
                "This seems to be no valid flare map. "
                "A Flare map consists of at least a header "
                "section, a tileset section and one tile layer.")
            return None

        return map
Exemplo n.º 13
0
    def write(self, map, fileName):
        file = QSaveFile(fileName)
        if (not file.open(QFile.WriteOnly | QFile.Text)):
            self.mError = self.tr("Could not open file for writing.")
            return False

        out = QTextStream(file)
        out.setCodec("UTF-8")
        mapWidth = map.width()
        mapHeight = map.height()
        # write [header]
        out << "[header]\n"
        out << "width=" << str(mapWidth) << "\n"
        out << "height=" << str(mapHeight) << "\n"
        out << "tilewidth=" << str(map.tileWidth()) << "\n"
        out << "tileheight=" << str(map.tileHeight()) << "\n"
        out << "orientation=" << str(orientationToString(
            map.orientation())) << "\n"
        # write all properties for this map
        for it in map.properties().__iter__():
            out << it[0] << "=" << it[1] << "\n"

        out << "\n"
        mapDir = QFileInfo(fileName).absoluteDir()
        out << "[tilesets]\n"
        for tileset in map.tilesets():
            imageSource = tileset.imageSource()
            source = mapDir.relativeFilePath(imageSource)
            out << "tileset=" << source \
                << "," << str(tileset.tileWidth()) \
                << "," << str(tileset.tileHeight()) \
                << "," << str(tileset.tileOffset().x()) \
                << "," << str(tileset.tileOffset().y()) \
                << "\n"

        out << "\n"
        gidMapper = GidMapper(map.tilesets())
        # write layers
        for layer in map.layers():
            tileLayer = layer.asTileLayer()
            if tileLayer:
                out << "[layer]\n"
                out << "type=" << layer.name() << "\n"
                out << "data=\n"
                for y in range(0, mapHeight):
                    for x in range(0, mapWidth):
                        t = tileLayer.cellAt(x, y)
                        id = 0
                        if (t.tile):
                            id = gidMapper.cellToGid(t)
                        out << id
                        if (x < mapWidth - 1):
                            out << ","

                    if (y < mapHeight - 1):
                        out << ","
                    out << "\n"

                out << "\n"

            group = layer.asObjectGroup()
            if group:
                for o in group.objects():
                    if o.type() != '':
                        out << "[" << group.name() << "]\n"
                        # display object name as comment
                        if o.name() != '':
                            out << "# " << o.name() << "\n"

                        out << "type=" << o.type() << "\n"
                        x, y, w, h = 0
                        if (map.orientation() == Map.Orthogonal):
                            x = o.x() / map.tileWidth()
                            y = o.y() / map.tileHeight()
                            w = o.width() / map.tileWidth()
                            h = o.height() / map.tileHeight()
                        else:
                            x = o.x() / map.tileHeight()
                            y = o.y() / map.tileHeight()
                            w = o.width() / map.tileHeight()
                            h = o.height() / map.tileHeight()

                        out << "location=" << x << "," << y
                        out << "," << w << "," << h << "\n"
                        # write all properties for this object
                        for it in o.properties().__iter__():
                            out << it[0] << "=" << it[1] << "\n"

                        out << "\n"

        if not file.commit():
            self.mError = file.errorString()
            return False

        return True
Exemplo n.º 14
0
 def __init__(self):
     self.mError = ''
     self.mGidMapper = GidMapper()
     self.mMap = None
     self.mMapDir = QDir()
     self.mReadingExternalTileset = False
Exemplo n.º 15
0
class VariantToMapConverter():

    def __init__(self):
        self.mError = ''
        self.mGidMapper = GidMapper()
        self.mMap = None
        self.mMapDir = QDir()
        self.mReadingExternalTileset = False

    # Using the MapReader context since the messages are the same
    def tr(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapReader', sourceText, disambiguation, n)

    def trUtf8(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapReader', sourceText, disambiguation, n)
        
    ##
    # Tries to convert the given \a variant to a Map instance. The \a mapDir
    # is necessary to resolve any relative references to external images.
    #
    # Returns 0 in case of an error. The error can be obstained using
    # self.errorString().
    ##
    def toMap(self, variant, mapDir):
        self.mGidMapper.clear()
        self.mMapDir = mapDir
        variantMap = variant
        orientationString = variantMap.get("orientation", '')
        orientation = orientationFromString(orientationString)
        if (orientation == Map.Orientation.Unknown):
            self.mError = self.tr("Unsupported map orientation: \"%s\""%orientationString)
            return None
        
        staggerAxisString = variantMap.get("staggeraxis", '')
        staggerAxis = staggerAxisFromString(staggerAxisString)
        staggerIndexString = variantMap.get("staggerindex", '')
        staggerIndex = staggerIndexFromString(staggerIndexString)
        renderOrderString = variantMap.get("renderorder", '')
        renderOrder = renderOrderFromString(renderOrderString)
        nextObjectId = variantMap.get("nextobjectid", 0)
        map = Map(orientation,
                           variantMap.get("width",0),
                           variantMap.get("height",0),
                           variantMap.get("tilewidth",0),
                           variantMap.get("tileheight",0))
        map.setHexSideLength(variantMap.get("hexsidelength", 0))
        map.setStaggerAxis(staggerAxis)
        map.setStaggerIndex(staggerIndex)
        map.setRenderOrder(renderOrder)
        if (nextObjectId):
            map.setNextObjectId(nextObjectId)
        self.mMap = map
        map.setProperties(self.toProperties(variantMap.get("properties", {})))
        bgColor = variantMap.get("backgroundcolor", '')
        if (bgColor!='' and QColor.isValidColor(bgColor)):
            map.setBackgroundColor(QColor(bgColor))
        for tilesetVariant in variantMap.get("tilesets", []):
            tileset = self.__toTileset(tilesetVariant)
            if not tileset:
                return None
            
            map.addTileset(tileset)
        
        for layerVariant in variantMap.get("layers", []):
            layer = self.toLayer(layerVariant)
            if not layer:
                return None
            
            map.addLayer(layer)
        
        return map
        
    ##
    # Returns the last error, if any.
    ##
    def errorString(self):
        return self.mError

    def toProperties(self, variant):
        variantMap = variant
        properties = Properties()
        for it in variantMap.items():
            properties[it[0]] = it[1]
        return properties
    
    def toTileset(self, variant, directory):
        self.mMapDir = directory
        self.mReadingExternalTileset = True
        tileset = self.__toTileset(variant)
        self.mReadingExternalTileset = False
        return tileset

    def __toTileset(self, variant):
        variantMap = variant
        firstGid = variantMap.get("firstgid",0)
        
        # Handle external tilesets
        sourceVariant = variantMap.get("source", '')
        if sourceVariant != '':
            source = resolvePath(self.mMapDir, sourceVariant)
            tileset, error = readTileset(source)
            if not tileset:
                self.mError = self.tr("Error while loading tileset '%s': %s"%(source, error))
            else:
                self.mGidMapper.insert(firstGid, tileset)
            return tileset

        name = variantMap.get("name",'')
        tileWidth = variantMap.get("tilewidth",0)
        tileHeight = variantMap.get("tileheight",0)
        spacing = variantMap.get("spacing",0)
        margin = variantMap.get("margin",0)
        tileOffset = variantMap.get("tileoffset", {})
        tileOffsetX = tileOffset.get("x",0)
        tileOffsetY = tileOffset.get("y",0)
        if (tileWidth <= 0 or tileHeight <= 0 or (firstGid == 0 and not self.mReadingExternalTileset)):
            self.mError = self.tr("Invalid tileset parameters for tileset '%s'"%name)
            return None
        
        tileset = Tileset.create(name, tileWidth, tileHeight, spacing, margin)
        tileset.setTileOffset(QPoint(tileOffsetX, tileOffsetY))
        trans = variantMap.get("transparentcolor", '')
        if (trans!='' and QColor.isValidColor(trans)):
            tileset.setTransparentColor(QColor(trans))
        imageVariant = variantMap.get("image",'')
        if imageVariant != '':
            imagePath = resolvePath(self.mMapDir, imageVariant)
            if (not tileset.loadFromImage(imagePath)):
                self.mError = self.tr("Error loading tileset image:\n'%s'"%imagePath)
                return None

        tileset.setProperties(self.toProperties(variantMap.get("properties", {})))
        
        # Read terrains
        terrainsVariantList = variantMap.get("terrains", [])
        for terrainMap in terrainsVariantList:
            tileset.addTerrain(terrainMap.get("name", ''), terrainMap.get("tile", 0))

        # Read tile terrain and external image information
        tilesVariantMap = variantMap.get("tiles", {})
        for it in tilesVariantMap.items():
            ok = False
            tileIndex = Int(it[0])
            if (tileIndex < 0):
                self.mError = self.tr("Tileset tile index negative:\n'%d'"%tileIndex)
            
            if (tileIndex >= tileset.tileCount()):
                # Extend the tileset to fit the tile
                if (tileIndex >= len(tilesVariantMap)):
                    # If tiles are  defined this way, there should be an entry
                    # for each tile.
                    # Limit the index to number of entries to prevent running out
                    # of memory on malicious input.f
                    self.mError = self.tr("Tileset tile index too high:\n'%d'"%tileIndex)
                    return None
                
                for i in range(tileset.tileCount(), tileIndex+1):
                    tileset.addTile(QPixmap())
            
            tile = tileset.tileAt(tileIndex)
            if (tile):
                tileVar = it[1]
                terrains = tileVar.get("terrain", [])
                if len(terrains) == 4:
                    for i in range(0, 4):
                        terrainId, ok = Int2(terrains[i])
                        if (ok and terrainId >= 0 and terrainId < tileset.terrainCount()):
                            tile.setCornerTerrainId(i, terrainId)

                probability, ok = Float2(tileVar.get("probability", 0.0))
                if (ok):
                    tile.setProbability(probability)
                imageVariant = tileVar.get("image",'')
                if imageVariant != '':
                    imagePath = resolvePath(self.mMapDir, imageVariant)
                    tileset.setTileImage(tileIndex, QPixmap(imagePath), imagePath)
                
                objectGroupVariant = tileVar.get("objectgroup", {})
                if len(objectGroupVariant) > 0:
                    tile.setObjectGroup(self.toObjectGroup(objectGroupVariant))
                frameList = tileVar.get("animation", [])
                lenFrames = len(frameList)
                if lenFrames > 0:
                    frames = QVector()
                    for i in range(lenFrames):
                        frames.append(Frame())
                    for i in range(lenFrames - 1, -1, -1):
                        frameVariantMap = frameList[i]
                        frame = frames[i]
                        frame.tileId = frameVariantMap.get("tileid", 0)
                        frame.duration = frameVariantMap.get("duration", 0)
                    
                    tile.setFrames(frames)

        
        # Read tile properties
        propertiesVariantMap = variantMap.get("tileproperties", {})
        
        for it in propertiesVariantMap.items():
            tileIndex = Int(it[0])
            propertiesVar = it[1]
            if (tileIndex >= 0 and tileIndex < tileset.tileCount()):
                properties = self.toProperties(propertiesVar)
                tileset.tileAt(tileIndex).setProperties(properties)

        if not self.mReadingExternalTileset:        
            self.mGidMapper.insert(firstGid, tileset)

        return tileset

    def toLayer(self, variant):
        variantMap = variant
        layer = None
        if (variantMap["type"] == "tilelayer"):
            layer = self.toTileLayer(variantMap)
        elif (variantMap["type"] == "objectgroup"):
            layer = self.toObjectGroup(variantMap)
        elif (variantMap["type"] == "imagelayer"):
            layer = self.toImageLayer(variantMap)
        if (layer):
            layer.setProperties(self.toProperties(variantMap.get("properties", {})))
            offset = QPointF(variantMap.get("offsetx", 0.0),  variantMap.get("offsety", 0.0))
            layer.setOffset(offset)

        return layer
    
    def toTileLayer(self, variantMap):
        name = variantMap.get("name",'')
        width = variantMap.get("width",0)
        height = variantMap.get("height",0)
        dataVariant = variantMap["data"]
        
        tileLayer = TileLayer(name,
                             variantMap.get("x",0),
                             variantMap.get("y",0),
                             width, height)
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        tileLayer.setOpacity(opacity)
        tileLayer.setVisible(visible)

        encoding = variantMap.get("encoding", '')
        compression = variantMap.get("compression", '')

        if encoding=='' or encoding == "csv":
            layerDataFormat = Map.LayerDataFormat.CSV
        elif (encoding == "base64"):
            if compression=='':
                layerDataFormat = Map.LayerDataFormat.Base64
            elif (compression == "gzip"):
                layerDataFormat = Map.LayerDataFormat.Base64Gzip
            elif (compression == "zlib"):
                layerDataFormat = Map.LayerDataFormat.Base64Zlib
            else:
                self.mError = self.tr("Compression method '%s' not supported"%compression)
                return None
        else:
            self.mError = self.tr("Unknown encoding: %1"%encoding)
            return None

        self.mMap.setLayerDataFormat(layerDataFormat)

        if layerDataFormat==Map.LayerDataFormat.XML or layerDataFormat==Map.LayerDataFormat.CSV:
            dataVariantList = dataVariant

            if len(dataVariantList) != width * height:
                self.mError = self.tr("Corrupt layer data for layer '%s'"%name)
                return None
            x = 0
            y = 0
            ok = False
            
            if (len(dataVariantList) != width * height):
                self.mError = self.tr("Corrupt layer data for layer '%s'"%name)
                return None
            for gidVariant in dataVariantList:
                gid, ok = Int2(gidVariant)
                if (not ok):
                    self.mError = self.tr("Unable to parse tile at (%d,%d) on layer '%s'"%(x, y, tileLayer.name()))
                    return None
                
                cell = self.mGidMapper.gidToCell(gid, ok)
                tileLayer.setCell(x, y, cell)
                x += 1
                if (x >= tileLayer.width()):
                    x = 0
                    y += 1
        elif layerDataFormat==Map.LayerDataFormat.Base64 \
            or layerDataFormat==Map.LayerDataFormat.Base64Zlib \
            or layerDataFormat==Map.LayerDataFormat.Base64Gzip:
            data = QByteArray(dataVariant)
            error = self.mGidMapper.decodeLayerData(tileLayer, data, layerDataFormat)

            if error==DecodeError.CorruptLayerData:
                self.mError = self.tr("Corrupt layer data for layer '%s'"%name)
                return None
            elif error==DecodeError.TileButNoTilesets:
                self.mError = self.tr("Tile used but no tilesets specified")
                return None
            elif error==DecodeError.InvalidTile:
                self.mError = self.tr("Invalid tile: %d"%self.mGidMapper.invalidTile())
                return None
            elif error==DecodeError.NoError:
                pass
            
        return tileLayer

    def toObjectGroup(self, variantMap):
        objectGroup = ObjectGroup(variantMap.get("name",''),
                               variantMap.get("x",0),
                               variantMap.get("y",0),
                               variantMap.get("width",0),
                               variantMap.get("height",0))
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        objectGroup.setOpacity(opacity)
        objectGroup.setVisible(visible)
        objectGroup.setColor(variantMap.get("color", ''))
        drawOrderString = variantMap.get("draworder", '')
        if drawOrderString != '':
            objectGroup.setDrawOrder(drawOrderFromString(drawOrderString))
            if (objectGroup.drawOrder() == ObjectGroup.DrawOrder.UnknownOrder):
                self.mError = self.tr("Invalid draw order: %s"%drawOrderString)
                return None

        for objectVariant in variantMap.get("objects", []):
            objectVariantMap = objectVariant
            name = objectVariantMap.get("name",'')
            type = objectVariantMap.get("type", '')
            id = objectVariantMap.get("id",0.0)
            gid = objectVariantMap.get("gid",0.0)
            x = objectVariantMap.get("x",0.0)
            y = objectVariantMap.get("y",0.0)
            width = objectVariantMap.get("width",0.0)
            height = objectVariantMap.get("height",0.0)
            rotation = objectVariantMap.get("rotation", 0.0)
            pos = QPointF(x, y)
            size = QSizeF(width, height)
            object = MapObject(name, type, pos, size)
            object.setId(id)
            object.setRotation(rotation)
            if (gid):
                cell, ok = self.mGidMapper.gidToCell(gid)
                object.setCell(cell)
                if not object.cell().isEmpty():
                    tileSize = object.cell().tile.size()
                    if width == 0:
                        object.setWidth(tileSize.width())
                    if height == 0:
                        object.setHeight(tileSize.height())
                        
            if (objectVariantMap.__contains__("visible")):
                object.setVisible(objectVariantMap.get("visible", True))
            object.setProperties(self.toProperties(objectVariantMap.get("properties", {})))
            objectGroup.addObject(object)
            polylineVariant = objectVariantMap.get("polyline", [])
            polygonVariant = objectVariantMap.get("polygon", [])
            if len(polygonVariant) > 0:
                object.setShape(MapObject.Polygon)
                object.setPolygon(self.toPolygon(polygonVariant))
            
            if len(polylineVariant) > 0:
                object.setShape(MapObject.Polyline)
                object.setPolygon(self.toPolygon(polylineVariant))
            
            if (objectVariantMap.__contains__("ellipse")):
                object.setShape(MapObject.Ellipse)
        
        return objectGroup
        
    def toImageLayer(self, variantMap):
        imageLayer = ImageLayer(variantMap.get("name",''),
                                                variantMap.get("x",0),
                                                variantMap.get("y",0),
                                                variantMap.get("width",0),
                                                variantMap.get("height",0))
        opacity = variantMap.get("opacity", 0.0)
        visible = variantMap.get("visible", True)
        imageLayer.setOpacity(opacity)
        imageLayer.setVisible(visible)
        trans = variantMap.get("transparentcolor", '')
        if (not trans.isEmpty() and QColor.isValidColor(trans)):
            imageLayer.setTransparentColor(QColor(trans))
        imageVariant = variantMap.get("image",'')
        if imageVariant != '':
            imagePath = resolvePath(self.mMapDir, imageVariant)
            if (not imageLayer.loadFromImage(QImage(imagePath), imagePath)):
                self.mError = self.tr("Error loading image:\n'%s'"%imagePath)
                return None

        return imageLayer
        
    def toPolygon(self, variant):
        polygon = QPolygonF()
        for pointVariant in variant:
            pointVariantMap = pointVariant
            pointX = pointVariantMap.get("x",0.0)
            pointY = pointVariantMap.get("y",0.0)
            polygon.append(QPointF(pointX, pointY))
        
        return polygon
Exemplo n.º 16
0
class MapReaderPrivate():
    def tr(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapReader', sourceText, disambiguation, n)

    def trUtf8(self, sourceText, disambiguation = '', n = -1):
        return QCoreApplication.translate('MapReader', sourceText, disambiguation, n)

    def __init__(self, mapReader):
        self.p = mapReader
        self.mMap = None
        self.mError = QString('')
        self.mReadingExternalTileset = False
        self.xml = QXmlStreamReader()
        self.mGidMapper = GidMapper()

    def readMap(self, device, path):
        self.mError = QString('')
        self.mPath = path
        map = None
        self.xml.setDevice(device)
        if (self.xml.readNextStartElement() and self.xml.name() == "map"):
            map = self.__readMap()
        else:
            self.xml.raiseError(self.tr("Not a map file."))

        self.mGidMapper.clear()
        return map

    def readTileset(self, device, path):
        self.mError = ''
        self.mPath = path
        tileset = None
        self.mReadingExternalTileset = True
        self.xml.setDevice(device)
        if (self.xml.readNextStartElement() and self.xml.name() == "tileset"):
            tileset = self.__readTileset()
        else:
            self.xml.raiseError(self.tr("Not a tileset file."))
        self.mReadingExternalTileset = False
        return tileset

    def openFile(self, file):
        if (not file.exists()):
            self.mError = self.tr("File not found: %s"%file.fileName())
            return False
        elif (not file.open(QFile.ReadOnly | QFile.Text)):
            self.mError = self.tr("Unable to read file: %s"%file.fileName())
            return False

        return True

    def errorString(self):
        if self.mError != '':
            return self.mError
        else:
            return self.tr("%d\n\nLine %d, column %s"%(self.xml.lineNumber(), self.xml.columnNumber(), self.xml.errorString()))

    def __readUnknownElement(self):
        qDebug("Unknown element (fixme): "+self.xml.name()+" at line "+self.xml.lineNumber()+", column "+self.xml.columnNumber())
        self.xml.skipCurrentElement()

    def __readMap(self):
        atts = self.xml.attributes()
        mapWidth = Int(atts.value("width"))
        mapHeight = Int(atts.value("height"))
        tileWidth = Int(atts.value("tilewidth"))
        tileHeight = Int(atts.value("tileheight"))
        hexSideLength = Int(atts.value("hexsidelength"))
        orientationString = atts.value("orientation")
        orientation = orientationFromString(orientationString)
        if (orientation == Map.Orientation.Unknown):
            self.xml.raiseError(self.tr("Unsupported map orientation: \"%s\""%orientationString))

        staggerAxisString = atts.value("staggeraxis")
        staggerAxis = staggerAxisFromString(staggerAxisString)
        staggerIndexString = atts.value("staggerindex")
        staggerIndex = staggerIndexFromString(staggerIndexString)
        renderOrderString = atts.value("renderorder")
        renderOrder = renderOrderFromString(renderOrderString)
        nextObjectId = Int(atts.value("nextobjectid"))
        self.mMap = Map(orientation, mapWidth, mapHeight, tileWidth, tileHeight)
        self.mMap.setHexSideLength(hexSideLength)
        self.mMap.setStaggerAxis(staggerAxis)
        self.mMap.setStaggerIndex(staggerIndex)
        self.mMap.setRenderOrder(renderOrder)
        if (nextObjectId):
            self.mMap.setNextObjectId(nextObjectId)

        bgColorString = atts.value("backgroundcolor")
        if len(bgColorString)>0:
            self.mMap.setBackgroundColor(QColor(bgColorString))
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                self.mMap.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "tileset"):
                self.mMap.addTileset(self.__readTileset())
            elif (self.xml.name() == "layer"):
                self.mMap.addLayer(self.__readLayer())
            elif (self.xml.name() == "objectgroup"):
                self.mMap.addLayer(self.__readObjectGroup())
            elif (self.xml.name() == "imagelayer"):
                self.mMap.addLayer(self.__readImageLayer())
            else:
                self.__readUnknownElement()

        # Clean up in case of error
        if (self.xml.hasError()):
            self.mMap = None

        return self.mMap

    def __readTileset(self):
        atts = self.xml.attributes()
        source = atts.value("source")
        firstGid = Int(atts.value("firstgid"))
        tileset = None
        if source == '': # Not an external tileset
            name = atts.value("name")
            tileWidth = Int(atts.value("tilewidth"))
            tileHeight = Int(atts.value("tileheight"))
            tileSpacing = Int(atts.value("spacing"))
            margin = Int(atts.value("margin"))
            if (tileWidth < 0 or tileHeight < 0
                or (firstGid == 0 and not self.mReadingExternalTileset)):
                self.xml.raiseError(self.tr("Invalid tileset parameters for tileset '%s'"%name))
            else:
                tileset = Tileset.create(name, tileWidth, tileHeight, tileSpacing, margin)

                while (self.xml.readNextStartElement()):
                    if (self.xml.name() == "tile"):
                        self.__readTilesetTile(tileset)
                    elif (self.xml.name() == "tileoffset"):
                        oa = self.xml.attributes()
                        x = Int(oa.value("x"))
                        y = Int(oa.value("y"))
                        tileset.setTileOffset(QPoint(x, y))
                        self.xml.skipCurrentElement()
                    elif (self.xml.name() == "properties"):
                        tileset.mergeProperties(self.__readProperties())
                    elif (self.xml.name() == "image"):
                        if (tileWidth == 0 or tileHeight == 0):
                            self.xml.raiseError(self.tr("Invalid tileset parameters for tileset '%s'"%name))
                            
                            tileset.clear()
                            break
                        else:
                            self.__readTilesetImage(tileset)
                    elif (self.xml.name() == "terraintypes"):
                        self.__readTilesetTerrainTypes(tileset)
                    else:
                        self.__readUnknownElement()
        else: # External tileset
            absoluteSource = self.p.resolveReference(source, self.mPath)
            tileset, error = self.p.readExternalTileset(absoluteSource)
            if (not tileset):
                self.xml.raiseError(self.tr("Error while loading tileset '%s': %s"%(absoluteSource, error)))

            self.xml.skipCurrentElement()

        if (tileset and not self.mReadingExternalTileset):
            self.mGidMapper.insert(firstGid, tileset)
        return tileset

    def __readTilesetTile(self, tileset):
        atts = self.xml.attributes()
        id = Int(atts.value("id"))
        if (id < 0):
            self.xml.raiseError(self.tr("Invalid tile ID: %d"%id))
            return

        hasImage = tileset.imageSource()!=''
        if (hasImage and id >= tileset.tileCount()):
            self.xml.raiseError(self.tr("Tile ID does not exist in tileset image: %d"%id))
            return

        if (id > tileset.tileCount()):
            self.xml.raiseError(self.tr("Invalid (nonconsecutive) tile ID: %d"%id))
            return

        # For tilesets without image source, consecutive tile IDs are allowed (for
        # tiles with individual images)
        if (id == tileset.tileCount()):
            tileset.addTile(QPixmap())
        tile = tileset.tileAt(id)
        # Read tile quadrant terrain ids
        terrain = atts.value("terrain")
        if terrain != '':
            quadrants = terrain.split(",")
            if (len(quadrants) == 4):
                for i in range(4):
                    if quadrants[i]=='':
                        t = -1
                    else:
                        t = Int(quadrants[i])
                    tile.setCornerTerrainId(i, t)

        # Read tile probability
        probability = atts.value("probability")
        if probability != '':
            tile.setProbability(Float(probability))
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                tile.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "image"):
                source = self.xml.attributes().value("source")
                if source != '':
                    source = self.p.resolveReference(source, self.mPath)
                tileset.setTileImage(id, QPixmap.fromImage(self.__readImage()), source)
            elif (self.xml.name() == "objectgroup"):
                tile.setObjectGroup(self.__readObjectGroup())
            elif (self.xml.name() == "animation"):
                tile.setFrames(self.__readAnimationFrames())
            else:
                self.__readUnknownElement()

        # Temporary code to support TMW-style animation frame properties
        if (not tile.isAnimated() and tile.hasProperty("animation-frame0")):
            frames = QVector()
            i = 0
            while(i>=0):
                frameName = "animation-frame" + str(i)
                delayName = "animation-delay" + str(i)
                if (tile.hasProperty(frameName) and tile.hasProperty(delayName)):
                    frame = Frame()
                    frame.tileId = tile.property(frameName)
                    frame.duration = tile.property(delayName) * 10
                    frames.append(frame)
                else:
                    break
                i += 1

            tile.setFrames(frames)

    def __readTilesetImage(self, tileset):
        atts = self.xml.attributes()
        source = atts.value("source")
        trans = atts.value("trans")
        if len(trans)>0:
            if (not trans.startswith('#')):
                trans = '#' + trans
            tileset.setTransparentColor(QColor(trans))

        if len(source)>0:
            source = self.p.resolveReference(source, self.mPath)
        # Set the width that the tileset had when the map was saved
        width = Int(atts.value("width"))
        self.mGidMapper.setTilesetWidth(tileset, width)
        if (not tileset.loadFromImage(self.__readImage(), source)):
            self.xml.raiseError(self.tr("Error loading tileset image:\n'%s'"%source))

    def __readTilesetTerrainTypes(self, tileset):
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "terrain"):
                atts = self.xml.attributes()
                name = atts.value("name")
                tile = Int(atts.value("tile"))
                terrain = tileset.addTerrain(name, tile)
                while (self.xml.readNextStartElement()):
                    if (self.xml.name() == "properties"):
                        terrain.mergeProperties(self.__readProperties())
                    else:
                        self.__readUnknownElement()

            else:
                self.__readUnknownElement()

    def __readImage(self):
        atts = self.xml.attributes()
        source = atts.value("source")
        format = atts.value("format")
        if len(source)==0:
            while (self.xml.readNextStartElement()):
                if (self.xml.name() == "data"):
                    atts = self.xml.attributes()
                    encoding = atts.value("encoding")
                    data = self.xml.readElementText().toLatin1()
                    if (encoding == "base64"):
                        data = QByteArray.fromBase64(data)

                    self.xml.skipCurrentElement()
                    return QImage.fromData(data, format.toLatin1())
                else:
                    self.__readUnknownElement()

        else:
            self.xml.skipCurrentElement()
            source = self.p.resolveReference(source, self.mPath)
            image = self.p.readExternalImage(source)
            if (image.isNull()):
                self.xml.raiseError(self.tr("Error loading image:\n'%s'"%source))
            return image

        return QImage()

    def __readLayer(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        tileLayer = TileLayer(name, x, y, width, height)
        readLayerAttributes(tileLayer, atts)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                tileLayer.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "data"):
                self.__readLayerData(tileLayer)
            else:
                self.__readUnknownElement()

        return tileLayer

    def __readLayerData(self, tileLayer):
        atts = self.xml.attributes()
        encoding = atts.value("encoding")
        compression = atts.value("compression")
        layerDataFormat = 0
        if (encoding == ''):
            layerDataFormat = Map.LayerDataFormat.XML
        elif (encoding == "csv"):
            layerDataFormat = Map.LayerDataFormat.CSV
        elif (encoding == "base64"):
            if (compression == ''):
                layerDataFormat = Map.LayerDataFormat.Base64
            elif (compression == "gzip"):
                layerDataFormat = Map.LayerDataFormat.Base64Gzip
            elif (compression == "zlib"):
                layerDataFormat = Map.LayerDataFormat.Base64Zlib
            else:
                self.xml.raiseError(self.tr("Compression method '%s' not supported"%compression))
                return
        else:
            self.xml.raiseError(self.tr("Unknown encoding: %s"%encoding))
            return
        
        self.mMap.setLayerDataFormat(layerDataFormat)
        
        x = 0
        y = 0
        while (self.xml.readNext() != QXmlStreamReader.Invalid):
            if (self.xml.isEndElement()):
                break
            elif (self.xml.isStartElement()):
                if (self.xml.name() == "tile"):
                    if (y >= tileLayer.height()):
                        self.xml.raiseError(self.tr("Too many <tile> elements"))
                        continue

                    atts = self.xml.attributes()
                    gid = Int(atts.value("gid"))
                    tileLayer.setCell(x, y, self.__cellForGid(gid))
                    x += 1
                    if (x >= tileLayer.width()):
                        x = 0
                        y += 1

                    self.xml.skipCurrentElement()
                else:
                    self.__readUnknownElement()
            elif (self.xml.isCharacters() and not self.xml.isWhitespace()):
                if (encoding == "base64"):
                    self.__decodeBinaryLayerData(tileLayer,
                                          self.xml.text(),
                                          layerDataFormat)
                elif (encoding == "csv"):
                    self.__decodeCSVLayerData(tileLayer, self.xml.text())

    def __decodeBinaryLayerData(self, tileLayer, data, format):
        error = self.mGidMapper.decodeLayerData(tileLayer, data, format)

        if error==DecodeError.CorruptLayerData:
            self.xml.raiseError(self.tr("Corrupt layer data for layer '%s'"%tileLayer.name()))
            return
        elif error==DecodeError.TileButNoTilesets:
            self.xml.raiseError(self.tr("Tile used but no tilesets specified"))
            return
        elif error==DecodeError.InvalidTile:
            self.xml.raiseError(self.tr("Invalid tile: %d"%self.mGidMapper.invalidTile()))
            return
        elif error==DecodeError.NoError:
            pass

    def __decodeCSVLayerData(self, tileLayer, text):
        trimText = text.strip()
        tiles = trimText.split(',')
        if (len(tiles) != tileLayer.width() * tileLayer.height()):
            self.xml.raiseError(self.tr("Corrupt layer data for layer '%s'"%tileLayer.name()))
            return

        for y in range(tileLayer.height()):
            for x in range(tileLayer.width()):
                conversionOk = False
                gid, conversionOk = Int2(tiles[y * tileLayer.width() + x])
                if (not conversionOk):
                    self.xml.raiseError(self.tr("Unable to parse tile at (%d,%d) on layer '%s'"%(x + 1, y + 1, tileLayer.name())))
                    return

                tileLayer.setCell(x, y, self.__cellForGid(gid))

    ##
    # Returns the cell for the given global tile ID. Errors are raised with
    # the QXmlStreamReader.
    #
    # @param gid the global tile ID
    # @return the cell data associated with the given global tile ID, or an
    #         empty cell if not found
    ##
    def __cellForGid(self, gid):
        ok = False
        result, ok = self.mGidMapper.gidToCell(gid)
        if (not ok):
            if (self.mGidMapper.isEmpty()):
                self.xml.raiseError(self.tr("Tile used but no tilesets specified"))
            else:
                self.xml.raiseError(self.tr("Invalid tile: %d"%gid))

        return result

    def __readImageLayer(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        imageLayer = ImageLayer(name, x, y, width, height)
        readLayerAttributes(imageLayer, atts)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "image"):
                self.__readImageLayerImage(imageLayer)
            elif (self.xml.name() == "properties"):
                imageLayer.mergeProperties(self.__readProperties())
            else:
                self.__readUnknownElement()

        return imageLayer

    def __readImageLayerImage(self, imageLayer):
        atts = self.xml.attributes()
        source = atts.value("source")
        trans = atts.value("trans")
        if trans != '':
            if (not trans.startswith('#')):
                trans = '#' + trans
            imageLayer.setTransparentColor(QColor(trans))

        source = self.p.resolveReference(source, self.mPath)
        imageLayerImage = self.p.readExternalImage(source)
        if (not imageLayer.loadFromImage(imageLayerImage, source)):
            self.xml.raiseError(self.tr("Error loading image layer image:\n'%s'"%source))
        self.xml.skipCurrentElement()

    def __readObjectGroup(self):
        atts = self.xml.attributes()
        name = atts.value("name")
        x = Int(atts.value("x"))
        y = Int(atts.value("y"))
        width = Int(atts.value("width"))
        height = Int(atts.value("height"))
        objectGroup = ObjectGroup(name, x, y, width, height)
        readLayerAttributes(objectGroup, atts)
        color = atts.value("color")
        if color != '':
            objectGroup.setColor(color)
        if (atts.hasAttribute("draworder")):
            value = atts.value("draworder")
            drawOrder = drawOrderFromString(value)
            if (drawOrder == ObjectGroup.DrawOrder.UnknownOrder):
                #del objectGroup
                self.xml.raiseError(self.tr("Invalid draw order: %s"%value))
                return None

            objectGroup.setDrawOrder(drawOrder)

        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "object"):
                objectGroup.addObject(self.__readObject())
            elif (self.xml.name() == "properties"):
                objectGroup.mergeProperties(self.__readProperties())
            else:
                self.__readUnknownElement()

        return objectGroup

    def __readObject(self):
        atts = self.xml.attributes()
        id = Int(atts.value("id"))
        name = atts.value("name")
        gid = Int(atts.value("gid"))
        x = Float(atts.value("x"))
        y = Float(atts.value("y"))
        width = Float(atts.value("width"))
        height = Float(atts.value("height"))
        type = atts.value("type")
        visibleRef = atts.value("visible")
        pos = QPointF(x, y)
        size = QSizeF(width, height)
        object = MapObject(name, type, pos, size)
        object.setId(id)

        try:
            rotation = Float(atts.value("rotation"))
            ok = True
        except:
            ok = False
        if (ok):
            object.setRotation(rotation)
        if (gid):
            object.setCell(self.__cellForGid(gid))
            if (not object.cell().isEmpty()):
                tileSize = object.cell().tile.size()
                if (width == 0):
                    object.setWidth(tileSize.width())
                if (height == 0):
                    object.setHeight(tileSize.height())
        
        try:
            visible = int(visibleRef)
            ok = True
        except:
            ok = False
        if ok:
            object.setVisible(visible)
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "properties"):
                object.mergeProperties(self.__readProperties())
            elif (self.xml.name() == "polygon"):
                object.setPolygon(self.__readPolygon())
                object.setShape(MapObject.Polygon)
            elif (self.xml.name() == "polyline"):
                object.setPolygon(self.__readPolygon())
                object.setShape(MapObject.Polyline)
            elif (self.xml.name() == "ellipse"):
                self.xml.skipCurrentElement()
                object.setShape(MapObject.Ellipse)
            else:
                self.__readUnknownElement()

        return object

    def __readPolygon(self):
        atts = self.xml.attributes()
        points = atts.value("points")
        pointsList = list(filter(lambda x:x.strip()!='', points.split(' ')))
        polygon = QPolygonF()
        ok = True
        for point in pointsList:
            try:
                x, y = point.split(',')
            except:
                ok = False
                break
            
            x, ok = Float2(x)
            if (not ok):
                break

            y, ok = Float2(y)
            if (not ok):
                break
            polygon.append(QPointF(x, y))

        if (not ok):
            self.xml.raiseError(self.tr("Invalid points data for polygon"))
        self.xml.skipCurrentElement()
        return polygon

    def __readAnimationFrames(self):
        frames = QVector()
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "frame"):
                atts = self.xml.attributes()
                frame = Frame()
                frame.tileId = Int(atts.value("tileid"))
                frame.duration = Int(atts.value("duration"))
                frames.append(frame)
                self.xml.skipCurrentElement()
            else:
                self.__readUnknownElement()

        return frames

    def __readProperties(self):
        properties = Properties()
        while (self.xml.readNextStartElement()):
            if (self.xml.name() == "property"):
                self.__readProperty(properties)
            else:
                self.__readUnknownElement()

        return properties

    def __readProperty(self, properties):
        atts = self.xml.attributes()
        propertyName = atts.value("name")
        propertyValue = atts.value("value")
        while (self.xml.readNext() != QXmlStreamReader.Invalid):
            if (self.xml.isEndElement()):
                break
            elif (self.xml.isCharacters() and not self.xml.isWhitespace()):
                if (propertyValue.isEmpty()):
                    propertyValue = self.xml.text()
            elif (self.xml.isStartElement()):
                self.__readUnknownElement()

        properties.insert(propertyName, propertyValue)
Exemplo n.º 17
0
    def __init__(self):
        super().__init__()

        self.mMapDir = QDir()
        self.mError = ''
        self.mGidMapper = GidMapper()
Exemplo n.º 18
0
    def write(self, map, fileName):
        file = QSaveFile(fileName)
        if (not file.open(QFile.WriteOnly | QFile.Text)):
            self.mError = self.tr("Could not open file for writing.")
            return False
        
        out = QTextStream(file)
        out.setCodec("UTF-8")
        mapWidth = map.width()
        mapHeight = map.height()
        # write [header]
        out << "[header]\n"
        out << "width=" << str(mapWidth) << "\n"
        out << "height=" << str(mapHeight) << "\n"
        out << "tilewidth=" << str(map.tileWidth()) << "\n"
        out << "tileheight=" << str(map.tileHeight()) << "\n"
        out << "orientation=" << str(orientationToString(map.orientation())) << "\n"
        # write all properties for this map
        for it in map.properties().__iter__():
            out << it[0] << "=" << it[1] << "\n"
        
        out << "\n"
        mapDir = QFileInfo(fileName).absoluteDir()
        out << "[tilesets]\n"
        for tileset in map.tilesets():
            imageSource = tileset.imageSource()
            source = mapDir.relativeFilePath(imageSource)
            out << "tileset=" << source \
                << "," << str(tileset.tileWidth()) \
                << "," << str(tileset.tileHeight()) \
                << "," << str(tileset.tileOffset().x()) \
                << "," << str(tileset.tileOffset().y()) \
                << "\n"
        
        out << "\n"
        gidMapper = GidMapper(map.tilesets())
        # write layers
        for layer in map.layers():
            tileLayer = layer.asTileLayer()
            if tileLayer:
                out << "[layer]\n"
                out << "type=" << layer.name() << "\n"
                out << "data=\n"
                for y in range(0, mapHeight):
                    for x in range(0, mapWidth):
                        t = tileLayer.cellAt(x, y)
                        id = 0
                        if (t.tile):
                            id = gidMapper.cellToGid(t)
                        out << id
                        if (x < mapWidth - 1):
                            out << ","
                    
                    if (y < mapHeight - 1):
                        out << ","
                    out << "\n"
                
                out << "\n"
            
            group = layer.asObjectGroup()
            if group:
                for o in group.objects():
                    if o.type() != '':
                        out << "[" << group.name() << "]\n"
                        # display object name as comment
                        if o.name() != '':
                            out << "# " << o.name() << "\n"
                        
                        out << "type=" << o.type() << "\n"
                        x,y,w,h = 0
                        if (map.orientation() == Map.Orthogonal):
                            x = o.x()/map.tileWidth()
                            y = o.y()/map.tileHeight()
                            w = o.width()/map.tileWidth()
                            h = o.height()/map.tileHeight()
                        else :
                            x = o.x()/map.tileHeight()
                            y = o.y()/map.tileHeight()
                            w = o.width()/map.tileHeight()
                            h = o.height()/map.tileHeight()
                        
                        out << "location=" << x << "," << y
                        out << "," << w << "," << h << "\n"
                        # write all properties for this object
                        for it in o.properties().__iter__():
                            out << it[0] << "=" << it[1] << "\n"
                        
                        out << "\n"


        if not file.commit():
            self.mError = file.errorString()
            return False

        return True