void TilesetDock::updateCurrentTiles() { TilesetView *view = currentTilesetView(); if (!view) return; const QItemSelectionModel *s = view->selectionModel(); if (!s) return; const QModelIndexList indexes = s->selection().indexes(); if (indexes.isEmpty()) return; const QModelIndex &first = indexes.first(); int minX = first.column(); int maxX = first.column(); int minY = first.row(); int maxY = first.row(); for (const QModelIndex &index : indexes) { if (minX > index.column()) minX = index.column(); if (maxX < index.column()) maxX = index.column(); if (minY > index.row()) minY = index.row(); if (maxY < index.row()) maxY = index.row(); } // Create a tile layer from the current selection TileLayer *tileLayer = new TileLayer(QString(), 0, 0, maxX - minX + 1, maxY - minY + 1); const TilesetModel *model = view->tilesetModel(); for (const QModelIndex &index : indexes) { tileLayer->setCell(index.column() - minX, index.row() - minY, Cell(model->tileAt(index))); } setCurrentTiles(tileLayer); }
TileLayer *TileLayer::copy(const QRegion ®ion) const { const QRegion area = region.intersected(QRect(0, 0, width(), height())); const QRect bounds = region.boundingRect(); const QRect areaBounds = area.boundingRect(); const int offsetX = qMax(0, areaBounds.x() - bounds.x()); const int offsetY = qMax(0, areaBounds.y() - bounds.y()); TileLayer *copied = new TileLayer(QString(), 0, 0, bounds.width(), bounds.height()); foreach (const QRect &rect, area.rects()) for (int x = rect.left(); x <= rect.right(); ++x) for (int y = rect.top(); y <= rect.bottom(); ++y) copied->setCell(x - areaBounds.x() + offsetX, y - areaBounds.y() + offsetY, cellAt(x, y)); return copied; }
TileLayer *BucketFillTool::getRandomTileLayer(const QRegion ®ion) const { QRect bb = region.boundingRect(); TileLayer *result = new TileLayer(QString(), bb.x(), bb.y(), bb.width(), bb.height()); if (region.isEmpty() || mRandomList.empty()) return result; foreach (const QRect &rect, region.rects()) { for (int _x = rect.left(); _x <= rect.right(); ++_x) { for (int _y = rect.top(); _y <= rect.bottom(); ++_y) { result->setCell(_x - bb.x(), _y - bb.y(), mRandomList.at(rand() % mRandomList.size())); } } } return result; }
TileLayer *TileLayer::copy(const QRegion ®ion) const { const QRect regionBounds = region.boundingRect(); const QRegion regionWithContents = region.intersected(mBounds); TileLayer *copied = new TileLayer(QString(), 0, 0, regionBounds.width(), regionBounds.height()); #if QT_VERSION < 0x050800 const auto rects = regionWithContents.rects(); for (const QRect &rect : rects) { #else for (const QRect &rect : regionWithContents) { #endif for (int x = rect.left(); x <= rect.right(); ++x) for (int y = rect.top(); y <= rect.bottom(); ++y) copied->setCell(x - regionBounds.x(), y - regionBounds.y(), cellAt(x, y)); } return copied; } void TileLayer::merge(const QPoint &pos, const TileLayer *layer) { // Determine the overlapping area QRect area = QRect(pos, QSize(layer->width(), layer->height())); area &= QRect(0, 0, width(), height()); for (int y = area.top(); y <= area.bottom(); ++y) { for (int x = area.left(); x <= area.right(); ++x) { const Cell &cell = layer->cellAt(x - pos.x(), y - pos.y()); if (!cell.isEmpty()) setCell(x, y, cell); } } }
void TilesetDock::updateCurrentTiles() { const int viewIndex = mViewStack->currentIndex(); if (viewIndex == -1) return; const QItemSelectionModel *s = tilesetViewAt(viewIndex)->selectionModel(); const QModelIndexList indexes = s->selection().indexes(); if (indexes.isEmpty()) return; const QModelIndex &first = indexes.first(); int minX = first.column(); int maxX = first.column(); int minY = first.row(); int maxY = first.row(); foreach (const QModelIndex &index, indexes) { if (minX > index.column()) minX = index.column(); if (maxX < index.column()) maxX = index.column(); if (minY > index.row()) minY = index.row(); if (maxY < index.row()) maxY = index.row(); emit statusInfoChanged(tr("Tile ID:") + QString::number(((TilesetModel*)(s->currentIndex().model()))->tileAt(s->currentIndex())->id())); } // Create a tile layer from the current selection TileLayer *tileLayer = new TileLayer(QString(), 0, 0, maxX - minX + 1, maxY - minY + 1); const TilesetModel *model = static_cast<const TilesetModel*>(s->model()); foreach (const QModelIndex &index, indexes) { tileLayer->setCell(index.column() - minX, index.row() - minY, Cell(model->tileAt(index))); }
void TileCollisionDock::setTile(Tile *tile) { if (mTile == tile) return; mTile = tile; mMapScene->disableSelectedTool(); MapDocument *previousDocument = mDummyMapDocument; mMapView->setEnabled(tile); if (tile) { Map::Orientation orientation = Map::Orthogonal; QSize tileSize = tile->size(); if (tile->tileset()->orientation() == Tileset::Isometric) { orientation = Map::Isometric; tileSize = tile->tileset()->gridSize(); } Map *map = new Map(orientation, 1, 1, tileSize.width(), tileSize.height()); map->addTileset(tile->sharedTileset()); TileLayer *tileLayer = new TileLayer(QString(), 0, 0, 1, 1); tileLayer->setCell(0, 0, Cell(tile)); map->addLayer(tileLayer); ObjectGroup *objectGroup; if (tile->objectGroup()) objectGroup = tile->objectGroup()->clone(); else objectGroup = new ObjectGroup; objectGroup->setDrawOrder(ObjectGroup::IndexOrder); map->setNextObjectId(objectGroup->highestObjectId() + 1); map->addLayer(objectGroup); mDummyMapDocument = new MapDocument(map); mDummyMapDocument->setCurrentLayer(objectGroup); mMapScene->setMapDocument(mDummyMapDocument); mToolManager->setMapDocument(mDummyMapDocument); mMapScene->enableSelectedTool(); connect(mDummyMapDocument->undoStack(), &QUndoStack::indexChanged, this, &TileCollisionDock::applyChanges); connect(mDummyMapDocument, &MapDocument::selectedObjectsChanged, this, &TileCollisionDock::selectedObjectsChanged); } else { mDummyMapDocument = nullptr; mMapScene->setMapDocument(nullptr); mToolManager->setMapDocument(nullptr); } emit dummyMapDocumentChanged(mDummyMapDocument); setHasSelectedObjects(false); if (previousDocument) { // Explicitly disconnect early from this signal, since it can get fired // from the QUndoStack destructor. disconnect(previousDocument->undoStack(), &QUndoStack::indexChanged, this, &TileCollisionDock::applyChanges); delete previousDocument; } }
Tiled::Map *ReplicaIslandPlugin::read(const QString &fileName) { using namespace Tiled; // Read data. QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { mError = tr("Cannot open Replica Island map file!"); return 0; } QDataStream in(&file); in.setByteOrder(QDataStream::LittleEndian); in.setFloatingPointPrecision(QDataStream::SinglePrecision); // Parse file header. quint8 mapSignature, layerCount, backgroundIndex; in >> mapSignature >> layerCount >> backgroundIndex; if (in.status() == QDataStream::ReadPastEnd || mapSignature != 96) { mError = tr("Can't parse file header!"); return 0; } // Create our map, setting width and height to 0 until we load a layer. Map *map = new Map(Map::Orthogonal, 0, 0, 32, 32); map->setProperty("background_index", QString::number(backgroundIndex)); // Load our Tilesets. QVector<SharedTileset> typeTilesets, tileIndexTilesets; loadTilesetsFromResources(map, typeTilesets, tileIndexTilesets); // Load each of our layers. for (quint8 i = 0; i < layerCount; i++) { // Parse layer header. quint8 type, tileIndex, levelSignature; float scrollSpeed; qint32 width, height; in >> type >> tileIndex >> scrollSpeed >> levelSignature >> width >> height; if (in.status() == QDataStream::ReadPastEnd || levelSignature != 42) { delete map; mError = 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 || map->height() != height) { delete map; mError = tr("Inconsistent layer sizes!"); return 0; } // Create a layer object. TileLayer *layer = new TileLayer(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. SharedTileset tileset = tilesetForLayer(type, tileIndex, typeTilesets, tileIndexTilesets); // Read our tile data all at once. QByteArray tileData(width*height, '\0'); int bytesRead = in.readRawData(tileData.data(), tileData.size()); if (bytesRead != tileData.size()) { delete map; mError = tr("File ended in middle of layer!"); return 0; } quint8 *tp = reinterpret_cast<quint8 *>(tileData.data()); // Add the tiles to our layer. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { quint8 tile_id = *tp++; if (tile_id != 255) { Tile *tile = tileset->findTile(tile_id); layer->setCell(x, y, Cell(tile)); } } } } // Make sure we read the entire *.bin file. if (in.status() != QDataStream::Ok || !in.atEnd()) { delete map; mError = tr("Unexpected data at end of file!"); return 0; } return map; }
TileLayer *WangFiller::fillRegion(const TileLayer &back, const QRegion &fillRegion) const { Q_ASSERT(mWangSet); QRect boundingRect = fillRegion.boundingRect(); TileLayer *tileLayer = new TileLayer(QString(), boundingRect.x(), boundingRect.y(), boundingRect.width(), boundingRect.height()); QVector<WangId> wangIds(tileLayer->width() * tileLayer->height(), 0); for (const QRect &rect : fillRegion.rects()) { for (int x = rect.left(); x <= rect.right(); ++x) { int index = x - tileLayer->x() + (rect.top() - tileLayer->y()) * tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(x, rect.top())); index = x - tileLayer->x() + (rect.bottom() - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(x, rect.bottom())); } for (int y = rect.top() + 1; y < rect.bottom(); ++y) { int index = rect.left() - tileLayer->x() + (y - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(rect.left(), y)); index = rect.right() - tileLayer->x() + (y - tileLayer->y())*tileLayer->width(); wangIds[index] = wangIdFromSurroundings(back, fillRegion, QPoint(rect.right(), y)); } } for (const QRect &rect : fillRegion.rects()) { for (int y = rect.top(); y <= rect.bottom(); ++y) { for (int x = rect.left(); x <= rect.right(); ++x) { QPoint currentPoint(x, y); int currentIndex = (currentPoint.y() - tileLayer->y()) * tileLayer->width() + (currentPoint.x() - tileLayer->x()); QList<WangTile> wangTilesList = mWangSet->findMatchingWangTiles(wangIds[currentIndex]); RandomPicker<WangTile> wangTiles; for (const WangTile &wangTile : wangTilesList) wangTiles.add(wangTile, mWangSet->wangTileProbability(wangTile)); while (!wangTiles.isEmpty()) { WangTile wangTile = wangTiles.take(); bool fill = true; if (!mWangSet->isComplete()) { QPoint adjacentPoints[8]; getSurroundingPoints(currentPoint, mStaggeredRenderer, mStaggerAxis, adjacentPoints); for (int i = 0; i < 8; ++i) { QPoint p = adjacentPoints[i]; if (!fillRegion.contains(p) || !tileLayer->cellAt(p - tileLayer->position()).isEmpty()) continue; p -= tileLayer->position(); int index = p.y() * tileLayer->width() + p.x(); WangId adjacentWangId = wangIds[index]; adjacentWangId.updateToAdjacent(wangTile.wangId(), (i + 4) % 8); if (!mWangSet->wildWangIdIsUsed(adjacentWangId)) { fill = wangTiles.isEmpty(); break; } } } if (fill) { tileLayer->setCell(currentPoint.x() - tileLayer->x(), currentPoint.y() - tileLayer->y(), wangTile.makeCell()); QPoint adjacentPoints[8]; getSurroundingPoints(currentPoint, mStaggeredRenderer, mStaggerAxis, adjacentPoints); for (int i = 0; i < 8; ++i) { QPoint p = adjacentPoints[i]; if (!fillRegion.contains(p) || !tileLayer->cellAt(p - tileLayer->position()).isEmpty()) continue; p -= tileLayer->position(); int index = p.y() * tileLayer->width() + p.x(); wangIds[index].updateToAdjacent(wangTile.wangId(), (i + 4) % 8); } break; } } } } } return tileLayer; }
void AbstractTileFillTool::randomFill(TileLayer &tileLayer, const QRegion ®ion) const { if (region.isEmpty() || mRandomCellPicker.isEmpty()) return; const auto localRegion = region.translated(-tileLayer.position()); #if QT_VERSION < 0x050800 const auto rects = localRegion.rects(); for (const QRect &rect : rects) { #else for (const QRect &rect : localRegion) { #endif for (int y = rect.top(); y <= rect.bottom(); ++y) { for (int x = rect.left(); x <= rect.right(); ++x) { tileLayer.setCell(x, y, mRandomCellPicker.pick()); } } } } void AbstractTileFillTool::wangFill(TileLayer &tileLayerToFill, const TileLayer &backgroundTileLayer, const QRegion ®ion) const { if (!mWangSet) return; WangFiller wangFiller(mWangSet, dynamic_cast<StaggeredRenderer *>(mapDocument()->renderer()), mapDocument()->map()->staggerAxis()); auto stamp = wangFiller.fillRegion(backgroundTileLayer, region); tileLayerToFill.setCells(0, 0, stamp.get()); } void AbstractTileFillTool::fillWithStamp(Map &map, const TileStamp &stamp, const QRegion &mask) { if (stamp.isEmpty()) return; const QSize size = stamp.maxSize(); if (size.isEmpty()) return; const QRect bounds = mask.boundingRect(); // Fill the entire map with random variations of the stamp for (int y = 0; y < bounds.height(); y += size.height()) { for (int x = 0; x < bounds.width(); x += size.width()) { const Map *stampMap = stamp.randomVariation().map; for (Layer *layer : stampMap->tileLayers()) { TileLayer *target = static_cast<TileLayer*>(map.findLayer(layer->name(), Layer::TileLayerType)); if (!target) { target = new TileLayer(layer->name(), bounds.topLeft(), bounds.size()); map.addLayer(target); } target->setCells(x, y, static_cast<TileLayer*>(layer)); } } } // Erase tiles outside of the masked region. This can easily be faster than // avoiding to place tiles outside of the region in the first place. for (Layer *layer : map.tileLayers()) { auto tileLayer = static_cast<TileLayer*>(layer); tileLayer->erase((QRegion(tileLayer->bounds()) - mask).translated(-tileLayer->position())); } } void AbstractTileFillTool::invalidateRandomAndMissingCache() { mRandomAndMissingCacheValid = false; }