fixed highscores
This commit is contained in:
@ -899,7 +899,7 @@ void TetrisApp::Impl::runLoop()
|
||||
int rightScore = coopGame->score(CoopGame::PlayerSide::Right);
|
||||
int combinedScore = leftScore + rightScore;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(combinedScore, coopGame->lines(), coopGame->level(), coopGame->elapsed(), combined);
|
||||
scores.submit(combinedScore, coopGame->lines(), coopGame->level(), coopGame->elapsed(), combined, "cooperate");
|
||||
Settings::instance().setPlayerName(playerName);
|
||||
isNewHighScore = false;
|
||||
SDL_StopTextInput(window);
|
||||
@ -911,7 +911,8 @@ void TetrisApp::Impl::runLoop()
|
||||
} else if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER) {
|
||||
if (playerName.empty()) playerName = "PLAYER";
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed(), playerName);
|
||||
std::string gt = (game->getMode() == GameMode::Challenge) ? "challenge" : "classic";
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed(), playerName, gt);
|
||||
Settings::instance().setPlayerName(playerName);
|
||||
isNewHighScore = false;
|
||||
SDL_StopTextInput(window);
|
||||
@ -1302,7 +1303,7 @@ void TetrisApp::Impl::runLoop()
|
||||
} else {
|
||||
isNewHighScore = false;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(combinedScore, coopGame->lines(), coopGame->level(), coopGame->elapsed());
|
||||
scores.submit(combinedScore, coopGame->lines(), coopGame->level(), coopGame->elapsed(), "P1 & P2", "cooperate");
|
||||
}
|
||||
state = AppState::GameOver;
|
||||
stateMgr->setState(state);
|
||||
@ -1328,7 +1329,10 @@ void TetrisApp::Impl::runLoop()
|
||||
} else {
|
||||
isNewHighScore = false;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed());
|
||||
{
|
||||
std::string gt = (game->getMode() == GameMode::Challenge) ? "challenge" : "classic";
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed(), "PLAYER", gt);
|
||||
}
|
||||
}
|
||||
state = AppState::GameOver;
|
||||
stateMgr->setState(state);
|
||||
|
||||
@ -1406,11 +1406,14 @@ void ApplicationManager::setupStateHandlers() {
|
||||
if (m_stateContext.game->isGameOver()) {
|
||||
// Submit score before transitioning
|
||||
if (m_stateContext.scores) {
|
||||
std::string gt = (m_stateContext.game->getMode() == GameMode::Challenge) ? "challenge" : "classic";
|
||||
m_stateContext.scores->submit(
|
||||
m_stateContext.game->score(),
|
||||
m_stateContext.game->lines(),
|
||||
m_stateContext.game->level(),
|
||||
m_stateContext.game->elapsed()
|
||||
m_stateContext.game->elapsed(),
|
||||
std::string("PLAYER"),
|
||||
gt
|
||||
);
|
||||
}
|
||||
m_stateManager->setState(AppState::GameOver);
|
||||
|
||||
@ -54,14 +54,33 @@ void ScoreManager::load() {
|
||||
ScoreEntry e;
|
||||
iss >> e.score >> e.lines >> e.level >> e.timeSec;
|
||||
if (iss) {
|
||||
// Try to read name (rest of line after timeSec)
|
||||
// Try to read name (rest of line after timeSec). We may also have a trailing gameType token.
|
||||
std::string remaining;
|
||||
std::getline(iss, remaining);
|
||||
if (!remaining.empty() && remaining[0] == ' ') {
|
||||
e.name = remaining.substr(1); // Remove leading space
|
||||
if (!remaining.empty() && remaining[0] == ' ') remaining = remaining.substr(1);
|
||||
if (!remaining.empty()) {
|
||||
static const std::vector<std::string> known = {"classic","cooperate","challenge","versus"};
|
||||
while (!remaining.empty() && (remaining.back() == '\n' || remaining.back() == '\r' || remaining.back() == ' ')) remaining.pop_back();
|
||||
size_t lastSpace = remaining.find_last_of(' ');
|
||||
std::string lastToken = (lastSpace == std::string::npos) ? remaining : remaining.substr(lastSpace + 1);
|
||||
bool matched = false;
|
||||
for (const auto &k : known) {
|
||||
if (lastToken == k) {
|
||||
matched = true;
|
||||
e.gameType = k;
|
||||
if (lastSpace == std::string::npos) e.name = "PLAYER";
|
||||
else e.name = remaining.substr(0, lastSpace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
e.name = remaining;
|
||||
e.gameType = "classic";
|
||||
}
|
||||
} else {
|
||||
e.name = "PLAYER";
|
||||
e.gameType = "classic";
|
||||
}
|
||||
// For backward compatibility local files may not include gameType; default is 'classic'
|
||||
e.gameType = "classic";
|
||||
scores.push_back(e);
|
||||
}
|
||||
if (scores.size() >= maxEntries) break;
|
||||
@ -91,6 +110,8 @@ void ScoreManager::submit(int score, int lines, int level, double timeSec, const
|
||||
newEntry.level = level;
|
||||
newEntry.timeSec = timeSec;
|
||||
newEntry.name = name;
|
||||
// preserve the game type locally so menu filtering works immediately
|
||||
newEntry.gameType = gameType;
|
||||
scores.push_back(newEntry);
|
||||
std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
|
||||
if (scores.size()>maxEntries) scores.resize(maxEntries);
|
||||
@ -105,6 +126,15 @@ bool ScoreManager::isHighScore(int score) const {
|
||||
return score > scores.back().score;
|
||||
}
|
||||
|
||||
void ScoreManager::replaceAll(const std::vector<ScoreEntry>& newScores) {
|
||||
scores = newScores;
|
||||
// Ensure ordering and trimming to our configured maxEntries
|
||||
std::sort(scores.begin(), scores.end(), [](auto&a,auto&b){return a.score>b.score;});
|
||||
if (scores.size() > maxEntries) scores.resize(maxEntries);
|
||||
// Persist new set to local file for next launch
|
||||
try { save(); } catch (...) { /* swallow */ }
|
||||
}
|
||||
|
||||
void ScoreManager::createSampleScores() {
|
||||
scores = {
|
||||
{159840, 189, 14, 972.0, "GREGOR"},
|
||||
|
||||
@ -10,6 +10,8 @@ public:
|
||||
explicit ScoreManager(size_t maxScores = 12);
|
||||
void load();
|
||||
void save() const;
|
||||
// Replace the in-memory scores (thread-safe caller should ensure non-blocking)
|
||||
void replaceAll(const std::vector<ScoreEntry>& newScores);
|
||||
// New optional `gameType` parameter will be sent as `game_type`.
|
||||
// Allowed values: "classic", "versus", "cooperate", "challenge".
|
||||
void submit(int score, int lines, int level, double timeSec, const std::string& name = "PLAYER", const std::string& gameType = "classic");
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "MenuState.h"
|
||||
#include "persistence/Scores.h"
|
||||
#include "../network/supabase_client.h"
|
||||
#include "graphics/Font.h"
|
||||
#include "../graphics/ui/HelpOverlay.h"
|
||||
#include "../core/GlobalState.h"
|
||||
@ -169,6 +170,24 @@ void MenuState::onEnter() {
|
||||
if (ctx.exitPopupSelectedButton) {
|
||||
*ctx.exitPopupSelectedButton = 1;
|
||||
}
|
||||
// Refresh highscores for classic/cooperate/challenge asynchronously
|
||||
try {
|
||||
std::thread([this]() {
|
||||
try {
|
||||
auto c_classic = supabase::FetchHighscores("classic", 12);
|
||||
auto c_coop = supabase::FetchHighscores("cooperate", 12);
|
||||
auto c_challenge = supabase::FetchHighscores("challenge", 12);
|
||||
std::vector<ScoreEntry> combined;
|
||||
combined.reserve(c_classic.size() + c_coop.size() + c_challenge.size());
|
||||
combined.insert(combined.end(), c_classic.begin(), c_classic.end());
|
||||
combined.insert(combined.end(), c_coop.begin(), c_coop.end());
|
||||
combined.insert(combined.end(), c_challenge.begin(), c_challenge.end());
|
||||
if (this->ctx.scores) this->ctx.scores->replaceAll(combined);
|
||||
} catch (...) {
|
||||
// swallow network errors - keep existing scores
|
||||
}
|
||||
}).detach();
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) {
|
||||
|
||||
Reference in New Issue
Block a user