Reimplement connection mirroring

This commit is contained in:
GriffinR 2024-07-08 16:01:30 -04:00
parent f1cfc3c78e
commit 1e09d08c9c
12 changed files with 405 additions and 251 deletions

View file

@ -128,7 +128,6 @@ private:
void setNewBorderDimensionsBlockdata(int newWidth, int newHeight);
signals:
void mapChanged(Map *map);
void modified();
void mapDimensionsChanged(const QSize &size);
void mapNeedsRedrawing();

View file

@ -5,6 +5,8 @@
#include <QString>
#include <QHash>
class Map;
class MapConnection {
public:
QString direction;
@ -12,12 +14,15 @@ public:
QString map_name;
};
inline bool operator==(const MapConnection &c1, const MapConnection &c2) {
return c1.map_name == c2.map_name;
}
struct MapConnectionMirror {
MapConnection * connection = nullptr;
Map * map = nullptr;
};
inline uint qHash(const MapConnection &key) {
return qHash(key.map_name);
inline bool operator==(const MapConnection &c1, const MapConnection &c2) {
return c1.direction == c2.direction &&
c1.offset == c2.offset &&
c1.map_name == c2.map_name;
}
#endif // MAPCONNECTION_H

View file

@ -75,9 +75,10 @@ public:
void setEditingConnections();
void setMapEditingButtonsEnabled(bool enabled);
void setConnectionsVisibility(bool visible);
void updateConnectionOffset(int offset);
void addNewConnection();
void removeConnection(ConnectionPixmapItem* connectionItem);
void addConnection(Map* map, MapConnection* connection);
void removeConnection(Map* map, MapConnection* connection);
void removeConnectionItem(ConnectionPixmapItem* connectionItem, bool removeMirror = true);
void removeSelectedConnection();
void addNewWildMonGroup(QWidget *window);
void deleteWildMonGroup();
@ -168,17 +169,16 @@ private:
void updateBorderVisibility();
QPoint calculateConnectionPosition(const MapConnection *connection, const QPixmap &pixmap);
void redrawConnection(ConnectionPixmapItem* connectionItem);
void updateConnectionItem(ConnectionPixmapItem* connectionItem);
void updateConnectionItemPos(ConnectionPixmapItem* connectionItem);
void createConnectionItem(MapConnection* connection);
void populateConnectionsList();
void addConnectionToList(ConnectionPixmapItem* connection);
void updateDiveEmergeMap(QString mapName, QString direction);
void onConnectionOffsetChanged(int newOffset);
void removeMirroredConnection(MapConnection*);
void updateMirroredConnectionOffset(MapConnection*);
void updateMirroredConnectionDirection(MapConnection*, QString);
void updateMirroredConnectionMap(MapConnection*, QString);
void updateMirroredConnection(MapConnection*, QString, QString, bool isDelete = false);
bool shouldMirrorConnection(const MapConnection &source);
MapConnectionMirror getMirroredConnection(const MapConnection&);
void addMirroredConnection(const MapConnection&);
void removeMirroredConnection(const MapConnection&);
void updateEncounterFields(EncounterFields newFields);
QString getMovementPermissionText(uint16_t collision, uint16_t elevation);
QString getMetatileDisplayMessage(uint16_t metatileId);
@ -194,7 +194,9 @@ private slots:
void setStraightPathCursorMode(QGraphicsSceneMouseEvent *event);
void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);
void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item);
void onConnectionMoved(MapConnection*);
void setConnectionOffset(MapConnection *connection, int offset);
void setConnectionMap(MapConnection *connection, const QString &mapName);
void setConnectionDirection(MapConnection *connection, const QString &direction);
void onConnectionItemSelected(ConnectionPixmapItem* connectionItem);
void onHoveredMovementPermissionChanged(uint16_t, uint16_t);
void onHoveredMovementPermissionCleared();
@ -215,7 +217,7 @@ signals:
void warpEventDoubleClicked(QString, int, Event::Group);
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void editedMapData();
void editedMapData(Map*);
void tilesetUpdated(QString);
};

View file

@ -178,7 +178,6 @@ private slots:
void paste();
void onConnectionItemDoubleClicked(QString, QString);
void onMapChanged(Map *map);
void onMapNeedsRedrawing();
void onTilesetsSaved(QString, QString);
void onWildMonDataChanged();
@ -366,6 +365,7 @@ private:
void clickToolButtonFromEditMode(QString editMode);
void markMapEdited();
void markMapEdited(Map*);
void showWindowTitle();
void initWindow();

View file

@ -44,7 +44,7 @@ protected:
signals:
void connectionItemSelected(ConnectionPixmapItem* connectionItem);
void connectionItemDoubleClicked(ConnectionPixmapItem* connectionItem);
void connectionMoved(MapConnection*);
void connectionMoved(MapConnection *, int newOffset);
void highlightChanged(bool highlighted);
};

View file

@ -19,15 +19,16 @@ class ConnectionsListItem : public QFrame
Q_OBJECT
public:
explicit ConnectionsListItem(QWidget *parent, MapConnection * connection, const QStringList &mapNames);
explicit ConnectionsListItem(QWidget *parent, MapConnection *connection, const QStringList &mapNames);
~ConnectionsListItem();
void updateUI();
void setSelected(bool selected);
MapConnection * connection;
private:
Ui::ConnectionsListItem *ui;
MapConnection * const connection;
bool isSelected = false;
protected:
@ -35,10 +36,12 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *);
signals:
void edited();
void deleteRequested();
void editedOffset(MapConnection *connection, int newOffset);
void editedDirection(MapConnection *connection, const QString &direction);
void editedMapName(MapConnection *connection, const QString &mapName);
void removed();
void selected();
void doubleClicked();
void doubleClicked(const QString &mapName);
private slots:
void on_comboBox_Direction_currentTextChanged(const QString &direction);

View file

@ -304,8 +304,8 @@ void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool
Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight);
}
emit mapChanged(this);
emit mapDimensionsChanged(QSize(getWidth(), getHeight()));
modify();
}
void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) {
@ -322,7 +322,7 @@ void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata,
Scripting::cb_BorderResized(oldWidth, oldHeight, newWidth, newHeight);
}
emit mapChanged(this);
modify();
}
void Map::openScript(QString label) {

View file

@ -0,0 +1,43 @@
#include "mapconnection.h"
#include "map.h"
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::setOffset(int offset) {
if (offset == m_offset)
return;
auto before = m_offset;
m_offset = offset;
emit offsetChanged(before, m_offset);
}
void MapConnection::setMapName(const QString &mapName) {
if (mapName == m_mapName)
return;
auto before = m_mapName;
m_mapName = mapName;
emit mapNameChanged(before, m_mapName);
}
/*
static QString MapConnection::oppositeDirection(const QString &direction) {
static const QMap<QString, QString> oppositeDirections = {
{"up", "down"}, {"down", "up"},
{"right", "left"}, {"left", "right"},
{"dive", "emerge"}, {"emerge", "dive"}
};
return oppositeDirections.value(direction);
}
static MapConnection* MapConnection::getMirror(const MapConnection*, const Map*) {
}
static MapConnection* MapConnection::newMirror(const MapConnection*) {
}*/

View file

@ -726,14 +726,14 @@ void Editor::populateConnectionsList() {
const QSignalBlocker blocker1(ui->comboBox_DiveMap);
const QSignalBlocker blocker2(ui->comboBox_EmergeMap);
// TODO: We should probably just be doing this once, and updating the items when new maps are created.
ui->comboBox_DiveMap->clear();
ui->comboBox_DiveMap->addItems(project->mapNames);
ui->comboBox_DiveMap->setCurrentText("");
ui->comboBox_DiveMap->lineEdit()->setClearButtonEnabled(true);
ui->comboBox_EmergeMap->clear();
ui->comboBox_DiveMap->addItems(project->mapNames);
ui->comboBox_EmergeMap->addItems(project->mapNames);
ui->comboBox_DiveMap->setCurrentText("");
ui->comboBox_EmergeMap->setCurrentText("");
ui->comboBox_DiveMap->lineEdit()->setClearButtonEnabled(true);
ui->comboBox_EmergeMap->lineEdit()->setClearButtonEnabled(true);
for (MapConnection* connection : map->connections) {
@ -745,24 +745,22 @@ void Editor::populateConnectionsList() {
}
// Clear any existing connections in list
for (auto w : ui->scrollAreaContents_ConnectionsList->findChildren<ConnectionsListItem*>())
w->deleteLater();
for (auto listItem : ui->scrollAreaContents_ConnectionsList->findChildren<ConnectionsListItem*>())
listItem->deleteLater();
for (auto item :connection_items)
addConnectionToList(item);
}
// TODO: When connecting a map to itself the new mirror is shaded incorrectly
// TODO: Set default focus to the selected connection, right now it defaults to the dive combo box
void Editor::addConnectionToList(ConnectionPixmapItem * connectionItem) {
ConnectionsListItem *listItem = new ConnectionsListItem(ui->scrollAreaContents_ConnectionsList, connectionItem->connection, project->mapNames);
ui->layout_ConnectionsList->insertWidget(ui->layout_ConnectionsList->count() - 1, listItem); // Insert above the vertical spacer
// Connect the pixmap item to the list item
connect(connectionItem, &ConnectionPixmapItem::connectionMoved, listItem, &ConnectionsListItem::updateUI);
// Sync the selection highlight between the list UI and the graphical map connection
connect(connectionItem, &ConnectionPixmapItem::highlightChanged, listItem, &ConnectionsListItem::setSelected);
connect(connectionItem, &ConnectionPixmapItem::destroyed, listItem, &ConnectionsListItem::deleteLater);
if (connectionItem == selected_connection_item)
listItem->setSelected(true);
connect(listItem, &ConnectionsListItem::selected, [this, connectionItem] {
// When the list item is selected, select the pixmap too
if (connectionItem == selected_connection_item) {
@ -774,32 +772,113 @@ void Editor::addConnectionToList(ConnectionPixmapItem * connectionItem) {
selected_connection_item = connectionItem;
selected_connection_item->updateHighlight(true);
});
connect(listItem, &ConnectionsListItem::edited, [this, connectionItem] {
// When the list item is edited update the pixmap
// TODO: This is probably slower than necessary (we don't need a full redraw if we're just moving it)
// TODO: Handle mirroring
redrawConnection(connectionItem);
emit editedMapData();
if (connectionItem == selected_connection_item)
listItem->setSelected(true);
// 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);
listItem->updateUI();
});
connect(listItem, &ConnectionsListItem::deleteRequested, [this, connectionItem] {
connect (listItem, &ConnectionsListItem::editedOffset, [this, connectionItem](MapConnection* connection, int offset) {
setConnectionOffset(connection, offset);
updateConnectionItemPos(connectionItem);
});
// 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](MapConnection* connection, QString direction) {
setConnectionDirection(connection, direction);
updateConnectionItem(connectionItem); // TODO: Simplify?
});
connect(listItem, &ConnectionsListItem::editedMapName, [this, connectionItem](MapConnection* connection, QString mapName) {
setConnectionMap(connection, mapName);
updateConnectionItem(connectionItem);
});
// 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 earlier connection to the pixmap's 'destroyed' signal.
removeConnection(connectionItem);
// The list item will be deleted via the above connection to the pixmap's 'destroyed' signal.
removeConnectionItem(connectionItem);
});
connect(listItem, &ConnectionsListItem::doubleClicked, [this, connectionItem] {
// Double clicking the list item opens the connected map
emit connectionItemDoubleClicked(connectionItem->connection->map_name, map->name);
// Double clicking the list item opens the connected map
connect(listItem, &ConnectionsListItem::doubleClicked, [this](QString connectedMapName) {
emit connectionItemDoubleClicked(connectedMapName, map->name);
});
}
void Editor::removeConnection(ConnectionPixmapItem* connectionItem) {
void Editor::addNewConnection() {
// Find direction with least number of connections.
QMap<QString, int> directionCounts = QMap<QString, int>({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}});
for (MapConnection* 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];
}
}
// Prefer not to connect the map to itself (we have to if it's the only map).
// TODO: Is this more or less sensible than a connection with no map name
QString defaultMapName = project->mapNames.first();
if (defaultMapName == map->name && project->mapNames.length() > 1) {
defaultMapName = project->mapNames.at(1);
}
MapConnection* newConnection = new MapConnection;
newConnection->direction = minDirection;
newConnection->offset = 0;
newConnection->map_name = defaultMapName;
addConnection(map, newConnection);
onConnectionItemSelected(connection_items.last());
addMirroredConnection(*newConnection);
}
void Editor::addConnection(Map* map, MapConnection * connection) {
map->connections.append(connection);
if (map == this->map) {
// Adding a connection to the current map, we need to display it visually.
// Note that for the Dive/Emerge combo boxes this is normally redundant
// as the user can only create these by having already set the text,
// *except* in the case where they're connecting a map to itself.
if (connection->direction == "dive") {
const QSignalBlocker blocker(ui->comboBox_DiveMap);
ui->comboBox_DiveMap->setCurrentText(connection->map_name);
} else if (connection->direction == "emerge") {
const QSignalBlocker blocker(ui->comboBox_EmergeMap);
ui->comboBox_EmergeMap->setCurrentText(connection->map_name);
} else {
createConnectionItem(connection);
addConnectionToList(connection_items.last());
}
}
emit editedMapData(map);
}
void Editor::removeConnectionItem(ConnectionPixmapItem* connectionItem, bool removeMirror) {
if (!connectionItem)
return;
map->connections.removeOne(connectionItem->connection);
connection_items.removeOne(connectionItem);
//removeMirroredConnection(connectionItem->connection); // TODO
if (connectionItem->connection) {
if (removeMirror) {
// If a map is connected to itself and we delete the connection then this function
// will be called twice, once for the target of the delete and once for its mirror.
// We only want to try deleting the mirror once, on the first call (because the mirror
// of the mirror is the original target again, and we already deleted it).
removeMirroredConnection(*connectionItem->connection);
}
removeConnection(map, connectionItem->connection);
}
connection_items.removeOne(connectionItem);
if (connectionItem->scene())
connectionItem->scene()->removeItem(connectionItem);
@ -808,14 +887,136 @@ void Editor::removeConnection(ConnectionPixmapItem* connectionItem) {
if (!connection_items.isEmpty())
onConnectionItemSelected(connection_items.first());
}
delete connectionItem->connection;
delete connectionItem;
emit editedMapData();
}
void Editor::removeConnection(Map* map, MapConnection* connection) {
if (!map)
return;
map->connections.removeOne(connection);
delete connection;
emit editedMapData(map);
}
void Editor::removeSelectedConnection() {
removeConnection(selected_connection_item);
removeConnectionItem(selected_connection_item);
}
static const QMap<QString, QString> oppositeDirections = {
{"up", "down"}, {"down", "up"},
{"right", "left"}, {"left", "right"},
{"dive", "emerge"}, {"emerge", "dive"}
};
bool Editor::shouldMirrorConnection(const MapConnection &source) {
// The current map's connections are not mirrored if the user has disabled this setting,
// unless it's a connection to itself (which is mirrored by definition).
// TODO: Confirm in-game representation of the above fact is accurate.
return ui->checkBox_MirrorConnections->isChecked() || (map && map->name == source.map_name);
}
MapConnectionMirror Editor::getMirroredConnection(const MapConnection &source) {
MapConnectionMirror mirror;
if (!map || !shouldMirrorConnection(source))
return mirror;
// Note: It's possible (and ok) for mirror.map == this->map
mirror.map = project->getMap(source.map_name);
if (!mirror.map)
return mirror;
MapConnection target;
target.direction = oppositeDirections.value(source.direction);
target.map_name = map->name;
target.offset = -source.offset;
// Find the matching connection in the connected map.
for (auto connection : mirror.map->connections) {
if (*connection == target) {
mirror.connection = connection;
break;
}
}
return mirror;
}
void Editor::addMirroredConnection(const MapConnection &source) {
MapConnectionMirror mirror = getMirroredConnection(source);
if (!mirror.map)
return;
mirror.connection = new MapConnection;
mirror.connection->direction = oppositeDirections.value(source.direction);
mirror.connection->map_name = map->name;
mirror.connection->offset = -source.offset;
addConnection(mirror.map, mirror.connection);
}
void Editor::removeMirroredConnection(const MapConnection &source) {
MapConnectionMirror mirror = getMirroredConnection(source);
if (!mirror.map || !mirror.connection)
return;
if (map == mirror.map) {
// The connection to delete is displayed on the currently-opened map, we need to delete it visually as well.
if (source.direction == "dive") {
const QSignalBlocker blocker(ui->comboBox_DiveMap);
ui->comboBox_DiveMap->setCurrentText("");
} else if (source.direction == "emerge") {
const QSignalBlocker blocker(ui->comboBox_EmergeMap);
ui->comboBox_EmergeMap->setCurrentText("");
} else {
// Find and delete the matching connection graphics
for (auto item :connection_items) {
if (item->connection == mirror.connection) {
removeConnectionItem(item, false);
return;
}
}
}
}
removeConnection(mirror.map, mirror.connection);
}
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)) {
logError(QString("Invalid %1 connection map name: '%2'").arg(direction).arg(mapName));
return;
}
// TODO: How does the game handle having multiple Dive/Emerge maps. Should we support this?
MapConnection* connection = nullptr;
for (MapConnection* conn : map->connections) {
if (conn->direction == direction) {
connection = conn;
break;
}
}
// We (lazily) always update the connection by deleting the old one and creating a new one.
// Unlike the displayed connections which can be updated rapidly by click+drag, this isn't
// really an issue for Dive/Emerge maps.
if (connection) {
removeMirroredConnection(*connection);
removeConnection(map, connection);
}
if (!mapName.isEmpty() && mapName != DYNAMIC_MAP_NAME) {
connection = new MapConnection;
connection->direction = direction;
connection->offset = 0;
connection->map_name = mapName;
addConnection(map, connection);
addMirroredConnection(*connection);
}
}
QPoint Editor::calculateConnectionPosition(const MapConnection *connection, const QPixmap &pixmap) {
@ -837,7 +1038,7 @@ QPoint Editor::calculateConnectionPosition(const MapConnection *connection, cons
return QPoint(x, y);
}
void Editor::redrawConnection(ConnectionPixmapItem* connectionItem) {
void Editor::updateConnectionItem(ConnectionPixmapItem* connectionItem) {
if (!connectionItem || !connectionItem->connection)
return;
@ -870,36 +1071,80 @@ void Editor::redrawConnection(ConnectionPixmapItem* connectionItem) {
connectionItem->setZValue(-1);
connectionItem->blockSignals(false);
// TODO:
//updateMirroredConnectionMap(selected_connection_item->connection, originalMapName);
maskNonVisibleConnectionTiles();
}
// TODO: Generalize, maybe use in addConnectionToList connection instead of full render
void Editor::updateConnectionOffset(int offset) {
/*if (!selected_connection_item)
void Editor::updateConnectionItemPos(ConnectionPixmapItem* connectionItem) {
if (!connectionItem || !connectionItem->connection)
return;
selected_connection_item->blockSignals(true);
selected_connection_item->connection->offset = 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);
MapConnection *connection = connectionItem->connection;
connectionItem->blockSignals(true);
if (connection->direction == "up" || connection->direction == "down") {
connectionItem->setX(connectionItem->initialX + (connection->offset - connectionItem->initialOffset) * 16);
} else if (connection->direction == "left" || connection->direction == "right") {
connectionItem->setY(connectionItem->initialY + (connection->offset - connectionItem->initialOffset) * 16);
}
selected_connection_item->blockSignals(false);
updateMirroredConnectionOffset(selected_connection_item->connection);
maskNonVisibleConnectionTiles();*/
connectionItem->blockSignals(false);
maskNonVisibleConnectionTiles();
}
void Editor::onConnectionMoved(MapConnection* connection) {
// TODO:
//updateMirroredConnectionOffset(connection);
void Editor::setConnectionOffset(MapConnection *connection, int offset) {
if (!connection || !map || connection->offset == offset)
return;
MapConnectionMirror mirror = getMirroredConnection(*connection);
if (mirror.connection && mirror.map) {
mirror.connection->offset = -offset;
if (mirror.map != map) {
emit editedMapData(mirror.map);
} else {
// The mirror is displayed on the current map, update its graphics
for (auto item :connection_items) {
if (item->connection == mirror.connection) {
updateConnectionItemPos(item);
break;
}
}
// TODO: We should be signaling to the list item from the pixmap item, rather than searching for it.
for (auto listItem : ui->scrollAreaContents_ConnectionsList->findChildren<ConnectionsListItem*>()) {
if (listItem->connection == mirror.connection){
listItem->updateUI();
break;
}
}
}
}
// TODO: If there's no mirror but there should be we're not creating one
connection->offset = offset;
emit editedMapData(map);
// TODO: This is likely the source of the visual masking bug while dragging (this happens after the move)
maskNonVisibleConnectionTiles();
emit editedMapData();
}
void Editor::setConnectionMap(MapConnection *connection, const QString &mapName) {
if (!connection || !map || connection->map_name == mapName)
return;
removeMirroredConnection(*connection);
connection->map_name = 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);
connection->direction = direction;
addMirroredConnection(*connection);
emit editedMapData(map);
}
void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) {
@ -1579,7 +1824,6 @@ void Editor::createConnectionItem(MapConnection* connection) {
item->setZValue(-1);
scene->addItem(item);
connect(item, &ConnectionPixmapItem::connectionMoved, this, &Editor::onConnectionMoved);
connect(item, &ConnectionPixmapItem::connectionItemSelected, this, &Editor::onConnectionItemSelected);
connect(item, &ConnectionPixmapItem::connectionItemDoubleClicked, [this, item] {
emit this->connectionItemDoubleClicked(item->connection->map_name, map->name);
@ -1703,144 +1947,6 @@ void Editor::displayMapGrid() {
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::onToggleGridClicked);
}
void Editor::addNewConnection() {
// Find direction with least number of connections.
QMap<QString, int> directionCounts = QMap<QString, int>({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}});
for (MapConnection* 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);
}
MapConnection* newConnection = new MapConnection;
newConnection->direction = minDirection;
newConnection->offset = 0;
newConnection->map_name = defaultMapName;
map->connections.append(newConnection);
createConnectionItem(newConnection);
addConnectionToList(connection_items.last());
onConnectionItemSelected(connection_items.last());
updateMirroredConnection(newConnection, newConnection->direction, newConnection->map_name);
}
void Editor::updateMirroredConnectionOffset(MapConnection* connection) {
updateMirroredConnection(connection, connection->direction, connection->map_name);
}
void Editor::updateMirroredConnectionDirection(MapConnection* connection, QString originalDirection) {
updateMirroredConnection(connection, originalDirection, connection->map_name);
}
void Editor::updateMirroredConnectionMap(MapConnection* connection, QString originalMapName) {
updateMirroredConnection(connection, connection->direction, originalMapName);
}
void Editor::removeMirroredConnection(MapConnection* connection) {
updateMirroredConnection(connection, connection->direction, connection->map_name, true);
}
void Editor::updateMirroredConnection(MapConnection* connection, QString originalDirection, QString originalMapName, bool isDelete) {
if (!ui->checkBox_MirrorConnections->isChecked())
return;
Map* otherMap = project->getMap(originalMapName);
if (!otherMap)
return;
static QMap<QString, QString> oppositeDirections = QMap<QString, QString>({
{"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.
MapConnection* mirrorConnection = nullptr;
for (MapConnection* 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 = nullptr;
otherMap = project->getMap(connection->map_name);
}
}
// Create a new mirrored connection, if a matching one doesn't already exist.
if (!mirrorConnection) {
mirrorConnection = new MapConnection;
mirrorConnection->direction = oppositeDirections.value(connection->direction);
mirrorConnection->map_name = map->name;
otherMap->connections.append(mirrorConnection);
}
mirrorConnection->offset = -connection->offset;
}
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)) {
logError(QString("Invalid %1 connection map name: '%2'").arg(direction).arg(mapName));
return;
}
MapConnection* connection = nullptr;
for (MapConnection* conn : map->connections) {
if (conn->direction == direction) {
connection = conn;
break;
}
}
if (mapName.isEmpty() || mapName == DYNAMIC_MAP_NAME) {
// Remove dive/emerge connection
if (connection) {
map->connections.removeOne(connection);
removeMirroredConnection(connection);
}
} else {
if (!connection) {
connection = new MapConnection;
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);
}
}
}
void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
{
if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
@ -1893,7 +1999,7 @@ void Editor::updateBorderVisibility() {
void Editor::updateCustomMapHeaderValues(QTableWidget *table)
{
map->customHeaders = CustomAttributesTable::getAttributes(table);
emit editedMapData();
emit editedMapData(map);
}
Tileset* Editor::getCurrentMapPrimaryTileset()

View file

@ -310,7 +310,7 @@ void MainWindow::initEditor() {
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, &MainWindow::markMapEdited);
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);
@ -413,10 +413,19 @@ void MainWindow::showWindowTitle() {
}
void MainWindow::markMapEdited() {
if (editor && editor->map) {
editor->map->hasUnsavedDataChanges = true;
if (editor) markMapEdited(editor->map);
}
void MainWindow::markMapEdited(Map* map) {
if (!map)
return;
map->hasUnsavedDataChanges = true;
// TODO: Only update the necessary list icon
updateMapList();
if (editor && editor->map == map)
showWindowTitle();
}
}
// Update the UI using information we've read from the user's project files.
@ -769,7 +778,6 @@ bool MainWindow::setMap(QString map_name, bool scrollTreeView) {
showWindowTitle();
connect(editor->map, &Map::mapChanged, this, &MainWindow::onMapChanged);
connect(editor->map, &Map::mapNeedsRedrawing, this, &MainWindow::onMapNeedsRedrawing);
connect(editor->map, &Map::modified, [this](){ this->markMapEdited(); });
@ -2440,10 +2448,6 @@ void MainWindow::onConnectionItemDoubleClicked(QString mapName, QString fromMapN
editor->setSelectedConnectionFromMap(fromMapName);
}
void MainWindow::onMapChanged(Map *) {
updateMapList();
}
void MainWindow::onMapNeedsRedrawing() {
redrawMapScene();
}
@ -2555,9 +2559,8 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) {
void MainWindow::on_pushButton_AddConnection_clicked()
{
// TODO: Bring up a prompt for information. Mark the current map *AND* the connected map as edited
// TODO: Bring up a prompt for information?
editor->addNewConnection();
markMapEdited();
}
void MainWindow::on_pushButton_NewWildMonGroup_clicked() {
@ -2584,20 +2587,15 @@ void MainWindow::on_button_OpenEmergeMap_clicked() {
userSetMap(mapName, true);
}
// TODO: Mirror change to/from other maps
void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName) {
// Include empty names as an update (user is deleting the connection)
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName)) {
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName))
editor->updateDiveMap(mapName);
markMapEdited();
}
}
void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName) {
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName)) {
if (mapName.isEmpty() || editor->project->mapNames.contains(mapName))
editor->updateEmergeMap(mapName);
markMapEdited();
}
}
void MainWindow::on_comboBox_PrimaryTileset_currentTextChanged(const QString &tilesetLabel)

View file

@ -38,8 +38,8 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari
y = this->initialY;
}
this->connection->offset = newOffset;
emit connectionMoved(this->connection);
if (this->connection->offset != newOffset)
emit connectionMoved(this->connection, newOffset);
return QPointF(x, y);
}
else {

View file

@ -5,8 +5,7 @@ static const QStringList directions = {"up", "down", "left", "right"};
ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connection, const QStringList &mapNames) :
QFrame(parent),
ui(new Ui::ConnectionsListItem),
connection(connection)
ui(new Ui::ConnectionsListItem)
{
ui->setupUi(this);
@ -24,6 +23,7 @@ ConnectionsListItem::ConnectionsListItem(QWidget *parent, MapConnection * connec
ui->spinBox_Offset->setMinimum(INT_MIN);
ui->spinBox_Offset->setMaximum(INT_MAX);
this->connection = connection;
this->updateUI();
}
@ -58,33 +58,31 @@ void ConnectionsListItem::mousePressEvent(QMouseEvent *) {
}
void ConnectionsListItem::mouseDoubleClickEvent(QMouseEvent *) {
emit doubleClicked();
emit doubleClicked(this->connection->map_name);
}
void ConnectionsListItem::on_comboBox_Direction_currentTextChanged(const QString &direction)
{
this->connection->direction = direction;
this->setSelected(true);
emit this->edited();
if (this->connection->direction != direction)
emit this->editedDirection(this->connection, direction);
}
void ConnectionsListItem::on_comboBox_Map_currentTextChanged(const QString &mapName)
{
if (ui->comboBox_Map->findText(mapName) >= 0) {
this->connection->map_name = mapName;
this->setSelected(true);
emit this->edited();
}
this->setSelected(true);
if (ui->comboBox_Map->findText(mapName) >= 0 && this->connection->map_name != mapName)
emit this->editedMapName(this->connection, mapName);
}
void ConnectionsListItem::on_spinBox_Offset_valueChanged(int offset)
{
this->connection->offset = offset;
this->setSelected(true);
emit this->edited();
if (this->connection->offset != offset)
emit editedOffset(this->connection, offset);
}
void ConnectionsListItem::on_button_Delete_clicked()
{
emit this->deleteRequested();
emit this->removed();
}