From cd1c5419b32137728f40379e75be50703c9c89c8 Mon Sep 17 00:00:00 2001 From: njohnson Date: Sat, 17 Jan 2026 18:54:19 -0500 Subject: [PATCH] Add splashscreen, add confirmation dialog, and improve logic. --- GWF_PowerupInjector.pro | 10 +- app.ico | Bin 0 -> 175882 bytes main.cpp | 33 +++- mainwindow.cpp | 418 ++++++++++++++++++++++++++++++++++++++-- mainwindow.h | 22 ++- mainwindow.ui | 64 ++---- splashscreen.cpp | 338 ++++++++++++++++++++++++++++++++ splashscreen.h | 58 ++++++ 8 files changed, 867 insertions(+), 76 deletions(-) create mode 100644 app.ico create mode 100644 splashscreen.cpp create mode 100644 splashscreen.h diff --git a/GWF_PowerupInjector.pro b/GWF_PowerupInjector.pro index 3dc3aab..594f635 100644 --- a/GWF_PowerupInjector.pro +++ b/GWF_PowerupInjector.pro @@ -1,17 +1,21 @@ -QT += core gui widgets +QT += core widgets gui CONFIG += c++17 +RC_ICONS = app.ico + # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + splashscreen.cpp HEADERS += \ - mainwindow.h + mainwindow.h \ + splashscreen.h FORMS += \ mainwindow.ui diff --git a/app.ico b/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..22f2c6a97b2f484a2f98480fea1655658fc22a7b GIT binary patch literal 175882 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 &{p_27cx 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 fyK`!r`$1~P#yTHgD{=6t4GnlBuOe6Z$|UH2 +#include int main(int argc, char *argv[]) { QApplication a(argc, argv); + + QCoreApplication::setOrganizationDomain("redline.llc"); + QCoreApplication::setOrganizationName("RedLine Solutions, LLC."); + QCoreApplication::setApplicationName("GWF PowerUp Injector"); + QCoreApplication::setApplicationVersion("v1.1.0"); + + // Show splash screen + SplashScreen splash; + splash.show(); + splash.startAnimation(); + a.processEvents(); + + // Create main window (doesn't load maps yet) MainWindow w; - w.show(); + + // Connect progress signals to splash screen + QObject::connect(&w, &MainWindow::loadingProgress, + [&splash](int percent, const QString &status) { + splash.setProgress(percent); + splash.setStatus(status); + }); + + QObject::connect(&w, &MainWindow::loadingComplete, [&]() { + // Small delay to show 100% progress + QThread::msleep(300); + splash.finish(&w); + }); + + // Start async initialization (loads maps with progress reporting) + w.initializeAsync(); + return a.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index ab6f5c2..a4099fe 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include #include MainWindow::MainWindow(QWidget *parent) @@ -9,22 +10,45 @@ MainWindow::MainWindow(QWidget *parent) , mSteamPath() { ui->setupUi(this); + // Don't load maps here - call initializeAsync() after showing splash screen +} + +void MainWindow::initializeAsync() +{ + emit loadingProgress(5, "Finding Steam installation..."); + QCoreApplication::processEvents(); findSteamPath(); + emit loadingProgress(10, "Scanning workshop directory..."); + QCoreApplication::processEvents(); + QDir gwfWorkshopDir(mSteamPath + "/steamapps/workshop/content/431240"); - if (!gwfWorkshopDir.exists()) { return; } + if (!gwfWorkshopDir.exists()) + { + emit loadingProgress(100, "Ready (no maps found)"); + emit loadingComplete(); + return; + } qDebug() << "Found Golf With Friends Workshop Path:" << gwfWorkshopDir.path(); - foreach (const QString entry, gwfWorkshopDir.entryList({}, QDir::NoFilter, QDir::Time)) + QStringList entries = gwfWorkshopDir.entryList({}, QDir::NoDotAndDotDot | QDir::Dirs, QDir::Time); + int totalEntries = entries.size(); + int processed = 0; + + foreach (const QString entry, entries) { - if (entry == "." || entry == "..") { continue; } + // Calculate and emit progress (10-90%) + int progress = 10 + (processed * 80 / qMax(totalEntries, 1)); + emit loadingProgress(progress, QString("Loading map: %1").arg(entry)); + QCoreApplication::processEvents(); QFile newEntry(gwfWorkshopDir.path() + "/" + entry + "/Map"); if (!newEntry.open(QIODevice::ReadWrite)) { qDebug() << "Failed to open workshop map:" << entry; + processed++; continue; } const QByteArray rawMapData = newEntry.readAll(); @@ -52,28 +76,65 @@ MainWindow::MainWindow(QWidget *parent) newInfo.mMapData = loadObj; mLevels.push_back(newInfo); + processed++; } + emit loadingProgress(95, "Updating level list..."); + QCoreApplication::processEvents(); + updateLevels(); + + emit loadingProgress(100, "Ready!"); + emit loadingComplete(); } void MainWindow::findSteamPath() { - QDir testDir("C:/Program Files (x86)/Steam"); - if (testDir.exists()) + // Check multiple common Steam installation locations + QStringList possiblePaths = { + "C:/Program Files (x86)/Steam", + "C:/Program Files/Steam", + "D:/Steam", + "D:/Games/Steam", + "E:/Steam", + "E:/Games/Steam" + }; + + for (const QString &path : possiblePaths) { - mSteamPath = testDir.path(); + QDir testDir(path); + if (testDir.exists()) + { + mSteamPath = testDir.path(); + break; + } } - else + + // If not found in common locations, ask user + if (mSteamPath.isEmpty()) { - mSteamPath = QFileDialog::getExistingDirectory(this, "Select Steam Directory", "C:/Program Files (x86)/", QFileDialog::ShowDirsOnly); + mSteamPath = QFileDialog::getExistingDirectory(this, "Select Steam Directory", + "C:/Program Files (x86)/", QFileDialog::ShowDirsOnly); } if (!mSteamPath.isEmpty()) { - ui->lineEdit_SteamPath->setText(QString("FOUND: %1").arg(mSteamPath)); + // Verify the GWF workshop folder exists + QDir gwfDir(mSteamPath + "/steamapps/workshop/content/431240"); + if (!gwfDir.exists()) + { + ui->lineEdit_SteamPath->setText(QString("FOUND: %1 (No GWF maps)").arg(mSteamPath)); + } + else + { + ui->lineEdit_SteamPath->setText(QString("FOUND: %1").arg(mSteamPath)); + } ui->lineEdit_SteamPath->setEnabled(false); } + else + { + ui->lineEdit_SteamPath->setText("NOT FOUND - Please install Steam or GWF workshop maps"); + } } void MainWindow::updateLevels() @@ -162,7 +223,14 @@ MainWindow::~MainWindow() void MainWindow::on_listWidget_MapSelect_currentRowChanged(int currentRow) { - LevelInfo selectedLevel = mLevels[currentRow]; + // Bounds checking to prevent crashes + if (currentRow < 0 || currentRow >= mLevels.size()) + { + clearMapDetails(); + return; + } + + const LevelInfo &selectedLevel = mLevels[currentRow]; ui->lineEdit_LevelName->setText(selectedLevel.mLevelName); ui->lineEdit_Description->setText(selectedLevel.mDescription); ui->lineEdit_ID->setText(selectedLevel.mPublishedID); @@ -173,6 +241,9 @@ void MainWindow::on_listWidget_MapSelect_currentRowChanged(int currentRow) QPixmap scaledPreview = selectedLevel.mPreview.scaled(ui->label_MapPreview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); ui->label_MapPreview->setPixmap(scaledPreview); + // Clear the powerup list before adding new items (fixes accumulation bug) + ui->listWidget_powerups->clear(); + for (int i = 0; i < selectedLevel.mPowerUps.size(); i++) { ui->listWidget_powerups->addItem(QString("Powerup %1").arg(i)); @@ -182,8 +253,25 @@ void MainWindow::on_listWidget_MapSelect_currentRowChanged(int currentRow) void MainWindow::on_listWidget_powerups_currentRowChanged(int currentRow) { - LevelInfo selectedLevel = mLevels[ui->listWidget_MapSelect->currentRow()]; - PowerUpInfo selectedPowerUp = selectedLevel.mPowerUps[currentRow]; + int mapRow = ui->listWidget_MapSelect->currentRow(); + + // Bounds checking for map selection + if (mapRow < 0 || mapRow >= mLevels.size()) + { + clearPowerupDetails(); + return; + } + + const LevelInfo &selectedLevel = mLevels[mapRow]; + + // Bounds checking for powerup selection + if (currentRow < 0 || currentRow >= selectedLevel.mPowerUps.size()) + { + clearPowerupDetails(); + return; + } + + const PowerUpInfo &selectedPowerUp = selectedLevel.mPowerUps[currentRow]; ui->doubleSpinBox_PosX->setValue(selectedPowerUp.mPosition.x()); ui->doubleSpinBox_PosY->setValue(selectedPowerUp.mPosition.y()); @@ -204,20 +292,26 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup) const QString mapPath = aLevelInfo.mMapPath; if (!QFile::exists(mapPath)) { - qDebug() << "File doesn't exist: " << mapPath; + QMessageBox::critical(this, "Save Error", + QString("Map file not found:\n%1").arg(mapPath)); return; } const QString backupMapPath = aLevelInfo.mMapPath + ".old"; if (!QFile::exists(backupMapPath) && aBackup) { - QFile::copy(mapPath, backupMapPath); + if (!QFile::copy(mapPath, backupMapPath)) + { + QMessageBox::warning(this, "Backup Warning", + "Could not create backup file. Proceeding without backup."); + } } QFile backupMapFile(backupMapPath); if (!backupMapFile.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to open backup file: " << mapPath; + QMessageBox::critical(this, "Save Error", + QString("Failed to open backup file for reading:\n%1").arg(backupMapPath)); return; } @@ -227,16 +321,16 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup) QFile mapFile(mapPath); if (!mapFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { - qDebug() << "Failed to open file: " << mapPath; + QMessageBox::critical(this, "Save Error", + QString("Failed to open map file for writing:\n%1").arg(mapPath)); return; } - QByteArray powerUpsData = ","; for (int i = 0; i < aLevelInfo.mPowerUps.size(); i++) { - PowerUpInfo powerUp = aLevelInfo.mPowerUps[i]; + const PowerUpInfo &powerUp = aLevelInfo.mPowerUps[i]; const QString powerUpData = QString("{\"sType\":%1,\"pX\":%2,\"pY\":%3,\"pZ\":%4,\"rW\":%5,\"rX\":%6,\"rY\":%7,\"rZ\":%8,\"sX\":%9,\"sY\":%10,\"sZ\":%11,\"obName\":\"Powerup Spawner\",\"photonData\":{\"photonViewID\":[%12]}},") .arg(0) .arg(powerUp.mPosition.x(), 0, 'f', 1) @@ -261,19 +355,69 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup) mapData.append(powerUpsData); mapData.append(backupData.mid(injectionPoint)); - mapFile.write(mapData); + if (mapFile.write(mapData) == -1) + { + QMessageBox::critical(this, "Save Error", + QString("Failed to write map data:\n%1").arg(mapPath)); + mapFile.close(); + return; + } + mapFile.close(); + qDebug() << "Map saved successfully:" << mapPath; } void MainWindow::on_pushButton_Generate_clicked() { int currentListRow = ui->listWidget_MapSelect->currentRow(); + + // Bounds check + if (currentListRow < 0 || currentListRow >= mLevels.size()) + { + QMessageBox::warning(this, "No Map Selected", + "Please select a map before generating powerups."); + return; + } + LevelInfo selectedLevel = mLevels[currentListRow]; + // Check if powerups already exist and ask user what to do + if (!selectedLevel.mPowerUps.isEmpty()) + { + QMessageBox::StandardButton reply = QMessageBox::question(this, + "Existing Powerups Found", + QString("This map already has %1 powerup(s).\n\n" + "Do you want to replace them with new generated powerups?\n\n" + "Click 'Yes' to replace, 'No' to add to existing, or 'Cancel' to abort.") + .arg(selectedLevel.mPowerUps.size()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Cancel); + + if (reply == QMessageBox::Cancel) + { + return; + } + if (reply == QMessageBox::Yes) + { + selectedLevel.mPowerUps.clear(); + } + // If No, keep existing and add new ones + } + QVector shuffledPositions = selectedLevel.mObjectPositions; + + if (shuffledPositions.isEmpty()) + { + QMessageBox::warning(this, "No Base Objects", + "This map has no base objects to use as spawn positions.\n" + "Powerups are generated near objects with 'base' in their name."); + return; + } + auto rng = QRandomGenerator::global(); std::shuffle(shuffledPositions.begin(), shuffledPositions.end(), *rng); + int generatedCount = 0; for (int i = 0; i < shuffledPositions.size(); i++) { if (i == MAX_POWERUPS) @@ -295,14 +439,246 @@ void MainWindow::on_pushButton_Generate_clicked() PowerUpInfo newPowerUp; newPowerUp.mPosition = QVector3D(pos.x(), pos.y() + 0.25, pos.z()); + newPowerUp.mRotation = QVector4D(0, 0, 0, 1); // Identity quaternion newPowerUp.mScale = QVector3D(1, 1, 1); selectedLevel.mPowerUps.push_back(newPowerUp); + generatedCount++; } } + saveMap(selectedLevel, true); - mLevels[ui->listWidget_MapSelect->currentRow()] = selectedLevel; + mLevels[currentListRow] = selectedLevel; - ui->listWidget_MapSelect->setCurrentRow(currentListRow); + // Refresh the UI to show the new powerups + ui->listWidget_MapSelect->setCurrentRow(-1); // Deselect first + ui->listWidget_MapSelect->setCurrentRow(currentListRow); // Reselect to refresh + + ui->statusBar->showMessage(QString("Generated %1 powerups successfully!").arg(generatedCount), 5000); +} + +void MainWindow::clearMapDetails() +{ + ui->lineEdit_LevelName->clear(); + ui->lineEdit_Description->clear(); + ui->lineEdit_ID->clear(); + ui->spinBox_Music->setValue(0); + ui->spinBox_Skybox->setValue(0); + ui->spinBox_PowerupCount->setValue(0); + ui->label_MapPreview->clear(); + ui->listWidget_powerups->clear(); + clearPowerupDetails(); +} + +void MainWindow::clearPowerupDetails() +{ + ui->doubleSpinBox_PosX->setValue(0); + ui->doubleSpinBox_PosY->setValue(0); + ui->doubleSpinBox_PosZ->setValue(0); + ui->doubleSpinBox_RotW->setValue(0); + ui->doubleSpinBox_RotX->setValue(0); + ui->doubleSpinBox_RotY->setValue(0); + ui->doubleSpinBox_RotZ->setValue(0); + ui->doubleSpinBox_ScaleX->setValue(0); + ui->doubleSpinBox_ScaleY->setValue(0); + ui->doubleSpinBox_ScaleZ->setValue(0); +} + +void MainWindow::refreshPowerupList(int mapRow) +{ + if (mapRow < 0 || mapRow >= mLevels.size()) + { + ui->listWidget_powerups->clear(); + return; + } + + ui->listWidget_powerups->clear(); + const LevelInfo &level = mLevels[mapRow]; + for (int i = 0; i < level.mPowerUps.size(); i++) + { + ui->listWidget_powerups->addItem(QString("Powerup %1").arg(i)); + } + ui->spinBox_PowerupCount->setValue(level.mPowerUps.size()); +} + +void MainWindow::on_pushButton_SavePowerUp_clicked() +{ + int mapRow = ui->listWidget_MapSelect->currentRow(); + int powerupRow = ui->listWidget_powerups->currentRow(); + + if (mapRow < 0 || mapRow >= mLevels.size()) + { + QMessageBox::warning(this, "No Map Selected", + "Please select a map first."); + return; + } + + if (powerupRow < 0 || powerupRow >= mLevels[mapRow].mPowerUps.size()) + { + QMessageBox::warning(this, "No Powerup Selected", + "Please select a powerup to save."); + return; + } + + // Read values from UI and update the powerup + PowerUpInfo &powerup = mLevels[mapRow].mPowerUps[powerupRow]; + powerup.mPosition.setX(ui->doubleSpinBox_PosX->value()); + powerup.mPosition.setY(ui->doubleSpinBox_PosY->value()); + powerup.mPosition.setZ(ui->doubleSpinBox_PosZ->value()); + powerup.mRotation.setW(ui->doubleSpinBox_RotW->value()); + powerup.mRotation.setX(ui->doubleSpinBox_RotX->value()); + powerup.mRotation.setY(ui->doubleSpinBox_RotY->value()); + powerup.mRotation.setZ(ui->doubleSpinBox_RotZ->value()); + powerup.mScale.setX(ui->doubleSpinBox_ScaleX->value()); + powerup.mScale.setY(ui->doubleSpinBox_ScaleY->value()); + powerup.mScale.setZ(ui->doubleSpinBox_ScaleZ->value()); + + // Save to disk + saveMap(mLevels[mapRow], true); + + ui->statusBar->showMessage("Powerup saved successfully!", 3000); +} + +void MainWindow::on_pushButton_NewPowerUp_clicked() +{ + int mapRow = ui->listWidget_MapSelect->currentRow(); + + if (mapRow < 0 || mapRow >= mLevels.size()) + { + QMessageBox::warning(this, "No Map Selected", + "Please select a map before adding a powerup."); + return; + } + + // Create new powerup at origin with default values + PowerUpInfo newPowerup; + newPowerup.mPosition = QVector3D(0, 0.25, 0); + newPowerup.mRotation = QVector4D(0, 0, 0, 1); // Identity quaternion + newPowerup.mScale = QVector3D(1, 1, 1); + + mLevels[mapRow].mPowerUps.append(newPowerup); + + // Refresh the powerup list + refreshPowerupList(mapRow); + + // Select the new powerup + ui->listWidget_powerups->setCurrentRow(mLevels[mapRow].mPowerUps.size() - 1); + + ui->statusBar->showMessage("New powerup added. Edit values and click Save.", 3000); +} + +void MainWindow::on_pushButton_DeletePowerUp_clicked() +{ + int mapRow = ui->listWidget_MapSelect->currentRow(); + int powerupRow = ui->listWidget_powerups->currentRow(); + + if (mapRow < 0 || mapRow >= mLevels.size()) + { + QMessageBox::warning(this, "No Map Selected", + "Please select a map first."); + return; + } + + if (powerupRow < 0 || powerupRow >= mLevels[mapRow].mPowerUps.size()) + { + QMessageBox::warning(this, "No Powerup Selected", + "Please select a powerup to delete."); + return; + } + + QMessageBox::StandardButton reply = QMessageBox::question(this, + "Confirm Delete", + QString("Are you sure you want to delete Powerup %1?").arg(powerupRow), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (reply == QMessageBox::Yes) + { + mLevels[mapRow].mPowerUps.remove(powerupRow); + + // Save to disk + saveMap(mLevels[mapRow], true); + + // Refresh powerup list + refreshPowerupList(mapRow); + clearPowerupDetails(); + + ui->statusBar->showMessage("Powerup deleted successfully!", 3000); + } +} + +void MainWindow::on_pushButton_Restore_clicked() +{ + int mapRow = ui->listWidget_MapSelect->currentRow(); + + if (mapRow < 0 || mapRow >= mLevels.size()) + { + QMessageBox::warning(this, "No Map Selected", + "Please select a map to restore."); + return; + } + + QString backupPath = mLevels[mapRow].mMapPath + ".old"; + if (!QFile::exists(backupPath)) + { + QMessageBox::information(this, "No Backup", + "No backup file exists for this map.\n" + "Backups are created automatically when you generate or modify powerups."); + return; + } + + QMessageBox::StandardButton reply = QMessageBox::question(this, + "Restore Backup", + "This will restore the map to its original state before any modifications.\n\n" + "All powerup changes will be lost. Are you sure you want to continue?", + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (reply == QMessageBox::Yes) + { + QString mapPath = mLevels[mapRow].mMapPath; + + // Remove current map file + if (!QFile::remove(mapPath)) + { + QMessageBox::critical(this, "Restore Error", + "Failed to remove current map file."); + return; + } + + // Copy backup to map file + if (!QFile::copy(backupPath, mapPath)) + { + QMessageBox::critical(this, "Restore Error", + "Failed to restore backup file."); + return; + } + + // Reload the map data + QFile mapFile(mapPath); + if (mapFile.open(QIODevice::ReadOnly)) + { + const QByteArray rawMapData = mapFile.readAll(); + size_t headerSize = rawMapData.indexOf('{'); + size_t footerSize = rawMapData.size() - rawMapData.lastIndexOf('}') - 1; + size_t payloadSize = rawMapData.size() - headerSize - footerSize; + const QByteArray mapData = rawMapData.mid(headerSize, payloadSize); + + QJsonDocument loadDoc = QJsonDocument::fromJson(mapData); + QJsonObject loadObj = loadDoc.object(); + + mLevels[mapRow].mPowerUps = findPowerUps(loadObj); + mLevels[mapRow].mMaxViewID = getMaxViewID(loadObj); + mLevels[mapRow].mMapData = loadObj; + + mapFile.close(); + } + + // Refresh UI + refreshPowerupList(mapRow); + clearPowerupDetails(); + + ui->statusBar->showMessage("Map restored from backup successfully!", 3000); + } } diff --git a/mainwindow.h b/mainwindow.h index fec65a7..02625e5 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -8,12 +8,16 @@ #include #include #include +#include +#include #include #include #include #include +#include + struct PowerUpInfo { QVector3D mPosition; @@ -53,22 +57,34 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); + void initializeAsync(); // Async initialization for splash screen + void findSteamPath(); void updateLevels(); QVector findPowerUps(QJsonObject aMapData); QVector getObjectPositions(QJsonObject aObj); void saveMap(LevelInfo aLevelInfo, bool aBackup = true); - QByteArray serializePowerUpCompact(const PowerUpInfo &p, int photonId); int getMaxViewID(QJsonObject aMapData); + +signals: + void loadingProgress(int percent, const QString &status); + void loadingComplete(); + private slots: void on_listWidget_MapSelect_currentRowChanged(int currentRow); - void on_listWidget_powerups_currentRowChanged(int currentRow); - void on_pushButton_Generate_clicked(); + void on_pushButton_SavePowerUp_clicked(); + void on_pushButton_NewPowerUp_clicked(); + void on_pushButton_DeletePowerUp_clicked(); + void on_pushButton_Restore_clicked(); private: + void clearMapDetails(); + void clearPowerupDetails(); + void refreshPowerupList(int mapRow); + Ui::MainWindow *ui; QString mSteamPath; diff --git a/mainwindow.ui b/mainwindow.ui index a66b0bf..7a9e0a7 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -17,7 +17,7 @@ - MainWindow + Golf With Your Friends PowerUp Injector @@ -252,9 +252,6 @@ - - false - -10000.000000000000000 @@ -275,9 +272,6 @@ - - false - -10000.000000000000000 @@ -298,9 +292,6 @@ - - false - -10000.000000000000000 @@ -321,9 +312,6 @@ - - false - -10000.000000000000000 @@ -344,9 +332,6 @@ - - false - -10000.000000000000000 @@ -367,9 +352,6 @@ - - false - -10000.000000000000000 @@ -390,9 +372,6 @@ - - false - -10000.000000000000000 @@ -434,9 +413,6 @@ - - false - -10000.000000000000000 @@ -457,9 +433,6 @@ - - false - -10000.000000000000000 @@ -480,9 +453,6 @@ - - false - -10000.000000000000000 @@ -513,9 +483,6 @@ - - false - Save Powerup @@ -523,14 +490,18 @@ - - false - New Powerup + + + + Delete + + + @@ -547,6 +518,13 @@ + + + + Restore Backup + + + @@ -588,17 +566,7 @@ - - - - 0 - 0 - 1330 - 21 - - - - + diff --git a/splashscreen.cpp b/splashscreen.cpp new file mode 100644 index 0000000..11c4142 --- /dev/null +++ b/splashscreen.cpp @@ -0,0 +1,338 @@ +#include "splashscreen.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SplashScreen::SplashScreen(QWidget *parent) + : QSplashScreen() + , mAnimationTimer(new QTimer(this)) +{ + Q_UNUSED(parent); + + // Create transparent pixmap + QPixmap pixmap(WIDTH, HEIGHT); + pixmap.fill(Qt::transparent); + setPixmap(pixmap); + + setWindowFlags(Qt::SplashScreen | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + setAttribute(Qt::WA_TranslucentBackground); + + // Center on screen + if (QScreen *screen = QApplication::primaryScreen()) { + QRect screenGeometry = screen->geometry(); + int x = (screenGeometry.width() - WIDTH) / 2; + int y = (screenGeometry.height() - HEIGHT) / 2; + move(x, y); + } + + // Connect animation timer + connect(mAnimationTimer, &QTimer::timeout, this, &SplashScreen::onAnimationTick); +} + +SplashScreen::~SplashScreen() +{ + stopAnimation(); +} + +void SplashScreen::setStatus(const QString &message) +{ + mStatus = message; + repaint(); + QApplication::processEvents(); +} + +void SplashScreen::setProgress(int value, int max) +{ + mProgress = value; + mProgressMax = max; + repaint(); + QApplication::processEvents(); +} + +void SplashScreen::startAnimation() +{ + mAnimationTimer->start(16); // ~60 FPS +} + +void SplashScreen::stopAnimation() +{ + mAnimationTimer->stop(); +} + +void SplashScreen::finish(QWidget *mainWindow) +{ + stopAnimation(); + if (mainWindow) { + mainWindow->show(); + } + close(); +} + +void SplashScreen::onAnimationTick() +{ + mAnimationPhase += 0.08f; + if (mAnimationPhase > 2 * M_PI) { + mAnimationPhase -= 2 * M_PI; + } + repaint(); +} + +void SplashScreen::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + // Allow clicking to dismiss during loading if desired +} + +void SplashScreen::keyPressEvent(QKeyEvent *event) +{ + Q_UNUSED(event); + // Allow key press to dismiss during loading if desired +} + +void SplashScreen::drawContents(QPainter *painter) +{ + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setRenderHint(QPainter::TextAntialiasing, true); + + drawBackground(painter); + drawPowerupIcons(painter); + drawTitle(painter); + + // Draw bouncing golf ball + int ballX = 400; + int ballY = 85 + static_cast(qSin(mAnimationPhase) * 15); + drawGolfBall(painter, ballX, ballY, 25); + + drawProgressBar(painter); + drawStatusText(painter); +} + +void SplashScreen::drawBackground(QPainter *painter) +{ + // Create clipping path for rounded corners + QPainterPath clipPath; + clipPath.addRoundedRect(0, 0, WIDTH, HEIGHT, 20, 20); + painter->setClipPath(clipPath); + + // Draw gradient background (golf course green) + QLinearGradient gradient(0, 0, 0, HEIGHT); + gradient.setColorAt(0, mGreenLight); + gradient.setColorAt(1, mGreenDark); + painter->setBrush(gradient); + painter->setPen(Qt::NoPen); + painter->drawRect(0, 0, WIDTH, HEIGHT); + + // Draw subtle grass texture lines + painter->setPen(QPen(mGreenDark.darker(110), 1)); + for (int y = 20; y < HEIGHT - 60; y += 12) { + int offset = static_cast(qSin(y * 0.1f + mAnimationPhase * 0.5f) * 3); + painter->drawLine(10 + offset, y, WIDTH - 10 + offset, y); + } + + // Draw decorative border + painter->setClipping(false); + painter->setPen(QPen(mGreenDark.darker(130), 3)); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(1, 1, WIDTH - 2, HEIGHT - 2, 20, 20); +} + +void SplashScreen::drawGolfBall(QPainter *painter, int x, int y, int radius) +{ + // Draw shadow + painter->setBrush(QColor(0, 0, 0, 50)); + painter->setPen(Qt::NoPen); + painter->drawEllipse(x - radius + 5, y - radius + 8, radius * 2, radius * 2 - 5); + + // Draw golf ball with gradient + QRadialGradient ballGradient(x - radius/3, y - radius/3, radius * 1.5); + ballGradient.setColorAt(0, QColor(255, 255, 255)); + ballGradient.setColorAt(0.7, QColor(240, 240, 240)); + ballGradient.setColorAt(1, QColor(200, 200, 200)); + painter->setBrush(ballGradient); + painter->setPen(QPen(QColor(180, 180, 180), 1)); + painter->drawEllipse(x - radius, y - radius, radius * 2, radius * 2); + + // Draw dimples + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(180, 180, 180, 100)); + int dimpleSize = 4; + // Top row + painter->drawEllipse(x - 8, y - 12, dimpleSize, dimpleSize); + painter->drawEllipse(x + 4, y - 12, dimpleSize, dimpleSize); + // Middle row + painter->drawEllipse(x - 12, y - 4, dimpleSize, dimpleSize); + painter->drawEllipse(x, y - 4, dimpleSize, dimpleSize); + painter->drawEllipse(x + 8, y - 4, dimpleSize, dimpleSize); + // Bottom row + painter->drawEllipse(x - 8, y + 4, dimpleSize, dimpleSize); + painter->drawEllipse(x + 4, y + 4, dimpleSize, dimpleSize); +} + +void SplashScreen::drawPowerupIcons(QPainter *painter) +{ + painter->setClipRect(0, 0, WIDTH, HEIGHT); + + // Draw scattered powerup icons around the edges + // Lightning bolt - top left + painter->setPen(Qt::NoPen); + painter->setBrush(QColor("#FFEB3B")); // Yellow + QPainterPath lightning; + lightning.moveTo(60, 30); + lightning.lineTo(75, 30); + lightning.lineTo(65, 50); + lightning.lineTo(80, 50); + lightning.lineTo(55, 80); + lightning.lineTo(65, 55); + lightning.lineTo(50, 55); + lightning.closeSubpath(); + painter->drawPath(lightning); + + // Star - bottom left + painter->setBrush(QColor("#FF9800")); // Orange + QPainterPath star; + int starX = 50, starY = 230; + for (int i = 0; i < 5; i++) { + double angle = i * 72 * M_PI / 180 - M_PI / 2; + double innerAngle = angle + 36 * M_PI / 180; + if (i == 0) { + star.moveTo(starX + 15 * qCos(angle), starY + 15 * qSin(angle)); + } else { + star.lineTo(starX + 15 * qCos(angle), starY + 15 * qSin(angle)); + } + star.lineTo(starX + 7 * qCos(innerAngle), starY + 7 * qSin(innerAngle)); + } + star.closeSubpath(); + painter->drawPath(star); + + // Speed lines - right side + painter->setPen(QPen(QColor("#03A9F4"), 3, Qt::SolidLine, Qt::RoundCap)); // Blue + painter->drawLine(420, 180, 460, 180); + painter->drawLine(425, 190, 470, 190); + painter->drawLine(420, 200, 460, 200); + + // Circle powerup - top right area + QRadialGradient circleGrad(440, 35, 12); + circleGrad.setColorAt(0, QColor("#E91E63")); // Pink + circleGrad.setColorAt(1, QColor("#C2185B")); + painter->setBrush(circleGrad); + painter->setPen(Qt::NoPen); + painter->drawEllipse(428, 23, 24, 24); + + // Small decorative dots + painter->setBrush(QColor(255, 255, 255, 100)); + painter->drawEllipse(90, 140, 6, 6); + painter->drawEllipse(380, 250, 8, 8); + painter->drawEllipse(120, 200, 5, 5); +} + +void SplashScreen::drawTitle(QPainter *painter) +{ + painter->setClipRect(0, 0, WIDTH, HEIGHT); + + // Draw drop shadow for title + QFont titleFont("Segoe UI", 32, QFont::Bold); + painter->setFont(titleFont); + + QString gwf = "GWF"; + QString rest = " PowerUp Injector"; + + QFontMetrics fm(titleFont); + int gwfWidth = fm.horizontalAdvance(gwf); + int restWidth = fm.horizontalAdvance(rest); + int totalWidth = gwfWidth + restWidth; + int titleX = (WIDTH - totalWidth) / 2; + int titleY = 100; + + // Draw shadow + painter->setPen(QColor(0, 0, 0, 80)); + painter->drawText(titleX + 2, titleY + 2, gwf); + painter->drawText(titleX + gwfWidth + 2, titleY + 2, rest); + + // Draw "GWF" in gold/yellow accent + painter->setPen(mGold); + painter->drawText(titleX, titleY, gwf); + + // Draw rest in white + painter->setPen(mWhite); + painter->drawText(titleX + gwfWidth, titleY, rest); + + // Tagline + QFont taglineFont("Segoe UI", 11); + painter->setFont(taglineFont); + painter->setPen(mLightGray); + QString tagline = "Add powerups to your workshop maps!"; + QFontMetrics taglineFm(taglineFont); + int taglineWidth = taglineFm.horizontalAdvance(tagline); + painter->drawText((WIDTH - taglineWidth) / 2, titleY + 28, tagline); + + // Version info + QFont versionFont("Segoe UI", 9); + painter->setFont(versionFont); + painter->setPen(QColor(255, 255, 255, 150)); + QString version = QString("Version %1").arg(QCoreApplication::applicationVersion()); + QFontMetrics versionFm(versionFont); + int versionWidth = versionFm.horizontalAdvance(version); + painter->drawText((WIDTH - versionWidth) / 2, titleY + 48, version); + + // Copyright + QString copyright = QString("%1 %2").arg(QCoreApplication::organizationName()) + .arg(QDate::currentDate().year()); + int copyrightWidth = versionFm.horizontalAdvance(copyright); + painter->drawText((WIDTH - copyrightWidth) / 2, HEIGHT - 15, copyright); +} + +void SplashScreen::drawProgressBar(QPainter *painter) +{ + int progressX = 40; + int progressY = HEIGHT - 60; + int progressWidth = WIDTH - 80; + int progressHeight = 12; + + // Progress bar background + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(0, 0, 0, 80)); + painter->drawRoundedRect(progressX, progressY, progressWidth, progressHeight, 6, 6); + + // Progress bar fill + if (mProgressMax > 0 && mProgress > 0) { + int fillWidth = (progressWidth * mProgress) / mProgressMax; + if (fillWidth > 0) { + // Gradient fill for progress + QLinearGradient gradient(progressX, progressY, progressX + fillWidth, progressY); + gradient.setColorAt(0, mGold); + gradient.setColorAt(0.5, mGold.lighter(110)); + gradient.setColorAt(1, mGold); + painter->setBrush(gradient); + painter->drawRoundedRect(progressX, progressY, fillWidth, progressHeight, 6, 6); + + // Add shine effect + painter->setBrush(QColor(255, 255, 255, 60)); + painter->drawRoundedRect(progressX, progressY, fillWidth, progressHeight / 2, 6, 6); + } + } +} + +void SplashScreen::drawStatusText(QPainter *painter) +{ + if (!mStatus.isEmpty()) { + QFont statusFont("Segoe UI", 9); + painter->setFont(statusFont); + painter->setPen(mWhite); + + int progressX = 40; + int progressY = HEIGHT - 60; + int progressWidth = WIDTH - 80; + + QFontMetrics statusFm(statusFont); + QString elidedStatus = statusFm.elidedText(mStatus, Qt::ElideMiddle, progressWidth); + painter->drawText(progressX, progressY - 8, elidedStatus); + } +} diff --git a/splashscreen.h b/splashscreen.h new file mode 100644 index 0000000..b39faa8 --- /dev/null +++ b/splashscreen.h @@ -0,0 +1,58 @@ +#ifndef SPLASHSCREEN_H +#define SPLASHSCREEN_H + +#include +#include +#include +#include +#include + +class SplashScreen : public QSplashScreen +{ + Q_OBJECT + +public: + explicit SplashScreen(QWidget *parent = nullptr); + ~SplashScreen(); + + void setStatus(const QString &message); + void setProgress(int value, int max = 100); + void startAnimation(); + void stopAnimation(); + void finish(QWidget *mainWindow); + +protected: + void drawContents(QPainter *painter) override; + void mousePressEvent(QMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + +private slots: + void onAnimationTick(); + +private: + void drawBackground(QPainter *painter); + void drawGolfBall(QPainter *painter, int x, int y, int radius); + void drawPowerupIcons(QPainter *painter); + void drawTitle(QPainter *painter); + void drawProgressBar(QPainter *painter); + void drawStatusText(QPainter *painter); + + QString mStatus; + int mProgress = 0; + int mProgressMax = 100; + + QTimer *mAnimationTimer; + float mAnimationPhase = 0.0f; // For bouncing golf ball animation + + // Theme colors - golf course green theme + QColor mGreenLight = QColor("#4CAF50"); // Light green + QColor mGreenDark = QColor("#2E7D32"); // Dark green + QColor mGold = QColor("#FFD700"); // Golden yellow for progress + QColor mWhite = QColor("#FFFFFF"); // White text + QColor mLightGray = QColor("#E0E0E0"); // Light gray for muted text + + static constexpr int WIDTH = 480; + static constexpr int HEIGHT = 300; +}; + +#endif // SPLASHSCREEN_H