def findStampFileName(name, currentFileName=''): invalidChars = QRegularExpression("[^\\w -]+") prefs = preferences.Preferences.instance() stampsDir = QDir(prefs.stampsDirectory()) suggestedFileName = name.toLower().remove(invalidChars) fileName = suggestedFileName + ".stamp" if (fileName == currentFileName or not stampsDir.exists(fileName)): return fileName n = 2 while (fileName != currentFileName and stampsDir.exists(fileName)): fileName = suggestedFileName + QString.number(n) + ".stamp" n += 1 return fileName
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 out = QTextStream(file) # Write the header header = map.property("header") for line in header.split("\\n"): out << line << '\n' width = map.width() height = map.height() asciiMap = QList() cachedTiles = QHash() propertyOrder = QList() propertyOrder.append("terrain") propertyOrder.append("object") propertyOrder.append("actor") propertyOrder.append("trap") propertyOrder.append("status") propertyOrder.append("spot") # Ability to handle overflow and strings for display outputLists = False asciiDisplay = ASCII_MIN overflowDisplay = 1 # Add the empty tile numEmptyTiles = 0 emptyTile = Properties() emptyTile["display"] = "?" cachedTiles["?"] = emptyTile # Process the map, collecting used display strings as we go for y in range(0, height): for x in range(0, width): currentTile = cachedTiles["?"] for layer in map.layers(): # If the layer name does not start with one of the tile properties, skip it layerKey = '' for currentProperty in propertyOrder: if (layer.name().lower().startswith( currentProperty.lower())): layerKey = currentProperty break if layerKey == '': continue tileLayer = layer.asTileLayer() objectLayer = layer.asObjectGroup() # Process the Tile Layer if (tileLayer): tile = tileLayer.cellAt(x, y).tile if (tile): currentTile["display"] = tile.property("display") currentTile[layerKey] = tile.property("value") # Process the Object Layer elif (objectLayer): for obj in objectLayer.objects(): if (math.floor(obj.y()) <= y and y <= math.floor(obj.y() + obj.height())): if (math.floor(obj.x()) <= x and x <= math.floor(obj.x() + obj.width())): # Check the Object Layer properties if either display or value was missing if (not obj.property("display").isEmpty()): currentTile["display"] = obj.property( "display") elif (not objectLayer.property( "display").isEmpty()): currentTile[ "display"] = objectLayer.property( "display") if (not obj.property("value").isEmpty()): currentTile[layerKey] = obj.property( "value") elif (not objectLayer.property( "value").isEmpty()): currentTile[ layerKey] = objectLayer.property( "value") # If the currentTile does not exist in the cache, add it if (not cachedTiles.contains(currentTile["display"])): cachedTiles[currentTile["display"]] = currentTile # Otherwise check that it EXACTLY matches the cached one # and if not... elif (currentTile != cachedTiles[currentTile["display"]]): # Search the cached tiles for a match foundInCache = False displayString = QString() for i in cachedTiles.items(): displayString = i[0] currentTile["display"] = displayString if (currentTile == i[1]): foundInCache = True break # If we haven't found a match then find a random display string # and cache it if (not foundInCache): while (True): # First try to use the ASCII characters if (asciiDisplay < ASCII_MAX): displayString = asciiDisplay asciiDisplay += 1 # Then fall back onto integers else: displayString = QString.number(overflowDisplay) overflowDisplay += 1 currentTile["display"] = displayString if (not cachedTiles.contains(displayString)): cachedTiles[displayString] = currentTile break elif (currentTile == cachedTiles[ currentTile["display"]]): break # Check the output type if len(currentTile["display"]) > 1: outputLists = True # Check if we are still the emptyTile if (currentTile == emptyTile): numEmptyTiles += 1 # Finally add the character to the asciiMap asciiMap.append(currentTile["display"]) # Write the definitions to the file out << "-- defineTile section\n" for i in cachedTiles.items(): displayString = i[0] # Only print the emptyTile definition if there were empty tiles if (displayString == "?" and numEmptyTiles == 0): continue # Need to escape " and \ characters displayString.replace('\\', "\\\\") displayString.replace('"', "\\\"") args = self.constructArgs(i[1], propertyOrder) if (not args.isEmpty()): args = QString(", %1").arg(args) out << "defineTile(\"%s\"%s)\n" % (displayString, args) # Check for an ObjectGroup named AddSpot out << "\n-- addSpot section\n" for layer in map.layers(): objectLayer = layer.asObjectGroup() if (objectLayer and objectLayer.name().lower().startsWith("addspot")): for obj in objectLayer.objects(): propertyOrder = QList() propertyOrder.append("type") propertyOrder.append("subtype") propertyOrder.append("additional") args = self.constructArgs(obj.properties(), propertyOrder) if (not args.isEmpty()): args = QString(", %1").arg(args) for y in range(math.floor(obj.y()), math.floor(obj.y() + obj.height())): for y in range(math.floor(obj.x()), math.floor(obj.x() + obj.width())): out << "addSpot({%s, %s}%s)\n" % (x, y, args) # Check for an ObjectGroup named AddZone out << "\n-- addZone section\n" for layer in map.layers(): objectLayer = layer.asObjectGroup() if (objectLayer and objectLayer.name().lower().startsWith("addzone")): for obj in objectLayer.objects(): propertyOrder = QList() propertyOrder.append("type") propertyOrder.append("subtype") propertyOrder.append("additional") args = self.constructArgs(obj.properties(), propertyOrder) if (not args.isEmpty()): args = QString(", %1").arg(args) top_left_x = math.floor(obj.x()) top_left_y = math.floor(obj.y()) bottom_right_x = math.floor(obj.x() + obj.width()) bottom_right_y = math.floor(obj.y() + obj.height()) out << "addZone({%s, %s, %s, %s}%s)" % ( top_left_x, top_left_y, bottom_right_x, bottom_right_y, args) # Write the map returnStart = QString() returnStop = QString() lineStart = QString() lineStop = QString() itemStart = QString() itemStop = QString() seperator = QString() if (outputLists): returnStart = "{" returnStop = "}" lineStart = "{" lineStop = "}," itemStart = "[[" itemStop = "]]" seperator = "," else: returnStart = "[[" returnStop = "]]" lineStart = "" lineStop = "" itemStart = "" itemStop = "" seperator = "" out << "\n-- ASCII map section\n" out << "return " << returnStart << '\n' for y in range(0, height): out << lineStart for x in range(0, width): out << itemStart << asciiMap[x + (y * width)] << itemStop << seperator if (y == height - 1): out << lineStop << returnStop else: out << lineStop << '\n' if not file.commit(): self.mError = file.errorString() return False return True
def read(self, fileName): # Read data. file = QFile(fileName) if (not file.open(QIODevice.ReadOnly)): self.mError = self.tr("Cannot open Replica Island map file!") return 0 _in = QDataStream(file) _in.setByteOrder(QDataStream.LittleEndian) _in.setFloatingPointPrecision(QDataStream.SinglePrecision) # Parse file header. mapSignature = _in.readUInt8() layerCount = _in.readUInt8() backgroundIndex = _in.readUInt8() if (_in.status() == QDataStream.ReadPastEnd or mapSignature != 96): self.mError = self.tr("Can't parse file header!") return 0 # Create our map, setting width and height to 0 until we load a layer. map = Map(Map.Orientation.Orthogonal, 0, 0, 32, 32) map.setProperty("background_index", QString.number(backgroundIndex)) # Load our Tilesets. typeTilesets = QVector() tileIndexTilesets = QVector() self.loadTilesetsFromResources(map, typeTilesets, tileIndexTilesets) # Load each of our layers. for i in range(layerCount): # Parse layer header. _type = _in.readUInt8() tileIndex = _in.readUInt8() scrollSpeed = _in.readFloat() levelSignature = _in.readUInt8() width = _in.readUInt32() height = _in.readUInt32() if (_in.status() == QDataStream.ReadPastEnd or levelSignature != 42): self.mError = self.tr("Can't parse layer header!") return 0 # Make sure our width and height are consistent. if (map.width() == 0): map.setWidth(width) if (map.height() == 0): map.setHeight(height) if (map.width() != width or map.height() != height): self.mError = self.tr("Inconsistent layer sizes!") return 0 # Create a layer object. layer = TileLayer(self.layerTypeToName(_type), 0, 0, width, height) layer.setProperty("type", QString.number(_type)) layer.setProperty("tile_index", QString.number(tileIndex)) layer.setProperty("scroll_speed", QString.number(scrollSpeed, 'f')) map.addLayer(layer) # Look up the tileset for this layer. tileset = tilesetForLayer(_type, tileIndex, typeTilesets, tileIndexTilesets) # Read our tile data all at once. #tileData = QByteArray(width*height, b'\x00') bytesNeeded = width*height tileData = _in.readRawData(bytesNeeded) bytesRead = len(tileData) if (bytesRead != bytesNeeded): self.mError = self.tr("File ended in middle of layer!") return 0 i = 0 # Add the tiles to our layer. for y in range(0, height): for x in range(0, width): tile_id = tileData[i]&0xff i += 1 if (tile_id != 255): tile = tileset.tileAt(tile_id) layer.setCell(x, y, Cell(tile)) # Make sure we read the entire *.bin file. if (_in.status() != QDataStream.Ok or not _in.atEnd()): self.mError = self.tr("Unexpected data at end of file!") return 0 return map
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 out = QTextStream(file) # Write the header header = map.property("header") for line in header.split("\\n"): out << line << '\n' width = map.width() height = map.height() asciiMap = QList() cachedTiles = QHash() propertyOrder = QList() propertyOrder.append("terrain") propertyOrder.append("object") propertyOrder.append("actor") propertyOrder.append("trap") propertyOrder.append("status") propertyOrder.append("spot") # Ability to handle overflow and strings for display outputLists = False asciiDisplay = ASCII_MIN overflowDisplay = 1 # Add the empty tile numEmptyTiles = 0 emptyTile = Properties() emptyTile["display"] = "?" cachedTiles["?"] = emptyTile # Process the map, collecting used display strings as we go for y in range(0, height): for x in range(0, width): currentTile = cachedTiles["?"] for layer in map.layers(): # If the layer name does not start with one of the tile properties, skip it layerKey = '' for currentProperty in propertyOrder: if (layer.name().lower().startswith(currentProperty.lower())) : layerKey = currentProperty break if layerKey == '': continue tileLayer = layer.asTileLayer() objectLayer = layer.asObjectGroup() # Process the Tile Layer if (tileLayer) : tile = tileLayer.cellAt(x, y).tile if (tile) : currentTile["display"] = tile.property("display") currentTile[layerKey] = tile.property("value") # Process the Object Layer elif (objectLayer) : for obj in objectLayer.objects(): if (math.floor(obj.y()) <= y and y <= math.floor(obj.y() + obj.height())) : if (math.floor(obj.x()) <= x and x <= math.floor(obj.x() + obj.width())) : # Check the Object Layer properties if either display or value was missing if (not obj.property("display").isEmpty()) : currentTile["display"] = obj.property("display") elif (not objectLayer.property("display").isEmpty()) : currentTile["display"] = objectLayer.property("display") if (not obj.property("value").isEmpty()) : currentTile[layerKey] = obj.property("value") elif (not objectLayer.property("value").isEmpty()) : currentTile[layerKey] = objectLayer.property("value") # If the currentTile does not exist in the cache, add it if (not cachedTiles.contains(currentTile["display"])) : cachedTiles[currentTile["display"]] = currentTile # Otherwise check that it EXACTLY matches the cached one # and if not... elif (currentTile != cachedTiles[currentTile["display"]]) : # Search the cached tiles for a match foundInCache = False displayString = QString() for i in cachedTiles.items(): displayString = i[0] currentTile["display"] = displayString if (currentTile == i[1]) : foundInCache = True break # If we haven't found a match then find a random display string # and cache it if (not foundInCache) : while (True) : # First try to use the ASCII characters if (asciiDisplay < ASCII_MAX) : displayString = asciiDisplay asciiDisplay += 1 # Then fall back onto integers else : displayString = QString.number(overflowDisplay) overflowDisplay += 1 currentTile["display"] = displayString if (not cachedTiles.contains(displayString)) : cachedTiles[displayString] = currentTile break elif (currentTile == cachedTiles[currentTile["display"]]) : break # Check the output type if len(currentTile["display"]) > 1: outputLists = True # Check if we are still the emptyTile if (currentTile == emptyTile) : numEmptyTiles += 1 # Finally add the character to the asciiMap asciiMap.append(currentTile["display"]) # Write the definitions to the file out << "-- defineTile section\n" for i in cachedTiles.items(): displayString = i[0] # Only print the emptyTile definition if there were empty tiles if (displayString == "?" and numEmptyTiles == 0) : continue # Need to escape " and \ characters displayString.replace('\\', "\\\\") displayString.replace('"', "\\\"") args = self.constructArgs(i[1], propertyOrder) if (not args.isEmpty()) : args = QString(", %1").arg(args) out << "defineTile(\"%s\"%s)\n"%(displayString, args) # Check for an ObjectGroup named AddSpot out << "\n-- addSpot section\n" for layer in map.layers(): objectLayer = layer.asObjectGroup() if (objectLayer and objectLayer.name().lower().startsWith("addspot")): for obj in objectLayer.objects(): propertyOrder = QList() propertyOrder.append("type") propertyOrder.append("subtype") propertyOrder.append("additional") args = self.constructArgs(obj.properties(), propertyOrder) if (not args.isEmpty()) : args = QString(", %1").arg(args) for y in range(math.floor(obj.y()), math.floor(obj.y() + obj.height())): for y in range(math.floor(obj.x()), math.floor(obj.x() + obj.width())): out << "addSpot({%s, %s}%s)\n"%(x, y, args) # Check for an ObjectGroup named AddZone out << "\n-- addZone section\n" for layer in map.layers(): objectLayer = layer.asObjectGroup() if (objectLayer and objectLayer.name().lower().startsWith("addzone")): for obj in objectLayer.objects(): propertyOrder = QList() propertyOrder.append("type") propertyOrder.append("subtype") propertyOrder.append("additional") args = self.constructArgs(obj.properties(), propertyOrder) if (not args.isEmpty()) : args = QString(", %1").arg(args) top_left_x = math.floor(obj.x()) top_left_y = math.floor(obj.y()) bottom_right_x = math.floor(obj.x() + obj.width()) bottom_right_y = math.floor(obj.y() + obj.height()) out << "addZone({%s, %s, %s, %s}%s)"%(top_left_x, top_left_y, bottom_right_x, bottom_right_y, args) # Write the map returnStart = QString() returnStop = QString() lineStart = QString() lineStop = QString() itemStart = QString() itemStop = QString() seperator = QString() if (outputLists) : returnStart = "{" returnStop = "}" lineStart = "{" lineStop = "}," itemStart = "[[" itemStop = "]]" seperator = "," else : returnStart = "[[" returnStop = "]]" lineStart = "" lineStop = "" itemStart = "" itemStop = "" seperator = "" out << "\n-- ASCII map section\n" out << "return " << returnStart << '\n' for y in range(0, height): out << lineStart for x in range(0, width): out << itemStart << asciiMap[x + (y * width)] << itemStop << seperator if (y == height - 1): out << lineStop << returnStop else : out << lineStop << '\n' if not file.commit(): self.mError = file.errorString() return False return True
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 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 read(self, fileName): # Read data. file = QFile(fileName) if (not file.open(QIODevice.ReadOnly)): self.mError = self.tr("Cannot open Replica Island map file!") return 0 _in = QDataStream(file) _in.setByteOrder(QDataStream.LittleEndian) _in.setFloatingPointPrecision(QDataStream.SinglePrecision) # Parse file header. mapSignature = _in.readUInt8() layerCount = _in.readUInt8() backgroundIndex = _in.readUInt8() if (_in.status() == QDataStream.ReadPastEnd or mapSignature != 96): self.mError = self.tr("Can't parse file header!") return 0 # Create our map, setting width and height to 0 until we load a layer. map = Map(Map.Orientation.Orthogonal, 0, 0, 32, 32) map.setProperty("background_index", QString.number(backgroundIndex)) # Load our Tilesets. typeTilesets = QVector() tileIndexTilesets = QVector() self.loadTilesetsFromResources(map, typeTilesets, tileIndexTilesets) # Load each of our layers. for i in range(layerCount): # Parse layer header. _type = _in.readUInt8() tileIndex = _in.readUInt8() scrollSpeed = _in.readFloat() levelSignature = _in.readUInt8() width = _in.readUInt32() height = _in.readUInt32() if (_in.status() == QDataStream.ReadPastEnd or levelSignature != 42): self.mError = self.tr("Can't parse layer header!") return 0 # Make sure our width and height are consistent. if (map.width() == 0): map.setWidth(width) if (map.height() == 0): map.setHeight(height) if (map.width() != width or map.height() != height): self.mError = self.tr("Inconsistent layer sizes!") return 0 # Create a layer object. layer = TileLayer(self.layerTypeToName(_type), 0, 0, width, height) layer.setProperty("type", QString.number(_type)) layer.setProperty("tile_index", QString.number(tileIndex)) layer.setProperty("scroll_speed", QString.number(scrollSpeed, 'f')) map.addLayer(layer) # Look up the tileset for this layer. tileset = tilesetForLayer(_type, tileIndex, typeTilesets, tileIndexTilesets) # Read our tile data all at once. #tileData = QByteArray(width*height, b'\x00') bytesNeeded = width * height tileData = _in.readRawData(bytesNeeded) bytesRead = len(tileData) if (bytesRead != bytesNeeded): self.mError = self.tr("File ended in middle of layer!") return 0 i = 0 # Add the tiles to our layer. for y in range(0, height): for x in range(0, width): tile_id = tileData[i] & 0xff i += 1 if (tile_id != 255): tile = tileset.tileAt(tile_id) layer.setCell(x, y, Cell(tile)) # Make sure we read the entire *.bin file. if (_in.status() != QDataStream.Ok or not _in.atEnd()): self.mError = self.tr("Unexpected data at end of file!") return 0 return map