diff --git a/editor.cpp b/editor.cpp index 2d23b437..1f8ddcd4 100755 --- a/editor.cpp +++ b/editor.cpp @@ -3,8 +3,9 @@ #include #include -Editor::Editor() +Editor::Editor(Ui::MainWindow* ui) { + this->ui = ui; selected_events = new QList; } @@ -37,9 +38,11 @@ void Editor::redo() { void Editor::setEditingMap() { current_view = map_item; if (map_item) { + displayMapConnections(); map_item->draw(); map_item->setVisible(true); map_item->setEnabled(true); + setConnectionsVisibility(true); } if (collision_item) { collision_item->setVisible(false); @@ -47,13 +50,15 @@ void Editor::setEditingMap() { if (objects_group) { objects_group->setVisible(false); } + setBorderItemsVisible(true); + setConnectionItemsVisible(false); } void Editor::setEditingCollision() { current_view = collision_item; if (collision_item) { - collision_item->draw(); collision_item->setVisible(true); + setConnectionsVisibility(true); } if (map_item) { map_item->setVisible(false); @@ -61,6 +66,8 @@ void Editor::setEditingCollision() { if (objects_group) { objects_group->setVisible(false); } + setBorderItemsVisible(true); + setConnectionItemsVisible(false); } void Editor::setEditingObjects() { @@ -71,10 +78,232 @@ void Editor::setEditingObjects() { if (map_item) { map_item->setVisible(true); map_item->setEnabled(false); + setConnectionsVisibility(true); } if (collision_item) { collision_item->setVisible(false); } + setBorderItemsVisible(true); + setConnectionItemsVisible(false); +} + +void Editor::setEditingConnections() { + current_view = map_item; + if (map_item) { + map_item->draw(); + map_item->setVisible(true); + map_item->setEnabled(false); + populateConnectionMapPickers(); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + setConnectionsVisibility(false); + setDiveEmergeControls(); + setConnectionEditControlsEnabled(selected_connection_item != NULL); + if (selected_connection_item) { + onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); + setConnectionMap(selected_connection_item->connection->map_name); + setCurrentConnectionDirection(selected_connection_item->connection->direction); + } + } + if (collision_item) { + collision_item->setVisible(false); + } + if (objects_group) { + objects_group->setVisible(false); + } + setBorderItemsVisible(true, 0.4); + setConnectionItemsVisible(true); +} + +void Editor::setDiveEmergeControls() { + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); + ui->comboBox_DiveMap->setCurrentText(""); + ui->comboBox_EmergeMap->setCurrentText(""); + for (Connection* connection : map->connections) { + if (connection->direction == "dive") { + ui->comboBox_DiveMap->setCurrentText(connection->map_name); + } else if (connection->direction == "emerge") { + ui->comboBox_EmergeMap->setCurrentText(connection->map_name); + } + } + ui->comboBox_DiveMap->blockSignals(false); + ui->comboBox_EmergeMap->blockSignals(false); +} + +void Editor::populateConnectionMapPickers() { + ui->comboBox_ConnectedMap->blockSignals(true); + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); + + ui->comboBox_ConnectedMap->clear(); + ui->comboBox_ConnectedMap->addItems(*project->mapNames); + ui->comboBox_DiveMap->clear(); + ui->comboBox_DiveMap->addItems(*project->mapNames); + ui->comboBox_EmergeMap->clear(); + ui->comboBox_EmergeMap->addItems(*project->mapNames); + + ui->comboBox_ConnectedMap->blockSignals(false); + ui->comboBox_DiveMap->blockSignals(true); + ui->comboBox_EmergeMap->blockSignals(true); +} + +void Editor::setConnectionItemsVisible(bool visible) { + for (ConnectionPixmapItem* item : connection_edit_items) { + item->setVisible(visible); + item->setEnabled(visible); + } +} + +void Editor::setBorderItemsVisible(bool visible, qreal opacity) { + for (QGraphicsPixmapItem* item : borderItems) { + item->setVisible(visible); + item->setOpacity(opacity); + } +} + +void Editor::setCurrentConnectionDirection(QString curDirection) { + if (!selected_connection_item) + return; + + selected_connection_item->connection->direction = curDirection; + + Map *connected_map = project->getMap(selected_connection_item->connection->map_name); + QPixmap pixmap = connected_map->renderConnection(*selected_connection_item->connection); + int offset = selected_connection_item->connection->offset.toInt(nullptr, 0); + selected_connection_item->initialOffset = offset; + int x = 0, y = 0; + if (selected_connection_item->connection->direction == "up") { + x = offset * 16; + y = -pixmap.height(); + } else if (selected_connection_item->connection->direction == "down") { + x = offset * 16; + y = map->getHeight() * 16; + } else if (selected_connection_item->connection->direction == "left") { + x = -pixmap.width(); + y = offset * 16; + } else if (selected_connection_item->connection->direction == "right") { + x = map->getWidth() * 16; + y = offset * 16; + } + + selected_connection_item->basePixmap = pixmap; + QPainter painter(&pixmap); + painter.setPen(QColor(255, 0, 255)); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + painter.end(); + selected_connection_item->setPixmap(pixmap); + selected_connection_item->initialX = x; + selected_connection_item->initialY = y; + selected_connection_item->blockSignals(true); + selected_connection_item->setX(x); + selected_connection_item->setY(y); + selected_connection_item->setZValue(-1); + selected_connection_item->blockSignals(false); + + setConnectionEditControlValues(selected_connection_item->connection); +} + +void Editor::updateCurrentConnectionDirection(QString curDirection) { + if (!selected_connection_item) + return; + + QString originalDirection = selected_connection_item->connection->direction; + setCurrentConnectionDirection(curDirection); + updateMirroredConnectionDirection(selected_connection_item->connection, originalDirection); +} + +void Editor::onConnectionMoved(Connection* connection) { + updateMirroredConnectionOffset(connection); + onConnectionOffsetChanged(connection->offset.toInt()); +} + +void Editor::onConnectionOffsetChanged(int newOffset) { + ui->spinBox_ConnectionOffset->blockSignals(true); + ui->spinBox_ConnectionOffset->setValue(newOffset); + ui->spinBox_ConnectionOffset->blockSignals(false); + +} + +void Editor::setConnectionEditControlValues(Connection* connection) { + QString mapName = connection ? connection->map_name : ""; + QString direction = connection ? connection->direction : ""; + int offset = connection ? connection->offset.toInt() : 0; + + ui->comboBox_ConnectedMap->blockSignals(true); + ui->comboBox_ConnectionDirection->blockSignals(true); + ui->spinBox_ConnectionOffset->blockSignals(true); + + ui->comboBox_ConnectedMap->setCurrentText(mapName); + ui->comboBox_ConnectionDirection->setCurrentText(direction); + ui->spinBox_ConnectionOffset->setValue(offset); + + ui->comboBox_ConnectedMap->blockSignals(false); + ui->comboBox_ConnectionDirection->blockSignals(false); + ui->spinBox_ConnectionOffset->blockSignals(false); +} + +void Editor::setConnectionEditControlsEnabled(bool enabled) { + ui->comboBox_ConnectionDirection->setEnabled(enabled); + ui->comboBox_ConnectedMap->setEnabled(enabled); + ui->spinBox_ConnectionOffset->setEnabled(enabled); + + if (!enabled) { + setConnectionEditControlValues(false); + } +} + +void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) { + if (!connectionItem) + return; + + for (ConnectionPixmapItem* item : connection_edit_items) { + bool isSelectedItem = item == connectionItem; + int zValue = isSelectedItem ? 0 : -1; + qreal opacity = isSelectedItem ? 1 : 0.75; + item->setZValue(zValue); + item->render(opacity); + if (isSelectedItem) { + QPixmap pixmap = item->pixmap(); + QPainter painter(&pixmap); + painter.setPen(QColor(255, 0, 255)); + painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); + painter.end(); + item->setPixmap(pixmap); + } + } + selected_connection_item = connectionItem; + setConnectionEditControlsEnabled(true); + setConnectionEditControlValues(selected_connection_item->connection); + ui->spinBox_ConnectionOffset->setMaximum(selected_connection_item->getMaxOffset()); + ui->spinBox_ConnectionOffset->setMinimum(selected_connection_item->getMinOffset()); + onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); +} + +void Editor::setSelectedConnectionFromMap(QString mapName) { + // Search for the first connection that connects to the given map map. + for (ConnectionPixmapItem* item : connection_edit_items) { + if (item->connection->map_name == mapName) { + onConnectionItemSelected(item); + break; + } + } +} + +void Editor::onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem) { + emit loadMapRequested(connectionItem->connection->map_name, map->name); +} + +void Editor::onConnectionDirectionChanged(QString newDirection) { + ui->comboBox_ConnectionDirection->blockSignals(true); + ui->comboBox_ConnectionDirection->setCurrentText(newDirection); + ui->comboBox_ConnectionDirection->blockSignals(false); +} + +void Editor::setConnectionsVisibility(bool visible) { + for (QGraphicsPixmapItem* item : map->connection_items) { + item->setVisible(visible); + item->setActive(visible); + } } void Editor::setMap(QString map_name) { @@ -205,33 +434,65 @@ DraggablePixmapItem *Editor::addMapObject(Event *event) { } void Editor::displayMapConnections() { + for (QGraphicsPixmapItem* item : map->connection_items) { + delete item; + } + map->connection_items.clear(); + + for (ConnectionPixmapItem* item : connection_edit_items) { + delete item; + } + selected_connection_item = NULL; + connection_edit_items.clear(); + for (Connection *connection : map->connections) { if (connection->direction == "dive" || connection->direction == "emerge") { continue; } - Map *connected_map = project->getMap(connection->map_name); - QPixmap pixmap = connected_map->renderConnection(*connection); - int offset = connection->offset.toInt(nullptr, 0); - int x = 0, y = 0; - if (connection->direction == "up") { - x = offset * 16; - y = -pixmap.height(); - } else if (connection->direction == "down") { - x = offset * 16; - y = map->getHeight() * 16; - } else if (connection->direction == "left") { - x = -pixmap.width(); - y = offset * 16; - } else if (connection->direction == "right") { - x = map->getWidth() * 16; - y = offset * 16; - } - QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); - item->setZValue(-1); - item->setX(x); - item->setY(y); - scene->addItem(item); + createConnectionItem(connection, false); } + + if (!connection_edit_items.empty()) { + onConnectionItemSelected(connection_edit_items.first()); + } +} + +void Editor::createConnectionItem(Connection* connection, bool hide) { + Map *connected_map = project->getMap(connection->map_name); + QPixmap pixmap = connected_map->renderConnection(*connection); + int offset = connection->offset.toInt(nullptr, 0); + int x = 0, y = 0; + if (connection->direction == "up") { + x = offset * 16; + y = -pixmap.height(); + } else if (connection->direction == "down") { + x = offset * 16; + y = map->getHeight() * 16; + } else if (connection->direction == "left") { + x = -pixmap.width(); + y = offset * 16; + } else if (connection->direction == "right") { + x = map->getWidth() * 16; + y = offset * 16; + } + + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + item->setZValue(-1); + item->setX(x); + item->setY(y); + scene->addItem(item); + map->connection_items.append(item); + item->setVisible(!hide); + + ConnectionPixmapItem *connection_edit_item = new ConnectionPixmapItem(pixmap, connection, x, y, map->getWidth(), map->getHeight()); + connection_edit_item->setX(x); + connection_edit_item->setY(y); + connection_edit_item->setZValue(-1); + scene->addItem(connection_edit_item); + connect(connection_edit_item, SIGNAL(connectionMoved(Connection*)), this, SLOT(onConnectionMoved(Connection*))); + connect(connection_edit_item, SIGNAL(connectionItemSelected(ConnectionPixmapItem*)), this, SLOT(onConnectionItemSelected(ConnectionPixmapItem*))); + connect(connection_edit_item, SIGNAL(connectionItemDoubleClicked(ConnectionPixmapItem*)), this, SLOT(onConnectionItemDoubleClicked(ConnectionPixmapItem*))); + connection_edit_items.append(connection_edit_item); } void Editor::displayMapBorder() { @@ -243,6 +504,7 @@ void Editor::displayMapBorder() { item->setY(y * 16); item->setZValue(-2); scene->addItem(item); + borderItems.append(item); } } @@ -252,17 +514,213 @@ void Editor::displayMapGrid() { for (int i = 0; i <= map->getWidth(); i++) { int x = i * 16; QGraphicsLineItem *line = scene->addLine(x, 0, x, pixelHeight); - line->setVisible(gridToggleCheckbox->isChecked()); - connect(gridToggleCheckbox, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); + line->setVisible(ui->checkBox_ToggleGrid->isChecked()); + connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); } for (int j = 0; j <= map->getHeight(); j++) { int y = j * 16; QGraphicsLineItem *line = scene->addLine(0, y, pixelWidth, y); - line->setVisible(gridToggleCheckbox->isChecked()); - connect(gridToggleCheckbox, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); + line->setVisible(ui->checkBox_ToggleGrid->isChecked()); + connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);}); } } +void Editor::updateConnectionOffset(int offset) { + if (!selected_connection_item) + return; + + selected_connection_item->blockSignals(true); + offset = qMin(offset, selected_connection_item->getMaxOffset()); + offset = qMax(offset, selected_connection_item->getMinOffset()); + selected_connection_item->connection->offset = QString::number(offset); + if (selected_connection_item->connection->direction == "up" || selected_connection_item->connection->direction == "down") { + selected_connection_item->setX(selected_connection_item->initialX + (offset - selected_connection_item->initialOffset) * 16); + } else if (selected_connection_item->connection->direction == "left" || selected_connection_item->connection->direction == "right") { + selected_connection_item->setY(selected_connection_item->initialY + (offset - selected_connection_item->initialOffset) * 16); + } + selected_connection_item->blockSignals(false); + updateMirroredConnectionOffset(selected_connection_item->connection); +} + +void Editor::setConnectionMap(QString mapName) { + if (!mapName.isEmpty() && !project->mapNames->contains(mapName)) { + qDebug() << "Invalid map name " << mapName << " specified for connection."; + return; + } + if (!selected_connection_item) + return; + + if (mapName.isEmpty()) { + removeCurrentConnection(); + return; + } + + QString originalMapName = selected_connection_item->connection->map_name; + setConnectionEditControlsEnabled(true); + selected_connection_item->connection->map_name = mapName; + setCurrentConnectionDirection(selected_connection_item->connection->direction); + updateMirroredConnectionMap(selected_connection_item->connection, originalMapName); +} + +void Editor::addNewConnection() { + // Find direction with least number of connections. + QMap directionCounts = QMap({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}}); + for (Connection* connection : map->connections) { + directionCounts[connection->direction]++; + } + QString minDirection = "up"; + int minCount = INT_MAX; + for (QString direction : directionCounts.keys()) { + if (directionCounts[direction] < minCount) { + minDirection = direction; + minCount = directionCounts[direction]; + } + } + + // Don't connect the map to itself. + QString defaultMapName = project->mapNames->first(); + if (defaultMapName == map->name) { + defaultMapName = project->mapNames->value(1); + } + + Connection* newConnection = new Connection; + newConnection->direction = minDirection; + newConnection->offset = "0"; + newConnection->map_name = defaultMapName; + map->connections.append(newConnection); + createConnectionItem(newConnection, true); + onConnectionItemSelected(connection_edit_items.last()); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + + updateMirroredConnection(newConnection, newConnection->direction, newConnection->map_name); +} + +void Editor::updateMirroredConnectionOffset(Connection* connection) { + updateMirroredConnection(connection, connection->direction, connection->map_name); +} +void Editor::updateMirroredConnectionDirection(Connection* connection, QString originalDirection) { + updateMirroredConnection(connection, originalDirection, connection->map_name); +} +void Editor::updateMirroredConnectionMap(Connection* connection, QString originalMapName) { + updateMirroredConnection(connection, connection->direction, originalMapName); +} +void Editor::removeMirroredConnection(Connection* connection) { + updateMirroredConnection(connection, connection->direction, connection->map_name, true); +} +void Editor::updateMirroredConnection(Connection* connection, QString originalDirection, QString originalMapName, bool isDelete) { + if (!ui->checkBox_MirrorConnections->isChecked()) + return; + + static QMap oppositeDirections = QMap({ + {"up", "down"}, {"right", "left"}, + {"down", "up"}, {"left", "right"}, + {"dive", "emerge"},{"emerge", "dive"}}); + QString oppositeDirection = oppositeDirections.value(originalDirection); + + // Find the matching connection in the connected map. + QMap *mapcache = project->map_cache; + Connection* mirrorConnection = NULL; + Map* otherMap = project->getMap(originalMapName); + for (Connection* conn : otherMap->connections) { + if (conn->direction == oppositeDirection && conn->map_name == map->name) { + mirrorConnection = conn; + } + } + + if (isDelete) { + if (mirrorConnection) { + otherMap->connections.removeOne(mirrorConnection); + delete mirrorConnection; + } + return; + } + + if (connection->direction != originalDirection || connection->map_name != originalMapName) { + if (mirrorConnection) { + otherMap->connections.removeOne(mirrorConnection); + delete mirrorConnection; + mirrorConnection = NULL; + otherMap = project->getMap(connection->map_name); + } + } + + // Create a new mirrored connection, if a matching one doesn't already exist. + if (!mirrorConnection) { + mirrorConnection = new Connection; + mirrorConnection->direction = oppositeDirections.value(connection->direction); + mirrorConnection->map_name = map->name; + otherMap->connections.append(mirrorConnection); + } + + mirrorConnection->offset = QString::number(-connection->offset.toInt()); +} + +void Editor::removeCurrentConnection() { + if (!selected_connection_item) + return; + + map->connections.removeOne(selected_connection_item->connection); + connection_edit_items.removeOne(selected_connection_item); + removeMirroredConnection(selected_connection_item->connection); + + scene->removeItem(selected_connection_item); + delete selected_connection_item; + selected_connection_item = NULL; + setConnectionEditControlsEnabled(false); + ui->spinBox_ConnectionOffset->setValue(0); + ui->label_NumConnections->setText(QString::number(map->connections.length())); + + if (connection_edit_items.length() > 0) { + onConnectionItemSelected(connection_edit_items.last()); + } +} + +void Editor::updateDiveMap(QString mapName) { + updateDiveEmergeMap(mapName, "dive"); +} + +void Editor::updateEmergeMap(QString mapName) { + updateDiveEmergeMap(mapName, "emerge"); +} + +void Editor::updateDiveEmergeMap(QString mapName, QString direction) { + if (!mapName.isEmpty() && !project->mapNamesToMapConstants->contains(mapName)) { + qDebug() << "Invalid " << direction << " map connection: " << mapName; + return; + } + + Connection* connection = NULL; + for (Connection* conn : map->connections) { + if (conn->direction == direction) { + connection = conn; + break; + } + } + + if (mapName.isEmpty()) { + // Remove dive/emerge connection + if (connection) { + map->connections.removeOne(connection); + removeMirroredConnection(connection); + } + } else { + if (!connection) { + connection = new Connection; + connection->direction = direction; + connection->offset = "0"; + connection->map_name = mapName; + map->connections.append(connection); + updateMirroredConnection(connection, connection->direction, connection->map_name); + } else { + QString originalMapName = connection->map_name; + connection->map_name = mapName; + updateMirroredConnectionMap(connection, originalMapName); + } + } + + ui->label_NumConnections->setText(QString::number(map->connections.length())); +} + void MetatilesPixmapItem::paintTileChanged(Map *map) { draw(); } @@ -363,6 +821,62 @@ void CollisionMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) { } } +int ConnectionPixmapItem::getMinOffset() { + if (connection->direction == "up" || connection->direction == "down") + return 1 - (this->pixmap().width() / 16); + else + return 1 - (this->pixmap().height() / 16); +} +int ConnectionPixmapItem::getMaxOffset() { + if (connection->direction == "up" || connection->direction == "down") + return baseMapWidth - 1; + else + return baseMapHeight - 1; +} +QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemPositionChange) { + QPointF newPos = value.toPointF(); + + qreal x, y; + int newOffset = initialOffset; + if (connection->direction == "up" || connection->direction == "down") { + x = round(newPos.x() / 16) * 16; + newOffset += (x - initialX) / 16; + newOffset = qMin(newOffset, this->getMaxOffset()); + newOffset = qMax(newOffset, this->getMinOffset()); + x = newOffset * 16; + } + else { + x = initialX; + } + + if (connection->direction == "right" || connection->direction == "left") { + y = round(newPos.y() / 16) * 16; + newOffset += (y - initialY) / 16; + newOffset = qMin(newOffset, this->getMaxOffset()); + newOffset = qMax(newOffset, this->getMinOffset()); + y = newOffset * 16; + } + else { + y = initialY; + } + + connection->offset = QString::number(newOffset); + emit connectionMoved(connection); + return QPointF(x, y); + } + else { + return QGraphicsItem::itemChange(change, value); + } +} +void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent* event) { + emit connectionItemSelected(this); +} +void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) { + emit connectionItemDoubleClicked(this); +} + void ElevationMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) { int x = ((int)pos.x()) / 16; int y = ((int)pos.y()) / 16; diff --git a/editor.h b/editor.h index e8ccfc0c..1d411505 100755 --- a/editor.h +++ b/editor.h @@ -9,10 +9,12 @@ #include #include "project.h" +#include "ui_mainwindow.h" class DraggablePixmapItem; class MapPixmapItem; class CollisionPixmapItem; +class ConnectionPixmapItem; class MetatilesPixmapItem; class CollisionMetatilesPixmapItem; class ElevationMetatilesPixmapItem; @@ -21,12 +23,12 @@ class Editor : public QObject { Q_OBJECT public: - Editor(); + Editor(Ui::MainWindow* ui); public: + Ui::MainWindow* ui; QObject *parent = NULL; Project *project = NULL; Map *map = NULL; - QCheckBox *gridToggleCheckbox = NULL; void saveProject(); void save(); void undo(); @@ -44,6 +46,17 @@ public: void setEditingMap(); void setEditingCollision(); void setEditingObjects(); + void setEditingConnections(); + void setCurrentConnectionDirection(QString curDirection); + void updateCurrentConnectionDirection(QString curDirection); + void setConnectionsVisibility(bool visible); + void updateConnectionOffset(int offset); + void setConnectionMap(QString mapName); + void addNewConnection(); + void removeCurrentConnection(); + void updateDiveMap(QString mapName); + void updateEmergeMap(QString mapName); + void setSelectedConnectionFromMap(QString mapName); DraggablePixmapItem *addMapObject(Event *event); void selectMapObject(DraggablePixmapItem *object); @@ -58,8 +71,11 @@ public: QGraphicsScene *scene = NULL; QGraphicsPixmapItem *current_view = NULL; MapPixmapItem *map_item = NULL; + ConnectionPixmapItem* selected_connection_item = NULL; + QList connection_edit_items; CollisionPixmapItem *collision_item = NULL; QGraphicsItemGroup *objects_group = NULL; + QList borderItems; QGraphicsScene *scene_metatiles = NULL; QGraphicsScene *scene_collision_metatiles = NULL; @@ -77,13 +93,34 @@ public: void objectsView_onMouseMove(QMouseEvent *event); void objectsView_onMouseRelease(QMouseEvent *event); +private: + void setConnectionItemsVisible(bool); + void setBorderItemsVisible(bool, qreal = 1); + void setConnectionEditControlValues(Connection*); + void setConnectionEditControlsEnabled(bool); + void createConnectionItem(Connection* connection, bool hide); + void populateConnectionMapPickers(); + void setDiveEmergeControls(); + void updateDiveEmergeMap(QString mapName, QString direction); + void onConnectionOffsetChanged(int newOffset); + void removeMirroredConnection(Connection*); + void updateMirroredConnectionOffset(Connection*); + void updateMirroredConnectionDirection(Connection*, QString); + void updateMirroredConnectionMap(Connection*, QString); + void updateMirroredConnection(Connection*, QString, QString, bool isDelete = false); + private slots: void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item); void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item); + void onConnectionMoved(Connection*); + void onConnectionItemSelected(ConnectionPixmapItem* connectionItem); + void onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem); + void onConnectionDirectionChanged(QString newDirection); signals: void objectsChanged(); void selectedObjectsChanged(); + void loadMapRequested(QString, QString); }; @@ -240,6 +277,49 @@ protected: void mouseReleaseEvent(QGraphicsSceneMouseEvent*); }; +class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem { + Q_OBJECT +public: + ConnectionPixmapItem(QPixmap pixmap, Connection* connection, int x, int y, int baseMapWidth, int baseMapHeight): QGraphicsPixmapItem(pixmap) { + this->basePixmap = pixmap; + this->connection = connection; + setFlag(ItemIsMovable); + setFlag(ItemSendsGeometryChanges); + this->initialX = x; + this->initialY = y; + this->initialOffset = connection->offset.toInt(); + this->baseMapWidth = baseMapWidth; + this->baseMapHeight = baseMapHeight; + } + void render(qreal opacity = 1) { + QPixmap newPixmap = basePixmap.copy(0, 0, basePixmap.width(), basePixmap.height()); + if (opacity < 1) { + QPainter painter(&newPixmap); + int alpha = (int)(255 * (1 - opacity)); + painter.fillRect(0, 0, newPixmap.width(), newPixmap.height(), QColor(0, 0, 0, alpha)); + painter.end(); + } + this->setPixmap(newPixmap); + } + int getMinOffset(); + int getMaxOffset(); + QPixmap basePixmap; + Connection* connection; + int initialX; + int initialY; + int initialOffset; + int baseMapWidth; + int baseMapHeight; +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + void mousePressEvent(QGraphicsSceneMouseEvent*); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*); +signals: + void connectionItemSelected(ConnectionPixmapItem* connectionItem); + void connectionItemDoubleClicked(ConnectionPixmapItem* connectionItem); + void connectionMoved(Connection*); +}; + class MetatilesPixmapItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: diff --git a/graphicsview.cpp b/graphicsview.cpp index 3e11cee9..361ec3a9 100755 --- a/graphicsview.cpp +++ b/graphicsview.cpp @@ -1,4 +1,5 @@ #include "graphicsview.h" +#include "editor.h" void GraphicsView::mousePressEvent(QMouseEvent *event) { QGraphicsView::mousePressEvent(event); diff --git a/graphicsview.h b/graphicsview.h index 0734715e..1d0711fc 100755 --- a/graphicsview.h +++ b/graphicsview.h @@ -4,7 +4,7 @@ #include #include -#include "editor.h" +class Editor; /* class GraphicsView_Object : public QObject @@ -26,7 +26,7 @@ public: public: // GraphicsView_Object object; - Editor *editor = NULL; + Editor *editor; protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); diff --git a/mainwindow.cpp b/mainwindow.cpp index 464d397d..f1cfa90d 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -26,10 +26,10 @@ MainWindow::MainWindow(QWidget *parent) : ui->setupUi(this); new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo())); - editor = new Editor; - editor->gridToggleCheckbox = ui->checkBox_ToggleGrid; + editor = new Editor(ui); connect(editor, SIGNAL(objectsChanged()), this, SLOT(updateSelectedObjects())); connect(editor, SIGNAL(selectedObjectsChanged()), this, SLOT(updateSelectedObjects())); + connect(editor, SIGNAL(loadMapRequested(QString, QString)), this, SLOT(onLoadMapRequested(QString, QString))); on_toolButton_Paint_clicked(); @@ -143,15 +143,7 @@ void MainWindow::setMap(QString map_name) { } editor->setMap(map_name); - if (ui->tabWidget->currentIndex() == 1) { - editor->setEditingObjects(); - } else { - if (ui->tabWidget_2->currentIndex() == 1) { - editor->setEditingCollision(); - } else { - editor->setEditingMap(); - } - } + on_tabWidget_currentChanged(ui->tabWidget->currentIndex()); ui->graphicsView_Map->setScene(editor->scene); ui->graphicsView_Map->setSceneRect(editor->scene->sceneRect()); @@ -162,6 +154,10 @@ void MainWindow::setMap(QString map_name) { ui->graphicsView_Objects_Map->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2); ui->graphicsView_Objects_Map->editor = editor; + ui->graphicsView_Connections->setScene(editor->scene); + ui->graphicsView_Connections->setSceneRect(editor->scene->sceneRect()); + ui->graphicsView_Connections->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2); + ui->graphicsView_Metatiles->setScene(editor->scene_metatiles); //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect()); ui->graphicsView_Metatiles->setFixedSize(editor->metatiles_item->pixmap().width() + 2, editor->metatiles_item->pixmap().height() + 2); @@ -294,6 +290,7 @@ void MainWindow::loadDataStructures() { project->readItemNames(); project->readFlagNames(); project->readVarNames(); + project->readMapsWithConnections(); } void MainWindow::populateMapList() { @@ -491,6 +488,8 @@ void MainWindow::on_tabWidget_currentChanged(int index) on_tabWidget_2_currentChanged(ui->tabWidget_2->currentIndex()); } else if (index == 1) { editor->setEditingObjects(); + } else if (index == 3) { + editor->setEditingConnections(); } } @@ -756,6 +755,11 @@ void MainWindow::checkToolButtons() { ui->toolButton_Dropper->setChecked(editor->map_edit_mode == "pick"); } +void MainWindow::onLoadMapRequested(QString mapName, QString fromMapName) { + setMap(mapName); + editor->setSelectedConnectionFromMap(fromMapName); +} + void MainWindow::onMapChanged(Map *map) { updateMapList(); } @@ -768,3 +772,38 @@ void MainWindow::on_action_Export_Map_Image_triggered() editor->map_item->pixmap().save(filepath); } } + +void MainWindow::on_comboBox_ConnectionDirection_currentIndexChanged(const QString &direction) +{ + editor->updateCurrentConnectionDirection(direction); +} + +void MainWindow::on_spinBox_ConnectionOffset_valueChanged(int offset) +{ + editor->updateConnectionOffset(offset); +} + +void MainWindow::on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName) +{ + editor->setConnectionMap(mapName); +} + +void MainWindow::on_pushButton_AddConnection_clicked() +{ + editor->addNewConnection(); +} + +void MainWindow::on_pushButton_RemoveConnection_clicked() +{ + editor->removeCurrentConnection(); +} + +void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) +{ + editor->updateDiveMap(mapName); +} + +void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName) +{ + editor->updateEmergeMap(mapName); +} diff --git a/mainwindow.h b/mainwindow.h index e1b443a1..5c01c2da 100755 --- a/mainwindow.h +++ b/mainwindow.h @@ -36,6 +36,7 @@ private slots: void undo(); void redo(); + void onLoadMapRequested(QString, QString); void onMapChanged(Map *map); void on_action_Save_triggered(); @@ -74,6 +75,20 @@ private slots: void on_action_Export_Map_Image_triggered(); + void on_comboBox_ConnectionDirection_currentIndexChanged(const QString &arg1); + + void on_spinBox_ConnectionOffset_valueChanged(int offset); + + void on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName); + + void on_pushButton_AddConnection_clicked(); + + void on_pushButton_RemoveConnection_clicked(); + + void on_comboBox_DiveMap_currentTextChanged(const QString &mapName); + + void on_comboBox_EmergeMap_currentTextChanged(const QString &mapName); + private: Ui::MainWindow *ui; QStandardItemModel *mapListModel; diff --git a/mainwindow.ui b/mainwindow.ui index 8aadf19d..e6fa825c 100755 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1199,6 +1199,500 @@ + + + Connections + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + + + + + :/icons/add.ico + + + + + + + + + + + + :/icons/delete.ico + + + + + + + + Number of Connections: + + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Mirror + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 32 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + up + + + + + right + + + + + down + + + + + left + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Map + + + + + + + true + + + + + + + Offset + + + + + + + -999 + + + 999 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 826 + 557 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 0 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Dive Map + + + + + + + true + + + + + + + Emerge Map + + + + + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + diff --git a/map.h b/map.h index 6d97ac69..67950b3b 100755 --- a/map.h +++ b/map.h @@ -8,6 +8,7 @@ #include #include #include +#include template @@ -181,6 +182,7 @@ public: QMap> events; QList connections; + QList connection_items; QPixmap renderConnection(Connection); QImage border_image; diff --git a/parseutil.h b/parseutil.h index aec0bec4..dfd52c16 100755 --- a/parseutil.h +++ b/parseutil.h @@ -3,6 +3,7 @@ #include #include +#include enum TokenType { Number, diff --git a/project.cpp b/project.cpp index fd54f6a9..cbf55183 100755 --- a/project.cpp +++ b/project.cpp @@ -39,7 +39,8 @@ Map* Project::loadMap(QString map_name) { Map *map; if (map_cache->contains(map_name)) { map = map_cache->value(map_name); - if (map->hasUnsavedChanges()) { + // TODO: uncomment when undo/redo history is fully implemented for all actions. + if (true/*map->hasUnsavedChanges()*/) { return map; } } else { @@ -67,6 +68,7 @@ void Project::loadMapConnections(Map *map) { } map->connections.clear(); + map->connection_items.clear(); if (!map->connections_label.isNull()) { QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name); QString text = readTextFile(path); @@ -196,7 +198,14 @@ void Project::saveMapHeader(Map *map) { text += QString("\t.4byte %1\n").arg(map->attributes_label); text += QString("\t.4byte %1\n").arg(map->events_label); text += QString("\t.4byte %1\n").arg(map->scripts_label); + + if (map->connections.length() == 0) { + map->connections_label = "0x0"; + } else { + map->connections_label = QString("%1_MapConnections").arg(map->name); + } text += QString("\t.4byte %1\n").arg(map->connections_label); + text += QString("\t.2byte %1\n").arg(map->song); text += QString("\t.2byte %1\n").arg(map->index); text += QString("\t.byte %1\n").arg(map->location); @@ -209,6 +218,48 @@ void Project::saveMapHeader(Map *map) { saveTextFile(header_path, text); } +void Project::saveMapConnections(Map *map) { + QString path = root + "/data/maps/" + map->name + "/connections.inc"; + if (map->connections.length() > 0) { + QString text = ""; + QString connectionsListLabel = QString("%1_MapConnectionsList").arg(map->name); + int numValidConnections = 0; + text += QString("%1::\n").arg(connectionsListLabel); + for (Connection* connection : map->connections) { + if (mapNamesToMapConstants->contains(connection->map_name)) { + text += QString("\tconnection %1, %2, %3\n") + .arg(connection->direction) + .arg(connection->offset) + .arg(mapNamesToMapConstants->value(connection->map_name)); + numValidConnections++; + } else { + qDebug() << QString("Failed to write map connection. %1 not a valid map name").arg(connection->map_name); + } + } + text += QString("\n"); + text += QString("%1::\n").arg(map->connections_label); + text += QString("\t.4byte %1\n").arg(numValidConnections); + text += QString("\t.4byte %1\n").arg(connectionsListLabel); + saveTextFile(path, text); + } else { + deleteFile(path); + } + + updateMapsWithConnections(map); +} + +void Project::updateMapsWithConnections(Map *map) { + if (map->connections.length() > 0) { + if (!mapsWithConnections.contains(map->name)) { + mapsWithConnections.append(map->name); + } + } else { + if (mapsWithConnections.contains(map->name)) { + mapsWithConnections.removeOne(map->name); + } + } +} + void Project::readMapAttributesTable() { int curMapIndex = 1; QString attributesText = readTextFile(getMapAttributesTableFilepath()); @@ -679,6 +730,7 @@ void Project::saveMap(Map *map) { saveMapBorder(map); saveMapHeader(map); + saveMapConnections(map); saveBlockdata(map); saveMapEvents(map); @@ -689,7 +741,9 @@ void Project::saveMap(Map *map) { } void Project::updateMapAttributes(Map* map) { - mapAttributesTableMaster.insert(map->index.toInt(nullptr, 0), map->name); + if (!mapAttributesTableMaster.contains(map->index.toInt())) { + mapAttributesTableMaster.insert(map->index.toInt(), map->name); + } // Deep copy QMap attrs = mapAttributes.value(map->name); @@ -702,6 +756,7 @@ void Project::saveAllDataStructures() { saveAllMapAttributes(); saveMapGroupsTable(); saveMapConstantsHeader(); + saveMapsWithConnections(); } void Project::loadTilesetAssets(Tileset* tileset) { @@ -913,6 +968,13 @@ void Project::appendTextFile(QString path, QString text) { } } +void Project::deleteFile(QString path) { + QFile file(path); + if (file.exists() && !file.remove()) { + qDebug() << QString("Could not delete file '%1': ").arg(path) + file.errorString(); + } +} + void Project::readMapGroups() { QString text = readTextFile(root + "/data/maps/_groups.inc"); if (text.isNull()) { @@ -1098,6 +1160,41 @@ void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QString } } +void Project::readMapsWithConnections() { + QString path = root + "/data/maps/connections.inc"; + QString text = readTextFile(path); + if (text.isNull()) { + return; + } + + mapsWithConnections.clear(); + QRegularExpression re("data\\/maps\\/(?\\w+)\\/connections.inc"); + QList* includes = parseAsm(text); + for (QStringList values : *includes) { + if (values.length() != 2) + continue; + + QRegularExpressionMatch match = re.match(values.value(1)); + if (match.hasMatch()) { + QString mapName = match.captured("mapName"); + mapsWithConnections.append(mapName); + } + } +} + +void Project::saveMapsWithConnections() { + QString path = root + "/data/maps/connections.inc"; + QString text = ""; + for (QString mapName : mapsWithConnections) { + if (mapNamesToMapConstants->contains(mapName)) { + text += QString("\t.include \"data/maps/%1/connections.inc\"\n").arg(mapName); + } else { + qDebug() << QString("Failed to write connection include. %1 not a valid map name").arg(mapName); + } + } + saveTextFile(path, text); +} + QStringList Project::getSongNames() { QStringList names; QString text = readTextFile(root + "/include/constants/songs.h"); @@ -1164,7 +1261,6 @@ void Project::loadObjectPixmaps(QList objects) { } if (event_type == "object") { - int sprite_id = constants.value(object->get("sprite")); QString info_label = pointers.value(sprite_id).replace("&", ""); diff --git a/project.h b/project.h index 43736c2a..58502c2f 100755 --- a/project.h +++ b/project.h @@ -26,6 +26,7 @@ public: QStringList *itemNames = NULL; QStringList *flagNames = NULL; QStringList *varNames = NULL; + QStringList mapsWithConnections; QMap *map_cache; Map* loadMap(QString); @@ -41,6 +42,7 @@ public: QString readTextFile(QString path); void saveTextFile(QString path, QString text); void appendTextFile(QString path, QString text); + void deleteFile(QString path); void readMapGroups(); Map* addNewMapToGroup(QString mapName, int groupNum); @@ -53,6 +55,7 @@ public: void readMapAttributesTable(); void readAllMapAttributes(); void readMapAttributes(Map*); + void readMapsWithConnections(); void getTilesets(Map*); void loadTilesetAssets(Tileset*); @@ -97,6 +100,9 @@ private: QString getMapAttributesTableFilepath(); QString getMapAssetsFilepath(); void saveMapHeader(Map*); + void saveMapConnections(Map*); + void updateMapsWithConnections(Map*); + void saveMapsWithConnections(); void saveMapAttributesTable(); void updateMapAttributes(Map* map); void readCDefinesSorted(QString, QStringList, QStringList*);