Browse Source

Merge pull request #40 from huderlem/connect

Edit Map Connections
yenatch 6 years ago
parent
commit
45d3e932ca
No account linked to committer's email address
11 changed files with 1294 additions and 46 deletions
  1. 542
    28
      editor.cpp
  2. 82
    2
      editor.h
  3. 1
    0
      graphicsview.cpp
  4. 2
    2
      graphicsview.h
  5. 50
    11
      mainwindow.cpp
  6. 15
    0
      mainwindow.h
  7. 494
    0
      mainwindow.ui
  8. 2
    0
      map.h
  9. 1
    0
      parseutil.h
  10. 99
    3
      project.cpp
  11. 6
    0
      project.h

+ 542
- 28
editor.cpp View File

@@ -3,8 +3,9 @@
3 3
 #include <QPainter>
4 4
 #include <QMouseEvent>
5 5
 
6
-Editor::Editor()
6
+Editor::Editor(Ui::MainWindow* ui)
7 7
 {
8
+    this->ui = ui;
8 9
     selected_events = new QList<DraggablePixmapItem*>;
9 10
 }
10 11
 
@@ -37,9 +38,11 @@ void Editor::redo() {
37 38
 void Editor::setEditingMap() {
38 39
     current_view = map_item;
39 40
     if (map_item) {
41
+        displayMapConnections();
40 42
         map_item->draw();
41 43
         map_item->setVisible(true);
42 44
         map_item->setEnabled(true);
45
+        setConnectionsVisibility(true);
43 46
     }
44 47
     if (collision_item) {
45 48
         collision_item->setVisible(false);
@@ -47,13 +50,15 @@ void Editor::setEditingMap() {
47 50
     if (objects_group) {
48 51
         objects_group->setVisible(false);
49 52
     }
53
+    setBorderItemsVisible(true);
54
+    setConnectionItemsVisible(false);
50 55
 }
51 56
 
52 57
 void Editor::setEditingCollision() {
53 58
     current_view = collision_item;
54 59
     if (collision_item) {
55
-        collision_item->draw();
56 60
         collision_item->setVisible(true);
61
+        setConnectionsVisibility(true);
57 62
     }
58 63
     if (map_item) {
59 64
         map_item->setVisible(false);
@@ -61,6 +66,8 @@ void Editor::setEditingCollision() {
61 66
     if (objects_group) {
62 67
         objects_group->setVisible(false);
63 68
     }
69
+    setBorderItemsVisible(true);
70
+    setConnectionItemsVisible(false);
64 71
 }
65 72
 
66 73
 void Editor::setEditingObjects() {
@@ -71,10 +78,232 @@ void Editor::setEditingObjects() {
71 78
     if (map_item) {
72 79
         map_item->setVisible(true);
73 80
         map_item->setEnabled(false);
81
+        setConnectionsVisibility(true);
74 82
     }
75 83
     if (collision_item) {
76 84
         collision_item->setVisible(false);
77 85
     }
86
+    setBorderItemsVisible(true);
87
+    setConnectionItemsVisible(false);
88
+}
89
+
90
+void Editor::setEditingConnections() {
91
+    current_view = map_item;
92
+    if (map_item) {
93
+        map_item->draw();
94
+        map_item->setVisible(true);
95
+        map_item->setEnabled(false);
96
+        populateConnectionMapPickers();
97
+        ui->label_NumConnections->setText(QString::number(map->connections.length()));
98
+        setConnectionsVisibility(false);
99
+        setDiveEmergeControls();
100
+        setConnectionEditControlsEnabled(selected_connection_item != NULL);
101
+        if (selected_connection_item) {
102
+            onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt());
103
+            setConnectionMap(selected_connection_item->connection->map_name);
104
+            setCurrentConnectionDirection(selected_connection_item->connection->direction);
105
+        }
106
+    }
107
+    if (collision_item) {
108
+        collision_item->setVisible(false);
109
+    }
110
+    if (objects_group) {
111
+        objects_group->setVisible(false);
112
+    }
113
+    setBorderItemsVisible(true, 0.4);
114
+    setConnectionItemsVisible(true);
115
+}
116
+
117
+void Editor::setDiveEmergeControls() {
118
+    ui->comboBox_DiveMap->blockSignals(true);
119
+    ui->comboBox_EmergeMap->blockSignals(true);
120
+    ui->comboBox_DiveMap->setCurrentText("");
121
+    ui->comboBox_EmergeMap->setCurrentText("");
122
+    for (Connection* connection : map->connections) {
123
+        if (connection->direction == "dive") {
124
+            ui->comboBox_DiveMap->setCurrentText(connection->map_name);
125
+        } else if (connection->direction == "emerge") {
126
+            ui->comboBox_EmergeMap->setCurrentText(connection->map_name);
127
+        }
128
+    }
129
+    ui->comboBox_DiveMap->blockSignals(false);
130
+    ui->comboBox_EmergeMap->blockSignals(false);
131
+}
132
+
133
+void Editor::populateConnectionMapPickers() {
134
+    ui->comboBox_ConnectedMap->blockSignals(true);
135
+    ui->comboBox_DiveMap->blockSignals(true);
136
+    ui->comboBox_EmergeMap->blockSignals(true);
137
+
138
+    ui->comboBox_ConnectedMap->clear();
139
+    ui->comboBox_ConnectedMap->addItems(*project->mapNames);
140
+    ui->comboBox_DiveMap->clear();
141
+    ui->comboBox_DiveMap->addItems(*project->mapNames);
142
+    ui->comboBox_EmergeMap->clear();
143
+    ui->comboBox_EmergeMap->addItems(*project->mapNames);
144
+
145
+    ui->comboBox_ConnectedMap->blockSignals(false);
146
+    ui->comboBox_DiveMap->blockSignals(true);
147
+    ui->comboBox_EmergeMap->blockSignals(true);
148
+}
149
+
150
+void Editor::setConnectionItemsVisible(bool visible) {
151
+    for (ConnectionPixmapItem* item : connection_edit_items) {
152
+        item->setVisible(visible);
153
+        item->setEnabled(visible);
154
+    }
155
+}
156
+
157
+void Editor::setBorderItemsVisible(bool visible, qreal opacity) {
158
+    for (QGraphicsPixmapItem* item : borderItems) {
159
+        item->setVisible(visible);
160
+        item->setOpacity(opacity);
161
+    }
162
+}
163
+
164
+void Editor::setCurrentConnectionDirection(QString curDirection) {
165
+    if (!selected_connection_item)
166
+        return;
167
+
168
+    selected_connection_item->connection->direction = curDirection;
169
+
170
+    Map *connected_map = project->getMap(selected_connection_item->connection->map_name);
171
+    QPixmap pixmap = connected_map->renderConnection(*selected_connection_item->connection);
172
+    int offset = selected_connection_item->connection->offset.toInt(nullptr, 0);
173
+    selected_connection_item->initialOffset = offset;
174
+    int x = 0, y = 0;
175
+    if (selected_connection_item->connection->direction == "up") {
176
+        x = offset * 16;
177
+        y = -pixmap.height();
178
+    } else if (selected_connection_item->connection->direction == "down") {
179
+        x = offset * 16;
180
+        y = map->getHeight() * 16;
181
+    } else if (selected_connection_item->connection->direction == "left") {
182
+        x = -pixmap.width();
183
+        y = offset * 16;
184
+    } else if (selected_connection_item->connection->direction == "right") {
185
+        x = map->getWidth() * 16;
186
+        y = offset * 16;
187
+    }
188
+
189
+    selected_connection_item->basePixmap = pixmap;
190
+    QPainter painter(&pixmap);
191
+    painter.setPen(QColor(255, 0, 255));
192
+    painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1);
193
+    painter.end();
194
+    selected_connection_item->setPixmap(pixmap);
195
+    selected_connection_item->initialX = x;
196
+    selected_connection_item->initialY = y;
197
+    selected_connection_item->blockSignals(true);
198
+    selected_connection_item->setX(x);
199
+    selected_connection_item->setY(y);
200
+    selected_connection_item->setZValue(-1);
201
+    selected_connection_item->blockSignals(false);
202
+
203
+    setConnectionEditControlValues(selected_connection_item->connection);
204
+}
205
+
206
+void Editor::updateCurrentConnectionDirection(QString curDirection) {
207
+    if (!selected_connection_item)
208
+        return;
209
+
210
+    QString originalDirection = selected_connection_item->connection->direction;
211
+    setCurrentConnectionDirection(curDirection);
212
+    updateMirroredConnectionDirection(selected_connection_item->connection, originalDirection);
213
+}
214
+
215
+void Editor::onConnectionMoved(Connection* connection) {
216
+    updateMirroredConnectionOffset(connection);
217
+    onConnectionOffsetChanged(connection->offset.toInt());
218
+}
219
+
220
+void Editor::onConnectionOffsetChanged(int newOffset) {
221
+    ui->spinBox_ConnectionOffset->blockSignals(true);
222
+    ui->spinBox_ConnectionOffset->setValue(newOffset);
223
+    ui->spinBox_ConnectionOffset->blockSignals(false);
224
+
225
+}
226
+
227
+void Editor::setConnectionEditControlValues(Connection* connection) {
228
+    QString mapName = connection ? connection->map_name : "";
229
+    QString direction = connection ? connection->direction : "";
230
+    int offset = connection ? connection->offset.toInt() : 0;
231
+
232
+    ui->comboBox_ConnectedMap->blockSignals(true);
233
+    ui->comboBox_ConnectionDirection->blockSignals(true);
234
+    ui->spinBox_ConnectionOffset->blockSignals(true);
235
+
236
+    ui->comboBox_ConnectedMap->setCurrentText(mapName);
237
+    ui->comboBox_ConnectionDirection->setCurrentText(direction);
238
+    ui->spinBox_ConnectionOffset->setValue(offset);
239
+
240
+    ui->comboBox_ConnectedMap->blockSignals(false);
241
+    ui->comboBox_ConnectionDirection->blockSignals(false);
242
+    ui->spinBox_ConnectionOffset->blockSignals(false);
243
+}
244
+
245
+void Editor::setConnectionEditControlsEnabled(bool enabled) {
246
+    ui->comboBox_ConnectionDirection->setEnabled(enabled);
247
+    ui->comboBox_ConnectedMap->setEnabled(enabled);
248
+    ui->spinBox_ConnectionOffset->setEnabled(enabled);
249
+
250
+    if (!enabled) {
251
+        setConnectionEditControlValues(false);
252
+    }
253
+}
254
+
255
+void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) {
256
+    if (!connectionItem)
257
+        return;
258
+
259
+    for (ConnectionPixmapItem* item : connection_edit_items) {
260
+        bool isSelectedItem = item == connectionItem;
261
+        int zValue = isSelectedItem ? 0 : -1;
262
+        qreal opacity = isSelectedItem ? 1 : 0.75;
263
+        item->setZValue(zValue);
264
+        item->render(opacity);
265
+        if (isSelectedItem) {
266
+            QPixmap pixmap = item->pixmap();
267
+            QPainter painter(&pixmap);
268
+            painter.setPen(QColor(255, 0, 255));
269
+            painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1);
270
+            painter.end();
271
+            item->setPixmap(pixmap);
272
+        }
273
+    }
274
+    selected_connection_item = connectionItem;
275
+    setConnectionEditControlsEnabled(true);
276
+    setConnectionEditControlValues(selected_connection_item->connection);
277
+    ui->spinBox_ConnectionOffset->setMaximum(selected_connection_item->getMaxOffset());
278
+    ui->spinBox_ConnectionOffset->setMinimum(selected_connection_item->getMinOffset());
279
+    onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt());
280
+}
281
+
282
+void Editor::setSelectedConnectionFromMap(QString mapName) {
283
+    // Search for the first connection that connects to the given map map.
284
+    for (ConnectionPixmapItem* item : connection_edit_items) {
285
+        if (item->connection->map_name == mapName) {
286
+            onConnectionItemSelected(item);
287
+            break;
288
+        }
289
+    }
290
+}
291
+
292
+void Editor::onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem) {
293
+    emit loadMapRequested(connectionItem->connection->map_name, map->name);
294
+}
295
+
296
+void Editor::onConnectionDirectionChanged(QString newDirection) {
297
+    ui->comboBox_ConnectionDirection->blockSignals(true);
298
+    ui->comboBox_ConnectionDirection->setCurrentText(newDirection);
299
+    ui->comboBox_ConnectionDirection->blockSignals(false);
300
+}
301
+
302
+void Editor::setConnectionsVisibility(bool visible) {
303
+    for (QGraphicsPixmapItem* item : map->connection_items) {
304
+        item->setVisible(visible);
305
+        item->setActive(visible);
306
+    }
78 307
 }
79 308
 
80 309
 void Editor::setMap(QString map_name) {
@@ -205,33 +434,65 @@ DraggablePixmapItem *Editor::addMapObject(Event *event) {
205 434
 }
206 435
 
207 436
 void Editor::displayMapConnections() {
437
+    for (QGraphicsPixmapItem* item : map->connection_items) {
438
+        delete item;
439
+    }
440
+    map->connection_items.clear();
441
+
442
+    for (ConnectionPixmapItem* item : connection_edit_items) {
443
+        delete item;
444
+    }
445
+    selected_connection_item = NULL;
446
+    connection_edit_items.clear();
447
+
208 448
     for (Connection *connection : map->connections) {
209 449
         if (connection->direction == "dive" || connection->direction == "emerge") {
210 450
             continue;
211 451
         }
212
-        Map *connected_map = project->getMap(connection->map_name);
213
-        QPixmap pixmap = connected_map->renderConnection(*connection);
214
-        int offset = connection->offset.toInt(nullptr, 0);
215
-        int x = 0, y = 0;
216
-        if (connection->direction == "up") {
217
-            x = offset * 16;
218
-            y = -pixmap.height();
219
-        } else if (connection->direction == "down") {
220
-            x = offset * 16;
221
-            y = map->getHeight() * 16;
222
-        } else if (connection->direction == "left") {
223
-            x = -pixmap.width();
224
-            y = offset * 16;
225
-        } else if (connection->direction == "right") {
226
-            x = map->getWidth() * 16;
227
-            y = offset * 16;
228
-        }
229
-        QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
230
-        item->setZValue(-1);
231
-        item->setX(x);
232
-        item->setY(y);
233
-        scene->addItem(item);
452
+        createConnectionItem(connection, false);
453
+    }
454
+
455
+    if (!connection_edit_items.empty()) {
456
+        onConnectionItemSelected(connection_edit_items.first());
457
+    }
458
+}
459
+
460
+void Editor::createConnectionItem(Connection* connection, bool hide) {
461
+    Map *connected_map = project->getMap(connection->map_name);
462
+    QPixmap pixmap = connected_map->renderConnection(*connection);
463
+    int offset = connection->offset.toInt(nullptr, 0);
464
+    int x = 0, y = 0;
465
+    if (connection->direction == "up") {
466
+        x = offset * 16;
467
+        y = -pixmap.height();
468
+    } else if (connection->direction == "down") {
469
+        x = offset * 16;
470
+        y = map->getHeight() * 16;
471
+    } else if (connection->direction == "left") {
472
+        x = -pixmap.width();
473
+        y = offset * 16;
474
+    } else if (connection->direction == "right") {
475
+        x = map->getWidth() * 16;
476
+        y = offset * 16;
234 477
     }
478
+
479
+    QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
480
+    item->setZValue(-1);
481
+    item->setX(x);
482
+    item->setY(y);
483
+    scene->addItem(item);
484
+    map->connection_items.append(item);
485
+    item->setVisible(!hide);
486
+
487
+    ConnectionPixmapItem *connection_edit_item = new ConnectionPixmapItem(pixmap, connection, x, y, map->getWidth(), map->getHeight());
488
+    connection_edit_item->setX(x);
489
+    connection_edit_item->setY(y);
490
+    connection_edit_item->setZValue(-1);
491
+    scene->addItem(connection_edit_item);
492
+    connect(connection_edit_item, SIGNAL(connectionMoved(Connection*)), this, SLOT(onConnectionMoved(Connection*)));
493
+    connect(connection_edit_item, SIGNAL(connectionItemSelected(ConnectionPixmapItem*)), this, SLOT(onConnectionItemSelected(ConnectionPixmapItem*)));
494
+    connect(connection_edit_item, SIGNAL(connectionItemDoubleClicked(ConnectionPixmapItem*)), this, SLOT(onConnectionItemDoubleClicked(ConnectionPixmapItem*)));
495
+    connection_edit_items.append(connection_edit_item);
235 496
 }
236 497
 
237 498
 void Editor::displayMapBorder() {
@@ -243,6 +504,7 @@ void Editor::displayMapBorder() {
243 504
         item->setY(y * 16);
244 505
         item->setZValue(-2);
245 506
         scene->addItem(item);
507
+        borderItems.append(item);
246 508
     }
247 509
 }
248 510
 
@@ -252,17 +514,213 @@ void Editor::displayMapGrid() {
252 514
     for (int i = 0; i <= map->getWidth(); i++) {
253 515
         int x = i * 16;
254 516
         QGraphicsLineItem *line = scene->addLine(x, 0, x, pixelHeight);
255
-        line->setVisible(gridToggleCheckbox->isChecked());
256
-        connect(gridToggleCheckbox, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
517
+        line->setVisible(ui->checkBox_ToggleGrid->isChecked());
518
+        connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
257 519
     }
258 520
     for (int j = 0; j <= map->getHeight(); j++) {
259 521
         int y = j * 16;
260 522
         QGraphicsLineItem *line = scene->addLine(0, y, pixelWidth, y);
261
-        line->setVisible(gridToggleCheckbox->isChecked());
262
-        connect(gridToggleCheckbox, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
523
+        line->setVisible(ui->checkBox_ToggleGrid->isChecked());
524
+        connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
263 525
     }
264 526
 }
265 527
 
528
+void Editor::updateConnectionOffset(int offset) {
529
+    if (!selected_connection_item)
530
+        return;
531
+
532
+    selected_connection_item->blockSignals(true);
533
+    offset = qMin(offset, selected_connection_item->getMaxOffset());
534
+    offset = qMax(offset, selected_connection_item->getMinOffset());
535
+    selected_connection_item->connection->offset = QString::number(offset);
536
+    if (selected_connection_item->connection->direction == "up" || selected_connection_item->connection->direction == "down") {
537
+        selected_connection_item->setX(selected_connection_item->initialX + (offset - selected_connection_item->initialOffset) * 16);
538
+    } else if (selected_connection_item->connection->direction == "left" || selected_connection_item->connection->direction == "right") {
539
+        selected_connection_item->setY(selected_connection_item->initialY + (offset - selected_connection_item->initialOffset) * 16);
540
+    }
541
+    selected_connection_item->blockSignals(false);
542
+    updateMirroredConnectionOffset(selected_connection_item->connection);
543
+}
544
+
545
+void Editor::setConnectionMap(QString mapName) {
546
+    if (!mapName.isEmpty() && !project->mapNames->contains(mapName)) {
547
+        qDebug() << "Invalid map name " << mapName << " specified for connection.";
548
+        return;
549
+    }
550
+    if (!selected_connection_item)
551
+        return;
552
+
553
+    if (mapName.isEmpty()) {
554
+        removeCurrentConnection();
555
+        return;
556
+    }
557
+
558
+    QString originalMapName = selected_connection_item->connection->map_name;
559
+    setConnectionEditControlsEnabled(true);
560
+    selected_connection_item->connection->map_name = mapName;
561
+    setCurrentConnectionDirection(selected_connection_item->connection->direction);
562
+    updateMirroredConnectionMap(selected_connection_item->connection, originalMapName);
563
+}
564
+
565
+void Editor::addNewConnection() {
566
+    // Find direction with least number of connections.
567
+    QMap<QString, int> directionCounts = QMap<QString, int>({{"up", 0}, {"right", 0}, {"down", 0}, {"left", 0}});
568
+    for (Connection* connection : map->connections) {
569
+        directionCounts[connection->direction]++;
570
+    }
571
+    QString minDirection = "up";
572
+    int minCount = INT_MAX;
573
+    for (QString direction : directionCounts.keys()) {
574
+        if (directionCounts[direction] < minCount) {
575
+            minDirection = direction;
576
+            minCount = directionCounts[direction];
577
+        }
578
+    }
579
+
580
+    // Don't connect the map to itself.
581
+    QString defaultMapName = project->mapNames->first();
582
+    if (defaultMapName == map->name) {
583
+        defaultMapName = project->mapNames->value(1);
584
+    }
585
+
586
+    Connection* newConnection = new Connection;
587
+    newConnection->direction = minDirection;
588
+    newConnection->offset = "0";
589
+    newConnection->map_name = defaultMapName;
590
+    map->connections.append(newConnection);
591
+    createConnectionItem(newConnection, true);
592
+    onConnectionItemSelected(connection_edit_items.last());
593
+    ui->label_NumConnections->setText(QString::number(map->connections.length()));
594
+
595
+    updateMirroredConnection(newConnection, newConnection->direction, newConnection->map_name);
596
+}
597
+
598
+void Editor::updateMirroredConnectionOffset(Connection* connection) {
599
+    updateMirroredConnection(connection, connection->direction, connection->map_name);
600
+}
601
+void Editor::updateMirroredConnectionDirection(Connection* connection, QString originalDirection) {
602
+    updateMirroredConnection(connection, originalDirection, connection->map_name);
603
+}
604
+void Editor::updateMirroredConnectionMap(Connection* connection, QString originalMapName) {
605
+    updateMirroredConnection(connection, connection->direction, originalMapName);
606
+}
607
+void Editor::removeMirroredConnection(Connection* connection) {
608
+    updateMirroredConnection(connection, connection->direction, connection->map_name, true);
609
+}
610
+void Editor::updateMirroredConnection(Connection* connection, QString originalDirection, QString originalMapName, bool isDelete) {
611
+    if (!ui->checkBox_MirrorConnections->isChecked())
612
+        return;
613
+
614
+    static QMap<QString, QString> oppositeDirections = QMap<QString, QString>({
615
+        {"up", "down"}, {"right", "left"},
616
+        {"down", "up"}, {"left", "right"},
617
+        {"dive", "emerge"},{"emerge", "dive"}});
618
+    QString oppositeDirection = oppositeDirections.value(originalDirection);
619
+
620
+    // Find the matching connection in the connected map.
621
+    QMap<QString, Map*> *mapcache = project->map_cache;
622
+    Connection* mirrorConnection = NULL;
623
+    Map* otherMap = project->getMap(originalMapName);
624
+    for (Connection* conn : otherMap->connections) {
625
+        if (conn->direction == oppositeDirection && conn->map_name == map->name) {
626
+            mirrorConnection = conn;
627
+        }
628
+    }
629
+
630
+    if (isDelete) {
631
+        if (mirrorConnection) {
632
+            otherMap->connections.removeOne(mirrorConnection);
633
+            delete mirrorConnection;
634
+        }
635
+        return;
636
+    }
637
+
638
+    if (connection->direction != originalDirection || connection->map_name != originalMapName) {
639
+        if (mirrorConnection) {
640
+            otherMap->connections.removeOne(mirrorConnection);
641
+            delete mirrorConnection;
642
+            mirrorConnection = NULL;
643
+            otherMap = project->getMap(connection->map_name);
644
+        }
645
+    }
646
+
647
+    // Create a new mirrored connection, if a matching one doesn't already exist.
648
+    if (!mirrorConnection) {
649
+        mirrorConnection = new Connection;
650
+        mirrorConnection->direction = oppositeDirections.value(connection->direction);
651
+        mirrorConnection->map_name = map->name;
652
+        otherMap->connections.append(mirrorConnection);
653
+    }
654
+
655
+    mirrorConnection->offset = QString::number(-connection->offset.toInt());
656
+}
657
+
658
+void Editor::removeCurrentConnection() {
659
+    if (!selected_connection_item)
660
+        return;
661
+
662
+    map->connections.removeOne(selected_connection_item->connection);
663
+    connection_edit_items.removeOne(selected_connection_item);
664
+    removeMirroredConnection(selected_connection_item->connection);
665
+
666
+    scene->removeItem(selected_connection_item);
667
+    delete selected_connection_item;
668
+    selected_connection_item = NULL;
669
+    setConnectionEditControlsEnabled(false);
670
+    ui->spinBox_ConnectionOffset->setValue(0);
671
+    ui->label_NumConnections->setText(QString::number(map->connections.length()));
672
+
673
+    if (connection_edit_items.length() > 0) {
674
+        onConnectionItemSelected(connection_edit_items.last());
675
+    }
676
+}
677
+
678
+void Editor::updateDiveMap(QString mapName) {
679
+    updateDiveEmergeMap(mapName, "dive");
680
+}
681
+
682
+void Editor::updateEmergeMap(QString mapName) {
683
+    updateDiveEmergeMap(mapName, "emerge");
684
+}
685
+
686
+void Editor::updateDiveEmergeMap(QString mapName, QString direction) {
687
+    if (!mapName.isEmpty() && !project->mapNamesToMapConstants->contains(mapName)) {
688
+        qDebug() << "Invalid " << direction << " map connection: " << mapName;
689
+        return;
690
+    }
691
+
692
+    Connection* connection = NULL;
693
+    for (Connection* conn : map->connections) {
694
+        if (conn->direction == direction) {
695
+            connection = conn;
696
+            break;
697
+        }
698
+    }
699
+
700
+    if (mapName.isEmpty()) {
701
+        // Remove dive/emerge connection
702
+        if (connection) {
703
+            map->connections.removeOne(connection);
704
+            removeMirroredConnection(connection);
705
+        }
706
+    } else {
707
+        if (!connection) {
708
+            connection = new Connection;
709
+            connection->direction = direction;
710
+            connection->offset = "0";
711
+            connection->map_name = mapName;
712
+            map->connections.append(connection);
713
+            updateMirroredConnection(connection, connection->direction, connection->map_name);
714
+        } else {
715
+            QString originalMapName = connection->map_name;
716
+            connection->map_name = mapName;
717
+            updateMirroredConnectionMap(connection, originalMapName);
718
+        }
719
+    }
720
+
721
+    ui->label_NumConnections->setText(QString::number(map->connections.length()));
722
+}
723
+
266 724
 void MetatilesPixmapItem::paintTileChanged(Map *map) {
267 725
     draw();
268 726
 }
@@ -363,6 +821,62 @@ void CollisionMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
363 821
     }
364 822
 }
365 823
 
824
+int ConnectionPixmapItem::getMinOffset() {
825
+    if (connection->direction == "up" || connection->direction == "down")
826
+        return 1 - (this->pixmap().width() / 16);
827
+    else
828
+        return 1 - (this->pixmap().height() / 16);
829
+}
830
+int ConnectionPixmapItem::getMaxOffset() {
831
+    if (connection->direction == "up" || connection->direction == "down")
832
+        return baseMapWidth - 1;
833
+    else
834
+        return baseMapHeight - 1;
835
+}
836
+QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value)
837
+{
838
+    if (change == ItemPositionChange) {
839
+        QPointF newPos = value.toPointF();
840
+
841
+        qreal x, y;
842
+        int newOffset = initialOffset;
843
+        if (connection->direction == "up" || connection->direction == "down") {
844
+            x = round(newPos.x() / 16) * 16;
845
+            newOffset += (x - initialX) / 16;
846
+            newOffset = qMin(newOffset, this->getMaxOffset());
847
+            newOffset = qMax(newOffset, this->getMinOffset());
848
+            x = newOffset * 16;
849
+        }
850
+        else {
851
+            x = initialX;
852
+        }
853
+
854
+        if (connection->direction == "right" || connection->direction == "left") {
855
+            y = round(newPos.y() / 16) * 16;
856
+            newOffset += (y - initialY) / 16;
857
+            newOffset = qMin(newOffset, this->getMaxOffset());
858
+            newOffset = qMax(newOffset, this->getMinOffset());
859
+            y = newOffset * 16;
860
+        }
861
+        else {
862
+            y = initialY;
863
+        }
864
+
865
+        connection->offset = QString::number(newOffset);
866
+        emit connectionMoved(connection);
867
+        return QPointF(x, y);
868
+    }
869
+    else {
870
+        return QGraphicsItem::itemChange(change, value);
871
+    }
872
+}
873
+void ConnectionPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent* event) {
874
+    emit connectionItemSelected(this);
875
+}
876
+void ConnectionPixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) {
877
+    emit connectionItemDoubleClicked(this);
878
+}
879
+
366 880
 void ElevationMetatilesPixmapItem::updateCurHoveredMetatile(QPointF pos) {
367 881
     int x = ((int)pos.x()) / 16;
368 882
     int y = ((int)pos.y()) / 16;

+ 82
- 2
editor.h View File

@@ -9,10 +9,12 @@
9 9
 #include <QCheckBox>
10 10
 
11 11
 #include "project.h"
12
+#include "ui_mainwindow.h"
12 13
 
13 14
 class DraggablePixmapItem;
14 15
 class MapPixmapItem;
15 16
 class CollisionPixmapItem;
17
+class ConnectionPixmapItem;
16 18
 class MetatilesPixmapItem;
17 19
 class CollisionMetatilesPixmapItem;
18 20
 class ElevationMetatilesPixmapItem;
@@ -21,12 +23,12 @@ class Editor : public QObject
21 23
 {
22 24
     Q_OBJECT
23 25
 public:
24
-    Editor();
26
+    Editor(Ui::MainWindow* ui);
25 27
 public:
28
+    Ui::MainWindow* ui;
26 29
     QObject *parent = NULL;
27 30
     Project *project = NULL;
28 31
     Map *map = NULL;
29
-    QCheckBox *gridToggleCheckbox = NULL;
30 32
     void saveProject();
31 33
     void save();
32 34
     void undo();
@@ -44,6 +46,17 @@ public:
44 46
     void setEditingMap();
45 47
     void setEditingCollision();
46 48
     void setEditingObjects();
49
+    void setEditingConnections();
50
+    void setCurrentConnectionDirection(QString curDirection);
51
+    void updateCurrentConnectionDirection(QString curDirection);
52
+    void setConnectionsVisibility(bool visible);
53
+    void updateConnectionOffset(int offset);
54
+    void setConnectionMap(QString mapName);
55
+    void addNewConnection();
56
+    void removeCurrentConnection();
57
+    void updateDiveMap(QString mapName);
58
+    void updateEmergeMap(QString mapName);
59
+    void setSelectedConnectionFromMap(QString mapName);
47 60
 
48 61
     DraggablePixmapItem *addMapObject(Event *event);
49 62
     void selectMapObject(DraggablePixmapItem *object);
@@ -58,8 +71,11 @@ public:
58 71
     QGraphicsScene *scene = NULL;
59 72
     QGraphicsPixmapItem *current_view = NULL;
60 73
     MapPixmapItem *map_item = NULL;
74
+    ConnectionPixmapItem* selected_connection_item = NULL;
75
+    QList<ConnectionPixmapItem*> connection_edit_items;
61 76
     CollisionPixmapItem *collision_item = NULL;
62 77
     QGraphicsItemGroup *objects_group = NULL;
78
+    QList<QGraphicsPixmapItem*> borderItems;
63 79
 
64 80
     QGraphicsScene *scene_metatiles = NULL;
65 81
     QGraphicsScene *scene_collision_metatiles = NULL;
@@ -77,13 +93,34 @@ public:
77 93
     void objectsView_onMouseMove(QMouseEvent *event);
78 94
     void objectsView_onMouseRelease(QMouseEvent *event);
79 95
 
96
+private:
97
+    void setConnectionItemsVisible(bool);
98
+    void setBorderItemsVisible(bool, qreal = 1);
99
+    void setConnectionEditControlValues(Connection*);
100
+    void setConnectionEditControlsEnabled(bool);
101
+    void createConnectionItem(Connection* connection, bool hide);
102
+    void populateConnectionMapPickers();
103
+    void setDiveEmergeControls();
104
+    void updateDiveEmergeMap(QString mapName, QString direction);
105
+    void onConnectionOffsetChanged(int newOffset);
106
+    void removeMirroredConnection(Connection*);
107
+    void updateMirroredConnectionOffset(Connection*);
108
+    void updateMirroredConnectionDirection(Connection*, QString);
109
+    void updateMirroredConnectionMap(Connection*, QString);
110
+    void updateMirroredConnection(Connection*, QString, QString, bool isDelete = false);
111
+
80 112
 private slots:
81 113
     void mouseEvent_map(QGraphicsSceneMouseEvent *event, MapPixmapItem *item);
82 114
     void mouseEvent_collision(QGraphicsSceneMouseEvent *event, CollisionPixmapItem *item);
115
+    void onConnectionMoved(Connection*);
116
+    void onConnectionItemSelected(ConnectionPixmapItem* connectionItem);
117
+    void onConnectionItemDoubleClicked(ConnectionPixmapItem* connectionItem);
118
+    void onConnectionDirectionChanged(QString newDirection);
83 119
 
84 120
 signals:
85 121
     void objectsChanged();
86 122
     void selectedObjectsChanged();
123
+    void loadMapRequested(QString, QString);
87 124
 };
88 125
 
89 126
 
@@ -240,6 +277,49 @@ protected:
240 277
     void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
241 278
 };
242 279
 
280
+class ConnectionPixmapItem : public QObject, public QGraphicsPixmapItem {
281
+    Q_OBJECT
282
+public:
283
+    ConnectionPixmapItem(QPixmap pixmap, Connection* connection, int x, int y, int baseMapWidth, int baseMapHeight): QGraphicsPixmapItem(pixmap) {
284
+        this->basePixmap = pixmap;
285
+        this->connection = connection;
286
+        setFlag(ItemIsMovable);
287
+        setFlag(ItemSendsGeometryChanges);
288
+        this->initialX = x;
289
+        this->initialY = y;
290
+        this->initialOffset = connection->offset.toInt();
291
+        this->baseMapWidth = baseMapWidth;
292
+        this->baseMapHeight = baseMapHeight;
293
+    }
294
+    void render(qreal opacity = 1) {
295
+        QPixmap newPixmap = basePixmap.copy(0, 0, basePixmap.width(), basePixmap.height());
296
+        if (opacity < 1) {
297
+            QPainter painter(&newPixmap);
298
+            int alpha = (int)(255 * (1 - opacity));
299
+            painter.fillRect(0, 0, newPixmap.width(), newPixmap.height(), QColor(0, 0, 0, alpha));
300
+            painter.end();
301
+        }
302
+        this->setPixmap(newPixmap);
303
+    }
304
+    int getMinOffset();
305
+    int getMaxOffset();
306
+    QPixmap basePixmap;
307
+    Connection* connection;
308
+    int initialX;
309
+    int initialY;
310
+    int initialOffset;
311
+    int baseMapWidth;
312
+    int baseMapHeight;
313
+protected:
314
+    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
315
+    void mousePressEvent(QGraphicsSceneMouseEvent*);
316
+    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent*);
317
+signals:
318
+    void connectionItemSelected(ConnectionPixmapItem* connectionItem);
319
+    void connectionItemDoubleClicked(ConnectionPixmapItem* connectionItem);
320
+    void connectionMoved(Connection*);
321
+};
322
+
243 323
 class MetatilesPixmapItem : public QObject, public QGraphicsPixmapItem {
244 324
     Q_OBJECT
245 325
 public:

+ 1
- 0
graphicsview.cpp View File

@@ -1,4 +1,5 @@
1 1
 #include "graphicsview.h"
2
+#include "editor.h"
2 3
 
3 4
 void GraphicsView::mousePressEvent(QMouseEvent *event) {
4 5
     QGraphicsView::mousePressEvent(event);

+ 2
- 2
graphicsview.h View File

@@ -4,7 +4,7 @@
4 4
 #include <QGraphicsView>
5 5
 #include <QMouseEvent>
6 6
 
7
-#include "editor.h"
7
+class Editor;
8 8
 
9 9
 /*
10 10
 class GraphicsView_Object : public QObject
@@ -26,7 +26,7 @@ public:
26 26
 
27 27
 public:
28 28
 //    GraphicsView_Object object;
29
-    Editor *editor = NULL;
29
+    Editor *editor;
30 30
 protected:
31 31
     void mousePressEvent(QMouseEvent *event);
32 32
     void mouseMoveEvent(QMouseEvent *event);

+ 50
- 11
mainwindow.cpp View File

@@ -26,10 +26,10 @@ MainWindow::MainWindow(QWidget *parent) :
26 26
     ui->setupUi(this);
27 27
     new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo()));
28 28
 
29
-    editor = new Editor;
30
-    editor->gridToggleCheckbox = ui->checkBox_ToggleGrid;
29
+    editor = new Editor(ui);
31 30
     connect(editor, SIGNAL(objectsChanged()), this, SLOT(updateSelectedObjects()));
32 31
     connect(editor, SIGNAL(selectedObjectsChanged()), this, SLOT(updateSelectedObjects()));
32
+    connect(editor, SIGNAL(loadMapRequested(QString, QString)), this, SLOT(onLoadMapRequested(QString, QString)));
33 33
 
34 34
     on_toolButton_Paint_clicked();
35 35
 
@@ -143,15 +143,7 @@ void MainWindow::setMap(QString map_name) {
143 143
     }
144 144
     editor->setMap(map_name);
145 145
 
146
-    if (ui->tabWidget->currentIndex() == 1) {
147
-        editor->setEditingObjects();
148
-    } else {
149
-        if (ui->tabWidget_2->currentIndex() == 1) {
150
-            editor->setEditingCollision();
151
-        } else {
152
-            editor->setEditingMap();
153
-        }
154
-    }
146
+    on_tabWidget_currentChanged(ui->tabWidget->currentIndex());
155 147
 
156 148
     ui->graphicsView_Map->setScene(editor->scene);
157 149
     ui->graphicsView_Map->setSceneRect(editor->scene->sceneRect());
@@ -162,6 +154,10 @@ void MainWindow::setMap(QString map_name) {
162 154
     ui->graphicsView_Objects_Map->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2);
163 155
     ui->graphicsView_Objects_Map->editor = editor;
164 156
 
157
+    ui->graphicsView_Connections->setScene(editor->scene);
158
+    ui->graphicsView_Connections->setSceneRect(editor->scene->sceneRect());
159
+    ui->graphicsView_Connections->setFixedSize(editor->scene->width() + 2, editor->scene->height() + 2);
160
+
165 161
     ui->graphicsView_Metatiles->setScene(editor->scene_metatiles);
166 162
     //ui->graphicsView_Metatiles->setSceneRect(editor->scene_metatiles->sceneRect());
167 163
     ui->graphicsView_Metatiles->setFixedSize(editor->metatiles_item->pixmap().width() + 2, editor->metatiles_item->pixmap().height() + 2);
@@ -294,6 +290,7 @@ void MainWindow::loadDataStructures() {
294 290
     project->readItemNames();
295 291
     project->readFlagNames();
296 292
     project->readVarNames();
293
+    project->readMapsWithConnections();
297 294
 }
298 295
 
299 296
 void MainWindow::populateMapList() {
@@ -491,6 +488,8 @@ void MainWindow::on_tabWidget_currentChanged(int index)
491 488
         on_tabWidget_2_currentChanged(ui->tabWidget_2->currentIndex());
492 489
     } else if (index == 1) {
493 490
         editor->setEditingObjects();
491
+    } else if (index == 3) {
492
+        editor->setEditingConnections();
494 493
     }
495 494
 }
496 495
 
@@ -756,6 +755,11 @@ void MainWindow::checkToolButtons() {
756 755
     ui->toolButton_Dropper->setChecked(editor->map_edit_mode == "pick");
757 756
 }
758 757
 
758
+void MainWindow::onLoadMapRequested(QString mapName, QString fromMapName) {
759
+    setMap(mapName);
760
+    editor->setSelectedConnectionFromMap(fromMapName);
761
+}
762
+
759 763
 void MainWindow::onMapChanged(Map *map) {
760 764
     updateMapList();
761 765
 }
@@ -768,3 +772,38 @@ void MainWindow::on_action_Export_Map_Image_triggered()
768 772
         editor->map_item->pixmap().save(filepath);
769 773
     }
770 774
 }
775
+
776
+void MainWindow::on_comboBox_ConnectionDirection_currentIndexChanged(const QString &direction)
777
+{
778
+    editor->updateCurrentConnectionDirection(direction);
779
+}
780
+
781
+void MainWindow::on_spinBox_ConnectionOffset_valueChanged(int offset)
782
+{
783
+    editor->updateConnectionOffset(offset);
784
+}
785
+
786
+void MainWindow::on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName)
787
+{
788
+    editor->setConnectionMap(mapName);
789
+}
790
+
791
+void MainWindow::on_pushButton_AddConnection_clicked()
792
+{
793
+    editor->addNewConnection();
794
+}
795
+
796
+void MainWindow::on_pushButton_RemoveConnection_clicked()
797
+{
798
+    editor->removeCurrentConnection();
799
+}
800
+
801
+void MainWindow::on_comboBox_DiveMap_currentTextChanged(const QString &mapName)
802
+{
803
+    editor->updateDiveMap(mapName);
804
+}
805
+
806
+void MainWindow::on_comboBox_EmergeMap_currentTextChanged(const QString &mapName)
807
+{
808
+    editor->updateEmergeMap(mapName);
809
+}

+ 15
- 0
mainwindow.h View File

@@ -36,6 +36,7 @@ private slots:
36 36
     void undo();
37 37
     void redo();
38 38
 
39
+    void onLoadMapRequested(QString, QString);
39 40
     void onMapChanged(Map *map);
40 41
 
41 42
     void on_action_Save_triggered();
@@ -74,6 +75,20 @@ private slots:
74 75
 
75 76
     void on_action_Export_Map_Image_triggered();
76 77
 
78
+    void on_comboBox_ConnectionDirection_currentIndexChanged(const QString &arg1);
79
+
80
+    void on_spinBox_ConnectionOffset_valueChanged(int offset);
81
+
82
+    void on_comboBox_ConnectedMap_currentTextChanged(const QString &mapName);
83
+
84
+    void on_pushButton_AddConnection_clicked();
85
+
86
+    void on_pushButton_RemoveConnection_clicked();
87
+
88
+    void on_comboBox_DiveMap_currentTextChanged(const QString &mapName);
89
+
90
+    void on_comboBox_EmergeMap_currentTextChanged(const QString &mapName);
91
+
77 92
 private:
78 93
     Ui::MainWindow *ui;
79 94
     QStandardItemModel *mapListModel;

+ 494
- 0
mainwindow.ui View File

@@ -1199,6 +1199,500 @@
1199 1199
          </widget>
1200 1200
         </widget>
1201 1201
        </widget>
1202
+       <widget class="QWidget" name="tab_Connections">
1203
+        <attribute name="title">
1204
+         <string>Connections</string>
1205
+        </attribute>
1206
+        <layout class="QGridLayout" name="gridLayout_12">
1207
+         <property name="leftMargin">
1208
+          <number>0</number>
1209
+         </property>
1210
+         <property name="topMargin">
1211
+          <number>0</number>
1212
+         </property>
1213
+         <property name="rightMargin">
1214
+          <number>0</number>
1215
+         </property>
1216
+         <property name="bottomMargin">
1217
+          <number>0</number>
1218
+         </property>
1219
+         <item row="3" column="0">
1220
+          <widget class="QFrame" name="gridFrame">
1221
+           <property name="sizePolicy">
1222
+            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1223
+             <horstretch>0</horstretch>
1224
+             <verstretch>0</verstretch>
1225
+            </sizepolicy>
1226
+           </property>
1227
+           <property name="frameShape">
1228
+            <enum>QFrame::StyledPanel</enum>
1229
+           </property>
1230
+           <property name="frameShadow">
1231
+            <enum>QFrame::Raised</enum>
1232
+           </property>
1233
+           <layout class="QGridLayout" name="gridLayout_11">
1234
+            <property name="leftMargin">
1235
+             <number>0</number>
1236
+            </property>
1237
+            <property name="topMargin">
1238
+             <number>0</number>
1239
+            </property>
1240
+            <property name="rightMargin">
1241
+             <number>0</number>
1242
+            </property>
1243
+            <property name="bottomMargin">
1244
+             <number>0</number>
1245
+            </property>
1246
+            <property name="spacing">
1247
+             <number>0</number>
1248
+            </property>
1249
+            <item row="1" column="0">
1250
+             <widget class="QFrame" name="horizontalFrame">
1251
+              <property name="sizePolicy">
1252
+               <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
1253
+                <horstretch>0</horstretch>
1254
+                <verstretch>0</verstretch>
1255
+               </sizepolicy>
1256
+              </property>
1257
+              <property name="minimumSize">
1258
+               <size>
1259
+                <width>0</width>
1260
+                <height>32</height>
1261
+               </size>
1262
+              </property>
1263
+              <property name="frameShape">
1264
+               <enum>QFrame::StyledPanel</enum>
1265
+              </property>
1266
+              <property name="frameShadow">
1267
+               <enum>QFrame::Raised</enum>
1268
+              </property>
1269
+              <layout class="QHBoxLayout" name="horizontalLayout_4">
1270
+               <property name="spacing">
1271
+                <number>4</number>
1272
+               </property>
1273
+               <property name="leftMargin">
1274
+                <number>4</number>
1275
+               </property>
1276
+               <property name="topMargin">
1277
+                <number>4</number>
1278
+               </property>
1279
+               <property name="rightMargin">
1280
+                <number>4</number>
1281
+               </property>
1282
+               <property name="bottomMargin">
1283
+                <number>4</number>
1284
+               </property>
1285
+               <item>
1286
+                <widget class="QPushButton" name="pushButton_AddConnection">
1287
+                 <property name="sizePolicy">
1288
+                  <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1289
+                   <horstretch>0</horstretch>
1290
+                   <verstretch>0</verstretch>
1291
+                  </sizepolicy>
1292
+                 </property>
1293
+                 <property name="text">
1294
+                  <string/>
1295
+                 </property>
1296
+                 <property name="icon">
1297
+                  <iconset>
1298
+                   <activeon>:/icons/add.ico</activeon>
1299
+                  </iconset>
1300
+                 </property>
1301
+                </widget>
1302
+               </item>
1303
+               <item>
1304
+                <widget class="QPushButton" name="pushButton_RemoveConnection">
1305
+                 <property name="text">
1306
+                  <string/>
1307
+                 </property>
1308
+                 <property name="icon">
1309
+                  <iconset>
1310
+                   <activeon>:/icons/delete.ico</activeon>
1311
+                  </iconset>
1312
+                 </property>
1313
+                </widget>
1314
+               </item>
1315
+               <item>
1316
+                <widget class="QLabel" name="label_13">
1317
+                 <property name="text">
1318
+                  <string>Number of Connections:</string>
1319
+                 </property>
1320
+                </widget>
1321
+               </item>
1322
+               <item>
1323
+                <widget class="QLabel" name="label_NumConnections">
1324
+                 <property name="text">
1325
+                  <string/>
1326
+                 </property>
1327
+                </widget>
1328
+               </item>
1329
+               <item>
1330
+                <spacer name="horizontalSpacer_11">
1331
+                 <property name="orientation">
1332
+                  <enum>Qt::Horizontal</enum>
1333
+                 </property>
1334
+                 <property name="sizeType">
1335
+                  <enum>QSizePolicy::Maximum</enum>
1336
+                 </property>
1337
+                 <property name="sizeHint" stdset="0">
1338
+                  <size>
1339
+                   <width>40</width>
1340
+                   <height>20</height>
1341
+                  </size>
1342
+                 </property>
1343
+                </spacer>
1344
+               </item>
1345
+               <item>
1346
+                <widget class="QCheckBox" name="checkBox_MirrorConnections">
1347
+                 <property name="sizePolicy">
1348
+                  <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
1349
+                   <horstretch>0</horstretch>
1350
+                   <verstretch>0</verstretch>
1351
+                  </sizepolicy>
1352
+                 </property>
1353
+                 <property name="text">
1354
+                  <string>Mirror</string>
1355
+                 </property>
1356
+                 <property name="checked">
1357
+                  <bool>true</bool>
1358
+                 </property>
1359
+                </widget>
1360
+               </item>
1361
+               <item>
1362
+                <spacer name="horizontalSpacer_9">
1363
+                 <property name="orientation">
1364
+                  <enum>Qt::Horizontal</enum>
1365
+                 </property>
1366
+                 <property name="sizeHint" stdset="0">
1367
+                  <size>
1368
+                   <width>40</width>
1369
+                   <height>20</height>
1370
+                  </size>
1371
+                 </property>
1372
+                </spacer>
1373
+               </item>
1374
+              </layout>
1375
+             </widget>
1376
+            </item>
1377
+            <item row="2" column="0">
1378
+             <widget class="QFrame" name="horizontalFrame">
1379
+              <property name="sizePolicy">
1380
+               <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
1381
+                <horstretch>0</horstretch>
1382
+                <verstretch>0</verstretch>
1383
+               </sizepolicy>
1384
+              </property>
1385
+              <property name="minimumSize">
1386
+               <size>
1387
+                <width>0</width>
1388
+                <height>32</height>
1389
+               </size>
1390
+              </property>
1391
+              <property name="frameShape">
1392
+               <enum>QFrame::StyledPanel</enum>
1393
+              </property>
1394
+              <property name="frameShadow">
1395
+               <enum>QFrame::Raised</enum>
1396
+              </property>
1397
+              <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0,0,0,0,0">
1398
+               <property name="spacing">
1399
+                <number>4</number>
1400
+               </property>
1401
+               <property name="leftMargin">
1402
+                <number>4</number>
1403
+               </property>
1404
+               <property name="topMargin">
1405
+                <number>4</number>
1406
+               </property>
1407
+               <property name="rightMargin">
1408
+                <number>4</number>
1409
+               </property>
1410
+               <property name="bottomMargin">
1411
+                <number>4</number>
1412
+               </property>
1413
+               <item>
1414
+                <widget class="QComboBox" name="comboBox_ConnectionDirection">
1415
+                 <item>
1416
+                  <property name="text">
1417
+                   <string>up</string>
1418
+                  </property>
1419
+                 </item>
1420
+                 <item>
1421
+                  <property name="text">
1422
+                   <string>right</string>
1423
+                  </property>
1424
+                 </item>
1425
+                 <item>
1426
+                  <property name="text">
1427
+                   <string>down</string>
1428
+                  </property>
1429
+                 </item>
1430
+                 <item>
1431
+                  <property name="text">
1432
+                   <string>left</string>
1433
+                  </property>
1434
+                 </item>
1435
+                </widget>
1436
+               </item>
1437
+               <item>
1438
+                <spacer name="horizontalSpacer_2">
1439
+                 <property name="orientation">
1440
+                  <enum>Qt::Horizontal</enum>
1441
+                 </property>
1442
+                 <property name="sizeType">
1443
+                  <enum>QSizePolicy::Fixed</enum>
1444
+                 </property>
1445
+                 <property name="sizeHint" stdset="0">
1446
+                  <size>
1447
+                   <width>20</width>
1448
+                   <height>20</height>
1449
+                  </size>
1450
+                 </property>
1451
+                </spacer>
1452
+               </item>
1453
+               <item>
1454
+                <widget class="QLabel" name="label_11">
1455
+                 <property name="text">
1456
+                  <string>Map</string>
1457
+                 </property>
1458
+                </widget>
1459
+               </item>
1460
+               <item>
1461
+                <widget class="QComboBox" name="comboBox_ConnectedMap">
1462
+                 <property name="editable">
1463
+                  <bool>true</bool>
1464
+                 </property>
1465
+                </widget>
1466
+               </item>
1467
+               <item>
1468
+                <widget class="QLabel" name="label_12">
1469
+                 <property name="text">
1470
+                  <string>Offset</string>
1471
+                 </property>
1472
+                </widget>
1473
+               </item>
1474
+               <item>
1475
+                <widget class="QSpinBox" name="spinBox_ConnectionOffset">
1476
+                 <property name="minimum">
1477
+                  <number>-999</number>
1478
+                 </property>
1479
+                 <property name="maximum">
1480
+                  <number>999</number>
1481
+                 </property>
1482
+                </widget>
1483
+               </item>
1484
+               <item>
1485
+                <spacer name="horizontalSpacer_6">
1486
+                 <property name="orientation">
1487
+                  <enum>Qt::Horizontal</enum>
1488
+                 </property>
1489
+                 <property name="sizeHint" stdset="0">
1490
+                  <size>
1491
+                   <width>40</width>
1492
+                   <height>20</height>
1493
+                  </size>
1494
+                 </property>
1495
+                </spacer>
1496
+               </item>
1497
+              </layout>
1498
+             </widget>
1499
+            </item>
1500
+            <item row="3" column="0">
1501
+             <widget class="QFrame" name="gridFrame">
1502
+              <property name="frameShape">
1503
+               <enum>QFrame::StyledPanel</enum>
1504
+              </property>
1505
+              <property name="frameShadow">
1506
+               <enum>QFrame::Raised</enum>
1507
+              </property>
1508
+              <layout class="QGridLayout" name="gridLayout_13">
1509
+               <property name="leftMargin">
1510
+                <number>0</number>
1511
+               </property>
1512
+               <property name="topMargin">
1513
+                <number>0</number>
1514
+               </property>
1515
+               <property name="rightMargin">
1516
+                <number>0</number>
1517
+               </property>
1518
+               <property name="bottomMargin">
1519
+                <number>0</number>
1520
+               </property>
1521
+               <property name="spacing">
1522
+                <number>0</number>
1523
+               </property>
1524
+               <item row="1" column="0">
1525
+                <widget class="QScrollArea" name="scrollArea_5">
1526
+                 <property name="sizePolicy">
1527
+                  <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1528
+                   <horstretch>1</horstretch>
1529
+                   <verstretch>0</verstretch>
1530
+                  </sizepolicy>
1531
+                 </property>
1532
+                 <property name="widgetResizable">
1533
+                  <bool>true</bool>
1534
+                 </property>
1535
+                 <widget class="QWidget" name="scrollAreaWidgetContents_3">
1536
+                  <property name="geometry">
1537
+                   <rect>
1538
+                    <x>0</x>
1539
+                    <y>0</y>
1540
+                    <width>826</width>
1541
+                    <height>557</height>
1542
+                   </rect>
1543
+                  </property>
1544
+                  <layout class="QGridLayout" name="gridLayout_14">
1545
+                   <item row="1" column="2">
1546
+                    <spacer name="horizontalSpacer_8">
1547
+                     <property name="orientation">
1548
+                      <enum>Qt::Horizontal</enum>
1549
+                     </property>
1550
+                     <property name="sizeHint" stdset="0">
1551
+                      <size>
1552
+                       <width>40</width>
1553
+                       <height>20</height>
1554
+                      </size>
1555
+                     </property>
1556
+                    </spacer>
1557
+                   </item>
1558
+                   <item row="2" column="1">
1559
+                    <spacer name="verticalSpacer_2">
1560
+                     <property name="orientation">
1561
+                      <enum>Qt::Vertical</enum>
1562
+                     </property>
1563
+                     <property name="sizeHint" stdset="0">
1564
+                      <size>
1565
+                       <width>20</width>
1566
+                       <height>40</height>
1567
+                      </size>
1568
+                     </property>
1569
+                    </spacer>
1570
+                   </item>
1571
+                   <item row="0" column="1">
1572
+                    <spacer name="verticalSpacer">
1573
+                     <property name="orientation">
1574
+                      <enum>Qt::Vertical</enum>
1575
+                     </property>
1576
+                     <property name="sizeHint" stdset="0">
1577
+                      <size>
1578
+                       <width>20</width>
1579
+                       <height>40</height>
1580
+                      </size>
1581
+                     </property>
1582
+                    </spacer>
1583
+                   </item>
1584
+                   <item row="1" column="1">
1585
+                    <widget class="QGraphicsView" name="graphicsView_Connections">
1586
+                     <property name="backgroundBrush">
1587
+                      <brush brushstyle="SolidPattern">
1588
+                       <color alpha="255">
1589
+                        <red>0</red>
1590
+                        <green>0</green>
1591
+                        <blue>0</blue>
1592
+                       </color>
1593
+                      </brush>
1594
+                     </property>
1595
+                    </widget>
1596
+                   </item>
1597
+                   <item row="1" column="0">
1598
+                    <spacer name="horizontalSpacer_7">
1599
+                     <property name="orientation">
1600
+                      <enum>Qt::Horizontal</enum>
1601
+                     </property>
1602
+                     <property name="sizeHint" stdset="0">
1603
+                      <size>
1604
+                       <width>40</width>
1605
+                       <height>20</height>
1606
+                      </size>
1607
+                     </property>
1608
+                    </spacer>
1609
+                   </item>
1610
+                  </layout>
1611
+                 </widget>
1612
+                </widget>
1613
+               </item>
1614
+               <item row="0" column="0">
1615
+                <widget class="QFrame" name="horizontalFrame">
1616
+                 <property name="sizePolicy">
1617
+                  <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
1618
+                   <horstretch>0</horstretch>
1619
+                   <verstretch>0</verstretch>
1620
+                  </sizepolicy>
1621
+                 </property>
1622
+                 <property name="frameShape">
1623
+                  <enum>QFrame::StyledPanel</enum>
1624
+                 </property>
1625
+                 <property name="frameShadow">
1626
+                  <enum>QFrame::Raised</enum>
1627
+                 </property>
1628
+                 <layout class="QHBoxLayout" name="horizontalLayout_6">
1629
+                  <property name="spacing">
1630
+                   <number>4</number>
1631
+                  </property>
1632
+                  <property name="leftMargin">
1633
+                   <number>4</number>
1634
+                  </property>
1635
+                  <property name="topMargin">
1636
+                   <number>4</number>
1637
+                  </property>
1638
+                  <property name="rightMargin">
1639
+                   <number>4</number>
1640
+                  </property>
1641
+                  <property name="bottomMargin">
1642
+                   <number>4</number>
1643
+                  </property>
1644
+                  <item>
1645
+                   <widget class="QLabel" name="label_14">
1646
+                    <property name="text">
1647
+                     <string>Dive Map</string>
1648
+                    </property>
1649
+                   </widget>
1650
+                  </item>
1651
+                  <item>
1652
+                   <widget class="QComboBox" name="comboBox_DiveMap">
1653
+                    <property name="editable">
1654
+                     <bool>true</bool>
1655
+                    </property>
1656
+                   </widget>
1657
+                  </item>
1658
+                  <item>
1659
+                   <widget class="QLabel" name="label_15">
1660
+                    <property name="text">
1661
+                     <string>Emerge Map</string>
1662
+                    </property>
1663
+                   </widget>
1664
+                  </item>
1665
+                  <item>
1666
+                   <widget class="QComboBox" name="comboBox_EmergeMap">
1667
+                    <property name="editable">
1668
+                     <bool>true</bool>
1669
+                    </property>
1670
+                   </widget>
1671
+                  </item>
1672
+                  <item>
1673
+                   <spacer name="horizontalSpacer_10">
1674
+                    <property name="orientation">
1675
+                     <enum>Qt::Horizontal</enum>
1676
+                    </property>
1677
+                    <property name="sizeHint" stdset="0">
1678
+                     <size>
1679
+                      <width>40</width>
1680
+                      <height>20</height>
1681
+                     </size>
1682
+                    </property>
1683
+                   </spacer>
1684
+                  </item>
1685
+                 </layout>
1686
+                </widget>
1687
+               </item>
1688
+              </layout>
1689
+             </widget>
1690
+            </item>
1691
+           </layout>
1692
+          </widget>
1693
+         </item>
1694
+        </layout>
1695
+       </widget>
1202 1696
       </widget>
1203 1697
      </widget>
1204 1698
     </item>

+ 2
- 0
map.h View File

@@ -8,6 +8,7 @@
8 8
 #include <QPixmap>
9 9
 #include <QObject>
10 10
 #include <QDebug>
11
+#include <QGraphicsPixmapItem>
11 12
 
12 13
 
13 14
 template <typename T>
@@ -181,6 +182,7 @@ public:
181 182
     QMap<QString, QList<Event*>> events;
182 183
 
183 184
     QList<Connection*> connections;
185
+    QList<QGraphicsPixmapItem*> connection_items;
184 186
     QPixmap renderConnection(Connection);
185 187
 
186 188
     QImage border_image;

+ 1
- 0
parseutil.h View File

@@ -3,6 +3,7 @@
3 3
 
4 4
 #include <QString>
5 5
 #include <QList>
6
+#include <QMap>
6 7
 
7 8
 enum TokenType {
8 9
     Number,

+ 99
- 3
project.cpp View File

@@ -39,7 +39,8 @@ Map* Project::loadMap(QString map_name) {
39 39
     Map *map;
40 40
     if (map_cache->contains(map_name)) {
41 41
         map = map_cache->value(map_name);
42
-        if (map->hasUnsavedChanges()) {
42
+        // TODO: uncomment when undo/redo history is fully implemented for all actions.
43
+        if (true/*map->hasUnsavedChanges()*/) {
43 44
             return map;
44 45
         }
45 46
     } else {
@@ -67,6 +68,7 @@ void Project::loadMapConnections(Map *map) {
67 68
     }
68 69
 
69 70
     map->connections.clear();
71
+    map->connection_items.clear();
70 72
     if (!map->connections_label.isNull()) {
71 73
         QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name);
72 74
         QString text = readTextFile(path);
@@ -196,7 +198,14 @@ void Project::saveMapHeader(Map *map) {
196 198
     text += QString("\t.4byte %1\n").arg(map->attributes_label);
197 199
     text += QString("\t.4byte %1\n").arg(map->events_label);
198 200
     text += QString("\t.4byte %1\n").arg(map->scripts_label);
201
+
202
+    if (map->connections.length() == 0) {
203
+        map->connections_label = "0x0";
204
+    } else {
205
+        map->connections_label = QString("%1_MapConnections").arg(map->name);
206
+    }
199 207
     text += QString("\t.4byte %1\n").arg(map->connections_label);
208
+
200 209
     text += QString("\t.2byte %1\n").arg(map->song);
201 210
     text += QString("\t.2byte %1\n").arg(map->index);
202 211
     text += QString("\t.byte %1\n").arg(map->location);
@@ -209,6 +218,48 @@ void Project::saveMapHeader(Map *map) {
209 218
     saveTextFile(header_path, text);
210 219
 }
211 220
 
221
+void Project::saveMapConnections(Map *map) {
222
+    QString path = root + "/data/maps/" + map->name + "/connections.inc";
223
+    if (map->connections.length() > 0) {
224
+        QString text = "";
225
+        QString connectionsListLabel = QString("%1_MapConnectionsList").arg(map->name);
226
+        int numValidConnections = 0;
227
+        text += QString("%1::\n").arg(connectionsListLabel);
228
+        for (Connection* connection : map->connections) {
229
+            if (mapNamesToMapConstants->contains(connection->map_name)) {
230
+                text += QString("\tconnection %1, %2, %3\n")
231
+                        .arg(connection->direction)
232
+                        .arg(connection->offset)
233
+                        .arg(mapNamesToMapConstants->value(connection->map_name));
234
+                numValidConnections++;
235
+            } else {
236
+                qDebug() << QString("Failed to write map connection. %1 not a valid map name").arg(connection->map_name);
237
+            }
238
+        }
239
+        text += QString("\n");
240
+        text += QString("%1::\n").arg(map->connections_label);
241
+        text += QString("\t.4byte %1\n").arg(numValidConnections);
242
+        text += QString("\t.4byte %1\n").arg(connectionsListLabel);
243
+        saveTextFile(path, text);
244
+    } else {
245
+        deleteFile(path);
246
+    }
247
+
248
+    updateMapsWithConnections(map);
249
+}
250
+
251
+void Project::updateMapsWithConnections(Map *map) {
252
+    if (map->connections.length() > 0) {
253
+        if (!mapsWithConnections.contains(map->name)) {
254
+            mapsWithConnections.append(map->name);
255
+        }
256
+    } else {
257
+        if (mapsWithConnections.contains(map->name)) {
258
+            mapsWithConnections.removeOne(map->name);
259
+        }
260
+    }
261
+}
262
+
212 263
 void Project::readMapAttributesTable() {
213 264
     int curMapIndex = 1;
214 265
     QString attributesText = readTextFile(getMapAttributesTableFilepath());
@@ -679,6 +730,7 @@ void Project::saveMap(Map *map) {
679 730
 
680 731
     saveMapBorder(map);
681 732
     saveMapHeader(map);
733
+    saveMapConnections(map);
682 734
     saveBlockdata(map);
683 735
     saveMapEvents(map);
684 736
 
@@ -689,7 +741,9 @@ void Project::saveMap(Map *map) {
689 741
 }
690 742
 
691 743
 void Project::updateMapAttributes(Map* map) {
692
-    mapAttributesTableMaster.insert(map->index.toInt(nullptr, 0), map->name);
744
+    if (!mapAttributesTableMaster.contains(map->index.toInt())) {
745
+        mapAttributesTableMaster.insert(map->index.toInt(), map->name);
746
+    }
693 747
 
694 748
     // Deep copy
695 749
     QMap<QString, QString> attrs = mapAttributes.value(map->name);
@@ -702,6 +756,7 @@ void Project::saveAllDataStructures() {
702 756
     saveAllMapAttributes();
703 757
     saveMapGroupsTable();
704 758
     saveMapConstantsHeader();
759
+    saveMapsWithConnections();
705 760
 }
706 761
 
707 762
 void Project::loadTilesetAssets(Tileset* tileset) {
@@ -913,6 +968,13 @@ void Project::appendTextFile(QString path, QString text) {
913 968
     }
914 969
 }
915 970
 
971
+void Project::deleteFile(QString path) {
972
+    QFile file(path);
973
+    if (file.exists() && !file.remove()) {
974
+        qDebug() << QString("Could not delete file '%1': ").arg(path) + file.errorString();
975
+    }
976
+}
977
+
916 978
 void Project::readMapGroups() {
917 979
     QString text = readTextFile(root + "/data/maps/_groups.inc");
918 980
     if (text.isNull()) {
@@ -1098,6 +1160,41 @@ void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QString
1098 1160
     }
1099 1161
 }
1100 1162
 
1163
+void Project::readMapsWithConnections() {
1164
+    QString path = root + "/data/maps/connections.inc";
1165
+    QString text = readTextFile(path);
1166
+    if (text.isNull()) {
1167
+        return;
1168
+    }
1169
+
1170
+    mapsWithConnections.clear();
1171
+    QRegularExpression re("data\\/maps\\/(?<mapName>\\w+)\\/connections.inc");
1172
+    QList<QStringList>* includes = parseAsm(text);
1173
+    for (QStringList values : *includes) {
1174
+        if (values.length() != 2)
1175
+            continue;
1176
+
1177
+        QRegularExpressionMatch match = re.match(values.value(1));
1178
+        if (match.hasMatch()) {
1179
+            QString mapName = match.captured("mapName");
1180
+            mapsWithConnections.append(mapName);
1181
+        }
1182
+    }
1183
+}
1184
+
1185
+void Project::saveMapsWithConnections() {
1186
+    QString path = root + "/data/maps/connections.inc";
1187
+    QString text = "";
1188
+    for (QString mapName : mapsWithConnections) {
1189
+        if (mapNamesToMapConstants->contains(mapName)) {
1190
+            text += QString("\t.include \"data/maps/%1/connections.inc\"\n").arg(mapName);
1191
+        } else {
1192
+            qDebug() << QString("Failed to write connection include. %1 not a valid map name").arg(mapName);
1193
+        }
1194
+    }
1195
+    saveTextFile(path, text);
1196
+}
1197
+
1101 1198
 QStringList Project::getSongNames() {
1102 1199
     QStringList names;
1103 1200
     QString text = readTextFile(root + "/include/constants/songs.h");
@@ -1164,7 +1261,6 @@ void Project::loadObjectPixmaps(QList<Event*> objects) {
1164 1261
         }
1165 1262
 
1166 1263
         if (event_type == "object") {
1167
-
1168 1264
             int sprite_id = constants.value(object->get("sprite"));
1169 1265
 
1170 1266
             QString info_label = pointers.value(sprite_id).replace("&", "");

+ 6
- 0
project.h View File

@@ -26,6 +26,7 @@ public:
26 26
     QStringList *itemNames = NULL;
27 27
     QStringList *flagNames = NULL;
28 28
     QStringList *varNames = NULL;
29
+    QStringList mapsWithConnections;
29 30
 
30 31
     QMap<QString, Map*> *map_cache;
31 32
     Map* loadMap(QString);
@@ -41,6 +42,7 @@ public:
41 42
     QString readTextFile(QString path);
42 43
     void saveTextFile(QString path, QString text);
43 44
     void appendTextFile(QString path, QString text);
45
+    void deleteFile(QString path);
44 46
 
45 47
     void readMapGroups();
46 48
     Map* addNewMapToGroup(QString mapName, int groupNum);
@@ -53,6 +55,7 @@ public:
53 55
     void readMapAttributesTable();
54 56
     void readAllMapAttributes();
55 57
     void readMapAttributes(Map*);
58
+    void readMapsWithConnections();
56 59
     void getTilesets(Map*);
57 60
     void loadTilesetAssets(Tileset*);
58 61
 
@@ -97,6 +100,9 @@ private:
97 100
     QString getMapAttributesTableFilepath();
98 101
     QString getMapAssetsFilepath();
99 102
     void saveMapHeader(Map*);
103
+    void saveMapConnections(Map*);
104
+    void updateMapsWithConnections(Map*);
105
+    void saveMapsWithConnections();
100 106
     void saveMapAttributesTable();
101 107
     void updateMapAttributes(Map* map);
102 108
     void readCDefinesSorted(QString, QStringList, QStringList*);