QByteArray AuroraMapWriter::toByteArray(const Map *map) {
    QByteArray bytes;
    QBuffer buffer(&bytes);
    buffer.open(QIODevice::WriteOnly);

    MapWriter w;
    w.writeMap(map, &buffer);

    return bytes;
}
Пример #2
0
QByteArray TmxMapWriter::toByteArray(const Map *map)
{
    QByteArray bytes;
    QBuffer buffer(&bytes);
    buffer.open(QIODevice::WriteOnly);

    MapWriter writer;
    writer.setLayerDataFormat(MapWriter::Base64Zlib);
    writer.writeMap(map, &buffer);

    return bytes;
}
bool AuroraMapWriter::writeTileset(const Tileset *tileset, const QString &fileName) {
    MapWriter writer;
    writer.setDtdEnabled(false);

    bool result = writer.writeTileset(tileset, fileName);
    if (!result) {
        mError = writer.errorString();
    } else {
        mError.clear();
    }

    return result;
}
Пример #4
0
bool TmxMapWriter::writeTileset(const Tileset *tileset,
                                const QString &fileName)
{
    Preferences *prefs = Preferences::instance();

    MapWriter writer;
    writer.setDtdEnabled(prefs->dtdEnabled());

    bool result = writer.writeTileset(tileset, fileName);
    if (!result)
        mError = writer.errorString();
    else
        mError.clear();

    return result;
}
Пример #5
0
bool TmxMapWriter::write(const Map *map, const QString &fileName)
{
    Preferences *prefs = Preferences::instance();

    MapWriter writer;
    writer.setLayerDataFormat(prefs->layerDataFormat());
    writer.setDtdEnabled(prefs->dtdEnabled());

    bool result = writer.writeMap(map, fileName);
    if (!result)
        mError = writer.errorString();
    else
        mError.clear();

    return result;
}
bool AuroraMapWriter::write(const Map *map, const QString &fileName) {
    Map::LayerDataFormat format = map->layerDataFormat();
    if (format == Map::Default)
        format = Map::Base64Zlib;
    
    format = Map::Base64Zlib;

    MapWriter writer;
    writer.setLayerDataFormat(format);
    writer.setDtdEnabled(false);

    bool result = writer.writeMap(map, fileName);
    if (!result) {
        mError = writer.errorString();
    } else {
        mError.clear();
    }

    return result;
}
Пример #7
0
int main(int argc, char *argv[])
{
    QGuiApplication a(argc, argv);

    a.setOrganizationDomain(QLatin1String("mapeditor.org"));
    a.setApplicationName(QLatin1String("TerrainGenerator"));
    a.setApplicationVersion(QLatin1String("1.0"));

    CommandLineOptions options;

    if (!parseCommandLineArguments(options)) {
        // Something went wrong, abort.
        return 1;
    }

    if (options.showVersion)
        showVersion();
    if (options.showHelp)
        showHelp();
    if (options.showVersion || options.showHelp)
        return 0;

    if (options.target.isEmpty()) {
        qWarning() << "Error: No target tileset provided";
        showHelp();
        return 1;
    }
    if (options.sources.isEmpty()) {
        qWarning() << "Error: No source tilesets provided";
        showHelp();
        return 1;
    }

    // Check terrain priorities.
    if (options.terrainPriority.isEmpty()) {
        qWarning("Error: No terrain priorities set (option -p).");
        showHelp();
        return 1;
    }

    MapReader reader;
    SharedTileset targetTileset;
    QList<SharedTileset> sources;

    if (!options.overwrite && QFile::exists(options.target)) {
        targetTileset = reader.readTileset(options.target);
        if (!targetTileset) {
            qCritical("Error reading target tileset:\n%s",
                      qUtf8Printable(reader.errorString()));
        }

        // Remove empty tiles from the end of the tileset
        int nextTileId = targetTileset->nextTileId();
        for (int id = nextTileId - 1; id >= 0; --id) {
            if (Tile *tile = targetTileset->findTile(id)) {
                if (isEmpty(tile->image().toImage())) {
                    targetTileset->deleteTile(id);
                    nextTileId = id;
                    continue;
                }
            }

            break;
        }
        targetTileset->setNextTileId(nextTileId);

        // If the target tileset already exists, it is also a source tileset
        sources.append(targetTileset);
    }

    // Read source tilesets.
    for (const QString &sourceFileName : options.sources) {
        SharedTileset source = reader.readTileset(sourceFileName);
        if (!source) {
            qCritical("Error reading source tileset '%s':\n%s",
                      qUtf8Printable(sourceFileName),
                      qUtf8Printable(reader.errorString()));
        }
        sources.append(source);
    }

    // If the target tileset does not exist yet, create it
    if (!targetTileset) {
        QString name = QFileInfo(options.target).completeBaseName();
        const SharedTileset &firstSource = sources.first();
        int tileWidth = firstSource->tileWidth();
        int tileHeight = firstSource->tileHeight();

        targetTileset = Tileset::create(name, tileWidth, tileHeight);
    }

    // Set up a mapping from terrain to tile, for quick lookup
    QMap<TileTerrainNames, Tile*> terrainToTile;
    for (const SharedTileset &tileset : sources)
        for (Tile *tile : tileset->tiles())
            if (tile->terrain() != 0xFFFFFFFF)
                if (!terrainToTile.contains(TileTerrainNames(tile))) // TODO: Optimize
                    terrainToTile.insert(TileTerrainNames(tile), tile);

    // Set up the list of all terrains, mapped by name.
    QMap<QString, Terrain*> terrains;
    for (const SharedTileset &tileset : sources)
        for (Terrain *terrain : tileset->terrains())
            if (!terrains.contains(terrain->name()))
                terrains.insert(terrain->name(), terrain);

    // Check if there is anything to combine.
    if (options.combineList.size() == 0) {
        qWarning() << "No terrain specified to combine (-c option).";
    } else {
        // Dump the combine lists.
        qWarning() << "Terrains to combine:";
        for (const QStringList &combine : options.combineList) {
            if (combine.isEmpty()) {
                qCritical("Empty combine set");
            }
            qWarning() << combine;

            // Make sure every terrain from this set was defined.
            for (const QString &terrainName : combine)
                if (!terrains.contains(terrainName))
                    qCritical("Terrain %s is in combine list, however it wasn't defined by any tileset.",
                              qUtf8Printable(terrainName));
        }
    }

    // Setup terrain priorities.
    TerrainLessThan lessThan;
    int priority = 0;
    for (const QString &terrainName : options.terrainPriority) {
        lessThan.terrainPriority.insert(terrainName, priority);
        ++priority;
    }

    qDebug() << "Terrains found:" << terrains.keys();

    // Check if all terrains from priority list were found and loaded.
    for (const QString &terrainName : lessThan.terrainPriority.keys())
        if (!terrains.contains(terrainName))
            qWarning() << "Terrain" << terrainName << "from priority list not found.";

    // Add terrain names not specified from command line.
    for (const QString &terrainName : terrains.keys()) {
        if (!lessThan.terrainPriority.contains(terrainName)) {
            qWarning() << "No priority set for" << terrainName;
            lessThan.terrainPriority.insert(terrainName, priority);
            ++priority;
        }
    }

    // Add terrains that are not defined in the target tileset yet
    // TODO: This step should be more configurable
    for (Terrain *terrain : terrains) {
        if (!hasTerrain(*targetTileset, terrain->name())) {
            Tile *terrainTile = terrain->imageTile();
            QPixmap terrainImage = terrainTile->image();

            Tile *newTerrainTile = targetTileset->addTile(terrainImage);
            newTerrainTile->setProperties(terrainTile->properties());

            Terrain *newTerrain =  targetTileset->addTerrain(terrain->name(),
                                                             newTerrainTile->id());

            // WARNING: This assumes the terrain tile has this terrain on all
            // its corners.
            newTerrainTile->setTerrain(makeTerrain(newTerrain->id()));
            terrainToTile.insert(TileTerrainNames(newTerrainTile),
                                 newTerrainTile);
        }
    }

    // Prepare a list of terrain combinations.
    QVector<TileTerrainNames> process;
    for (const QStringList &combine : options.combineList) {
        QList<Terrain*> terrainList;
        // Get the terrains to combine
        for (const QString &terrainName : combine)
            terrainList.append(terrains[terrainName]);

        // Construct a vector with all terrain combinations to process
        for (Terrain *topLeft : terrainList) {
            for (Terrain *topRight : terrainList) {
                for (Terrain *bottomLeft : terrainList) {
                    for (Terrain *bottomRight : terrainList) {
                        process.append(TileTerrainNames(topLeft->name(),
                                                        topRight->name(),
                                                        bottomLeft->name(),
                                                        bottomRight->name()));
                    }
                }
            }
        }
    }

    // Go through each combination of terrains and add the tile to the target
    // tileset if it's not in there yet.
    for (const TileTerrainNames &terrainNames : process) {
        Tile *tile = terrainToTile.value(terrainNames);

        if (tile && tile->tileset() == targetTileset)
            continue;

        QPixmap image;
        Properties properties;

        if (!tile) {
            qWarning() << "Generating" << terrainNames;

            // Start a new image
            QImage tileImage = QImage(targetTileset->tileWidth(),
                                      targetTileset->tileHeight(),
                                      QImage::Format_ARGB32);
            tileImage.fill(Qt::transparent);
            QPainter painter(&tileImage);

            QStringList terrainList = terrainNames.terrainList();
            qSort(terrainList.begin(), terrainList.end(), lessThan);

            // Draw the lowest terrain to avoid pixel gaps
            QString baseTerrain = terrainList.first();
            QPixmap baseImage = terrains[baseTerrain]->imageTile()->image();
            painter.drawPixmap(0, 0, baseImage);

            for (const QString &terrainName : terrainList) {
                TileTerrainNames filtered = terrainNames.filter(terrainName);
                Tile *tile = terrainToTile.value(filtered);
                if (!tile) {
                    qWarning() << "Missing" << filtered;
                    continue;
                }

                painter.drawPixmap(0, 0, tile->image());
                properties.merge(tile->properties());
            }

            image = QPixmap::fromImage(tileImage);
        } else {
            qWarning() << "Copying" << terrainNames << "from"
                       << QFileInfo(tile->tileset()->fileName()).fileName();

            image = tile->image();
            properties = tile->properties();
        }

        Tile *newTile = targetTileset->addTile(image);
        newTile->setTerrain(terrainNames.toTerrain(*targetTileset));
        newTile->setProperties(properties);
        terrainToTile.insert(terrainNames, newTile);
    }

    if (targetTileset->tileCount() == 0)
        qCritical("Target tileset is empty");

    if (options.embedImage) {
        // Make sure there is no source name, this way the image will be saved in the TSX file.
        targetTileset->setImageSource(QString());
    } else {
        // Save the target tileset image as separate file.
        int columns = qMin(options.columns, targetTileset->tileCount());
        int rows = targetTileset->tileCount() / options.columns;
        if (targetTileset->tileCount() % options.columns > 0)
            ++rows;

        qWarning() << "Writing external tileset image.";
        // Save the target tileset image
        QImage image(targetTileset->tileWidth() * columns,
                     targetTileset->tileHeight() * rows,
                     QImage::Format_ARGB32);

        image.fill(Qt::transparent);
        QPainter painter(&image);

        for (Tile *tile : targetTileset->tiles()) {
            int x = (tile->id() % options.columns) * targetTileset->tileWidth();
            int y = (tile->id() / options.columns) * targetTileset->tileHeight();
            painter.drawPixmap(x, y, tile->image());
        }

        QString imageFileName = QFileInfo(options.target).completeBaseName();
        imageFileName += ".png";
        image.save(imageFileName);

        targetTileset->setImageSource(imageFileName);
        targetTileset->setColumnCount(options.columns);
    }

    // Save the target tileset
    MapWriter writer;
    targetTileset->setFileName(QString());
    writer.writeTileset(*targetTileset, options.target);

    return 0;
}
Пример #8
0
	void ProcessSolidBlock( unsigned short x_base, unsigned short y_base, MapWriter& mapwriter )
	{
	  unsigned int idx2_offset = 0;
	  SOLIDX2_ELEM idx2_elem;
	  memset( &idx2_elem, 0, sizeof idx2_elem );
	  idx2_elem.baseindex = mapwriter.NextSolidIndex();

	  unsigned short x_add_max = SOLIDX_X_SIZE, y_add_max = SOLIDX_Y_SIZE;
	  if ( x_base + x_add_max > uo_map_width )
		x_add_max = uo_map_width - x_base;
	  if ( y_base + y_add_max > uo_map_height )
		y_add_max = uo_map_height - y_base;

	  for ( unsigned short x_add = 0; x_add < x_add_max; ++x_add )
	  {
		for ( unsigned short y_add = 0; y_add < y_add_max; ++y_add )
		{
		  unsigned short x = x_base + x_add;
		  unsigned short y = y_base + y_add;

		  StaticList statics;

		  // read the map, and treat it like a static.
		  short z;
		  USTRUCT_MAPINFO mi;

		  safe_getmapinfo( x, y, &z, &mi );

          if ( mi.landtile > 0x3FFF )
            INFO_PRINT.Format( "Tile 0x{:X} at ({},{},{}) is an invalid ID!\n" ) << mi.landtile << x << y << z;

		  // for water, don't average with surrounding tiles.
		  if ( landtile_uoflags( mi.landtile ) & USTRUCT_TILE::FLAG_LIQUID )
			z = mi.z;
		  short low_z = get_lowestadjacentz( x, y, z );

		  short lt_height = z - low_z;
		  z = low_z;

          if ( mi.landtile > 0x3FFF )
            INFO_PRINT.Format( "Tile 0x{:X} at ({},{},{}) is an invalid ID!\n" ) << mi.landtile << x << y << z;

		  unsigned int lt_flags = landtile_uoflags( mi.landtile );
		  if ( ~lt_flags & USTRUCT_TILE::FLAG_BLOCKING )
		  { // this seems to be the default.
			lt_flags |= USTRUCT_TILE::FLAG_PLATFORM;
		  }
		  lt_flags |= USTRUCT_TILE::FLAG_NO_SHOOT;    // added to make sure people using noshoot will have shapes
		  // generated by this tile in future block LOS, shouldn't
		  // affect people using old LOS method one way or another.
		  lt_flags |= USTRUCT_TILE::FLAG_FLOOR;
		  lt_flags |= USTRUCT_TILE::FLAG_HALF_HEIGHT; // the entire map is this way

		  if ( lt_flags & USTRUCT_TILE::FLAG_WALL )
			lt_height = 20;

		  readstatics( statics, x, y,
					   USTRUCT_TILE::FLAG_BLOCKING |
					   USTRUCT_TILE::FLAG_PLATFORM |
					   USTRUCT_TILE::FLAG_HALF_HEIGHT |
					   USTRUCT_TILE::FLAG_LIQUID |
					   USTRUCT_TILE::FLAG_HOVEROVER
					   //USTRUCT_TILE::FLAG__WALK
					   );

		  for ( unsigned i = 0; i < statics.size(); ++i )
		  {
			StaticRec srec = statics[i];

			unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot, cfg_LOS_through_windows );

			if ( ( ~polflags & FLAG::MOVELAND ) &&
				 ( ~polflags & FLAG::MOVESEA ) &&
				 ( ~polflags & FLAG::BLOCKSIGHT ) &&
				 ( ~polflags & FLAG::BLOCKING ) &&
				 ( ~polflags & FLAG::OVERFLIGHT ) )
			{
			  // remove it.  we'll re-sort later.
			  statics.erase( statics.begin() + i );
			  --i; // do-over
			}
			if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
				 /*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
			{
			  // remove it.  we'll re-sort later.
			  statics.erase( statics.begin() + i );
			  --i; // do-over
			}
		  }

		  bool addMap = true;

		  for ( const auto &srec : statics )
		  {
			// Look for water tiles.  If there are any, discard the map (which is usually at -15 anyway)
			if ( z + lt_height <= srec.z && ( ( srec.z - ( z + lt_height ) ) <= 10 ) && // only where the map is below or same Z as the static
				 srec.graphic >= 0x1796 && srec.graphic <= 0x17B2 ) // FIXME hardcoded
			{
			  // arr, there be water here
			  addMap = false;
			}

			// if there's a static on top of one of these "wall" landtiles, make it override.
			if ( ( lt_flags & USTRUCT_TILE::FLAG_WALL ) && // wall?
				 z <= srec.z &&
				 srec.z - z <= lt_height )
			{

			  lt_height = srec.z - z;
			}
		  }
		  // shadows above caves
		  if ( is_cave_shadow( mi ) && !statics.empty() )
		  {
			addMap = false;
		  }



		  // If the map is a NODRAW tile, and there are statics, discard the map tile
		  if ( mi.landtile == 2 && !statics.empty() )
			addMap = false;

		  if ( addMap )
			statics.push_back( StaticRec( 0, static_cast<signed char>( z ), lt_flags, static_cast<char>( lt_height ) ) );

		  sort( statics.begin(), statics.end(), StaticsByZ() );
		  reverse( statics.begin(), statics.end() );

          std::vector<MapShape> shapes;

		  // try to consolidate like shapes, and discard ones we don't care about.
		  while ( !statics.empty() )
		  {
			StaticRec srec = statics.back();
			statics.pop_back();

			unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot, cfg_LOS_through_windows );
			if ( ( ~polflags & FLAG::MOVELAND ) &&
				 ( ~polflags & FLAG::MOVESEA ) &&
				 ( ~polflags & FLAG::BLOCKSIGHT ) &&
				 ( ~polflags & FLAG::BLOCKING ) &&
				 ( ~polflags & FLAG::OVERFLIGHT ) )
			{
			  passert_always( 0 );
			  continue;
			}
			if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
				 ( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
				 /*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
			{
			  passert_always( 0 );
			  continue;
			}

			if ( shapes.empty() )
			{
			  // this, whatever it is, is the map base.
			  //TODO: look for water statics and use THOSE as the map.
			  MapShape shape;
			  shape.z = srec.z;      //these will be converted below to
			  shape.height = 0;      //make the map "solid"
			  shape.flags = static_cast<unsigned char>( polflags );
			  // no matter what, the lowest level is gradual
			  shape.flags |= FLAG::GRADUAL;
			  shapes.push_back( shape );

			  //for wall flag - map tile always height 0, at bottom. if map tile has height, add it as a static
			  if ( srec.height != 0 )
			  {
				MapShape _shape;
				_shape.z = srec.z;
				_shape.height = srec.height;
				_shape.flags = polflags;
				shapes.push_back( _shape );

			  }
			  continue;
			}

			MapShape& prev = shapes.back();
			// we're adding it.
			MapShape shape;
			shape.z = srec.z;
			shape.height = srec.height;
			shape.flags = polflags;

			//always add the map shape seperately
			if ( shapes.size() == 1 )
			{
			  shapes.push_back( shape );
			  continue;
			}

			if ( shape.z < prev.z + prev.height )
			{
			  // things can't exist in the same place.
			  // shrink the bottom part of this shape.
			  // if that would give it negative height, then skip it.
			  short height_remove = prev.z + prev.height - shape.z;
			  if ( height_remove <= shape.height )
			  {
				shape.z += height_remove;
				shape.height -= height_remove;
			  }
			  else
			  { // example: 5530, 14
				continue;
			  }
			}

			// sometimes water has "sand" a couple z-coords above it.
			// We'll try to detect this (really, anything that is up to 4 dist from water)
			// and extend the thing above downward.
			if ( ( prev.flags & FLAG::MOVESEA ) &&
				 ( shape.z > prev.z + prev.height ) &&
				 ( shape.z <= prev.z + prev.height + 4 ) )
			{
			  short height_add = shape.z - prev.z - prev.height;
			  shape.z -= height_add;
			  shape.height += height_add;
			}
			if ( ( prev.flags & FLAG::MOVESEA ) &&
				 ( prev.z + prev.height == -5 ) &&
				 ( shape.flags & FLAG::MOVESEA ) &&
				 ( shape.z == 25 ) )
			{
			  // oddly, there are some water tiles at z=25 in some places...I don't get it
			  continue;
			}

			//string prevflags_s = flagstr(prev.flags);
			//const char* prevflags = prevflags_s.c_str();
			//string shapeflags_s = flagstr(shape.flags);
			//const char* shapeflags = shapeflags_s.c_str();

			if ( shape.z > prev.z + prev.height )
			{
			  //
			  // elevated above what's below, must include separately
			  //

			  shapes.push_back( shape );
			  continue;
			}

			passert_always( shape.z == prev.z + prev.height );

			if ( shape.z == prev.z + prev.height )
			{
			  //
			  // sitting right on top of the previous solid
			  //

			  // standable atop non-standable: standable
			  // nonstandable atop standable: nonstandable
			  // etc
			  bool can_combine = flags_match( prev.flags, shape.flags, FLAG::BLOCKSIGHT | FLAG::BLOCKING );
			  if ( prev.flags & FLAG::MOVELAND &&
				   ~shape.flags & FLAG::BLOCKING &&
				   ~shape.flags & FLAG::MOVELAND )
			  {
				can_combine = false;
			  }

			  if ( can_combine )
			  {
				prev.flags = shape.flags;
				prev.height += shape.height;
			  }
			  else // if one blocks LOS, but not the other, they can't be combined this way.
			  {
				shapes.push_back( shape );
				continue;
			  }
			}
		  }

		  // the first StaticShape is the map base.
		  MapShape base = shapes[0];
		  shapes.erase( shapes.begin() );
		  MAPCELL cell;
		  passert_always( base.height == 0 );
		  cell.z = static_cast<signed char>( base.z ); //assume now map has height=1. a static was already added if it was >0
		  cell.flags = static_cast<u8>( base.flags );
		  if ( !shapes.empty() )
			cell.flags |= FLAG::MORE_SOLIDS;

		  mapwriter.SetMapCell( x, y, cell );

		  if ( !shapes.empty() )
		  {
			++with_more_solids;
			total_statics += static_cast<unsigned int>( shapes.size() );
			if ( idx2_offset == 0 )
			  idx2_offset = mapwriter.NextSolidx2Offset();

			unsigned int addindex = mapwriter.NextSolidIndex() - idx2_elem.baseindex;
			if ( addindex > std::numeric_limits<unsigned short>::max() )
                throw std::runtime_error("addoffset overflow");
			idx2_elem.addindex[x_add][y_add] = static_cast<unsigned short>( addindex );
			int count = static_cast<int>( shapes.size() );
			for ( int j = 0; j < count; ++j )
			{
			  MapShape shape = shapes[j];
			  char _z, height, flags;
			  _z = static_cast<char>( shapes[j].z );
			  height = static_cast<char>( shape.height );
			  flags = static_cast<u8>( shape.flags );
			  if ( !height )//make 0 height solid
			  {
				--_z;
				++height;
			  }

			  if ( j != count - 1 )
				flags |= FLAG::MORE_SOLIDS;
			  SOLIDS_ELEM solid;
			  solid.z = _z;
			  solid.height = height;
			  solid.flags = flags;
			  mapwriter.AppendSolid( solid );
			}
		  }
		}
	  }
	  if ( idx2_offset )
	  {
		++nonempty;
		mapwriter.AppendSolidx2Elem( idx2_elem );
	  }
	  else
	  {
		++empty;
	  }
	  mapwriter.SetSolidx2Offset( x_base, y_base, idx2_offset );
	}