#include "mainwindow.h" #include #include #include #include #include #include #define DEFAULT_AIRPORT_NAME QByteArray("Default Airport\0") MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), level_size(0), selected_item(-1), tileSet(tr("Space"), this), tileMoveUp(tr("Up"), this) { ui.setupUi(this); this->setWindowTitle(APP_FULL_NAME); connect(ui.LoadMap_Btn, SIGNAL(released()), this, SLOT(loadMap())); connect(ui.CreateMap_Btn, SIGNAL(released()), this, SLOT(onCreateMap())); connect(ui.saveMap_Btn, SIGNAL(released()), this, SLOT(onSaveMap(void))); connect(ui.showNumbers_Checkbox, SIGNAL(stateChanged(int)), this, SLOT(onShowNumbers(int))); connect(ui.airportName_Label, SIGNAL(textChanged(QString)), this, SLOT(onAirportNameModified(QString))); connect(&gscene, SIGNAL(positionClicked(QPointF)), this, SLOT(onMapItemClicked(QPointF))); connect(&gscene, SIGNAL(noItemSelected(void)), this, SLOT(onNoItemSelected(void))); connect(&gscene, SIGNAL(updateSelectedItem(void)), this, SLOT(onListItemSelected(void))); // Configure keyboard shortcuts. connect(&tileSet, SIGNAL(activated()), this, SLOT(onListItemSelected(void))); connect(&tileMoveUp, SIGNAL(activated(void)), this, SLOT(moveUp(void))); appSettings(); loadTilesetData(); loadBuildingData(); } MainWindow::~MainWindow() { foreach (QGraphicsTextItem* it, textItems) { if (it != nullptr) { delete it; } } } void MainWindow::loadBuildingData(void) { const QString filePath = "./buildings.ini"; QFile f(filePath); if (f.open(QFile::ReadOnly)) { } else { showError(tr("Could not open file ") + filePath); } } void MainWindow::onShowNumbers(int) { processMapFile(map_buffer); } void MainWindow::onMapItemClicked(QPointF pos) { QPoint realPos; pos.setX(pos.x() - (TILE_SIZE / 2)); realPos.setX(static_cast(pos.x() + (pos.y() * 2))); realPos.setY(static_cast((pos.y() * 2) - pos.x())); int tile_no = 0; tile_no = realPos.x() / TILE_SIZE; tile_no += (realPos.y() / TILE_SIZE) * level_size; if (tile_no < (level_size * level_size)) { selected_item = tile_no; processMapFile(map_buffer); } else { selected_item = -1; } } void MainWindow::onListItemSelected(void) { foreach (const QListWidgetItem* const it, ui.tileList->selectedItems()) { if (it != nullptr) { if (selected_item != -1) { const int map_buffer_pos = static_cast((DATA_HEADER_SIZE + 1) + (static_cast(selected_item) * sizeof(quint16))); //map_buffer_pos++; // MSB: building data, LSB: terrain data. if (map_buffer_pos < map_buffer.count()) { const char row = static_cast(ui.tileList->row(it)); map_buffer[map_buffer_pos] = row; if (ui.mirror_CheckBox->isChecked() ) { map_buffer[map_buffer_pos] = map_buffer[map_buffer_pos] | TILE_MIRROR_FLAG; } processMapFile(map_buffer); } } } } } void MainWindow::onSaveMap(void) { const QString path = QFileDialog::getSaveFileName(this, "Save map file", _last_dir, "Map files (*.LVL)"); QFile f(path); if (checkFile(f, QFile::WriteOnly) == false) { return; } f.write(map_buffer); f.close(); } void MainWindow::onNoItemSelected(void) { selected_item = -1; processMapFile(map_buffer); } void MainWindow::onCreateMap(void) { bool ok; QStringList items; items << "8"; items << "16"; items << "24"; const QString strSize = QInputDialog::getItem(this, tr("Create new map"), tr("Select map size:"), items, 0, false, &ok ); if (ok && (not strSize.isEmpty())) { QByteArray data; data.append("ATC"); const quint8 size = static_cast(strSize.toInt(&ok)); if (ok) { switch (size) { case 8: // Fall through. case 16: // Fall through. case 24: level_size = size; data.append(static_cast(size)); break; default: showError(tr("Invalid map size ") + strSize); break; } data.append(DEFAULT_AIRPORT_NAME); for (int i = 0x04 + DEFAULT_AIRPORT_NAME.count(); i < 0x1C; i++) { data.append('\0'); } for (int i = (data.count() - 1); i < DATA_HEADER_SIZE; i++) { data.append(static_cast(0xFF)); } for (quint8 i = 0; i < size; i++) { for (quint8 j = 0; j < size; j++) { data.append(static_cast(0)); // Building data data.append(static_cast(0)); // Terrain data } } processMapFile(data); } } } void MainWindow::loadMap(void) { const QString path = QFileDialog::getOpenFileName(this, "Open map file", _last_dir, "Map files (*.LVL)"); QFile f(path); if (checkFile(f)) { processMapFile(f.readAll()); } } void MainWindow::processMapFile(const QByteArray& data) { map_buffer = data; QDataStream ds(data); char header[3]; ds.readRawData(header, 3); if (strncmp(header, "ATC", 3) == 0) { char ch; ds.readRawData(&ch, sizeof(char)); level_size = ch; if (not tilesetPaths[0].isEmpty() && not tilesetPaths[1].isEmpty()) { QPixmap tile1(tilesetPaths[0]); QPixmap tile2(tilesetPaths[1]); const int expected_filesize = (DATA_HEADER_SIZE + (level_size * level_size)); if (data.count() >= expected_filesize) { parseMapData(ds, tile1, tile2); } else { showError(tr("Invalid file size. Expected ") + QString::number(expected_filesize, 10)); } } } else { showError(tr("Invalid header") + "\"" + header + "\""); } } void MainWindow::parseMapData(QDataStream& ds, const QPixmap& tileSet, const QPixmap& tileSet2) { char airportName[0x1A]; ds.readRawData(airportName, sizeof(airportName) / sizeof(airportName[0])); ui.airportName_Label->setText(QString(airportName)); ds.skipRawData(0x3B - 0x1A); gscene.clear(); gscene.clearFocus(); for (int j = 0; j < level_size; j++) { for (int i = 0; i < level_size; i++) { enum { TILE_GRASS = 0, TILE_ASPHALT_WITH_BORDERS, TILE_WATER, TILE_ASPHALT, TILE_RWY_MID, TILE_RWY_START_1, TILE_RWY_START_2, TILE_PARKING, TILE_PARKING_2, TILE_TAXIWAY_INTERSECT_GRASS, TILE_TAXIWAY_GRASS, TILE_TAXIWAY_CORNER_GRASS, TILE_HALF_WATER_1, TILE_HALF_WATER_2, TILE_RWY_HOLDING_POINT, TILE_RWY_HOLDING_POINT_2, TILE_RWY_EXIT, TILE_TAXIWAY_CORNER_GRASS_2, TILE_TAXIWAY_4WAY_CROSSING, TILE_RWY_EXIT_2, LAST_TILE_TILESET1 = TILE_RWY_EXIT_2, TILE_UNUSED_1, TILE_TAXIWAY_CORNER_GRASS_3, FIRST_TILE_TILESET2 = TILE_UNUSED_1, LAST_TILE_TILESET2 = TILE_TAXIWAY_CORNER_GRASS_3 }; int u; int v; char byte[2]; ds.readRawData(byte, 2); quint8 CurrentTile = static_cast(byte[1]); quint8 CurrentBuilding = static_cast(byte[0]); quint8 buildingNoMirror = CurrentBuilding & 0x7F; quint8 tileNoMirror = CurrentTile & 0x7F; const QPixmap* p = nullptr; if (tileNoMirror <= LAST_TILE_TILESET1) { p = &tileSet; } else if (tileNoMirror <= LAST_TILE_TILESET2) { p = &tileSet2; CurrentTile -= FIRST_TILE_TILESET2; tileNoMirror -= FIRST_TILE_TILESET2; } if (p != nullptr) { if (CurrentTile & TILE_MIRROR_FLAG) { u = static_cast((tileNoMirror % 4) * 64); v = static_cast((tileNoMirror / 4) * 48); } else { u = static_cast((CurrentTile % 4) * 64); v = static_cast((CurrentTile / 4) * 48); } QImage cropped = p->copy(u, v, 64, 48).toImage(); if (CurrentTile & TILE_MIRROR_FLAG) { cropped = cropped.mirrored(true, false); } bool selected = false; if (selected_item != -1) { if (selected_item == ((j * level_size) + i)) { selected = true; } } cropped = cropped.convertToFormat(QImage::Format_ARGB32); // or maybe other format for (int i = 0; i < cropped.width(); i++) { for (int j = 0; j < cropped.height(); j++) { QColor rgb = cropped.pixel(i, j); if (rgb == QColor(Qt::magenta)) { cropped.setPixel(i, j, qRgba(0,0,0,0)); } else if (selected ) { QColor c = cropped.pixelColor(i, j); c.setRed(255 - c.red()); c.setBlue(255 - c.blue()); c.setGreen(255 - c.green()); cropped.setPixel(i, j, qRgb(c.red(), c.green(), c.blue())); } } } QGraphicsPixmapItem* const it = gscene.addPixmap(QPixmap::fromImage(cropped)); if (it != nullptr) { const int x = ((i * TILE_SIZE) - (i * (TILE_SIZE / 2))) - (j * (TILE_SIZE / 2)); const int y = (j * (TILE_SIZE / 4)) + (i * (TILE_SIZE / 4)); it->setX(x); it->setY(y); if (ui.showNumbers_Checkbox->isChecked() ) { QGraphicsTextItem* const io = new QGraphicsTextItem(); if (io != nullptr) { io->setPos(x + (TILE_SIZE / 4), y); io->setPlainText(QString::number(i + (j * level_size))); gscene.addItem(io); /* Append pointer to the list so it can be * safely removed on the constructor. */ textItems.append(io); } } } } } } ui.graphicsView->setScene(&gscene); ui.graphicsView->show(); } bool MainWindow::checkFile(QFile& f, QFile::OpenModeFlag flags) { const QFileInfo fi(f); if (not f.open(flags)) { return false; } QDir d(fi.absoluteFilePath()); _last_dir = d.absolutePath(); return true; } void MainWindow::appSettings(void) { QSettings settings("./settings.ini", QSettings::IniFormat); settings.beginGroup("app_settings"); _last_dir = settings.value("last_dir").toString(); restoreGeometry(settings.value("window_geometry").toByteArray()); settings.endGroup(); } void MainWindow::closeEvent(QCloseEvent*) { QSettings settings("./settings.ini", QSettings::IniFormat); settings.beginGroup("app_settings"); settings.setValue("last_dir", _last_dir); settings.setValue("window_geometry", saveGeometry()); settings.endGroup(); } void MainWindow::loadTilesetData(void) { const QString filePath = "./tileset.ini"; QFile f(filePath); if (f.exists()) { QSettings tilesetFile("./tileset.ini", QSettings::IniFormat); QStringList tilesets_to_check; tilesets_to_check << "tileset1"; tilesets_to_check << "tileset2"; int j = 0; int i = 0; foreach (QString tileset, tilesets_to_check) { tilesetFile.beginGroup(tileset); tilesetPaths[j++] = tilesetFile.value("path").toString(); while (1) { QString tileNumber = "tile" + QString::number(i); QString tileName = tilesetFile.value(tileNumber, "").toString(); if (tileName.isEmpty() ) { break; } tilesetData.insert(i++, tileName); ui.tileList->addItem(tileName); } tilesetFile.endGroup(); } } else { showError(tr("Could not find tile data file ") + filePath); } } void MainWindow::onAirportNameModified(QString name) { if (map_buffer.isEmpty() ) { return; } for (int i = 0x04, j = 0; i < 0x1C; i++) { if (j < name.count() ) { map_buffer[i] = name.at(j++).toLatin1(); } else { map_buffer[i] = '\0'; } } } void MainWindow::moveUp(void) { } void MainWindow::showError(const QString& error) { QMessageBox::critical(this, APP_FULL_NAME, error); }