示例#1
0
void BucketFillTool::updateRandomList()
{
    mRandomList.clear();
    mMissingTilesets.clear();

    foreach (const TileStampVariation &variation, mStamp.variations()) {
        TileLayer *tileLayer = static_cast<TileLayer*>(variation.map->layerAt(0));
        mapDocument()->unifyTilesets(variation.map, mMissingTilesets);
        for (int x = 0; x < tileLayer->width(); x++)
            for (int y = 0; y < tileLayer->height(); y++)
                if (!tileLayer->cellAt(x, y).isEmpty())
                    mRandomList.append(tileLayer->cellAt(x, y));
    }
}
示例#2
0
void MapToVariantConverter::addTileLayerData(QVariantMap &variant,
                                             const TileLayer &tileLayer,
                                             Map::LayerDataFormat format,
                                             const QRect &bounds) const
{
    switch (format) {
    case Map::XML:
    case Map::CSV: {
        QVariantList tileVariants;
        for (int y = bounds.top(); y <= bounds.bottom(); ++y)
            for (int x = bounds.left(); x <= bounds.right(); ++x)
                tileVariants << mGidMapper.cellToGid(tileLayer.cellAt(x, y));

        variant[QLatin1String("data")] = tileVariants;
        break;
    }
    case Map::Base64:
    case Map::Base64Zlib:
    case Map::Base64Gzip: {
        QByteArray layerData = mGidMapper.encodeLayerData(tileLayer, format, bounds);
        variant[QLatin1String("data")] = layerData;
        break;
    }
    }
}
示例#3
0
void TerrainBrush::capture()
{
    TileLayer *tileLayer = currentTileLayer();
    Q_ASSERT(tileLayer);

    // TODO: we need to know which corner the mouse is closest to...

    const Cell &cell = tileLayer->cellAt(tilePosition());
    Terrain *t = cell.tile->terrainAtCorner(0);
    setTerrain(t);
}
示例#4
0
void SelectSameTileTool::tilePositionChanged(const QPoint &tilePos)
{
    // Make sure that a tile layer is selected and contains current tile pos.
    TileLayer *tileLayer = currentTileLayer();
    if (!tileLayer)
        return;

    QRegion resultRegion;
    if (tileLayer->contains(tilePos)) {
        const Cell &matchCell = tileLayer->cellAt(tilePos);
        resultRegion = tileLayer->region([&] (const Cell &cell) { return cell == matchCell; });
    }
    mSelectedRegion = resultRegion;
    brushItem()->setTileRegion(mSelectedRegion);
}
示例#5
0
WangId WangFiller::wangIdFromSurroundings(const TileLayer &back,
                                          const QRegion &fillRegion,
                                          QPoint point) const
{
    Cell surroundingCells[8];

    QPoint adjacentPoints[8];
    getSurroundingPoints(point, mStaggeredRenderer, mStaggerAxis, adjacentPoints);

    for (int i = 0; i < 8; ++i) {
        if (!fillRegion.contains(adjacentPoints[i]))
            surroundingCells[i] = back.cellAt(adjacentPoints[i]);
    }

    return mWangSet->wangIdFromSurrounding(surroundingCells);
}
示例#6
0
// Writer
bool DroidcraftPlugin::write(const Tiled::Map *map, const QString &fileName)
{
    using namespace Tiled;

    // Check layer count and type
    if (map->layerCount() != 1 || !map->layerAt(0)->isTileLayer()) {
        mError = tr("The map needs to have exactly one tile layer!");
        return false;
    }

    TileLayer *mapLayer = map->layerAt(0)->asTileLayer();

    // Check layer size
    if (mapLayer->width() != 48 || mapLayer->height() != 48) {
        mError = tr("The layer must have a size of 48 x 48 tiles!");
        return false;
    }

    // Create QByteArray and compress it
    QByteArray uncompressed = QByteArray(48 * 48, 0);

    const int width = mapLayer->width();
    const int height = mapLayer->height();

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            if (Tile *tile = mapLayer->cellAt(x, y).tile)
                uncompressed[y * width + x] = (unsigned char) tile->id();
        }
    }

    QByteArray compressed = compress(uncompressed, Gzip);

    // Write QByteArray
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly)) {
        mError = tr("Could not open file for writing.");
        return false;
    }

    file.write(compressed);
    file.close();

    return true;
}
示例#7
0
/**
 * Updates the list used random stamps.
 * This is done by taking all non-null tiles from the original stamp mStamp.
 */
void StampBrush::updateRandomList()
{
    mRandomCellPicker.clear();
    mMissingTilesets.clear();

    if (mStamp.isEmpty())
        return;

    foreach (const TileStampVariation &variation, mStamp.variations()) {
        TileLayer *tileLayer = static_cast<TileLayer*>(variation.map->layerAt(0));
        mapDocument()->unifyTilesets(variation.map, mMissingTilesets);
        for (int x = 0; x < tileLayer->width(); x++) {
            for (int y = 0; y < tileLayer->height(); y++) {
                const Cell &cell = tileLayer->cellAt(x, y);
                if (!cell.isEmpty())
                    mRandomCellPicker.add(cell, cell.tile->probability());
            }
        }
    }
}
示例#8
0
/**
 * Updates the list used random stamps.
 * This is done by taking all non-null tiles from the original stamp mStamp.
 */
void StampBrush::updateRandomList()
{
    mRandomCellPicker.clear();

    if (!mIsRandom)
        return;

    mMissingTilesets.clear();

    for (const TileStampVariation &variation : mStamp.variations()) {
        mapDocument()->unifyTilesets(variation.map, mMissingTilesets);
        TileLayer *tileLayer = variation.tileLayer();
        for (int x = 0; x < tileLayer->width(); x++) {
            for (int y = 0; y < tileLayer->height(); y++) {
                const Cell &cell = tileLayer->cellAt(x, y);
                if (const Tile *tile = cell.tile())
                    mRandomCellPicker.add(cell, tile->probability());
            }
        }
    }
}
示例#9
0
void CellPropertiesTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) 
    {
	    TileLayer *tileLayer = currentTileLayer();
	    Q_ASSERT(tileLayer);
	
	    const QPoint tilePos = tilePosition();

            Cell *cell = tileLayer->getCellAt(tilePos.x(),tilePos.y());

            // Add properties from tile
            if (!cell->isEmpty() ) {
                Properties properties = cell->properties();
                Properties tileprop = tileLayer->cellAt(tilePos.x(),tilePos.y()).tile->properties();

                QStringList req = tileprop.value(QString::fromLatin1("required")).split(QString::fromLatin1(","));
                req << tileprop.value(QString::fromLatin1("optional")).split(QString::fromLatin1(","));

                foreach (const QString &str, req) {
                    int p = str.indexOf(QString::fromLatin1("="));
                    QString key, value(QString::fromLatin1(""));
                    if (0==p) {
                        // only name
                        key = str.trimmed();
                    } else {
                        key = str.left(p).trimmed();
                        value = str.right(str.length() - p - 1);
                        // options = value.split("/");
                    }
                    if (key.isEmpty())
                        continue;

                    // Keep the key if exist, otherwise insert the new value (empty by default)
                    if (!properties.contains(key))
                        properties.insert(key, value);
                }
示例#10
0
void BucketFillTool::tilePositionChanged(const QPoint &tilePos)
{
    AbstractTileFillTool::tilePositionChanged(tilePos);

    if (isCapturing())
        return;

    // Skip filling if the stamp is empty and not in wangFill mode
    if (mStamp.isEmpty() && mFillMethod != WangFill)
        return;

    // Make sure that a tile layer is selected
    TileLayer *tileLayer = currentTileLayer();
    if (!tileLayer)
        return;

    bool shiftPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier;
    bool fillRegionChanged = false;

    TilePainter regionComputer(mapDocument(), tileLayer);

    // This clears the connections so we don't get callbacks
    clearConnections(mapDocument());

    // Optimization: we don't need to recalculate the fill area
    // if the new mouse position is still over the filled region
    // and the shift modifier hasn't changed.
    if (!mFillRegion.contains(tilePos) || shiftPressed != mLastShiftStatus) {

        // Clear overlay to make way for a new one
        AbstractTileFillTool::clearOverlay();

        // Cache information about how the fill region was created
        mLastShiftStatus = shiftPressed;

        // Get the new fill region
        if (!shiftPressed) {
            // If not holding shift, a region is computed from the current pos
            bool computeRegion = true;

            // If the stamp is a single tile, ignore that tile when making the region
            if (mFillMethod != WangFill && mStamp.variations().size() == 1) {
                const TileStampVariation &variation = mStamp.variations().first();
                TileLayer *stampLayer = variation.tileLayer();
                if (stampLayer->size() == QSize(1, 1) &&
                        stampLayer->cellAt(0, 0) == regionComputer.cellAt(tilePos))
                    computeRegion = false;
            }

            if (computeRegion)
                mFillRegion = regionComputer.computePaintableFillRegion(tilePos);
        } else {
            // If holding shift, the region is the selection bounds
            mFillRegion = mapDocument()->selectedArea();

            // Fill region is the whole map if there is no selection
            if (mFillRegion.isEmpty())
                mFillRegion = tileLayer->rect();

            // The mouse needs to be in the region
            if (!mFillRegion.contains(tilePos))
                mFillRegion = QRegion();
        }
        fillRegionChanged = true;
    }

    // Ensure that a fill region was created before making an overlay layer
    if (mFillRegion.isEmpty())
        return;

    if (mLastFillMethod != mFillMethod) {
        mLastFillMethod = mFillMethod;
        fillRegionChanged = true;
    }

    if (!mFillOverlay) {
        // Create a new overlay region
        const QRect fillBounds = mFillRegion.boundingRect();
        mFillOverlay = SharedTileLayer::create(QString(),
                                               fillBounds.x(),
                                               fillBounds.y(),
                                               fillBounds.width(),
                                               fillBounds.height());
    }

    // Paint the new overlay
    switch (mFillMethod) {
    case TileFill:
        if (fillRegionChanged || mStamp.variations().size() > 1) {
            fillWithStamp(*mFillOverlay, mStamp,
                          mFillRegion.translated(-mFillOverlay->position()));
            fillRegionChanged = true;
        }
        break;
    case RandomFill:
        randomFill(*mFillOverlay, mFillRegion);
        fillRegionChanged = true;
        break;
    case WangFill:
        wangFill(*mFillOverlay, *tileLayer, mFillRegion);
        fillRegionChanged = true;
        break;
    }

    if (fillRegionChanged) {
        // Update the brush item to draw the overlay
        brushItem()->setTileLayer(mFillOverlay);
    }
    // Create connections to know when the overlay should be cleared
    makeConnections();
}
示例#11
0
std::unique_ptr<TileLayer> WangFiller::fillRegion(const TileLayer &back,
                                                  const QRegion &fillRegion) const
{
    Q_ASSERT(mWangSet);

    const QRect boundingRect = fillRegion.boundingRect();

    std::unique_ptr<TileLayer> tileLayer { new TileLayer(QString(),
                                                         boundingRect.x(),
                                                         boundingRect.y(),
                                                         boundingRect.width(),
                                                         boundingRect.height()) };

    QVector<WangId> wangIds(tileLayer->width() * tileLayer->height(), 0);
#if QT_VERSION < 0x050800
    const auto rects = fillRegion.rects();
    for (const QRect &rect : rects) {
#else
    for (const QRect &rect : fillRegion) {
#endif
        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));
        }
    }

#if QT_VERSION < 0x050800
    for (const QRect &rect : rects) {
#else
    for (const QRect &rect : fillRegion) {
#endif
        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;
}

const Cell &WangFiller::getCell(const TileLayer &back,
                                const TileLayer &front,
                                const QRegion &fillRegion,
                                QPoint point) const
{
    if (!fillRegion.contains(point))
        return back.cellAt(point);
    else
        return front.cellAt(point.x() - front.x(), point.y() - front.y());
}


WangId WangFiller::wangIdFromSurroundings(const TileLayer &back,
                                          const TileLayer &front,
                                          const QRegion &fillRegion,
                                          QPoint point) const
{
    Cell surroundingCells[8];
    QPoint adjacentPoints[8];
    getSurroundingPoints(point, mStaggeredRenderer, mStaggerAxis, adjacentPoints);

    for (int i = 0; i < 8; ++i)
        surroundingCells[i] = getCell(back, front, fillRegion, adjacentPoints[i]);

    return mWangSet->wangIdFromSurrounding(surroundingCells);
}

WangId WangFiller::wangIdFromSurroundings(const TileLayer &back,
                                          const QRegion &fillRegion,
                                          QPoint point) const
{
    Cell surroundingCells[8];

    QPoint adjacentPoints[8];
    getSurroundingPoints(point, mStaggeredRenderer, mStaggerAxis, adjacentPoints);

    for (int i = 0; i < 8; ++i) {
        if (!fillRegion.contains(adjacentPoints[i]))
            surroundingCells[i] = back.cellAt(adjacentPoints[i]);
    }

    return mWangSet->wangIdFromSurrounding(surroundingCells);
}
示例#12
0
void BucketFillTool::tilePositionChanged(const QPoint &tilePos)
{
    if (mStamp.isEmpty())
        return;

    bool shiftPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier;
    bool fillRegionChanged = false;

    // Make sure that a tile layer is selected
    TileLayer *tileLayer = currentTileLayer();
    if (!tileLayer)
        return;

    // todo: When there are multiple variations, it would make sense to choose
    // random variations while filling. When the variations have different
    // sizes, probably the bounding box of all variations should be used.
    Map *variation = mStamp.randomVariation();
    TileLayer *stampLayer = static_cast<TileLayer*>(variation->layerAt(0));

    // Skip filling if the stamp is empty
    if (!stampLayer || stampLayer->isEmpty())
        return;

    TilePainter regionComputer(mapDocument(), tileLayer);
    // If the stamp is a single tile, ignore it when making the region
    if (stampLayer->width() == 1 && stampLayer->height() == 1 && !shiftPressed &&
            stampLayer->cellAt(0, 0) == regionComputer.cellAt(tilePos.x(),
                                                              tilePos.y()))
        return;

    // This clears the connections so we don't get callbacks
    clearConnections(mapDocument());

    // Optimization: we don't need to recalculate the fill area
    // if the new mouse position is still over the filled region
    // and the shift modifier hasn't changed.
    if (!mFillRegion.contains(tilePos) || shiftPressed != mLastShiftStatus || mIsRandom) {

        // Clear overlay to make way for a new one
        clearOverlay();

        // Cache information about how the fill region was created
        mLastShiftStatus = shiftPressed;

        // Get the new fill region
        if (!shiftPressed) {
            // If not holding shift, a region is generated from the current pos
            mFillRegion = regionComputer.computePaintableFillRegion(tilePos);
        } else {
            // If holding shift, the region is the selection bounds
            mFillRegion = mapDocument()->selectedArea();

            // Fill region is the whole map if there is no selection
            if (mFillRegion.isEmpty())
                mFillRegion = tileLayer->bounds();

            // The mouse needs to be in the region
            if (!mFillRegion.contains(tilePos))
                mFillRegion = QRegion();
        }
        fillRegionChanged = true;
    }

    // Ensure that a fill region was created before making an overlay layer
    if (mFillRegion.isEmpty())
        return;

    if (mLastRandomStatus != mIsRandom) {
        mLastRandomStatus = mIsRandom;
        fillRegionChanged = true;
    }

    if (!mFillOverlay) {
        // Create a new overlay region
        const QRect fillBounds = mFillRegion.boundingRect();
        mFillOverlay = SharedTileLayer(new TileLayer(QString(),
                                                     fillBounds.x(),
                                                     fillBounds.y(),
                                                     fillBounds.width(),
                                                     fillBounds.height()));
    }

    // Paint the new overlay
    if (!mIsRandom) {
        if (fillRegionChanged) {
            mMissingTilesets.clear();
            mapDocument()->unifyTilesets(variation, mMissingTilesets);

            TilePainter tilePainter(mapDocument(), mFillOverlay.data());
            tilePainter.drawStamp(stampLayer, mFillRegion);
        }
    } else {
        randomFill(mFillOverlay.data(), mFillRegion);
        fillRegionChanged = true;
    }

    if (fillRegionChanged) {
        // Update the brush item to draw the overlay
        brushItem()->setTileLayer(mFillOverlay);
    }
    // Create connections to know when the overlay should be cleared
    makeConnections();
}