874 lines
24 KiB
C++
874 lines
24 KiB
C++
#include "mainwindow.h"
|
|
#include <QPainter>
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsPixmapItem>
|
|
#include <QInputDialog>
|
|
#include <QMessageBox>
|
|
#include <QShortcut>
|
|
|
|
#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(Qt::MouseButton)), this, SLOT(onListItemSelected(Qt::MouseButton)));
|
|
|
|
// 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()
|
|
{
|
|
#if 0
|
|
foreach (QGraphicsTextItem* it, textItems)
|
|
{
|
|
if (it != nullptr)
|
|
{
|
|
delete it;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::loadBuildingData(void)
|
|
{
|
|
const QString path = "./buildings.ini";
|
|
|
|
QSettings settings(path, QSettings::IniFormat);
|
|
|
|
if (QFile(path).exists())
|
|
{
|
|
settings.beginGroup("buildings");
|
|
|
|
buildingPath = settings.value("path").toString();
|
|
|
|
int i = 0;
|
|
|
|
while (1)
|
|
{
|
|
QString buildingNumber = "building" + QString::number(i);
|
|
QString buildingName = settings.value(buildingNumber, "").toString();
|
|
|
|
if (buildingName.isEmpty() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
buildingData.insert(i++, buildingName);
|
|
ui.buildingList->addItem(buildingName);
|
|
}
|
|
|
|
settings.endGroup();
|
|
}
|
|
else
|
|
{
|
|
showError(path + tr(" could not be found"));
|
|
}
|
|
}
|
|
|
|
void MainWindow::onShowNumbers(int)
|
|
{
|
|
processMapFile(map_buffer);
|
|
}
|
|
|
|
void MainWindow::onMapItemClicked(QPointF position)
|
|
{
|
|
QPoint realPos;
|
|
|
|
position.setX(position.x() - (TILE_SIZE / 2));
|
|
|
|
realPos.setX(static_cast<int>(position.x() + (position.y() * 2)));
|
|
realPos.setY(static_cast<int>((position.y() * 2) - position.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(const Qt::MouseButton button)
|
|
{
|
|
QListWidget* listWidget;
|
|
|
|
switch (button)
|
|
{
|
|
case Qt::LeftButton:
|
|
listWidget = ui.tileList;
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
listWidget = ui.buildingList;
|
|
break;
|
|
|
|
default:
|
|
listWidget = nullptr;
|
|
break;
|
|
}
|
|
|
|
if (listWidget != nullptr)
|
|
{
|
|
foreach (const QListWidgetItem* const it, listWidget->selectedItems())
|
|
{
|
|
if (it != nullptr)
|
|
{
|
|
if (selected_item != -1)
|
|
{
|
|
int map_buffer_pos;
|
|
|
|
// MSB: building data, LSB: terrain data.
|
|
switch (button)
|
|
{
|
|
case Qt::LeftButton:
|
|
map_buffer_pos = static_cast<int>(DATA_HEADER_SIZE
|
|
+ (static_cast<unsigned long>(selected_item + 1) * sizeof(quint16)));
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
map_buffer_pos = static_cast<int>(DATA_HEADER_SIZE
|
|
+ (static_cast<unsigned long>(selected_item + 1) * sizeof(quint16))) - 1;
|
|
break;
|
|
|
|
default:
|
|
map_buffer_pos = -1;
|
|
break;
|
|
}
|
|
|
|
if (map_buffer_pos != -1)
|
|
{
|
|
if (map_buffer_pos < map_buffer.count())
|
|
{
|
|
const char row = static_cast<char>(listWidget->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<quint8>(strSize.toInt(&ok));
|
|
|
|
if (ok)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 8:
|
|
// Fall through.
|
|
case 16:
|
|
// Fall through.
|
|
case 24:
|
|
level_size = size;
|
|
data.append(static_cast<char>(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<char>(0xFF));
|
|
}
|
|
|
|
for (quint8 i = 0; i < size; i++)
|
|
{
|
|
for (quint8 j = 0; j < size; j++)
|
|
{
|
|
data.append(static_cast<char>(0)); // Building data
|
|
data.append(static_cast<char>(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())
|
|
{
|
|
const int expected_filesize = (DATA_HEADER_SIZE + (level_size * level_size));
|
|
|
|
if (data.count() >= expected_filesize)
|
|
{
|
|
parseMapData(ds);
|
|
}
|
|
else
|
|
{
|
|
showError(tr("Invalid file size. Expected ")
|
|
+ QString::number(expected_filesize, 10));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
showError(tr("Invalid header") + "\"" + header + "\"");
|
|
}
|
|
}
|
|
|
|
void MainWindow::parseMapData(QDataStream& ds)
|
|
{
|
|
char airportName[0x1A];
|
|
|
|
ds.readRawData(airportName, sizeof(airportName) / sizeof(airportName[0]));
|
|
|
|
ui.airportName_Label->setText(QString(airportName));
|
|
|
|
ds.skipRawData(0x3C - 0x1A);
|
|
|
|
gscene.clear();
|
|
gscene.clearFocus();
|
|
|
|
QList<quint8> buildingData;
|
|
|
|
|
|
for (int j = 0; j < level_size; j++)
|
|
{
|
|
for (int i = 0; i < level_size; i++)
|
|
{
|
|
char byte[2];
|
|
ds.readRawData(byte, 2);
|
|
|
|
buildingData.append(static_cast<quint8>(byte[0]));
|
|
addTile(static_cast<quint8>(byte[1]), i, j);
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < level_size; j++)
|
|
{
|
|
for (int i = 0; i < level_size; i++)
|
|
{
|
|
addBuilding(buildingData.at(i + (j* level_size)), i, j);
|
|
}
|
|
}
|
|
|
|
ui.graphicsView->setScene(&gscene);
|
|
ui.graphicsView->show();
|
|
ui.graphicsView->centerOn(QPointF(320, 480));
|
|
}
|
|
|
|
#define NODATA \
|
|
{ \
|
|
false, \
|
|
{ \
|
|
0, \
|
|
0, \
|
|
0 \
|
|
}, \
|
|
0, \
|
|
0, \
|
|
0, \
|
|
0, \
|
|
0, \
|
|
0 \
|
|
}
|
|
|
|
#define BUILDING_DATA(building) \
|
|
{ \
|
|
true, \
|
|
{ \
|
|
building##_OFFSET_X, \
|
|
building##_OFFSET_Y, \
|
|
0 \
|
|
}, \
|
|
building##_ORIGIN_X, \
|
|
building##_ORIGIN_Y, \
|
|
building##_W, \
|
|
building##_H, \
|
|
building##_U, \
|
|
building##_V \
|
|
}
|
|
|
|
void MainWindow::addBuilding(quint8 CurrentBuilding, const int i, const int j)
|
|
{
|
|
if (CurrentBuilding)
|
|
{
|
|
enum
|
|
{
|
|
BUILDING_NONE,
|
|
BUILDING_HANGAR,
|
|
BUILDING_ILS,
|
|
BUILDING_ATC_TOWER,
|
|
BUILDING_ATC_LOC,
|
|
BUILDING_TERMINAL,
|
|
BUILDING_TERMINAL_2,
|
|
BUILDING_GATE,
|
|
|
|
LAST_BUILDING = BUILDING_GATE,
|
|
MAX_BUILDING_ID
|
|
};
|
|
|
|
enum
|
|
{
|
|
BUILDING_ATC_LOC_OFFSET_X = TILE_SIZE >> 1,
|
|
BUILDING_ATC_LOC_OFFSET_Y = TILE_SIZE >> 1,
|
|
|
|
BUILDING_ILS_OFFSET_X = 0,
|
|
BUILDING_ILS_OFFSET_Y = 0,
|
|
|
|
BUILDING_GATE_OFFSET_X = (TILE_SIZE >> 1) - 4,
|
|
BUILDING_GATE_OFFSET_Y = 0,
|
|
|
|
BUILDING_HANGAR_OFFSET_X = 4,
|
|
BUILDING_HANGAR_OFFSET_Y = TILE_SIZE >> 1,
|
|
|
|
BUILDING_TERMINAL_OFFSET_X = 0,
|
|
BUILDING_TERMINAL_OFFSET_Y = TILE_SIZE >> 1,
|
|
|
|
BUILDING_TERMINAL_2_OFFSET_X = BUILDING_TERMINAL_OFFSET_X,
|
|
BUILDING_TERMINAL_2_OFFSET_Y = BUILDING_TERMINAL_OFFSET_Y,
|
|
|
|
BUILDING_ATC_TOWER_OFFSET_X = TILE_SIZE >> 2,
|
|
BUILDING_ATC_TOWER_OFFSET_Y = TILE_SIZE >> 1,
|
|
};
|
|
|
|
enum
|
|
{
|
|
BUILDING_ILS_U = 34,
|
|
BUILDING_ILS_V = 0,
|
|
BUILDING_ILS_W = 24,
|
|
BUILDING_ILS_H = 34,
|
|
|
|
BUILDING_GATE_U = 0,
|
|
BUILDING_GATE_V = 70,
|
|
BUILDING_GATE_W = 28,
|
|
BUILDING_GATE_H = 25,
|
|
|
|
BUILDING_HANGAR_U = 0,
|
|
BUILDING_HANGAR_V = 0,
|
|
BUILDING_HANGAR_W = 34,
|
|
BUILDING_HANGAR_H = 28,
|
|
|
|
BUILDING_TERMINAL_U = 0,
|
|
BUILDING_TERMINAL_V = 34,
|
|
BUILDING_TERMINAL_W = 51,
|
|
BUILDING_TERMINAL_H = 36,
|
|
|
|
BUILDING_TERMINAL_2_U = 51,
|
|
BUILDING_TERMINAL_2_V = BUILDING_TERMINAL_V,
|
|
BUILDING_TERMINAL_2_W = BUILDING_TERMINAL_W,
|
|
BUILDING_TERMINAL_2_H = BUILDING_TERMINAL_H,
|
|
|
|
BUILDING_ATC_TOWER_U = 58,
|
|
BUILDING_ATC_TOWER_V = 0,
|
|
BUILDING_ATC_TOWER_W = 29,
|
|
BUILDING_ATC_TOWER_H = 34,
|
|
};
|
|
|
|
enum
|
|
{
|
|
BUILDING_ILS_ORIGIN_X = 10,
|
|
BUILDING_ILS_ORIGIN_Y = 22,
|
|
|
|
BUILDING_GATE_ORIGIN_X = 20,
|
|
BUILDING_GATE_ORIGIN_Y = 8,
|
|
|
|
BUILDING_TERMINAL_ORIGIN_X = 20,
|
|
BUILDING_TERMINAL_ORIGIN_Y = 11,
|
|
|
|
BUILDING_TERMINAL_2_ORIGIN_X = BUILDING_TERMINAL_ORIGIN_X,
|
|
BUILDING_TERMINAL_2_ORIGIN_Y = BUILDING_TERMINAL_ORIGIN_Y,
|
|
|
|
BUILDING_HANGAR_ORIGIN_X = 16,
|
|
BUILDING_HANGAR_ORIGIN_Y = 12,
|
|
|
|
BUILDING_ATC_TOWER_ORIGIN_X = 12,
|
|
BUILDING_ATC_TOWER_ORIGIN_Y = 20,
|
|
};
|
|
|
|
static const struct
|
|
{
|
|
bool init;
|
|
IsometricPos IsoPos; // Offset inside tile
|
|
short orig_x; // Coordinate X origin inside building sprite
|
|
short orig_y; // Coordinate Y origin inside building sprite
|
|
short w; // Building width
|
|
short h; // Building height
|
|
short u; // Building X offset inside texture page
|
|
short v; // Building Y offset inside texture page
|
|
} GameBuildingData[MAX_BUILDING_ID] =
|
|
{
|
|
NODATA,
|
|
BUILDING_DATA(BUILDING_HANGAR),
|
|
BUILDING_DATA(BUILDING_ILS),
|
|
BUILDING_DATA(BUILDING_ATC_TOWER),
|
|
NODATA,
|
|
BUILDING_DATA(BUILDING_TERMINAL),
|
|
BUILDING_DATA(BUILDING_TERMINAL_2),
|
|
BUILDING_DATA(BUILDING_GATE)
|
|
};
|
|
|
|
enum
|
|
{
|
|
TILE_SIZE_BIT_SHIFT = 6
|
|
};
|
|
|
|
const quint8 CurrentBuildingNoMirror = CurrentBuilding & 0x7F;
|
|
|
|
if (CurrentBuildingNoMirror < MAX_BUILDING_ID)
|
|
{
|
|
if (GameBuildingData[CurrentBuildingNoMirror].init)
|
|
{
|
|
// Determine rendering order depending on Y value.
|
|
const short x_bldg_offset = GameBuildingData[CurrentBuildingNoMirror].IsoPos.x;
|
|
const short y_bldg_offset = GameBuildingData[CurrentBuildingNoMirror].IsoPos.y;
|
|
// Question: why is it needed to substract 16 to "z"?
|
|
const short z_bldg_offset = GameBuildingData[CurrentBuildingNoMirror].IsoPos.z - 16;
|
|
|
|
IsometricPos buildingIsoPos;
|
|
|
|
buildingIsoPos.x = static_cast<short>(i << TILE_SIZE_BIT_SHIFT) + x_bldg_offset;
|
|
// Question: why is it needed to substract 1 to "j"?
|
|
buildingIsoPos.y = static_cast<short>((j - 1) << TILE_SIZE_BIT_SHIFT) + y_bldg_offset;
|
|
buildingIsoPos.z = z_bldg_offset;
|
|
|
|
// Isometric -> Cartesian conversion
|
|
const CartesianPos buildingCartPos = isometricToCartesian(buildingIsoPos);
|
|
|
|
const QPixmap p = QPixmap(buildingPath);
|
|
|
|
QImage cropped = p.copy(GameBuildingData[CurrentBuildingNoMirror].u,
|
|
GameBuildingData[CurrentBuildingNoMirror].v,
|
|
GameBuildingData[CurrentBuildingNoMirror].w,
|
|
GameBuildingData[CurrentBuildingNoMirror].h).toImage();
|
|
|
|
cropped = cropped.convertToFormat(QImage::Format_ARGB32); // or maybe other format
|
|
|
|
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)
|
|
{
|
|
// Define new coordinates for building.
|
|
const int x = buildingCartPos.x - GameBuildingData[CurrentBuilding].orig_x;
|
|
const int y = buildingCartPos.y - GameBuildingData[CurrentBuilding].orig_y;
|
|
|
|
it->setX(x);
|
|
it->setY(y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#undef NODATA
|
|
#undef BUILDING_DATA
|
|
|
|
void MainWindow::addTile(quint8 CurrentTile, const int i, const int j)
|
|
{
|
|
enum
|
|
{
|
|
TILE_GRASS,
|
|
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
|
|
};
|
|
|
|
const QPixmap tileset(tilesetPaths[0]);
|
|
const QPixmap tileset2(tilesetPaths[1]);
|
|
|
|
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)
|
|
{
|
|
int u;
|
|
int v;
|
|
|
|
if (CurrentTile & TILE_MIRROR_FLAG)
|
|
{
|
|
u = static_cast<int>((tileNoMirror % 4) * 64);
|
|
v = static_cast<int>((tileNoMirror / 4) * 48);
|
|
}
|
|
else
|
|
{
|
|
u = static_cast<int>((CurrentTile % 4) * 64);
|
|
v = static_cast<int>((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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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";
|
|
|
|
if (QFile(filePath).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(const QString name)
|
|
{
|
|
if (not map_buffer.isEmpty())
|
|
{
|
|
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);
|
|
}
|