Add splashscreen, add confirmation dialog, and improve logic.

This commit is contained in:
njohnson 2026-01-17 18:54:19 -05:00
parent b09f8a85e0
commit cd1c5419b3
8 changed files with 867 additions and 76 deletions

View File

@ -1,17 +1,21 @@
QT += core gui widgets QT += core widgets gui
CONFIG += c++17 CONFIG += c++17
RC_ICONS = app.ico
# You can make your code fail to compile if it uses deprecated APIs. # You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line. # 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 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ SOURCES += \
main.cpp \ main.cpp \
mainwindow.cpp mainwindow.cpp \
splashscreen.cpp
HEADERS += \ HEADERS += \
mainwindow.h mainwindow.h \
splashscreen.h
FORMS += \ FORMS += \
mainwindow.ui mainwindow.ui

BIN
app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -1,11 +1,42 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "splashscreen.h"
#include <QApplication> #include <QApplication>
#include <QThread>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication a(argc, 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; 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(); return a.exec();
} }

View File

@ -1,6 +1,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include <QMessageBox>
#include <QRandomGenerator> #include <QRandomGenerator>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
@ -9,22 +10,45 @@ MainWindow::MainWindow(QWidget *parent)
, mSteamPath() , mSteamPath()
{ {
ui->setupUi(this); 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(); findSteamPath();
emit loadingProgress(10, "Scanning workshop directory...");
QCoreApplication::processEvents();
QDir gwfWorkshopDir(mSteamPath + "/steamapps/workshop/content/431240"); 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(); 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"); QFile newEntry(gwfWorkshopDir.path() + "/" + entry + "/Map");
if (!newEntry.open(QIODevice::ReadWrite)) if (!newEntry.open(QIODevice::ReadWrite))
{ {
qDebug() << "Failed to open workshop map:" << entry; qDebug() << "Failed to open workshop map:" << entry;
processed++;
continue; continue;
} }
const QByteArray rawMapData = newEntry.readAll(); const QByteArray rawMapData = newEntry.readAll();
@ -52,28 +76,65 @@ MainWindow::MainWindow(QWidget *parent)
newInfo.mMapData = loadObj; newInfo.mMapData = loadObj;
mLevels.push_back(newInfo); mLevels.push_back(newInfo);
processed++;
} }
emit loadingProgress(95, "Updating level list...");
QCoreApplication::processEvents();
updateLevels(); updateLevels();
emit loadingProgress(100, "Ready!");
emit loadingComplete();
} }
void MainWindow::findSteamPath() void MainWindow::findSteamPath()
{ {
QDir testDir("C:/Program Files (x86)/Steam"); // Check multiple common Steam installation locations
if (testDir.exists()) 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()) 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); ui->lineEdit_SteamPath->setEnabled(false);
} }
else
{
ui->lineEdit_SteamPath->setText("NOT FOUND - Please install Steam or GWF workshop maps");
}
} }
void MainWindow::updateLevels() void MainWindow::updateLevels()
@ -162,7 +223,14 @@ MainWindow::~MainWindow()
void MainWindow::on_listWidget_MapSelect_currentRowChanged(int currentRow) 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_LevelName->setText(selectedLevel.mLevelName);
ui->lineEdit_Description->setText(selectedLevel.mDescription); ui->lineEdit_Description->setText(selectedLevel.mDescription);
ui->lineEdit_ID->setText(selectedLevel.mPublishedID); 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); QPixmap scaledPreview = selectedLevel.mPreview.scaled(ui->label_MapPreview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->label_MapPreview->setPixmap(scaledPreview); 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++) for (int i = 0; i < selectedLevel.mPowerUps.size(); i++)
{ {
ui->listWidget_powerups->addItem(QString("Powerup %1").arg(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) void MainWindow::on_listWidget_powerups_currentRowChanged(int currentRow)
{ {
LevelInfo selectedLevel = mLevels[ui->listWidget_MapSelect->currentRow()]; int mapRow = ui->listWidget_MapSelect->currentRow();
PowerUpInfo selectedPowerUp = selectedLevel.mPowerUps[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_PosX->setValue(selectedPowerUp.mPosition.x());
ui->doubleSpinBox_PosY->setValue(selectedPowerUp.mPosition.y()); ui->doubleSpinBox_PosY->setValue(selectedPowerUp.mPosition.y());
@ -204,20 +292,26 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup)
const QString mapPath = aLevelInfo.mMapPath; const QString mapPath = aLevelInfo.mMapPath;
if (!QFile::exists(mapPath)) 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; return;
} }
const QString backupMapPath = aLevelInfo.mMapPath + ".old"; const QString backupMapPath = aLevelInfo.mMapPath + ".old";
if (!QFile::exists(backupMapPath) && aBackup) 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); QFile backupMapFile(backupMapPath);
if (!backupMapFile.open(QIODevice::ReadOnly)) 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; return;
} }
@ -227,16 +321,16 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup)
QFile mapFile(mapPath); QFile mapFile(mapPath);
if (!mapFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) 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; return;
} }
QByteArray powerUpsData = ","; QByteArray powerUpsData = ",";
for (int i = 0; i < aLevelInfo.mPowerUps.size(); i++) 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]}},") 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(0)
.arg(powerUp.mPosition.x(), 0, 'f', 1) .arg(powerUp.mPosition.x(), 0, 'f', 1)
@ -261,19 +355,69 @@ void MainWindow::saveMap(LevelInfo aLevelInfo, bool aBackup)
mapData.append(powerUpsData); mapData.append(powerUpsData);
mapData.append(backupData.mid(injectionPoint)); 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(); mapFile.close();
qDebug() << "Map saved successfully:" << mapPath;
} }
void MainWindow::on_pushButton_Generate_clicked() void MainWindow::on_pushButton_Generate_clicked()
{ {
int currentListRow = ui->listWidget_MapSelect->currentRow(); 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]; 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<QVector3D> shuffledPositions = selectedLevel.mObjectPositions; QVector<QVector3D> 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(); auto rng = QRandomGenerator::global();
std::shuffle(shuffledPositions.begin(), shuffledPositions.end(), *rng); std::shuffle(shuffledPositions.begin(), shuffledPositions.end(), *rng);
int generatedCount = 0;
for (int i = 0; i < shuffledPositions.size(); i++) for (int i = 0; i < shuffledPositions.size(); i++)
{ {
if (i == MAX_POWERUPS) if (i == MAX_POWERUPS)
@ -295,14 +439,246 @@ void MainWindow::on_pushButton_Generate_clicked()
PowerUpInfo newPowerUp; PowerUpInfo newPowerUp;
newPowerUp.mPosition = QVector3D(pos.x(), pos.y() + 0.25, pos.z()); 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); newPowerUp.mScale = QVector3D(1, 1, 1);
selectedLevel.mPowerUps.push_back(newPowerUp); selectedLevel.mPowerUps.push_back(newPowerUp);
generatedCount++;
} }
} }
saveMap(selectedLevel, true); 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);
}
} }

View File

@ -8,12 +8,16 @@
#include <QDir> #include <QDir>
#include <QFileDialog> #include <QFileDialog>
#include <QVector3D> #include <QVector3D>
#include <QVector4D>
#include <QPixmap>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonValue> #include <QJsonValue>
#include <QJsonObject> #include <QJsonObject>
#include <QMessageBox>
struct PowerUpInfo struct PowerUpInfo
{ {
QVector3D mPosition; QVector3D mPosition;
@ -53,22 +57,34 @@ public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
void initializeAsync(); // Async initialization for splash screen
void findSteamPath(); void findSteamPath();
void updateLevels(); void updateLevels();
QVector<PowerUpInfo> findPowerUps(QJsonObject aMapData); QVector<PowerUpInfo> findPowerUps(QJsonObject aMapData);
QVector<QVector3D> getObjectPositions(QJsonObject aObj); QVector<QVector3D> getObjectPositions(QJsonObject aObj);
void saveMap(LevelInfo aLevelInfo, bool aBackup = true); void saveMap(LevelInfo aLevelInfo, bool aBackup = true);
QByteArray serializePowerUpCompact(const PowerUpInfo &p, int photonId);
int getMaxViewID(QJsonObject aMapData); int getMaxViewID(QJsonObject aMapData);
signals:
void loadingProgress(int percent, const QString &status);
void loadingComplete();
private slots: private slots:
void on_listWidget_MapSelect_currentRowChanged(int currentRow); void on_listWidget_MapSelect_currentRowChanged(int currentRow);
void on_listWidget_powerups_currentRowChanged(int currentRow); void on_listWidget_powerups_currentRowChanged(int currentRow);
void on_pushButton_Generate_clicked(); 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: private:
void clearMapDetails();
void clearPowerupDetails();
void refreshPowerupList(int mapRow);
Ui::MainWindow *ui; Ui::MainWindow *ui;
QString mSteamPath; QString mSteamPath;

View File

@ -17,7 +17,7 @@
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>MainWindow</string> <string>Golf With Your Friends PowerUp Injector</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
@ -252,9 +252,6 @@
</item> </item>
<item row="1" column="1" colspan="2"> <item row="1" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosX"> <widget class="QDoubleSpinBox" name="doubleSpinBox_PosX">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -275,9 +272,6 @@
</item> </item>
<item row="1" column="4" colspan="2"> <item row="1" column="4" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosY"> <widget class="QDoubleSpinBox" name="doubleSpinBox_PosY">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -298,9 +292,6 @@
</item> </item>
<item row="1" column="7" colspan="2"> <item row="1" column="7" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosZ"> <widget class="QDoubleSpinBox" name="doubleSpinBox_PosZ">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -321,9 +312,6 @@
</item> </item>
<item row="3" column="10"> <item row="3" column="10">
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotZ"> <widget class="QDoubleSpinBox" name="doubleSpinBox_RotZ">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -344,9 +332,6 @@
</item> </item>
<item row="5" column="1" colspan="2"> <item row="5" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleX"> <widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleX">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -367,9 +352,6 @@
</item> </item>
<item row="5" column="4" colspan="2"> <item row="5" column="4" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleY"> <widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleY">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -390,9 +372,6 @@
</item> </item>
<item row="5" column="7" colspan="2"> <item row="5" column="7" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleZ"> <widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleZ">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -434,9 +413,6 @@
</item> </item>
<item row="3" column="7" colspan="2"> <item row="3" column="7" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotY"> <widget class="QDoubleSpinBox" name="doubleSpinBox_RotY">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -457,9 +433,6 @@
</item> </item>
<item row="3" column="1" colspan="2"> <item row="3" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotW"> <widget class="QDoubleSpinBox" name="doubleSpinBox_RotW">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -480,9 +453,6 @@
</item> </item>
<item row="3" column="4" colspan="2"> <item row="3" column="4" colspan="2">
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotX"> <widget class="QDoubleSpinBox" name="doubleSpinBox_RotX">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum"> <property name="minimum">
<double>-10000.000000000000000</double> <double>-10000.000000000000000</double>
</property> </property>
@ -513,9 +483,6 @@
<layout class="QHBoxLayout" name="horizontalLayout_13"> <layout class="QHBoxLayout" name="horizontalLayout_13">
<item> <item>
<widget class="QPushButton" name="pushButton_SavePowerUp"> <widget class="QPushButton" name="pushButton_SavePowerUp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Save Powerup</string> <string>Save Powerup</string>
</property> </property>
@ -523,14 +490,18 @@
</item> </item>
<item> <item>
<widget class="QPushButton" name="pushButton_NewPowerUp"> <widget class="QPushButton" name="pushButton_NewPowerUp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>New Powerup</string> <string>New Powerup</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="pushButton_DeletePowerUp">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -547,6 +518,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="pushButton_Restore">
<property name="text">
<string>Restore Backup</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="pushButton_Generate"> <widget class="QPushButton" name="pushButton_Generate">
<property name="text"> <property name="text">
@ -588,17 +566,7 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar"> <widget class="QStatusBar" name="statusBar"/>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1330</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

338
splashscreen.cpp Normal file
View File

@ -0,0 +1,338 @@
#include "splashscreen.h"
#include <QPainter>
#include <QPainterPath>
#include <QPixmap>
#include <QApplication>
#include <QScreen>
#include <QCoreApplication>
#include <QDate>
#include <QtMath>
#include <QLinearGradient>
#include <QRadialGradient>
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<int>(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<int>(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);
}
}

58
splashscreen.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef SPLASHSCREEN_H
#define SPLASHSCREEN_H
#include <QSplashScreen>
#include <QTimer>
#include <QColor>
#include <QMouseEvent>
#include <QKeyEvent>
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