Take advantage of MapConnection as QObject

This commit is contained in:
GriffinR 2024-08-04 19:11:29 -04:00
parent b5c7f9f86b
commit 4af1c4d463
14 changed files with 374 additions and 382 deletions

View file

@ -98,6 +98,8 @@ public:
QStringList getScriptLabels(Event::Group group = Event::Group::None);
void removeEvent(Event *);
void addEvent(Event *);
bool removeConnection(MapConnection *);
void addConnection(MapConnection *);
QPixmap renderConnection(const QString &, MapLayout *);
QPixmap renderBorder(bool ignoreCache = false);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
@ -132,6 +134,7 @@ signals:
void mapDimensionsChanged(const QSize &size);
void mapNeedsRedrawing();
void openScriptRequested(QString label);
void connectionAdded(MapConnection*);
};
#endif // MAP_H

View file

@ -4,6 +4,9 @@
#include <QString>
#include <QObject>
#include <QMap>
class Project;
class MapConnection : public QObject
{
@ -23,19 +26,32 @@ public:
int offset() const { return m_offset; }
void setOffset(int offset);
MapConnection * createMirror();
bool isMirror(const MapConnection*);
MapConnection* findMirror();
MapConnection* createMirror();
QPixmap getPixmap();
static QPointer<Project> project;
static const QMap<QString, QString> oppositeDirections;
static const QStringList cardinalDirections;
static bool isCardinal(const QString &direction);
static bool isHorizontal(const QString &direction);
static bool isVertical(const QString &direction);
static QString oppositeDirection(const QString &direction) { return oppositeDirections.value(direction, direction); }
private:
QString m_direction;
QString m_hostMapName;
QString m_targetMapName;
int m_offset;
bool m_ignoreMirror;
void mirrorDirection(const QString &direction);
void mirrorHostMapName(const QString &hostMapName);
void mirrorTargetMapName(const QString &targetMapName);
void mirrorOffset(int offset);
void markMapEdited();
signals:
void directionChanged(const QString &before, const QString &after);

View file

@ -46,6 +46,7 @@ public:
QPointer<Project> project = nullptr;
Map *map = nullptr;
Settings *settings;
void setProject(Project * project);
void saveProject();
void save();
void closeProject();
@ -78,8 +79,8 @@ public:
void updateDiveEmergeVisibility();
void addNewConnection();
void addConnection(MapConnection* connection, bool addMirror = true);
void removeConnection(MapConnection* connection, bool removeMirror = true);
void removeConnectionItem(ConnectionPixmapItem* connectionItem);
void removeConnection(MapConnection* connection, bool addMirror = true);
void removeConnectionPixmap(ConnectionPixmapItem* connectionItem);
void removeSelectedConnection();
void addNewWildMonGroup(QWidget *window);
void deleteWildMonGroup();
@ -186,15 +187,11 @@ private:
void clearWildMonTables();
void updateBorderVisibility();
QPoint calculateConnectionPosition(MapConnection *connection, const QPixmap &pixmap);
QPixmap getConnectionPixmap(MapConnection *connection);
void updateConnectionItem(ConnectionPixmapItem* connectionItem);
void updateConnectionItemPos(ConnectionPixmapItem* connectionItem);
void createConnectionItem(MapConnection* connection);
void createDiveEmergeConnection(MapConnection* connection);
void updateConnectionPixmap(ConnectionPixmapItem* connectionItem);
void updateConnectionPixmapPos(ConnectionPixmapItem* connectionItem);
void displayConnection(MapConnection* connection);
void displayDiveEmergeConnection(MapConnection* connection);
void setDiveEmergeMapName(QString mapName, QString direction);
MapConnection* getMirroredConnection(MapConnection*);
void addMirroredConnection(MapConnection*);
void removeMirroredConnection(MapConnection*);
void updateEncounterFields(EncounterFields newFields);
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId);
@ -210,9 +207,6 @@ private slots:
void setStraightPathCursorMode(QGraphicsSceneMouseEvent *event);
void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);
void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item);
void setConnectionOffset(MapConnection *connection, int offset);
void setConnectionMap(MapConnection *connection, const QString &mapName);
void setConnectionDirection(MapConnection *connection, const QString &direction);
void setSelectedConnection(ConnectionPixmapItem* connectionItem);
void onHoveredMovementPermissionChanged(uint16_t, uint16_t);
void onHoveredMovementPermissionCleared();
@ -225,15 +219,15 @@ private slots:
void onSelectedMetatilesChanged();
void onWheelZoom(int);
void onToggleGridClicked(bool);
void onMapConnectionDoubleClicked(MapConnection*);
signals:
void objectsChanged();
void connectionItemDoubleClicked(QString, QString);
void openConnectedMap(QString, QString);
void wildMonDataChanged();
void warpEventDoubleClicked(QString, int, Event::Group);
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void editedMapData(Map*);
void tilesetUpdated(QString);
};

View file

@ -178,13 +178,14 @@ private slots:
void copy();
void paste();
void onConnectionItemDoubleClicked(QString, QString);
void onOpenConnectedMap(QString, QString);
void onMapNeedsRedrawing();
void onTilesetsSaved(QString, QString);
void onWildMonDataChanged();
void openNewMapPopupWindow();
void onNewMapCreated();
void onMapCacheCleared();
void onMapLoaded(Map *map);
void importMapFromAdvanceMap1_92();
void onMapRulerStatusChanged(const QString &);
void applyUserShortcuts();

View file

@ -257,6 +257,7 @@ signals:
void reloadProject();
void uncheckMonitorFilesAction();
void mapCacheCleared();
void mapLoaded(Map *map);
};
#endif // PROJECT_H

View file

@ -4,6 +4,7 @@
#include "mapconnection.h"
#include <QGraphicsPixmapItem>
#include <QPainter>
#include <QPointer>
class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
@ -18,11 +19,10 @@ public:
this->initialX = x;
this->initialY = y;
this->initialOffset = connection->offset();
this->setX(x);
this->setY(y);
this->setPos(x, y);
}
QPixmap basePixmap;
MapConnection* connection;
QPointer<MapConnection> connection;
int initialX;
int initialY;
int initialOffset;
@ -41,8 +41,7 @@ protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*);
signals:
void connectionItemDoubleClicked(ConnectionPixmapItem* connectionItem);
void connectionMoved(MapConnection *, int newOffset);
void connectionItemDoubleClicked(MapConnection*);
void selectionChanged(bool selected);
};

View file

@ -5,6 +5,7 @@
#include <QFrame>
#include <QMouseEvent>
#include <QPointer>
namespace Ui {
class ConnectionsListItem;
@ -25,7 +26,7 @@ public:
void updateUI();
void setSelected(bool selected);
MapConnection * connection;
QPointer<MapConnection> connection;
private:
Ui::ConnectionsListItem *ui;
@ -36,12 +37,9 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *);
signals:
void editedOffset(MapConnection *connection, int newOffset);
void editedDirection(MapConnection *connection, const QString &direction);
void editedMapName(MapConnection *connection, const QString &mapName);
void removed();
void removed(MapConnection*);
void selected();
void doubleClicked(const QString &mapName);
void doubleClicked(MapConnection*);
private slots:
void on_comboBox_Direction_currentTextChanged(const QString &direction);

View file

@ -218,6 +218,9 @@ QPixmap Map::renderBorder(bool ignoreCache) {
}
QPixmap Map::renderConnection(const QString &direction, MapLayout * fromLayout) {
if (direction == "dive" || direction == "emerge")
return render();
if (!MapConnection::isCardinal(direction))
return QPixmap();
@ -510,6 +513,22 @@ void Map::addEvent(Event *event) {
if (!ownedEvents.contains(event)) ownedEvents.append(event);
}
bool Map::removeConnection(MapConnection *connection) {
if (connections.removeOne(connection)) {
modify();
return true;
}
return false;
}
void Map::addConnection(MapConnection *connection) {
if (!connection || connections.contains(connection))
return;
connections.append(connection);
modify();
emit connectionAdded(connection);
}
void Map::modify() {
emit modified();
}

View file

@ -1,7 +1,9 @@
#include <QMap>
#include "mapconnection.h"
#include "project.h"
const QMap<QString, QString> oppositeDirections = {
QPointer<Project> MapConnection::project = nullptr;
const QMap<QString, QString> MapConnection::oppositeDirections = {
{"up", "down"}, {"down", "up"},
{"right", "left"}, {"left", "right"},
{"dive", "emerge"}, {"emerge", "dive"}
@ -12,47 +14,7 @@ MapConnection::MapConnection(const QString &direction, const QString &hostMapNam
m_hostMapName = hostMapName;
m_targetMapName = targetMapName;
m_offset = offset;
}
void MapConnection::setDirection(const QString &direction) {
if (direction == m_direction)
return;
auto before = m_direction;
m_direction = direction;
emit directionChanged(before, m_direction);
}
void MapConnection::setHostMapName(const QString &hostMapName) {
if (hostMapName == m_hostMapName)
return;
auto before = m_hostMapName;
m_hostMapName = hostMapName;
emit hostMapNameChanged(before, m_hostMapName);
}
void MapConnection::setTargetMapName(const QString &targetMapName) {
if (targetMapName == m_targetMapName)
return;
auto before = m_targetMapName;
m_targetMapName = targetMapName;
emit targetMapNameChanged(before, m_targetMapName);
}
void MapConnection::setOffset(int offset) {
if (offset == m_offset)
return;
auto before = m_offset;
m_offset = offset;
emit offsetChanged(before, m_offset);
}
MapConnection * MapConnection::createMirror() {
return new MapConnection(oppositeDirections.value(m_direction, m_direction), m_targetMapName, m_hostMapName, -m_offset);
m_ignoreMirror = false;
}
bool MapConnection::isMirror(const MapConnection* other) {
@ -62,7 +24,138 @@ bool MapConnection::isMirror(const MapConnection* other) {
return m_hostMapName == other->m_targetMapName
&& m_targetMapName == other->m_hostMapName
&& m_offset == -other->m_offset
&& m_direction == oppositeDirections.value(other->m_direction, other->m_direction);
&& m_direction == oppositeDirection(other->m_direction);
}
MapConnection* MapConnection::findMirror() {
if (!porymapConfig.mirrorConnectingMaps || !project || m_ignoreMirror)
return nullptr;
Map *connectedMap = project->getMap(m_targetMapName);
if (!connectedMap)
return nullptr;
// Find the matching connection in the connected map.
// Note: There is no strict source -> mirror pairing, i.e. we are not guaranteed
// to always get the same MapConnection if there are multiple identical copies.
for (auto connection : connectedMap->connections) {
if (this != connection && this->isMirror(connection))
return connection;
}
return nullptr;
}
MapConnection * MapConnection::createMirror() {
if (!porymapConfig.mirrorConnectingMaps)
return nullptr;
return new MapConnection(oppositeDirection(m_direction), m_targetMapName, m_hostMapName, -m_offset);
}
void MapConnection::markMapEdited() {
if (project) {
Map * map = project->getMap(m_hostMapName);
if (map) map->modify();
}
}
QPixmap MapConnection::getPixmap() {
if (!project)
return QPixmap();
Map *hostMap = project->getMap(m_hostMapName);
Map *targetMap = project->getMap(m_targetMapName);
if (!hostMap || !targetMap)
return QPixmap();
return targetMap->renderConnection(m_direction, hostMap->layout);
}
void MapConnection::setDirection(const QString &direction) {
if (direction == m_direction)
return;
mirrorDirection(direction);
auto before = m_direction;
m_direction = direction;
emit directionChanged(before, m_direction);
markMapEdited();
}
void MapConnection::mirrorDirection(const QString &direction) {
MapConnection * mirror = findMirror();
if (!mirror)
return;
mirror->m_ignoreMirror = true;
mirror->setDirection(oppositeDirection(direction));
mirror->m_ignoreMirror = false;
}
void MapConnection::setHostMapName(const QString &hostMapName) {
if (hostMapName == m_hostMapName)
return;
mirrorHostMapName(hostMapName);
auto before = m_hostMapName;
m_hostMapName = hostMapName;
if (project) {
// TODO: This is probably unexpected design, esp because creating a MapConnection doesn't insert to host map
// Reassign connection to new host map
Map * oldMap = project->getMap(before);
Map * newMap = project->getMap(m_hostMapName);
if (oldMap) oldMap->removeConnection(this);
if (newMap) newMap->addConnection(this);
}
emit hostMapNameChanged(before, m_hostMapName);
}
void MapConnection::mirrorHostMapName(const QString &hostMapName) {
MapConnection * mirror = findMirror();
if (!mirror)
return;
mirror->m_ignoreMirror = true;
mirror->setTargetMapName(hostMapName);
mirror->m_ignoreMirror = false;
}
void MapConnection::setTargetMapName(const QString &targetMapName) {
if (targetMapName == m_targetMapName)
return;
mirrorTargetMapName(targetMapName);
auto before = m_targetMapName;
m_targetMapName = targetMapName;
emit targetMapNameChanged(before, m_targetMapName);
markMapEdited();
}
void MapConnection::mirrorTargetMapName(const QString &targetMapName) {
MapConnection * mirror = findMirror();
if (!mirror)
return;
mirror->m_ignoreMirror = true;
mirror->setHostMapName(targetMapName);
mirror->m_ignoreMirror = false;
}
void MapConnection::setOffset(int offset) {
if (offset == m_offset)
return;
mirrorOffset(offset);
auto before = m_offset;
m_offset = offset;
emit offsetChanged(before, m_offset);
markMapEdited();
}
void MapConnection::mirrorOffset(int offset) {
MapConnection * mirror = findMirror();
if (!mirror)
return;
mirror->m_ignoreMirror = true;
mirror->setOffset(-offset);
mirror->m_ignoreMirror = false;
}
const QStringList MapConnection::cardinalDirections = {

View file

@ -78,6 +78,14 @@ void Editor::saveUiFields() {
saveEncounterTabData();
}
void Editor::setProject(Project * project) {
if (this->project) {
closeProject();
}
this->project = project;
MapConnection::project = project;
}
void Editor::closeProject() {
if (!this->project)
return;
@ -725,70 +733,83 @@ void Editor::updateEncounterFields(EncounterFields newFields) {
project->wildMonFields = newFields;
}
void Editor::createConnectionItem(MapConnection* connection) {
void Editor::displayConnection(MapConnection* connection) {
if (!connection)
return;
// It's possible for a map connection to be displayed repeatedly if it's being
// updated by mirroring and the target map is changing to/from the current map.
connection->disconnect();
if (connection->direction() == "dive" || connection->direction() == "emerge") {
displayDiveEmergeConnection(connection);
return;
}
// Create connection image
QPixmap pixmap = getConnectionPixmap(connection);
QPixmap pixmap = connection->getPixmap();
QPoint pos = calculateConnectionPosition(connection, pixmap);
ConnectionPixmapItem *connectionItem = new ConnectionPixmapItem(pixmap, connection, pos.x(), pos.y());
connectionItem->render();
scene->addItem(connectionItem);
ConnectionPixmapItem *pixmapItem = new ConnectionPixmapItem(pixmap, connection, pos.x(), pos.y());
pixmapItem->render();
scene->addItem(pixmapItem);
// Create item for the list panel
ConnectionsListItem *listItem = new ConnectionsListItem(ui->scrollAreaContents_ConnectionsList, connectionItem->connection, project->mapNames);
ConnectionsListItem *listItem = new ConnectionsListItem(ui->scrollAreaContents_ConnectionsList, pixmapItem->connection, project->mapNames);
ui->layout_ConnectionsList->insertWidget(ui->layout_ConnectionsList->count() - 1, listItem); // Insert above the vertical spacer
connect(connectionItem, &ConnectionPixmapItem::selectionChanged, [this, connectionItem](bool selected) {
if (selected) setSelectedConnection(connectionItem);
// Double clicking the list item or pixmap opens the connected map
connect(listItem, &ConnectionsListItem::doubleClicked, this, &Editor::onMapConnectionDoubleClicked);
connect(pixmapItem, &ConnectionPixmapItem::connectionItemDoubleClicked, this, &Editor::onMapConnectionDoubleClicked);
// Sync the selection highlight between the list UI and the pixmap
connect(pixmapItem, &ConnectionPixmapItem::selectionChanged, [this, listItem, pixmapItem](bool selected) {
listItem->setSelected(selected);
if (selected) setSelectedConnection(pixmapItem);
});
connect(connectionItem, &ConnectionPixmapItem::connectionItemDoubleClicked, [this, connectionItem] {
emit this->connectionItemDoubleClicked(connectionItem->connection->targetMapName(), map->name);
connect(listItem, &ConnectionsListItem::selected, [this, pixmapItem] {
setSelectedConnection(pixmapItem);
});
// Sync the selection highlight between the list UI and the graphical map connection
connect(connectionItem, &ConnectionPixmapItem::selectionChanged, listItem, &ConnectionsListItem::setSelected);
connect(listItem, &ConnectionsListItem::selected, [this, connectionItem] {
setSelectedConnection(connectionItem);
});
// Sync edits to 'offset' between the list UI and the graphical map connection
connect(connectionItem, &ConnectionPixmapItem::connectionMoved, [this, listItem](MapConnection* connection, int offset) {
setConnectionOffset(connection, offset);
// Sync edits to 'offset' between the list UI and the pixmap
connect(connection, &MapConnection::offsetChanged, [this, listItem, pixmapItem](int, int) {
listItem->updateUI();
});
connect (listItem, &ConnectionsListItem::editedOffset, [this, connectionItem](MapConnection* connection, int offset) {
setConnectionOffset(connection, offset);
updateConnectionItemPos(connectionItem);
updateConnectionPixmapPos(pixmapItem);
});
// Sync edits to 'direction' or 'map' between the list UI and the graphical map connection.
// These are 1-way because the direction and map cannot be edited graphically, only via the list UI.
connect(listItem, &ConnectionsListItem::editedDirection, [this, connectionItem, listItem](MapConnection* connection, QString direction) {
setConnectionDirection(connection, direction);
updateConnectionItem(connectionItem); // TODO: Simplify
listItem->updateUI(); // Changing direction may have changed our offset too
});
connect(listItem, &ConnectionsListItem::editedMapName, [this, connectionItem](MapConnection* connection, QString mapName) {
setConnectionMap(connection, mapName);
updateConnectionItem(connectionItem);
// Sync edits to 'direction' between the list UI and the pixmap
connect(connection, &MapConnection::directionChanged, [this, listItem, pixmapItem](QString before, QString after) {
if (MapConnection::isHorizontal(before) != MapConnection::isHorizontal(after)
|| MapConnection::isVertical(before) != MapConnection::isVertical(after)) {
// If the direction has changed between vertical/horizontal then the old offset may not make sense, so we reset it
pixmapItem->connection->setOffset(0);
}
listItem->updateUI();
updateConnectionPixmap(pixmapItem);
});
// Sync deleting the map connection
connect(connectionItem, &ConnectionPixmapItem::destroyed, listItem, &ConnectionsListItem::deleteLater);
connect(listItem, &ConnectionsListItem::removed, [this, connectionItem] {
// 'Remove' button has been clicked. Delete the pixmap.
// The list item will be deleted via the above connection to the pixmap's 'destroyed' signal.
removeConnectionItem(connectionItem);
// Sync edits to 'map' between the list UI and the pixmap
connect(connection, &MapConnection::targetMapNameChanged, [this, listItem, pixmapItem](QString, QString) {
listItem->updateUI();
updateConnectionPixmap(pixmapItem);
});
// Double clicking the list item opens the connected map
connect(listItem, &ConnectionsListItem::doubleClicked, [this](QString connectedMapName) {
emit connectionItemDoubleClicked(connectedMapName, map->name);
connect(connection, &MapConnection::hostMapNameChanged, [this, pixmapItem](QString before, QString) {
// A connection was removed from the current map by reassignment. We're deleting the UI elements,
// but not the associated connection (it still exists, but now it belongs to a different map).
// At the moment this is only possible when updating the mirror for a map connected to itself,
// users have no other way (or need) to relocate a map connection like this.
if (before == this->map->name)
removeConnectionPixmap(pixmapItem);
});
connection_items.append(connectionItem);
// User manually deleting connection via the remove button
connect(listItem, &ConnectionsListItem::removed, [this](MapConnection* connection) { removeConnection(connection); });
// Sync removing the UI elements. They may be removed when deleting connections or when displaying a new map.
connect(connection, &MapConnection::destroyed, [this, pixmapItem] { removeConnectionPixmap(pixmapItem); });
connect(pixmapItem, &ConnectionPixmapItem::destroyed, listItem, &ConnectionsListItem::deleteLater);
connection_items.append(pixmapItem);
}
@ -821,33 +842,38 @@ void Editor::addConnection(MapConnection * connection, bool addMirror) {
if (!connection)
return;
if (addMirror)
addMirroredConnection(connection);
if (addMirror) addConnection(connection->createMirror(), false);
Map *map = project->getMap(connection->hostMapName());
if (!map)
return;
map->connections.append(connection);
if (map == this->map) {
// Adding a connection to the current map, we need to display it visually.
if (connection->direction() == "dive" || connection->direction() == "emerge") {
createDiveEmergeConnection(connection);
} else {
createConnectionItem(connection);
}
}
emit editedMapData(map);
if (map) map->addConnection(connection);
// TODO: Edit history
}
void Editor::removeConnectionItem(ConnectionPixmapItem* connectionItem) {
if (!connectionItem)
void Editor::removeConnection(MapConnection* connection, bool removeMirror) {
if (!connection)
return;
int index = connection_items.indexOf(connectionItem);
if (removeMirror) removeConnection(connection->findMirror(), false);
Map* map = project->getMap(connection->hostMapName());
if (map) map->removeConnection(connection);
delete connection;
// TODO: Edit history
}
void Editor::removeSelectedConnection() {
if (selected_connection_item)
removeConnection(selected_connection_item->connection);
}
void Editor::removeConnectionPixmap(ConnectionPixmapItem* pixmapItem) {
if (!pixmapItem)
return;
int index = connection_items.indexOf(pixmapItem);
if (index >= 0) {
connection_items.removeAt(index);
if (connectionItem == selected_connection_item) {
if (pixmapItem == selected_connection_item) {
// This was the selected connection, select the next one up in the list.
selected_connection_item = nullptr;
if (index != 0) index--;
@ -856,117 +882,53 @@ void Editor::removeConnectionItem(ConnectionPixmapItem* connectionItem) {
}
}
if (connectionItem->scene())
connectionItem->scene()->removeItem(connectionItem);
if (pixmapItem->scene())
pixmapItem->scene()->removeItem(pixmapItem);
removeConnection(connectionItem->connection);
delete connectionItem;
}
void Editor::removeSelectedConnection() {
removeConnectionItem(selected_connection_item);
}
void Editor::removeConnection(MapConnection* connection, bool removeMirror) {
if (!connection)
return;
if (removeMirror)
removeMirroredConnection(connection);
Map* map = project->getMap(connection->hostMapName());
if (!map)
return;
if (map == this->map) {
// The connection to delete is displayed on the currently-opened map, we need to delete it visually as well.
if (connection->direction() == "dive") {
clearDiveMap();
} else if (connection->direction() == "emerge") {
clearEmergeMap();
} else {
// Find and delete the matching connection graphics.
// If this was called via removeConnectionItem we rely on
// the item having already been removed from this list.
for (auto item :connection_items) {
if (item->connection == connection) {
item->connection = nullptr;
removeConnectionItem(item);
break;
}
}
}
}
map->connections.removeOne(connection);
delete connection;
emit editedMapData(map);
}
MapConnection* Editor::getMirroredConnection(MapConnection* source) {
if (!source || !porymapConfig.mirrorConnectingMaps)
return nullptr;
// Note: It's possible (and ok) for connectedMap == this->map
Map *connectedMap = project->getMap(source->targetMapName());
if (!connectedMap)
return nullptr;
// Find the matching connection in the connected map.
// Note: There is no strict source -> mirror pairing, i.e. we are not guaranteed
// to always get the same MapConnection if there are multiple identical copies.
for (auto connection : connectedMap->connections) {
if (connection != source && connection->isMirror(source))
return connection;
}
return nullptr;
}
void Editor::addMirroredConnection(MapConnection* source) {
if (!source || !porymapConfig.mirrorConnectingMaps)
return;
addConnection(source->createMirror(), false);
}
void Editor::removeMirroredConnection(MapConnection* source) {
removeConnection(getMirroredConnection(source), false);
delete pixmapItem;
}
// TODO: Self-connecting a Dive/Emerge map connection will not actually replace any existing Dive/Emerge connection, if there is one.
void Editor::createDiveEmergeConnection(MapConnection* connection) {
void Editor::displayDiveEmergeConnection(MapConnection* connection) {
if (!connection)
return;
// Create image of Dive/Emerge map
QPixmap pixmap;
Map *connectedMap = project->getMap(connection->targetMapName());
if (!connectedMap || connectedMap == this->map) {
// There's no point in rendering a map on top of itself.
// We create an empty image anyway to allow for changes later.
pixmap = QPixmap();
// Note: We only support editing 1 Dive and Emerge connection per map.
// In a vanilla game only the first Dive/Emerge connection is considered, so allowing
// users to have multiple is likely to lead to confusion. In case users have changed
// this we won't delete extra diving connections, but we'll only display the first one.
if (connection->direction() == "dive") {
if (dive_map_overlay) return;
} else if (connection->direction() == "emerge") {
if (emerge_map_overlay) return;
} else {
pixmap = connectedMap->render();
// Invalid direction
return;
}
// Create image of Dive/Emerge map (unless we'd be rendering the current map on top of itself)
QPixmap pixmap = (connection->targetMapName() == this->map->name) ? QPixmap() : connection->getPixmap();
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
scene->addItem(item);
if (connection->direction() == "dive") {
clearDiveMap();
dive_map_overlay = item;
const QSignalBlocker blocker(ui->comboBox_DiveMap);
ui->comboBox_DiveMap->setCurrentText(connection->targetMapName());
} else if (connection->direction() == "emerge") {
clearEmergeMap();
connect(connection, &MapConnection::destroyed, this, &Editor::clearDiveMap);
connect(connection, &MapConnection::hostMapNameChanged, this, &Editor::clearDiveMap);
} else {
emerge_map_overlay = item;
const QSignalBlocker blocker(ui->comboBox_EmergeMap);
ui->comboBox_EmergeMap->setCurrentText(connection->targetMapName());
} else {
// Shouldn't happen
scene->removeItem(item);
delete item;
connect(connection, &MapConnection::destroyed, this, &Editor::clearEmergeMap);
connect(connection, &MapConnection::hostMapNameChanged, this, &Editor::clearEmergeMap);
}
// Update pixmap if the connected map is swapped
connect(connection, &MapConnection::targetMapNameChanged, [this, item, connection] {
item->setPixmap((connection->targetMapName() == this->map->name) ? QPixmap() : connection->getPixmap());
});
}
void Editor::updateDiveMap(QString mapName) {
@ -978,11 +940,6 @@ void Editor::updateEmergeMap(QString mapName) {
}
void Editor::setDiveEmergeMapName(QString mapName, QString direction) {
if (!mapName.isEmpty() && !project->mapNamesToMapConstants.contains(mapName)) {
logError(QString("Invalid %1 connection map name: '%2'").arg(direction).arg(mapName));
return;
}
// Only the first Dive/Emerge map (if present) is considered, as in-game.
MapConnection* connection = nullptr;
for (MapConnection* conn : map->connections) {
@ -996,13 +953,13 @@ void Editor::setDiveEmergeMapName(QString mapName, QString direction) {
// Update existing connection
if (mapName.isEmpty()) {
removeConnection(connection);
// TODO: Queue up another Dive/Emerge map if there is one
} else {
setConnectionMap(connection, mapName);
connection->setTargetMapName(mapName);
}
} else if (!mapName.isEmpty()) {
// Create new connection
connection = new MapConnection(direction, map->name, mapName);
addConnection(connection);
addConnection(new MapConnection(direction, map->name, mapName));
}
updateDiveEmergeVisibility();
}
@ -1024,6 +981,7 @@ void Editor::updateDiveEmergeVisibility() {
}
}
// TODO: Fold into PixmapItem
QPoint Editor::calculateConnectionPosition(MapConnection *connection, const QPixmap &pixmap) {
const QString direction = connection->direction();
int offset = connection->offset();
@ -1045,132 +1003,46 @@ QPoint Editor::calculateConnectionPosition(MapConnection *connection, const QPix
return QPoint(x, y);
}
QPixmap Editor::getConnectionPixmap(MapConnection *connection) {
Map *connectedMap = project->getMap(connection->targetMapName());
// connectedMap will be null for MAP_DYNAMIC and any map that fails to load.
// The connection will be editable from the list, but no image will be displayed on the map.
if (!connectedMap)
return QPixmap();
return connectedMap->renderConnection(connection->direction(), this->map->layout);
}
void Editor::updateConnectionItem(ConnectionPixmapItem* connectionItem) {
if (!connectionItem || !connectionItem->connection)
void Editor::updateConnectionPixmap(ConnectionPixmapItem* pixmapItem) {
if (!pixmapItem || !pixmapItem->connection)
return;
const QString mapName = connectionItem->connection->targetMapName();
if (mapName.isEmpty())
return;
pixmapItem->initialOffset = pixmapItem->connection->offset();
pixmapItem->basePixmap = pixmapItem->connection->getPixmap();
QPoint pos = calculateConnectionPosition(pixmapItem->connection, pixmapItem->basePixmap);
if (!project->mapNames.contains(mapName)) {
logError(QString("Invalid map name '%1' specified for connection.").arg(mapName));
return;
}
connectionItem->initialOffset = connectionItem->connection->offset();
connectionItem->basePixmap = getConnectionPixmap(connectionItem->connection);
QPoint pos = calculateConnectionPosition(connectionItem->connection, connectionItem->basePixmap);
const QSignalBlocker blocker(connectionItem);
connectionItem->setPixmap(connectionItem->basePixmap);
connectionItem->initialX = pos.x();
connectionItem->initialY = pos.y();
connectionItem->setX(pos.x());
connectionItem->setY(pos.y());
connectionItem->render();
const QSignalBlocker blocker(pixmapItem);
pixmapItem->setPixmap(pixmapItem->basePixmap);
pixmapItem->initialX = pos.x();
pixmapItem->initialY = pos.y();
pixmapItem->setPos(pos);
pixmapItem->render();
maskNonVisibleConnectionTiles();
}
void Editor::updateConnectionItemPos(ConnectionPixmapItem* connectionItem) {
if (!connectionItem || !connectionItem->connection)
void Editor::updateConnectionPixmapPos(ConnectionPixmapItem* pixmapItem) {
if (!pixmapItem || !pixmapItem->connection)
return;
const QSignalBlocker blocker(connectionItem);
MapConnection *connection = connectionItem->connection;
const QSignalBlocker blocker(pixmapItem);
MapConnection *connection = pixmapItem->connection;
if (MapConnection::isVertical(connection->direction())) {
connectionItem->setX(connectionItem->initialX + (connection->offset() - connectionItem->initialOffset) * 16);
qreal x = pixmapItem->initialX + (connection->offset() - pixmapItem->initialOffset) * 16;
if (x != pixmapItem->x()) pixmapItem->setX(x);
} else if (MapConnection::isHorizontal(connection->direction())) {
connectionItem->setY(connectionItem->initialY + (connection->offset() - connectionItem->initialOffset) * 16);
qreal y = pixmapItem->initialY + (connection->offset() - pixmapItem->initialOffset) * 16;
if (y != pixmapItem->y()) pixmapItem->setY(y);
}
maskNonVisibleConnectionTiles();
}
void Editor::setConnectionOffset(MapConnection *connection, int offset) {
if (!connection || !this->map || connection->offset() == offset || !MapConnection::isCardinal(connection->direction()))
return;
MapConnection *mirror = getMirroredConnection(connection);
if (mirror) {
mirror->setOffset(-offset);
Map *connectedMap = project->getMap(mirror->hostMapName());
if (connectedMap != this->map) {
emit editedMapData(connectedMap);
} else {
// TODO: Remove, this will be handled by connecting to the MapConnection
// The mirror is displayed on the current map, update its graphics
for (auto item :connection_items) {
if (item->connection == mirror) {
updateConnectionItemPos(item);
break;
}
}
for (auto listItem : ui->scrollAreaContents_ConnectionsList->findChildren<ConnectionsListItem*>()) {
if (listItem->connection == mirror){
listItem->updateUI();
break;
}
}
}
}
connection->setOffset(offset);
emit editedMapData(this->map);
// TODO: This is likely the source of the visual masking bug while dragging (this happens after the move)
maskNonVisibleConnectionTiles();
}
void Editor::setConnectionMap(MapConnection *connection, const QString &mapName) {
if (!connection || !map || connection->targetMapName() == mapName)
return;
removeMirroredConnection(connection);
connection->setTargetMapName(mapName);
addMirroredConnection(connection);
emit editedMapData(map);
}
void Editor::setConnectionDirection(MapConnection *connection, const QString &direction) {
if (!connection || !map || connection->direction() == direction)
return;
// TODO: Lazy
removeMirroredConnection(connection);
if (MapConnection::isHorizontal(connection->direction()) != MapConnection::isHorizontal(direction)
|| MapConnection::isVertical(connection->direction()) != MapConnection::isVertical(direction)) {
// If the direction has changed between vertical/horizontal then the old offset may not make sense, so we reset it
setConnectionOffset(connection, 0);
}
connection->setDirection(direction);
addMirroredConnection(connection);
emit editedMapData(map);
}
void Editor::setSelectedConnection(ConnectionPixmapItem* connectionItem) {
if (!connectionItem || connectionItem == selected_connection_item)
void Editor::setSelectedConnection(ConnectionPixmapItem* pixmapItem) {
if (!pixmapItem || pixmapItem == selected_connection_item)
return;
if (selected_connection_item) selected_connection_item->setSelected(false);
selected_connection_item = connectionItem;
selected_connection_item = pixmapItem;
selected_connection_item->setSelected(true);
}
@ -1185,6 +1057,10 @@ void Editor::setSelectedConnectionFromMap(QString mapName) {
}
}
void Editor::onMapConnectionDoubleClicked(MapConnection* connection) {
emit openConnectedMap(connection->targetMapName(), connection->hostMapName());
}
void Editor::onBorderMetatilesChanged() {
displayMapBorder();
updateBorderVisibility(); // TODO: Why do we need to call this here
@ -1368,6 +1244,8 @@ bool Editor::setMap(QString map_name) {
// multiple times if set again in the future
if (map) {
map->disconnect(this);
for (auto connection : map->connections)
connection->disconnect();
}
if (project) {
@ -1387,6 +1265,7 @@ bool Editor::setMap(QString map_name) {
map_ruler->setMapDimensions(QSize(map->getWidth(), map->getHeight()));
connect(map, &Map::mapDimensionsChanged, map_ruler, &MapRuler::setMapDimensions);
connect(map, &Map::openScriptRequested, this, &Editor::openScript);
connect(map, &Map::connectionAdded, this, &Editor::displayConnection);
updateSelectedEvents();
}
@ -1907,23 +1786,8 @@ void Editor::clearEmergeMap() {
void Editor::displayMapConnections() {
clearMapConnections();
// Note: We only support editing 1 Dive and Emerge connection per map.
// In a vanilla game only the first Dive/Emerge connection is considered, so allowing
// users to have multiple is likely to lead to confusion. In case users have changed
// this we won't delete extra diving connections, but we'll only display the first one.
// TODO: Move text check to inside createDiveEmergeConnection?
for (MapConnection *connection : map->connections) {
if (connection->direction() == "dive") {
if (ui->comboBox_DiveMap->currentText().isEmpty())
createDiveEmergeConnection(connection);
} else if (connection->direction() == "emerge") {
if (ui->comboBox_EmergeMap->currentText().isEmpty())
createDiveEmergeConnection(connection);
} else {
// We allow any unknown direction names here. They'll be editable from the list menu.
createConnectionItem(connection);
}
}
for (MapConnection *connection : map->connections)
displayConnection(connection);
if (!connection_items.empty())
setSelectedConnection(connection_items.first());
@ -2001,7 +1865,7 @@ void Editor::updateMapConnections() {
for (auto item : connection_items) {
if (!item->connection)
continue;
item->basePixmap = getConnectionPixmap(item->connection);
item->basePixmap = item->connection->getPixmap();
item->render();
}
@ -2107,7 +1971,7 @@ void Editor::updateBorderVisibility() {
void Editor::updateCustomMapHeaderValues(QTableWidget *table)
{
map->customHeaders = CustomAttributesTable::getAttributes(table);
emit editedMapData(map);
map->modify();
}
Tileset* Editor::getCurrentMapPrimaryTileset()

View file

@ -306,12 +306,11 @@ void MainWindow::checkForUpdates(bool) {}
void MainWindow::initEditor() {
this->editor = new Editor(ui);
connect(this->editor, &Editor::objectsChanged, this, &MainWindow::updateObjects);
connect(this->editor, &Editor::connectionItemDoubleClicked, this, &MainWindow::onConnectionItemDoubleClicked);
connect(this->editor, &Editor::openConnectedMap, this, &MainWindow::onOpenConnectedMap);
connect(this->editor, &Editor::warpEventDoubleClicked, this, &MainWindow::openWarpMap);
connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged);
connect(this->editor, &Editor::wildMonDataChanged, this, &MainWindow::onWildMonDataChanged);
connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged);
connect(this->editor, &Editor::editedMapData, [this](Map* map) { this->markMapEdited(map); });
connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated);
connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this->editor, &Editor::openMapScripts);
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this->editor, &Editor::openProjectInTextEditor);
@ -569,15 +568,17 @@ bool MainWindow::openProject(QString dir, bool initial) {
Scripting::init(this);
// Create the project
this->editor->project = new Project(this);
QObject::connect(this->editor->project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered);
QObject::connect(this->editor->project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared);
QObject::connect(this->editor->project, &Project::uncheckMonitorFilesAction, [this]() {
auto project = new Project(this);
project->set_root(dir);
QObject::connect(project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered);
QObject::connect(project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared);
QObject::connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
QObject::connect(project, &Project::uncheckMonitorFilesAction, [this]() {
porymapConfig.monitorFiles = false;
if (this->preferenceEditor)
this->preferenceEditor->updateFields();
});
this->editor->project->set_root(dir);
this->editor->setProject(project);
// Make sure project looks reasonable before attempting to load it
if (!checkProjectSanity()) {
@ -805,7 +806,6 @@ bool MainWindow::setMap(QString map_name, bool scrollTreeView) {
showWindowTitle();
connect(editor->map, &Map::mapNeedsRedrawing, this, &MainWindow::onMapNeedsRedrawing);
connect(editor->map, &Map::modified, [this](){ this->markMapEdited(); });
userConfig.recentMap = map_name;
updateMapList();
@ -2526,7 +2526,7 @@ void MainWindow::clickToolButtonFromEditMode(QString editMode) {
}
}
void MainWindow::onConnectionItemDoubleClicked(QString mapName, QString fromMapName) {
void MainWindow::onOpenConnectedMap(QString mapName, QString fromMapName) {
if (mapName != fromMapName && userSetMap(mapName, true))
editor->setSelectedConnectionFromMap(fromMapName);
}
@ -2539,6 +2539,10 @@ void MainWindow::onMapCacheCleared() {
editor->map = nullptr;
}
void MainWindow::onMapLoaded(Map *map) {
connect(map, &Map::modified, [this, map] { this->markMapEdited(map); });
}
void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
// If saved tilesets are currently in-use, update them and redraw
// Otherwise overwrite the cache for the saved tileset

View file

@ -186,10 +186,13 @@ Map* Project::loadMap(QString map_name) {
map->setName(map_name);
}
if (!(loadMapData(map) && loadMapLayout(map)))
if (!(loadMapData(map) && loadMapLayout(map))){
delete map;
return nullptr;
}
mapCache.insert(map_name, map);
emit mapLoaded(map);
return map;
}

View file

@ -51,8 +51,7 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari
y = this->initialY;
}
if (this->connection->offset() != newOffset)
emit connectionMoved(this->connection, newOffset);
this->connection->setOffset(newOffset);
return QPointF(x, y);
}
else {
@ -84,5 +83,5 @@ void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *) {
}
void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) {
emit connectionItemDoubleClicked(this);
emit connectionItemDoubleClicked(this->connection);
}

View file

@ -56,27 +56,25 @@ void ConnectionsListItem::mousePressEvent(QMouseEvent *) {
}
void ConnectionsListItem::mouseDoubleClickEvent(QMouseEvent *) {
emit doubleClicked(this->connection->targetMapName());
emit doubleClicked(this->connection);
}
void ConnectionsListItem::on_comboBox_Direction_currentTextChanged(const QString &direction) {
this->setSelected(true);
if (this->connection->direction() != direction)
emit this->editedDirection(this->connection, direction);
this->connection->setDirection(direction);
}
void ConnectionsListItem::on_comboBox_Map_currentTextChanged(const QString &mapName) {
this->setSelected(true);
if (ui->comboBox_Map->findText(mapName) >= 0 && this->connection->targetMapName() != mapName)
emit this->editedMapName(this->connection, mapName);
if (ui->comboBox_Map->findText(mapName) >= 0)
this->connection->setTargetMapName(mapName);
}
void ConnectionsListItem::on_spinBox_Offset_valueChanged(int offset) {
this->setSelected(true);
if (this->connection->offset() != offset)
emit editedOffset(this->connection, offset);
this->connection->setOffset(offset);
}
void ConnectionsListItem::on_button_Delete_clicked() {
emit this->removed();
emit this->removed(this->connection);
}