Bladeren bron

Merge pull request #20 from huderlem/maps

Support adding new maps
yenatch 6 jaren geleden
bovenliggende
commit
ce49e788ec
No account linked to committer's email address
9 gewijzigde bestanden met toevoegingen van 655 en 48 verwijderingen
  1. 1
    1
      asm.cpp
  2. 2
    0
      editor.cpp
  3. 1
    0
      main.cpp
  4. 87
    23
      mainwindow.cpp
  5. 14
    0
      mainwindow.h
  6. 1
    1
      map.cpp
  7. 3
    0
      map.h
  8. 513
    19
      project.cpp
  9. 33
    4
      project.h

+ 1
- 1
asm.cpp Bestand weergeven

@@ -26,7 +26,7 @@ QList<QStringList>* Asm::parse(QString text) {
26 26
         //QString macro;
27 27
         //QStringList *params;
28 28
         strip_comment(&line);
29
-        if (line.isEmpty()) {
29
+        if (line.trimmed().isEmpty()) {
30 30
         } else if (line.contains(':')) {
31 31
             label = line.left(line.indexOf(':'));
32 32
             QStringList *list = new QStringList;

+ 2
- 0
editor.cpp Bestand weergeven

@@ -10,12 +10,14 @@ Editor::Editor()
10 10
 void Editor::saveProject() {
11 11
     if (project) {
12 12
         project->saveAllMaps();
13
+        project->saveAllDataStructures();
13 14
     }
14 15
 }
15 16
 
16 17
 void Editor::save() {
17 18
     if (project && map) {
18 19
         project->saveMap(map);
20
+        project->saveAllDataStructures();
19 21
     }
20 22
 }
21 23
 

+ 1
- 0
main.cpp Bestand weergeven

@@ -4,6 +4,7 @@
4 4
 int main(int argc, char *argv[])
5 5
 {
6 6
     QApplication a(argc, argv);
7
+    a.setStyle("fusion");
7 8
     MainWindow w;
8 9
     w.show();
9 10
 

+ 87
- 23
mainwindow.cpp Bestand weergeven

@@ -61,36 +61,38 @@ void MainWindow::openProject(QString dir) {
61 61
         editor->project = new Project;
62 62
         editor->project->root = dir;
63 63
         setWindowTitle(editor->project->getProjectTitle() + " - pretmap");
64
+        loadDataStructures();
64 65
         populateMapList();
65 66
         setMap(getDefaultMap());
66 67
     } else {
67 68
         setWindowTitle(editor->project->getProjectTitle() + " - pretmap");
69
+        loadDataStructures();
68 70
         populateMapList();
69 71
     }
70 72
 }
71 73
 
72 74
 QString MainWindow::getDefaultMap() {
73 75
     if (editor && editor->project) {
74
-        QList<QStringList*> *names = editor->project->groupedMapNames;
75
-        if (names) {
76
+        QList<QStringList> names = editor->project->groupedMapNames;
77
+        if (!names.isEmpty()) {
76 78
             QSettings settings;
77 79
             QString key = "project:" + editor->project->root;
78 80
             if (settings.contains(key)) {
79 81
                 QMap<QString, QVariant> qmap = settings.value(key).toMap();
80 82
                 if (qmap.contains("recent_map")) {
81 83
                     QString map_name = qmap.value("recent_map").toString();
82
-                    for (int i = 0; i < names->length(); i++) {
83
-                        if (names->value(i)->contains(map_name)) {
84
+                    for (int i = 0; i < names.length(); i++) {
85
+                        if (names.value(i).contains(map_name)) {
84 86
                             return map_name;
85 87
                         }
86 88
                     }
87 89
                 }
88 90
             }
89 91
             // Failing that, just get the first map in the list.
90
-            for (int i = 0; i < names->length(); i++) {
91
-                QStringList *list = names->value(i);
92
-                if (list->length()) {
93
-                    return list->value(0);
92
+            for (int i = 0; i < names.length(); i++) {
93
+                QStringList list = names.value(i);
94
+                if (list.length()) {
95
+                    return list.value(0);
94 96
                 }
95 97
             }
96 98
         }
@@ -278,6 +280,11 @@ void MainWindow::on_checkBox_ShowLocation_clicked(bool checked)
278 280
     }
279 281
 }
280 282
 
283
+void MainWindow::loadDataStructures() {
284
+    Project *project = editor->project;
285
+    project->readMapAttributesTable();
286
+    project->readAllMapAttributes();
287
+}
281 288
 
282 289
 void MainWindow::populateMapList() {
283 290
     Project *project = editor->project;
@@ -289,17 +296,18 @@ void MainWindow::populateMapList() {
289 296
     QIcon folderIcon;
290 297
     folderIcon.addFile(QStringLiteral(":/icons/folder_closed.ico"), QSize(), QIcon::Normal, QIcon::Off);
291 298
 
292
-    QIcon mapIcon;
293
-    mapIcon.addFile(QStringLiteral(":/icons/map.ico"), QSize(), QIcon::Normal, QIcon::Off);
294
-    mapIcon.addFile(QStringLiteral(":/icons/image.ico"), QSize(), QIcon::Normal, QIcon::On);
299
+    mapIcon = new QIcon;
300
+    mapIcon->addFile(QStringLiteral(":/icons/map.ico"), QSize(), QIcon::Normal, QIcon::Off);
301
+    mapIcon->addFile(QStringLiteral(":/icons/image.ico"), QSize(), QIcon::Normal, QIcon::On);
295 302
 
296
-    QStandardItemModel *model = new QStandardItemModel;
303
+    mapListModel = new QStandardItemModel;
304
+    mapGroupsModel = new QList<QStandardItem*>;
297 305
 
298 306
     QStandardItem *entry = new QStandardItem;
299 307
     entry->setText(project->getProjectTitle());
300 308
     entry->setIcon(folderIcon);
301 309
     entry->setEditable(false);
302
-    model->appendRow(entry);
310
+    mapListModel->appendRow(entry);
303 311
 
304 312
     QStandardItem *maps = new QStandardItem;
305 313
     maps->setText("maps");
@@ -314,26 +322,82 @@ void MainWindow::populateMapList() {
314 322
         group->setText(group_name);
315 323
         group->setIcon(mapFolderIcon);
316 324
         group->setEditable(false);
325
+        group->setData(group_name, Qt::UserRole);
326
+        group->setData("map_group", MapListUserRoles::TypeRole);
327
+        group->setData(i, MapListUserRoles::GroupRole);
317 328
         maps->appendRow(group);
318
-        QStringList *names = project->groupedMapNames->value(i);
319
-        for (int j = 0; j < names->length(); j++) {
320
-            QString map_name = names->value(j);
321
-            QStandardItem *map = new QStandardItem;
322
-            map->setText(QString("[%1.%2] ").arg(i).arg(j, 2, 10, QLatin1Char('0')) + map_name);
323
-            map->setIcon(mapIcon);
324
-            map->setEditable(false);
325
-            map->setData(map_name, Qt::UserRole);
329
+        mapGroupsModel->append(group);
330
+        QStringList names = project->groupedMapNames.value(i);
331
+        for (int j = 0; j < names.length(); j++) {
332
+            QString map_name = names.value(j);
333
+            QStandardItem *map = createMapItem(map_name, i, j);
326 334
             group->appendRow(map);
327
-            //ui->mapList->setExpanded(model->indexFromItem(map), false); // redundant
328 335
         }
329 336
     }
330 337
 
331
-    ui->mapList->setModel(model);
338
+    // Right-clicking on items in the map list tree view brings up a context menu.
339
+    ui->mapList->setContextMenuPolicy(Qt::CustomContextMenu);
340
+    connect(ui->mapList, SIGNAL(customContextMenuRequested(const QPoint &)),
341
+            this, SLOT(onOpenMapListContextMenu(const QPoint &)));
342
+
343
+    ui->mapList->setModel(mapListModel);
332 344
     ui->mapList->setUpdatesEnabled(true);
333 345
     ui->mapList->expandToDepth(2);
334 346
     ui->mapList->repaint();
335 347
 }
336 348
 
349
+QStandardItem* MainWindow::createMapItem(QString mapName, int groupNum, int inGroupNum) {
350
+    QStandardItem *map = new QStandardItem;
351
+    map->setText(QString("[%1.%2] ").arg(groupNum).arg(inGroupNum, 2, 10, QLatin1Char('0')) + mapName);
352
+    map->setIcon(*mapIcon);
353
+    map->setEditable(false);
354
+    map->setData(mapName, Qt::UserRole);
355
+    map->setData("map_name", MapListUserRoles::TypeRole);
356
+    return map;
357
+}
358
+
359
+void MainWindow::onOpenMapListContextMenu(const QPoint &point)
360
+{
361
+    QModelIndex index = ui->mapList->indexAt(point);
362
+    if (!index.isValid()) {
363
+        return;
364
+    }
365
+
366
+    QStandardItem *selectedItem = mapListModel->itemFromIndex(index);
367
+    QVariant itemType = selectedItem->data(MapListUserRoles::TypeRole);
368
+    if (!itemType.isValid()) {
369
+        return;
370
+    }
371
+
372
+    // Build custom context menu depending on which type of item was selected (map group, map name, etc.)
373
+    if (itemType == "map_group") {
374
+        QString groupName = selectedItem->data(Qt::UserRole).toString();
375
+        int groupNum = selectedItem->data(MapListUserRoles::GroupRole).toInt();
376
+        QMenu* menu = new QMenu();
377
+        QActionGroup* actions = new QActionGroup(menu);
378
+        actions->addAction(menu->addAction("Add New Map to Group"))->setData(groupNum);
379
+        connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToGroupClick(QAction*)));
380
+        menu->exec(QCursor::pos());
381
+    }
382
+}
383
+
384
+void MainWindow::onAddNewMapToGroupClick(QAction* triggeredAction)
385
+{
386
+    int groupNum = triggeredAction->data().toInt();
387
+    QStandardItem* groupItem = mapGroupsModel->at(groupNum);
388
+
389
+    QString newMapName = editor->project->getNewMapName();
390
+    Map* newMap = editor->project->addNewMapToGroup(newMapName, groupNum);
391
+    editor->project->saveMap(newMap);
392
+    editor->project->saveAllDataStructures();
393
+
394
+    int numMapsInGroup = groupItem->rowCount();
395
+    QStandardItem *newMapItem = createMapItem(newMapName, groupNum, numMapsInGroup);
396
+    groupItem->appendRow(newMapItem);
397
+
398
+    setMap(newMapName);
399
+}
400
+
337 401
 void MainWindow::on_mapList_activated(const QModelIndex &index)
338 402
 {
339 403
     QVariant data = index.data(Qt::UserRole);

+ 14
- 0
mainwindow.h Bestand weergeven

@@ -4,6 +4,7 @@
4 4
 #include <QString>
5 5
 #include <QModelIndex>
6 6
 #include <QMainWindow>
7
+#include <QStandardItemModel>
7 8
 #include <QGraphicsPixmapItem>
8 9
 #include <QGraphicsItemGroup>
9 10
 #include <QGraphicsSceneMouseEvent>
@@ -65,15 +66,23 @@ private slots:
65 66
 
66 67
     void on_toolButton_Dropper_clicked();
67 68
 
69
+    void onOpenMapListContextMenu(const QPoint &point);
70
+    void onAddNewMapToGroupClick(QAction* triggeredAction);
71
+
68 72
 private:
69 73
     Ui::MainWindow *ui;
74
+    QStandardItemModel *mapListModel;
75
+    QList<QStandardItem*> *mapGroupsModel;
70 76
     Editor *editor = NULL;
77
+    QIcon* mapIcon;
71 78
     void setMap(QString);
79
+    void loadDataStructures();
72 80
     void populateMapList();
73 81
     QString getExistingDirectory(QString);
74 82
     void openProject(QString dir);
75 83
     QString getDefaultMap();
76 84
     void setRecentMap(QString map_name);
85
+    QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum);
77 86
 
78 87
     void markAllEdited(QAbstractItemModel *model);
79 88
     void markEdited(QModelIndex index);
@@ -83,4 +92,9 @@ private:
83 92
     void checkToolButtons();
84 93
 };
85 94
 
95
+enum MapListUserRoles {
96
+    GroupRole = Qt::UserRole + 1, // Used to hold the map group number.
97
+    TypeRole = Qt::UserRole + 2,  // Used to differentiate between the different layers of the map list tree view.
98
+};
99
+
86 100
 #endif // MAINWINDOW_H

+ 1
- 1
map.cpp Bestand weergeven

@@ -732,5 +732,5 @@ void Map::addEvent(Event *event) {
732 732
 }
733 733
 
734 734
 bool Map::hasUnsavedChanges() {
735
-    return !history.isSaved();
735
+    return !history.isSaved() || !isPersistedToFile;
736 736
 }

+ 3
- 0
map.h Bestand weergeven

@@ -76,6 +76,7 @@ public:
76 76
 public:
77 77
     QString name;
78 78
     QString constantName;
79
+    QString group_num;
79 80
     QString attributes_label;
80 81
     QString events_label;
81 82
     QString scripts_label;
@@ -102,6 +103,8 @@ public:
102 103
 
103 104
     Blockdata* blockdata = NULL;
104 105
 
106
+    bool isPersistedToFile = true;
107
+
105 108
 public:
106 109
     void setName(QString mapName);
107 110
     static QString mapConstantFromName(QString mapName);

+ 513
- 19
project.cpp Bestand weergeven

@@ -6,17 +6,21 @@
6 6
 #include "event.h"
7 7
 
8 8
 #include <QDebug>
9
+#include <QDir>
9 10
 #include <QFile>
10 11
 #include <QTextStream>
12
+#include <QStandardItem>
11 13
 #include <QMessageBox>
12 14
 #include <QRegularExpression>
13 15
 
14 16
 Project::Project()
15 17
 {
16 18
     groupNames = new QStringList;
17
-    groupedMapNames = new QList<QStringList*>;
19
+    map_groups = new QMap<QString, int>;
18 20
     mapNames = new QStringList;
19 21
     map_cache = new QMap<QString, Map*>;
22
+    mapConstantsToMapNames = new QMap<QString, QString>;
23
+    mapNamesToMapConstants = new QMap<QString, QString>;
20 24
     tileset_cache = new QMap<QString, Tileset*>;
21 25
 }
22 26
 
@@ -29,9 +33,17 @@ QString Project::getProjectTitle() {
29 33
 }
30 34
 
31 35
 Map* Project::loadMap(QString map_name) {
32
-    Map *map = new Map;
36
+    Map *map;
37
+    if (map_cache->contains(map_name)) {
38
+        map = map_cache->value(map_name);
39
+        if (map->hasUnsavedChanges()) {
40
+            return map;
41
+        }
42
+    } else {
43
+        map = new Map;
44
+        map->setName(map_name);
45
+    }
33 46
 
34
-    map->setName(map_name);
35 47
     readMapHeader(map);
36 48
     readMapAttributes(map);
37 49
     getTilesets(map);
@@ -47,6 +59,10 @@ Map* Project::loadMap(QString map_name) {
47 59
 }
48 60
 
49 61
 void Project::loadMapConnections(Map *map) {
62
+    if (!map->isPersistedToFile) {
63
+        return;
64
+    }
65
+
50 66
     map->connections.clear();
51 67
     if (!map->connections_label.isNull()) {
52 68
         QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name);
@@ -67,8 +83,8 @@ void Project::loadMapConnections(Map *map) {
67 83
                     connection->direction = command.value(1);
68 84
                     connection->offset = command.value(2);
69 85
                     QString mapConstant = command.value(3);
70
-                    if (mapConstantsToMapNames.contains(mapConstant)) {
71
-                        connection->map_name = mapConstantsToMapNames[mapConstant];
86
+                    if (mapConstantsToMapNames->contains(mapConstant)) {
87
+                        connection->map_name = mapConstantsToMapNames->value(mapConstant);
72 88
                         map->connections.append(connection);
73 89
                     } else {
74 90
                         qDebug() << QString("Failed to find connected map for map constant '%1'").arg(mapConstant);
@@ -79,6 +95,10 @@ void Project::loadMapConnections(Map *map) {
79 95
     }
80 96
 }
81 97
 
98
+void Project::setNewMapConnections(Map *map) {
99
+    map->connections.clear();
100
+}
101
+
82 102
 QList<QStringList>* Project::getLabelMacros(QList<QStringList> *list, QString label) {
83 103
     bool in_label = false;
84 104
     QList<QStringList> *new_list = new QList<QStringList>;
@@ -122,6 +142,10 @@ QStringList* Project::getLabelValues(QList<QStringList> *list, QString label) {
122 142
 }
123 143
 
124 144
 void Project::readMapHeader(Map* map) {
145
+    if (!map->isPersistedToFile) {
146
+        return;
147
+    }
148
+
125 149
     QString label = map->name;
126 150
     Asm *parser = new Asm;
127 151
 
@@ -145,6 +169,22 @@ void Project::readMapHeader(Map* map) {
145 169
     map->battle_scene = header->value(12);
146 170
 }
147 171
 
172
+void Project::setNewMapHeader(Map* map, int mapIndex) {
173
+    map->attributes_label = QString("%1_MapAttributes").arg(map->name);
174
+    map->events_label = QString("%1_MapEvents").arg(map->name);;
175
+    map->scripts_label = QString("%1_MapScripts").arg(map->name);;
176
+    map->connections_label = "0x0";
177
+    map->song = "BGM_DAN02";
178
+    map->index = QString("%1").arg(mapIndex);
179
+    map->location = "0";
180
+    map->visibility = "0";
181
+    map->weather = "2";
182
+    map->type = "1";
183
+    map->unknown = "0";
184
+    map->show_location = "1";
185
+    map->battle_scene = "0";
186
+}
187
+
148 188
 void Project::saveMapHeader(Map *map) {
149 189
     QString label = map->name;
150 190
     QString header_path = root + "/data/maps/" + label + "/header.inc";
@@ -166,10 +206,65 @@ void Project::saveMapHeader(Map *map) {
166 206
     saveTextFile(header_path, text);
167 207
 }
168 208
 
209
+void Project::readMapAttributesTable() {
210
+    int curMapIndex = 1;
211
+    QString attributesText = readTextFile(getMapAttributesTableFilepath());
212
+    QList<QStringList>* values = parse(attributesText);
213
+    bool inAttributePointers = false;
214
+    for (int i = 0; i < values->length(); i++) {
215
+        QStringList params = values->value(i);
216
+        QString macro = params.value(0);
217
+        if (macro == ".label") {
218
+            if (inAttributePointers) {
219
+                break;
220
+            }
221
+            if (params.value(1) == "gMapAttributes") {
222
+                inAttributePointers = true;
223
+            }
224
+        } else if (macro == ".4byte" && inAttributePointers) {
225
+            QString mapName = params.value(1);
226
+            if (!mapName.contains("UnknownMapAttributes")) {
227
+                // Strip off "_MapAttributes" from the label if it's a real map label.
228
+                mapName = mapName.remove(mapName.length() - 14, 14);
229
+            }
230
+            mapAttributesTable.insert(curMapIndex, mapName);
231
+            curMapIndex++;
232
+        }
233
+    }
234
+
235
+    // Deep copy
236
+    mapAttributesTableMaster = mapAttributesTable;
237
+    mapAttributesTableMaster.detach();
238
+}
239
+
240
+void Project::saveMapAttributesTable() {
241
+    QString text = "";
242
+    text += QString("\t.align 2\n");
243
+    text += QString("gMapAttributes::\n");
244
+    for (int i = 0; i < mapAttributesTableMaster.count(); i++) {
245
+        int mapIndex = i + 1;
246
+        QString mapName = mapAttributesTableMaster.value(mapIndex);
247
+        if (!mapName.contains("UnknownMapAttributes")) {
248
+            text += QString("\t.4byte %1_MapAttributes\n").arg(mapName);
249
+        } else {
250
+            text += QString("\t.4byte %1\n").arg(mapName);
251
+        }
252
+    }
253
+    saveTextFile(getMapAttributesTableFilepath(), text);
254
+}
255
+
256
+QString Project::getMapAttributesTableFilepath() {
257
+    return QString("%1/data/maps/attributes_table.inc").arg(root);
258
+}
259
+
169 260
 void Project::readMapAttributes(Map* map) {
261
+    if (!map->isPersistedToFile) {
262
+        return;
263
+    }
264
+
170 265
     Asm *parser = new Asm;
171 266
 
172
-    QString assets_text = readTextFile(root + "/data/maps/_assets.inc");
267
+    QString assets_text = readTextFile(getMapAssetsFilepath());
173 268
     if (assets_text.isNull()) {
174 269
         return;
175 270
     }
@@ -182,6 +277,259 @@ void Project::readMapAttributes(Map* map) {
182 277
     map->tileset_secondary_label = attributes->value(5);
183 278
 }
184 279
 
280
+void Project::readAllMapAttributes() {
281
+    mapAttributes.clear();
282
+
283
+    Asm *parser = new Asm;
284
+    QString assets_text = readTextFile(getMapAssetsFilepath());
285
+    if (assets_text.isNull()) {
286
+        return;
287
+    }
288
+
289
+    QList<QStringList> *commands = parser->parse(assets_text);
290
+
291
+    // Assume the _assets.inc file is grouped consistently in the order of:
292
+    // 1. <map_name>_MapBorder
293
+    // 2. <map_name>_MapBlockdata
294
+    // 3. <map_name>_MapAttributes
295
+    int i = 0;
296
+    while (i < commands->length()) {
297
+        // Read MapBorder assets.
298
+        QStringList borderParams = commands->value(i++);
299
+        bool isUnknownMapBorder = borderParams.value(1).startsWith("UnknownMapBorder_");
300
+        if (borderParams.value(0) != ".label" || (!borderParams.value(1).endsWith("_MapBorder") && !isUnknownMapBorder)) {
301
+            qDebug() << QString("Expected MapBorder label, but found %1").arg(borderParams.value(1));
302
+            continue;
303
+        }
304
+        QString borderLabel = borderParams.value(1);
305
+        QString mapName;
306
+        if (!isUnknownMapBorder) {
307
+            mapName = borderLabel.remove(borderLabel.length() - 10, 10);
308
+        } else {
309
+            // Unknown map name has to match the MapAttributes label.
310
+            mapName = borderLabel.replace("Border", "Attributes");
311
+        }
312
+
313
+        mapAttributes[mapName].insert("border_label", borderParams.value(1));
314
+        borderParams = commands->value(i++);
315
+        mapAttributes[mapName].insert("border_filepath", borderParams.value(1).replace("\"", ""));
316
+
317
+        // Read MapBlockData assets.
318
+        QStringList blockDataParams = commands->value(i++);
319
+        bool isUnknownMapBlockdata = blockDataParams.value(1).startsWith("UnknownMapBlockdata_");
320
+        if (blockDataParams.value(0) != ".label" || (!blockDataParams.value(1).endsWith("_MapBlockdata") && !isUnknownMapBlockdata)) {
321
+            qDebug() << QString("Expected MapBlockdata label, but found %1").arg(blockDataParams.value(1));
322
+            continue;
323
+        }
324
+        QString blockDataLabel = blockDataParams.value(1);
325
+        mapAttributes[mapName].insert("blockdata_label", blockDataLabel);
326
+        blockDataParams = commands->value(i++);
327
+        mapAttributes[mapName].insert("blockdata_filepath", blockDataParams.value(1).replace("\"", ""));
328
+
329
+        // Read MapAttributes assets.
330
+        i++; // skip .align
331
+        // Maps can share MapAttributes, so  gather a list of them.
332
+        QStringList attributeMapLabels;
333
+        QStringList attributesParams;
334
+        QStringList* sharedAttrMaps = new QStringList;
335
+        while (i < commands->length()) {
336
+            attributesParams = commands->value(i);
337
+            if (attributesParams.value(0) != ".label") {
338
+                break;
339
+            }
340
+            QString attrLabel = attributesParams.value(1);
341
+            attributeMapLabels.append(attrLabel);
342
+            sharedAttrMaps->append(attrLabel);
343
+            i++;
344
+        }
345
+
346
+        // Apply the map attributes to each of the shared maps.
347
+        QString attrWidth = commands->value(i++).value(1);
348
+        QString attrHeight = commands->value(i++).value(1);
349
+        QString attrBorderLabel = commands->value(i++).value(1);
350
+        QString attrBlockdataLabel = commands->value(i++).value(1);
351
+        QString attrTilesetPrimary = commands->value(i++).value(1);
352
+        QString attrTilesetSecondary = commands->value(i++).value(1);
353
+        for (QString attributeMapLabel: attributeMapLabels) {
354
+            QString altMapName = attributeMapLabel;
355
+            if (!altMapName.startsWith("UnknownMapAttributes_")) {
356
+                altMapName.remove(altMapName.length() - 14, 14);
357
+            }
358
+            if (!mapAttributes.contains(altMapName)) {
359
+                mapAttributes.insert(altMapName, QMap<QString, QString>());
360
+            }
361
+            mapAttributes[altMapName].insert("attributes_label", attributeMapLabel);
362
+            mapAttributes[altMapName].insert("width", attrWidth);
363
+            mapAttributes[altMapName].insert("height", attrHeight);
364
+            mapAttributes[altMapName].insert("border_label", attrBorderLabel);
365
+            mapAttributes[altMapName].insert("blockdata_label", attrBlockdataLabel);
366
+            mapAttributes[altMapName].insert("tileset_primary", attrTilesetPrimary);
367
+            mapAttributes[altMapName].insert("tileset_secondary", attrTilesetSecondary);
368
+
369
+            if (sharedAttrMaps->length() > 1) {
370
+                mapAttributes[altMapName].insert("shared_attr_maps", sharedAttrMaps->join(":"));
371
+            }
372
+        }
373
+    }
374
+
375
+    // Deep copy
376
+    mapAttributesMaster = mapAttributes;
377
+    mapAttributesMaster.detach();
378
+}
379
+
380
+void Project::saveAllMapAttributes() {
381
+    QString text = "";
382
+    for (int i = 0; i < mapAttributesTableMaster.count(); i++) {
383
+        int mapIndex = i + 1;
384
+        QString mapName = mapAttributesTableMaster.value(mapIndex);
385
+        QMap<QString, QString> attrs = mapAttributesMaster.value(mapName);
386
+
387
+        // Find the map attributes object that contains the border data.
388
+        QMap<QString, QString> attrsWithBorder;
389
+        if (attrs.contains("border_filepath")) {
390
+            attrsWithBorder = attrs;
391
+        } else {
392
+            QStringList labels = attrs.value("shared_attr_maps").split(":");
393
+            for (QString label : labels) {
394
+                label.remove(label.length() - 14, 14);
395
+                if (mapAttributesMaster.contains(label) && mapAttributesMaster.value(label).contains("border_filepath")) {
396
+                    attrsWithBorder = mapAttributesMaster.value(label);
397
+                    break;
398
+                }
399
+            }
400
+        }
401
+        if (!attrsWithBorder.isEmpty()) {
402
+            text += QString("%1::\n").arg(attrsWithBorder.value("border_label"));
403
+            text += QString("\t.incbin \"%1\"\n").arg(attrsWithBorder.value("border_filepath"));
404
+            text += QString("\n");
405
+        }
406
+
407
+        // Find the map attributes object that contains the blockdata.
408
+        QMap<QString, QString> attrsWithBlockdata;
409
+        if (attrs.contains("blockdata_filepath")) {
410
+            attrsWithBlockdata = attrs;
411
+        } else {
412
+            QStringList labels = attrs["shared_attr_maps"].split(":");
413
+            for (QString label : labels) {
414
+                label.remove(label.length() - 14, 14);
415
+                if (mapAttributesMaster.contains(label) && mapAttributesMaster.value(label).contains("blockdata_filepath")) {
416
+                    attrsWithBlockdata = mapAttributesMaster.value(label);
417
+                    break;
418
+                }
419
+            }
420
+        }
421
+        if (!attrsWithBlockdata.isEmpty()) {
422
+            text += QString("%1::\n").arg(attrsWithBlockdata.value("blockdata_label"));
423
+            text += QString("\t.incbin \"%1\"\n").arg(attrsWithBorder.value("blockdata_filepath"));
424
+            text += QString("\n");
425
+        }
426
+
427
+        text += QString("\t.align 2\n");
428
+        if (attrs.contains("shared_attr_maps")) {
429
+            QStringList labels = attrs.value("shared_attr_maps").split(":");
430
+            for (QString label : labels) {
431
+                text += QString("%1::\n").arg(label);
432
+            }
433
+        } else {
434
+            text += QString("%1::\n").arg(attrs.value("attributes_label"));
435
+        }
436
+        text += QString("\t.4byte %1\n").arg(attrs.value("width"));
437
+        text += QString("\t.4byte %1\n").arg(attrs.value("height"));
438
+        text += QString("\t.4byte %1\n").arg(attrs.value("border_label"));
439
+        text += QString("\t.4byte %1\n").arg(attrs.value("blockdata_label"));
440
+        text += QString("\t.4byte %1\n").arg(attrs.value("tileset_primary"));
441
+        text += QString("\t.4byte %1\n").arg(attrs.value("tileset_secondary"));
442
+        text += QString("\n");
443
+    }
444
+
445
+    saveTextFile(getMapAssetsFilepath(), text);
446
+}
447
+
448
+QString Project::getMapAssetsFilepath() {
449
+    return root + "/data/maps/_assets.inc";
450
+}
451
+
452
+void Project::setNewMapAttributes(Map* map) {
453
+    map->width = "20";
454
+    map->height = "20";
455
+    map->border_label = QString("%1_MapBorder").arg(map->name);
456
+    map->blockdata_label = QString("%1_MapBlockdata").arg(map->name);
457
+    map->tileset_primary_label = "gTileset_General";
458
+    map->tileset_secondary_label = "gTileset_Petalburg";
459
+
460
+    // Insert new entry into the global map attributes.
461
+    QMap<QString, QString> attrs;
462
+    attrs.insert("border_label", QString("%1_MapBorder").arg(map->name));
463
+    attrs.insert("border_filepath", QString("data/maps/%1/border.bin").arg(map->name));
464
+    attrs.insert("blockdata_label", QString("%1_MapBlockdata").arg(map->name));
465
+    attrs.insert("blockdata_filepath", QString("data/maps/%1/map.bin").arg(map->name));
466
+    attrs.insert("attributes_label", QString("%1_MapAttributes").arg(map->name));
467
+    attrs.insert("width", map->width);
468
+    attrs.insert("height", map->height);
469
+    attrs.insert("tileset_primary", map->tileset_primary_label);
470
+    attrs.insert("tileset_secondary", map->tileset_secondary_label);
471
+    mapAttributes.insert(map->name, attrs);
472
+}
473
+
474
+void Project::saveMapGroupsTable() {
475
+    QString text = "";
476
+    int groupNum = 0;
477
+    for (QStringList mapNames : groupedMapNames) {
478
+        text += QString("\t.align 2\n");
479
+        text += QString("gMapGroup%1::\n").arg(groupNum);
480
+        for (QString mapName : mapNames) {
481
+            text += QString("\t.4byte %1\n").arg(mapName);
482
+        }
483
+        text += QString("\n");
484
+        groupNum++;
485
+    }
486
+
487
+    text += QString("\t.align 2\n");
488
+    text += QString("gMapGroups::\n");
489
+    for (int i = 0; i < groupNum; i++) {
490
+        text += QString("\t.4byte gMapGroup%1\n").arg(i);
491
+    }
492
+
493
+    saveTextFile(root + "/data/maps/_groups.inc", text);
494
+}
495
+
496
+void Project::saveMapConstantsHeader() {
497
+    QString text = QString("#ifndef GUARD_CONSTANTS_MAPS_H\n");
498
+    text += QString("#define GUARD_CONSTANTS_MAPS_H\n");
499
+    text += QString("\n");
500
+
501
+    int groupNum = 0;
502
+    for (QStringList mapNames : groupedMapNames) {
503
+        text += QString("// Map Group %1\n").arg(groupNum);
504
+        int maxLength = 0;
505
+        for (QString mapName : mapNames) {
506
+            QString mapConstantName = mapNamesToMapConstants->value(mapName);
507
+            if (mapConstantName.length() > maxLength)
508
+                maxLength = mapConstantName.length();
509
+        }
510
+        int groupIndex = 0;
511
+        for (QString mapName : mapNames) {
512
+            QString mapConstantName = mapNamesToMapConstants->value(mapName);
513
+            text += QString("#define %1%2(%3 | (%4 << 8))\n")
514
+                    .arg(mapConstantName)
515
+                    .arg(QString(" ").repeated(maxLength - mapConstantName.length() + 1))
516
+                    .arg(groupIndex)
517
+                    .arg(groupNum);
518
+            groupIndex++;
519
+        }
520
+        text += QString("\n");
521
+        groupNum++;
522
+    }
523
+
524
+    text += QString("\n");
525
+    text += QString("#define MAP_NONE (0x7F | (0x7F << 8))\n");
526
+    text += QString("#define MAP_UNDEFINED (0xFF | (0xFF << 8))\n\n\n");
527
+    text += QString("#define MAP_GROUP(map) (MAP_##map >> 8)\n");
528
+    text += QString("#define MAP_NUM(map) (MAP_##map & 0xFF)\n\n");
529
+    text += QString("#endif  // GUARD_CONSTANTS_MAPS_H\n");
530
+    saveTextFile(root + "/include/constants/maps.h", text);
531
+}
532
+
185 533
 void Project::getTilesets(Map* map) {
186 534
     map->tileset_primary = getTileset(map->tileset_primary_label);
187 535
     map->tileset_secondary = getTileset(map->tileset_secondary_label);
@@ -210,7 +558,7 @@ Tileset* Project::loadTileset(QString label) {
210 558
 }
211 559
 
212 560
 QString Project::getBlockdataPath(Map* map) {
213
-    QString text = readTextFile(root + "/data/maps/_assets.inc");
561
+    QString text = readTextFile(getMapAssetsFilepath());
214 562
     QStringList *values = getLabelValues(parse(text), map->blockdata_label);
215 563
     QString path;
216 564
     if (!values->isEmpty()) {
@@ -222,7 +570,7 @@ QString Project::getBlockdataPath(Map* map) {
222 570
 }
223 571
 
224 572
 QString Project::getMapBorderPath(Map *map) {
225
-    QString text = readTextFile(root + "/data/maps/_assets.inc");
573
+    QString text = readTextFile(getMapAssetsFilepath());
226 574
     QStringList *values = getLabelValues(parse(text), map->border_label);
227 575
     QString path;
228 576
     if (!values->isEmpty()) {
@@ -234,15 +582,45 @@ QString Project::getMapBorderPath(Map *map) {
234 582
 }
235 583
 
236 584
 void Project::loadBlockdata(Map* map) {
585
+    if (!map->isPersistedToFile) {
586
+        return;
587
+    }
588
+
237 589
     QString path = getBlockdataPath(map);
238 590
     map->blockdata = readBlockdata(path);
239 591
 }
240 592
 
593
+void Project::setNewMapBlockdata(Map* map) {
594
+    Blockdata *blockdata = new Blockdata;
595
+    for (int i = 0; i < map->getWidth() * map->getHeight(); i++) {
596
+        blockdata->addBlock(qint16(0x3001));
597
+    }
598
+    map->blockdata = blockdata;
599
+}
600
+
241 601
 void Project::loadMapBorder(Map *map) {
602
+    if (!map->isPersistedToFile) {
603
+        return;
604
+    }
605
+
242 606
     QString path = getMapBorderPath(map);
243 607
     map->border = readBlockdata(path);
244 608
 }
245 609
 
610
+void Project::setNewMapBorder(Map *map) {
611
+    Blockdata *blockdata = new Blockdata;
612
+    blockdata->addBlock(qint16(0x01D4));
613
+    blockdata->addBlock(qint16(0x01D5));
614
+    blockdata->addBlock(qint16(0x01DC));
615
+    blockdata->addBlock(qint16(0x01DD));
616
+    map->border = blockdata;
617
+}
618
+
619
+void Project::saveMapBorder(Map *map) {
620
+    QString path = getMapBorderPath(map);
621
+    writeBlockdata(path, map->border);
622
+}
623
+
246 624
 void Project::saveBlockdata(Map* map) {
247 625
     QString path = getBlockdataPath(map);
248 626
     writeBlockdata(path, map->blockdata);
@@ -267,9 +645,60 @@ void Project::saveAllMaps() {
267 645
 }
268 646
 
269 647
 void Project::saveMap(Map *map) {
270
-    saveBlockdata(map);
648
+    // Create/Modify a few collateral files for brand new maps.
649
+    if (!map->isPersistedToFile) {
650
+        QString newMapDataDir = QString(root + "/data/maps/%1").arg(map->name);
651
+        if (!QDir::root().mkdir(newMapDataDir)) {
652
+            qDebug() << "Error: failed to create directory for new map. " << newMapDataDir;
653
+        }
654
+
655
+        // TODO: In the future, these files needs more structure to allow for proper parsing/saving.
656
+        // Create file data/scripts/maps/<map_name>.inc
657
+        QString text = QString("%1_MapScripts::\n\t.byte 0\n").arg(map->name);
658
+        saveTextFile(root + "/data/scripts/maps/" + map->name + ".inc", text);
659
+
660
+        // Create file data/text/maps/<map_name>.inc
661
+        saveTextFile(root + "/data/text/maps/" + map->name + ".inc", "\n");
662
+
663
+        // Simply append to data/event_scripts.s.
664
+        text = QString("\n\t.include \"data/scripts/maps/%1.inc\"\n").arg(map->name);
665
+        text += QString("\t.include \"data/text/maps/%1.inc\"\n").arg(map->name);
666
+        appendTextFile(root + "/data/event_scripts.s", text);
667
+
668
+        // Simply append to data/map_events.s.
669
+        text = QString("\n\t.include \"data/maps/events/%1.inc\"\n").arg(map->name);
670
+        appendTextFile(root + "/data/map_events.s", text);
671
+
672
+        // Simply append to data/maps/headers.inc.
673
+        text = QString("\t.include \"data/maps/%1/header.inc\"\n").arg(map->name);
674
+        appendTextFile(root + "/data/maps/headers.inc", text);
675
+    }
676
+
677
+    saveMapBorder(map);
271 678
     saveMapHeader(map);
679
+    saveBlockdata(map);
272 680
     saveMapEvents(map);
681
+
682
+    // Update global data structures with current map data.
683
+    updateMapAttributes(map);
684
+
685
+    map->isPersistedToFile = true;
686
+}
687
+
688
+void Project::updateMapAttributes(Map* map) {
689
+    mapAttributesTableMaster.insert(map->index.toInt(nullptr, 0), map->name);
690
+
691
+    // Deep copy
692
+    QMap<QString, QString> attrs = mapAttributes.value(map->name);
693
+    attrs.detach();
694
+    mapAttributesMaster.insert(map->name, attrs);
695
+}
696
+
697
+void Project::saveAllDataStructures() {
698
+    saveMapAttributesTable();
699
+    saveAllMapAttributes();
700
+    saveMapGroupsTable();
701
+    saveMapConstantsHeader();
273 702
 }
274 703
 
275 704
 void Project::loadTilesetAssets(Tileset* tileset) {
@@ -472,6 +901,15 @@ void Project::saveTextFile(QString path, QString text) {
472 901
     }
473 902
 }
474 903
 
904
+void Project::appendTextFile(QString path, QString text) {
905
+    QFile file(path);
906
+    if (file.open(QIODevice::Append)) {
907
+        file.write(text.toUtf8());
908
+    } else {
909
+        qDebug() << QString("Could not open '%1' for appending: ").arg(path) + file.errorString();
910
+    }
911
+}
912
+
475 913
 void Project::readMapGroups() {
476 914
     QString text = readTextFile(root + "/data/maps/_groups.inc");
477 915
     if (text.isNull()) {
@@ -501,10 +939,9 @@ void Project::readMapGroups() {
501 939
         }
502 940
     }
503 941
 
504
-    QList<QStringList*> *groupedMaps = new QList<QStringList*>;
942
+    QList<QStringList> groupedMaps;
505 943
     for (int i = 0; i < groups->length(); i++) {
506
-        QStringList *list = new QStringList;
507
-        groupedMaps->append(list);
944
+        groupedMaps.append(QStringList());
508 945
     }
509 946
 
510 947
     QStringList *maps = new QStringList;
@@ -518,14 +955,14 @@ void Project::readMapGroups() {
518 955
             if (group != -1) {
519 956
                 for (int j = 1; j < params.length(); j++) {
520 957
                     QString mapName = params.value(j);
521
-                    QStringList *list = groupedMaps->value(group);
522
-                    list->append(mapName);
958
+                    groupedMaps[group].append(mapName);
523 959
                     maps->append(mapName);
960
+                    map_groups->insert(mapName, group);
524 961
 
525 962
                     // Build the mapping and reverse mapping between map constants and map names.
526 963
                     QString mapConstant = Map::mapConstantFromName(mapName);
527
-                    mapConstantsToMapNames.insert(mapConstant, mapName);
528
-                    mapNamesToMapConstants.insert(mapName, mapConstant);
964
+                    mapConstantsToMapNames->insert(mapConstant, mapName);
965
+                    mapNamesToMapConstants->insert(mapName, mapConstant);
529 966
                 }
530 967
             }
531 968
         }
@@ -536,6 +973,45 @@ void Project::readMapGroups() {
536 973
     mapNames = maps;
537 974
 }
538 975
 
976
+Map* Project::addNewMapToGroup(QString mapName, int groupNum) {
977
+    int mapIndex = mapAttributesTable.count() + 1;
978
+    mapAttributesTable.insert(mapIndex, mapName);
979
+
980
+    // Setup new map in memory, but don't write to file until map is actually saved later.
981
+    mapNames->append(mapName);
982
+    map_groups->insert(mapName, groupNum);
983
+    groupedMapNames[groupNum].append(mapName);
984
+
985
+    Map *map = new Map;
986
+    map->isPersistedToFile = false;
987
+    map->setName(mapName);
988
+    mapConstantsToMapNames->insert(map->constantName, map->name);
989
+    mapNamesToMapConstants->insert(map->name, map->constantName);
990
+    setNewMapHeader(map, mapIndex);
991
+    setNewMapAttributes(map);
992
+    getTilesets(map);
993
+    setNewMapBlockdata(map);
994
+    setNewMapBorder(map);
995
+    setNewMapEvents(map);
996
+    setNewMapConnections(map);
997
+    map->commit();
998
+    map->history.save();
999
+    map_cache->insert(mapName, map);
1000
+
1001
+    return map;
1002
+}
1003
+
1004
+QString Project::getNewMapName() {
1005
+    // Ensure default name doesn't already exist.
1006
+    int i = 0;
1007
+    QString newMapName;
1008
+    do {
1009
+        newMapName = QString("NewMap%1").arg(++i);
1010
+    } while (mapNames->contains(newMapName));
1011
+
1012
+    return newMapName;
1013
+}
1014
+
539 1015
 QList<QStringList>* Project::parse(QString text) {
540 1016
     Asm *parser = new Asm;
541 1017
     return parser->parse(text);
@@ -739,7 +1215,7 @@ void Project::saveMapEvents(Map *map) {
739 1215
             text += QString(", %1").arg(warp->get("y"));
740 1216
             text += QString(", %1").arg(warp->get("elevation"));
741 1217
             text += QString(", %1").arg(warp->get("destination_warp"));
742
-            text += QString(", %1").arg(mapNamesToMapConstants[warp->get("destination_map_name")]);
1218
+            text += QString(", %1").arg(mapNamesToMapConstants->value(warp->get("destination_map_name")));
743 1219
             text += "\n";
744 1220
         }
745 1221
         text += "\n";
@@ -811,6 +1287,10 @@ void Project::saveMapEvents(Map *map) {
811 1287
 }
812 1288
 
813 1289
 void Project::readMapEvents(Map *map) {
1290
+    if (!map->isPersistedToFile) {
1291
+        return;
1292
+    }
1293
+
814 1294
     // lazy
815 1295
     QString path = root + QString("/data/maps/events/%1.inc").arg(map->name);
816 1296
     QString text = readTextFile(path);
@@ -882,8 +1362,8 @@ void Project::readMapEvents(Map *map) {
882 1362
 
883 1363
             // Ensure the warp destination map constant is valid before adding it to the warps.
884 1364
             QString mapConstant = command.value(i++);
885
-            if (mapConstantsToMapNames.contains(mapConstant)) {
886
-                warp->put("destination_map_name", mapConstantsToMapNames[mapConstant]);
1365
+            if (mapConstantsToMapNames->contains(mapConstant)) {
1366
+                warp->put("destination_map_name", mapConstantsToMapNames->value(mapConstant));
887 1367
                 warp->put("event_type", "warp");
888 1368
                 map->events["warp"].append(warp);
889 1369
             } else {
@@ -973,6 +1453,20 @@ void Project::readMapEvents(Map *map) {
973 1453
     }
974 1454
 }
975 1455
 
1456
+void Project::setNewMapEvents(Map *map) {
1457
+    map->object_events_label = "0x0";
1458
+    map->warps_label = "0x0";
1459
+    map->coord_events_label = "0x0";
1460
+    map->bg_events_label = "0x0";
1461
+    map->events["object"].clear();
1462
+    map->events["warp"].clear();
1463
+    map->events["trap"].clear();
1464
+    map->events["trap_weather"].clear();
1465
+    map->events["sign"].clear();
1466
+    map->events["event_hidden_item"].clear();
1467
+    map->events["event_secret_base"].clear();
1468
+}
1469
+
976 1470
 QStringList Project::readCArray(QString text, QString label) {
977 1471
     QStringList list;
978 1472
 

+ 33
- 4
project.h Bestand weergeven

@@ -6,6 +6,7 @@
6 6
 
7 7
 #include <QStringList>
8 8
 #include <QList>
9
+#include <QStandardItem>
9 10
 
10 11
 class Project
11 12
 {
@@ -13,10 +14,16 @@ public:
13 14
     Project();
14 15
     QString root;
15 16
     QStringList *groupNames = NULL;
16
-    QList<QStringList*> *groupedMapNames = NULL;
17
+    QMap<QString, int> *map_groups;
18
+    QList<QStringList> groupedMapNames;
17 19
     QStringList *mapNames = NULL;
18
-    QMap<QString, QString> mapConstantsToMapNames;
19
-    QMap<QString, QString> mapNamesToMapConstants;
20
+    QMap<QString, QString>* mapConstantsToMapNames;
21
+    QMap<QString, QString>* mapNamesToMapConstants;
22
+    QMap<int, QString> mapAttributesTable;
23
+    QMap<int, QString> mapAttributesTableMaster;
24
+    QMap<QString, QMap<QString, QString>> mapAttributes;
25
+    QMap<QString, QMap<QString, QString>> mapAttributesMaster;
26
+
20 27
 
21 28
     QMap<QString, Map*> *map_cache;
22 29
     Map* loadMap(QString);
@@ -31,23 +38,32 @@ public:
31 38
 
32 39
     QString readTextFile(QString path);
33 40
     void saveTextFile(QString path, QString text);
41
+    void appendTextFile(QString path, QString text);
34 42
 
35 43
     void readMapGroups();
44
+    Map* addNewMapToGroup(QString mapName, int groupNum);
45
+    QString getNewMapName();
36 46
     QString getProjectTitle();
37 47
 
38 48
     QList<QStringList>* getLabelMacros(QList<QStringList>*, QString);
39 49
     QStringList* getLabelValues(QList<QStringList>*, QString);
40 50
     void readMapHeader(Map*);
51
+    void readMapAttributesTable();
52
+    void readAllMapAttributes();
41 53
     void readMapAttributes(Map*);
42 54
     void getTilesets(Map*);
43 55
     void loadTilesetAssets(Tileset*);
44 56
 
45 57
     QString getBlockdataPath(Map*);
46 58
     void saveBlockdata(Map*);
59
+    void saveMapBorder(Map*);
47 60
     void writeBlockdata(QString, Blockdata*);
48 61
     void saveAllMaps();
49 62
     void saveMap(Map*);
50
-    void saveMapHeader(Map*);
63
+    void saveAllDataStructures();
64
+    void saveAllMapAttributes();
65
+    void saveMapGroupsTable();
66
+    void saveMapConstantsHeader();
51 67
 
52 68
     QList<QStringList>* parse(QString text);
53 69
     QStringList getSongNames();
@@ -73,6 +89,19 @@ public:
73 89
     QStringList readCArray(QString text, QString label);
74 90
     QString readCIncbin(QString text, QString label);
75 91
     QMap<QString, int> readCDefines(QString text, QStringList prefixes);
92
+private:
93
+    QString getMapAttributesTableFilepath();
94
+    QString getMapAssetsFilepath();
95
+    void saveMapHeader(Map*);
96
+    void saveMapAttributesTable();
97
+    void updateMapAttributes(Map* map);
98
+
99
+    void setNewMapHeader(Map* map, int mapIndex);
100
+    void setNewMapAttributes(Map* map);
101
+    void setNewMapBlockdata(Map* map);
102
+    void setNewMapBorder(Map *map);
103
+    void setNewMapEvents(Map *map);
104
+    void setNewMapConnections(Map *map);
76 105
 };
77 106
 
78 107
 #endif // PROJECT_H