Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e894f6cb3d | ||
|
|
cd1c5419b3 | ||
| e694e4fc84 | |||
| 171e7ebc48 | |||
| c111914d24 |
@ -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
|
||||
|
||||
33
README.md
33
README.md
@ -1,3 +1,34 @@
|
||||
# GWF_PowerUpInjector
|
||||
|
||||
Ever wanted to introduce powerups into an already made GWF workshop map? Now you can with the GWF Powerup Injector. It allows you to select any downloaded workshop map, inject powerup spawners at procedural locations, and save.
|
||||
Ever wanted to introduce powerups into an existing GWF Workshop map? Now you can with the **GWF PowerUp Injector**. It allows you to select any downloaded Workshop map, inject powerup spawners at procedurally chosen locations, and save.
|
||||
|
||||
# License / Disclaimer
|
||||
|
||||
GWF_PowerUpInjector is licensed under the **GNU General Public License v3.0 (GPL-3.0)**. You are free to use GWF_PowerUpInjector (and its source code) under the terms of the GPL. GWF_PowerUpInjector is distributed in the hope that it will be useful and enjoyed, but it comes **WITHOUT ANY WARRANTY**; without even the implied warranty of **MERCHANTABILITY** or **FITNESS FOR A PARTICULAR PURPOSE**. See the LICENSE file for more information.
|
||||
|
||||
This repository is not associated with Blacklight Interactive, Team17, or the developers. These tools are developed to allow users to enhance the already enjoyable experience of *Golf With Your Friends*.
|
||||
|
||||
# Instructions
|
||||
|
||||
1. Download the latest release of the tool.
|
||||
2. Extract and run `GWF_PowerUpInjector.exe`.
|
||||
3. Enter the path to your Steam directory.
|
||||
- This is only needed if the tool doesn't find the Steam directory automatically.
|
||||
4. Select the Workshop map from the list on the left.
|
||||
5. Navigate to the **PowerUps** section and click **Generate**.
|
||||
6. Load the map as usual within *Golf With Your Friends*.
|
||||
|
||||
# Problems and Troubleshooting
|
||||
|
||||
- Currently, the application appends powerups to the current map. This means clicking **Generate** multiple times will add duplicate powerups each time. If this happens and you want to revert the map, you have two options:
|
||||
|
||||
a) Re-download the map (unsubscribe and re-subscribe in Steam).
|
||||
|
||||
**OR**
|
||||
|
||||
b) Restore the `Map.old` file in the Steam Workshop directory
|
||||
(e.g., `C:\Program Files (x86)\Steam\steamapps\workshop\content\431240\[MAP_ID]`).
|
||||
|
||||
# Notes
|
||||
|
||||
- Developed for the Steam version of *Golf With Your Friends*.
|
||||
|
||||
33
main.cpp
33
main.cpp
@ -1,11 +1,42 @@
|
||||
#include "mainwindow.h"
|
||||
#include "splashscreen.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QThread>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
418
mainwindow.cpp
418
mainwindow.cpp
@ -1,6 +1,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
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<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();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
22
mainwindow.h
22
mainwindow.h
@ -8,12 +8,16 @@
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
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<PowerUpInfo> findPowerUps(QJsonObject aMapData);
|
||||
|
||||
QVector<QVector3D> 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;
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
<string>Golf With Your Friends PowerUp Injector</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
@ -252,9 +252,6 @@
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -275,9 +272,6 @@
|
||||
</item>
|
||||
<item row="1" column="4" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -298,9 +292,6 @@
|
||||
</item>
|
||||
<item row="1" column="7" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_PosZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -321,9 +312,6 @@
|
||||
</item>
|
||||
<item row="3" column="10">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -344,9 +332,6 @@
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -367,9 +352,6 @@
|
||||
</item>
|
||||
<item row="5" column="4" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -390,9 +372,6 @@
|
||||
</item>
|
||||
<item row="5" column="7" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_ScaleZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -434,9 +413,6 @@
|
||||
</item>
|
||||
<item row="3" column="7" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -457,9 +433,6 @@
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotW">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -480,9 +453,6 @@
|
||||
</item>
|
||||
<item row="3" column="4" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_RotX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-10000.000000000000000</double>
|
||||
</property>
|
||||
@ -513,9 +483,6 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_SavePowerUp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Powerup</string>
|
||||
</property>
|
||||
@ -523,14 +490,18 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_NewPowerUp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New Powerup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_DeletePowerUp">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -547,6 +518,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_Restore">
|
||||
<property name="text">
|
||||
<string>Restore Backup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_Generate">
|
||||
<property name="text">
|
||||
@ -588,17 +566,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<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 class="QStatusBar" name="statusBar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
338
splashscreen.cpp
Normal file
338
splashscreen.cpp
Normal 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
58
splashscreen.h
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user