Skip to content

Commit

Permalink
Add online high scores
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisD3D committed Apr 16, 2024
1 parent 6f50c67 commit 292b1e0
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 71 deletions.
1 change: 1 addition & 0 deletions src/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Map {
TileType getTileAt(const Position &) const;

// Getters
QString getFile() const { return file; }
int getWidth() const { return width; }
int getHeight() const { return height; }
int getInitX() const { return init_x; }
Expand Down
45 changes: 45 additions & 0 deletions src/network_manager.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "network_manager.hpp"


FileDownloader::FileDownloader(const QUrl &url, QObject *parent) : QObject(parent), m_WebCtrl(this) {
connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));

Expand All @@ -23,3 +24,47 @@ void FileDownloader::fileDownloaded(QNetworkReply *pReply) {
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}

ScoreManager::ScoreManager(QObject *parent) : QObject(parent) {
manager = new QNetworkAccessManager(this); // Initialize the manager in the constructor

connect(manager, &QNetworkAccessManager::finished, this, &ScoreManager::onRequestFinished);
}

void ScoreManager::upload_score(const QString &map_name, const QString &name, int score) const {
QNetworkRequest request(
QUrl("http://snakeqt.denisd3d.fr/highscores/" + map_name + "/" + name + "/" + QString::number(score)));
request.setRawHeader("X-App-Name", "SnakeQt");

manager->get(request);
}

void ScoreManager::get_highscores(const QString &map_name) const {
QNetworkRequest request(QUrl("http://snakeqt.denisd3d.fr/highscores/" + map_name));

manager->get(request);

}

void ScoreManager::onRequestFinished(QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Network error: " << reply->errorString();
return;
}

if (!reply->request().hasRawHeader("X-App-Name")) {
const auto responseData = reply->readAll();
const QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
auto l = QList<QPair<QString, QVariant> >();
const QJsonObject obj = jsonDoc.object();
for (const auto &key: obj.keys()) {
l.append(QPair(key, obj[key].toInt()));
}

std::sort(l.begin(), l.end(), [](const QPair<QString, QVariant>& a, const QPair<QString, QVariant>& b) {
return a.second.toInt() > b.second.toInt();
});

emit highscoresReceived(l);
}
}
20 changes: 20 additions & 0 deletions src/network_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>

class FileDownloader final : public QObject {
Q_OBJECT
Expand All @@ -28,4 +30,22 @@ private slots:
QByteArray m_DownloadedData;
};

class ScoreManager final : public QObject {
Q_OBJECT

QNetworkAccessManager *manager;

public:
explicit ScoreManager(QObject *parent = nullptr);

void upload_score(const QString &map_name, const QString &name, int score) const;

void get_highscores(const QString &map_name) const;

signals:
void highscoresReceived(const QList<QPair<QString, QVariant> > &highscores);

private slots:
void onRequestFinished(QNetworkReply *reply);
};
#endif // NETWORK_MANAGER_H
2 changes: 1 addition & 1 deletion src/screens/browsemapscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ void BrowseMapScreen::loadClicked() {
void BrowseMapScreen::mapSelectionChanged() {
if (mapList->currentItem() != nullptr) {
loadButton->setText(mapList->currentItem()->data(Qt::UserRole).toBool() ? "Play" : "Download and Play");
loadButton->setStyleSheet(mapList->currentItem()->data(Qt::UserRole).toBool() ? "font-size: 20px;" : "font-size: 17px;");
loadButton->setStyleSheet(mapList->currentItem()->data(Qt::UserRole).toBool() ? "" : "font-size: 17px;");
}
}

Expand Down
149 changes: 87 additions & 62 deletions src/screens/endgamescreen.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#include "endgamescreen.hpp"
#include "snakewindow.hpp"

#include <qfontdatabase.h>
#include <qheaderview.h>

EndGameScreen::EndGameScreen(const int score, QWidget *parent)
: QWidget(parent), scoreLabel(new QLabel(this)), nameInput(new QLineEdit(this)) {
EndGameScreen::EndGameScreen(const QString &map_name, const int score, QWidget *parent)
: QWidget(parent), scoreLabel(new QLabel(this)), nameInput(new QLineEdit(this)), map_name(map_name), score(score),
scoreManager(new ScoreManager(this)) {
// Load custom font
const int id = QFontDatabase::addApplicationFont(":/images/game_played.otf");
const QString family = QFontDatabase::applicationFontFamilies(id).at(0);
Expand All @@ -25,106 +26,130 @@ EndGameScreen::EndGameScreen(const int score, QWidget *parent)
scoreLabel->setAlignment(Qt::AlignCenter); // Centrez le texte

// tableau score
QTableWidget *scoreTable = new QTableWidget(5, 2, this);
scoreTable->setHorizontalHeaderLabels(QStringList() << "NOM" << "SCORE");
QString playerName[] = {"Denis", "Augustin", "Tactique", "Toni", "Bailauto"};
int playerScore[] = {300, 250, 210, 20, 10};

// Boucle pour remplir le tableau avec les valeurs spécifiques
for (int i = 0; i < 5; ++i) {
QTableWidgetItem *nameItem = new QTableWidgetItem(playerName[i]);
QTableWidgetItem *scoreItem = new QTableWidgetItem(QString::number(playerScore[i]));
scoreTable->setItem(i, 0, nameItem);
scoreTable->setItem(i, 1, scoreItem);
}

//widget vide avec un layout horizontal pour centrer le tableau
QWidget *tableWidget = new QWidget(this);
QHBoxLayout *tableLayout = new QHBoxLayout(tableWidget);
tableLayout->addWidget(scoreTable);
tableLayout->setContentsMargins(0, 0, 0, 0);
tableLayout->setSpacing(0);

// Ajouter les widgets au layout principal
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(endGameLabel);
mainLayout->addWidget(scoreLabel);
mainLayout->addWidget(tableWidget, 0, Qt::AlignHCenter); // Centrer horizontalement le widget contenant le tableau


// l'étiquette de score au layout
layout->addStretch();
layout->addWidget(endGameLabel);
layout->addWidget(scoreLabel);
layout->addStretch();
layout->addSpacing(20); // Ajouter un espace de 20 pixels
layout->addWidget(scoreTable);
layout->addSpacing(20);
scoreTable = new QTableWidget(this);
scoreTable->setColumnCount(2); // 2 colonnes
scoreTable->setHorizontalHeaderLabels(QStringList() << "Name" << "Score"); // Noms des colonnes
scoreTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// Étirer les colonnes pour remplir l'espace
scoreManager->get_highscores(map_name);

//zone de saisie de texte pour le nom
QVBoxLayout *textInputLayout = new QVBoxLayout;
nameInput->setPlaceholderText("Enter your name"); // Texte d'invite
nameInput->setFont(QFont("Arial", 16)); // Police de taille 16
textInputLayout->addWidget(nameInput);
nameInput->setFont(QFont("Arial", 16)); // Police de taille 16n
QFile file("playername.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
const auto playerName = in.readLine();
nameInput->setText(playerName);
}

//bouton de soumission du nom
QPushButton *submitButton = new QPushButton("Submit", this);
textInputLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
auto *submitButton = new QPushButton("Submit my score", this);

// Centrer horizontalement le champ de saisie et le bouton
layout->setAlignment(Qt::AlignHCenter);

connect(submitButton, &QPushButton::clicked, this, &EndGameScreen::submitName);

// Création du bouton de retour au menu principal
QPushButton *returnButton = new QPushButton("Main Menu", this);
auto *returnButton = new QPushButton("Main Menu", this);
snakefont.setPointSize(20);
returnButton->setFont(snakefont);
returnButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");
returnButton->setFixedSize(200, 50); // Taille fixe pour le bouton
connect(returnButton, &QPushButton::clicked, [this]() {
// widget parent (SnakeWindow)
SnakeWindow *snakeWindow = qobject_cast<SnakeWindow *>(parentWidget());
if (snakeWindow) {
// Appeler returnToMainMenu de SnakeWindow
snakeWindow->returnToMainMenu();
}

connect(returnButton, &QPushButton::clicked, this, &EndGameScreen::returnToMainMenu);

auto *replayButton = new QPushButton("Replay", this);
snakefont.setPointSize(20);
replayButton->setFont(snakefont);
replayButton->setStyleSheet("padding-left: 20px; padding-right: 20px;");
replayButton->setFixedSize(200, 50); // Taille fixe pour le bouton

connect(replayButton, &QPushButton::clicked, [this, map_name] {
emit replayMap();
});

auto *leftLayout = new QVBoxLayout;
leftLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));
leftLayout->addWidget(nameInput);
leftLayout->addWidget(submitButton);

auto *centerLayout = new QHBoxLayout;
centerLayout->addLayout(leftLayout);
centerLayout->addWidget(scoreTable);

// l'étiquette de score au layout
layout->addStretch();
layout->addWidget(endGameLabel);
layout->addWidget(scoreLabel);
layout->addStretch();
layout->addSpacing(20); // Ajouter un espace de 20 pixels
layout->addLayout(centerLayout);
layout->addSpacing(20);

// Ajout des bouton au layout
auto *layoutH = new QHBoxLayout;
layoutH->addWidget(returnButton);
layoutH->addWidget(replayButton);

// Ajouter les widgets au layout
layout->addWidget(nameInput);
layout->addWidget(submitButton);
// Ajout du bouton au layout
layout->addWidget(returnButton);
layout->addLayout(layoutH);

// Déf layout pour la fenêtre
setLayout(layout);

connect(scoreManager, &ScoreManager::highscoresReceived, this, &EndGameScreen::onHighscoresReceived);
}

//slot submitName pour clic bouton de soumission du nom
void EndGameScreen::submitName() {
QString playerName = nameInput->text(); // Récupérer le texte
// Save player name for next game
const auto playerName = nameInput->text(); // Récupérer le texte

if (playerName.isEmpty())
return;

//fichier CSV en mode écriture
QFile file("scores.csv");
if (!file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
//fichier txt en mode écriture
QFile file("playername.txt");
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Erreur lors de l'ouverture du fichier";
return;
}

// Créer un flux QTextStream pour écrire dans le fichier
QTextStream out(&file);
out << playerName << endl;
out << playerName << Qt::endl;

file.close();

// Upload highscore
scoreManager->upload_score(map_name, playerName, score);
scoreManager->get_highscores(map_name);
}


void EndGameScreen::returnToMainMenu() {
// Mettez ici le code pour revenir au menu principal, par exemple :
emit back();
}

void EndGameScreen::onHighscoresReceived(const QList<QPair<QString, QVariant> > &highscores) {
scoreTable->setRowCount(highscores.size()); // Set the number of rows in the table

int row = 0;
for (auto it = highscores.begin(); it != highscores.end(); ++it) {
// Create a new item for the player's name
auto *nameItem = new QTableWidgetItem(it->first);
nameItem->setFlags(nameItem->flags() ^ Qt::ItemIsEditable); // Make the item non-editable

}
// Create a new item for the player's score
auto *scoreItem = new QTableWidgetItem(it->second.toString());
scoreItem->setFlags(scoreItem->flags() ^ Qt::ItemIsEditable); // Make the item non-editable

// Add the items to the table
scoreTable->setItem(row, 0, nameItem);
scoreTable->setItem(row, 1, scoreItem);

++row;
}
}
16 changes: 12 additions & 4 deletions src/screens/endgamescreen.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


#ifndef SNAKEQT_ENDGAMESCREEN_HPP
#define SNAKEQT_ENDGAMESCREEN_HPP

Expand All @@ -13,24 +11,34 @@
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include "../network_manager.hpp"


class EndGameScreen final : public QWidget {
Q_OBJECT
Q_OBJECT

public:
explicit EndGameScreen(int score, QWidget *parent = nullptr);
explicit EndGameScreen(const QString &map_name, int score, QWidget *parent = nullptr);

private slots:
void submitName();

void returnToMainMenu();

void onHighscoresReceived(const QList<QPair<QString, QVariant> > &highscores);

private:
QLabel *scoreLabel;
QLineEdit *nameInput;
QString map_name;
int score;
ScoreManager *scoreManager;
QTableWidget *scoreTable;

signals:
void back();

void replayMap();
};

#endif //SNAKEQT_ENDGAMESCREEN_HPP
23 changes: 20 additions & 3 deletions src/screens/snakewindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,32 @@ void SnakeWindow::handleExitClicked() {
}

void SnakeWindow::handleGameOver(const int score) {
const QString filePath = gameScreen->jeu.getMap().getFile();
const QFileInfo fileInfo(filePath);
const QString baseName = fileInfo.baseName();

// Switch to the end game screen when the game is over
endGameScreen = new EndGameScreen(score, this);
endGameScreen = new EndGameScreen(baseName, score, this);
stackedWidget->addWidget(endGameScreen);
stackedWidget->setCurrentWidget(endGameScreen);
connect(endGameScreen, &EndGameScreen::back, [this] {
stackedWidget->setCurrentWidget(mainMenu);
});

connect(endGameScreen, &EndGameScreen::replayMap, this, &SnakeWindow::replayMap);

update();
}

void SnakeWindow::returnToMainMenu() {
void SnakeWindow::returnToMainMenu() const {
// Return to the main menu
stackedWidget->setCurrentWidget(mainMenu);
}
}

void SnakeWindow::replayMap() {
gameScreen = new GameScreen(this, gameScreen->jeu.getMap().getFile());
stackedWidget->addWidget(gameScreen);
stackedWidget->setCurrentWidget(gameScreen);

connect(gameScreen, &GameScreen::gameOver, this, &SnakeWindow::handleGameOver);
}
Loading

0 comments on commit 292b1e0

Please sign in to comment.