fixed timer functions

This commit is contained in:
2025-11-23 18:41:52 +01:00
parent d5fdae397d
commit adec55526e
7 changed files with 100 additions and 22 deletions

View File

@ -30,6 +30,9 @@ namespace Config {
constexpr double ARR_RATE = 40.0; // Auto Repeat Rate in ms constexpr double ARR_RATE = 40.0; // Auto Repeat Rate in ms
constexpr float LEVEL_FADE_DURATION = 3500.0f; // Level background fade time in ms constexpr float LEVEL_FADE_DURATION = 3500.0f; // Level background fade time in ms
constexpr int MAX_LEVELS = 20; // Maximum selectable starting level constexpr int MAX_LEVELS = 20; // Maximum selectable starting level
// Gravity speed multiplier: 1.0 = normal, 2.0 = 2x slower, 0.5 = 2x faster
constexpr double GRAVITY_SPEED_MULTIPLIER = 1;
} }
// UI Layout constants // UI Layout constants

View File

@ -512,6 +512,11 @@ bool ApplicationManager::initializeGame() {
m_game = std::make_unique<Game>(m_startLevelSelection); m_game = std::make_unique<Game>(m_startLevelSelection);
// Wire up sound callbacks as main.cpp did // Wire up sound callbacks as main.cpp did
if (m_game) { if (m_game) {
// Apply global gravity speed multiplier from config
m_game->setGravityGlobalMultiplier(Config::Gameplay::GRAVITY_SPEED_MULTIPLIER);
// Reset to recalculate gravity with the new multiplier
m_game->reset(m_startLevelSelection);
m_game->setSoundCallback([&](int linesCleared){ m_game->setSoundCallback([&](int linesCleared){
SoundEffectManager::instance().playSound("clear_line", 1.0f); SoundEffectManager::instance().playSound("clear_line", 1.0f);
// voice lines handled via asset manager loaded sounds // voice lines handled via asset manager loaded sounds

View File

@ -56,11 +56,53 @@ void Game::reset(int startLevel_) {
_score = 0; _lines = 0; _level = startLevel_; startLevel = startLevel_; _score = 0; _lines = 0; _level = startLevel_; startLevel = startLevel_;
// Initialize gravity using NES timing table (ms per cell by level) // Initialize gravity using NES timing table (ms per cell by level)
gravityMs = gravityMsForLevel(_level, gravityGlobalMultiplier); gravityMs = gravityMsForLevel(_level, gravityGlobalMultiplier);
fallAcc = 0; _elapsedSec = 0; gameOver=false; paused=false; fallAcc = 0; gameOver=false; paused=false;
_startTime = SDL_GetPerformanceCounter();
_pausedTime = 0;
_lastPauseStart = 0;
hold = Piece{}; hold.type = PIECE_COUNT; canHold=true; hold = Piece{}; hold.type = PIECE_COUNT; canHold=true;
refillBag(); spawn(); refillBag(); spawn();
} }
double Game::elapsed() const {
if (!_startTime) return 0.0;
Uint64 currentTime = SDL_GetPerformanceCounter();
Uint64 totalPausedTime = _pausedTime;
// If currently paused, add time since pause started
if (paused && _lastPauseStart > 0) {
totalPausedTime += (currentTime - _lastPauseStart);
}
Uint64 activeTime = currentTime - _startTime - totalPausedTime;
double seconds = (double)activeTime / (double)SDL_GetPerformanceFrequency();
return seconds;
}
void Game::updateElapsedTime() {
// This method is now just for API compatibility
// Actual elapsed time is calculated on-demand in elapsed()
}
void Game::setPaused(bool p) {
if (p == paused) return; // No change
if (p) {
// Pausing - record when pause started
_lastPauseStart = SDL_GetPerformanceCounter();
} else {
// Unpausing - add elapsed pause time to total
if (_lastPauseStart > 0) {
Uint64 currentTime = SDL_GetPerformanceCounter();
_pausedTime += (currentTime - _lastPauseStart);
_lastPauseStart = 0;
}
}
paused = p;
}
void Game::refillBag() { void Game::refillBag() {
bag.clear(); bag.clear();
for (int i=0;i<PIECE_COUNT;++i) bag.push_back(static_cast<PieceType>(i)); for (int i=0;i<PIECE_COUNT;++i) bag.push_back(static_cast<PieceType>(i));
@ -241,13 +283,15 @@ bool Game::tryMoveDown() {
void Game::tickGravity(double frameMs) { void Game::tickGravity(double frameMs) {
if (paused) return; // Don't tick gravity when paused if (paused) return; // Don't tick gravity when paused
// Soft drop: 20x faster for rapid continuous dropping
double effectiveGravityMs = softDropping ? (gravityMs / 5.0) : gravityMs;
fallAcc += frameMs; fallAcc += frameMs;
while (fallAcc >= gravityMs) { while (fallAcc >= effectiveGravityMs) {
// Attempt to move down by one row // Attempt to move down by one row
if (tryMoveDown()) { if (tryMoveDown()) {
// Award soft drop points only if player is actively holding Down // Award soft drop points only if player is actively holding Down
// JS: POINTS.SOFT_DROP = 1 per cell for soft drop
if (softDropping) { if (softDropping) {
_score += 1; _score += 1;
} }
@ -256,13 +300,14 @@ void Game::tickGravity(double frameMs) {
lockPiece(); lockPiece();
if (gameOver) break; if (gameOver) break;
} }
fallAcc -= gravityMs; fallAcc -= effectiveGravityMs;
} }
} }
void Game::softDropBoost(double frameMs) { void Game::softDropBoost(double frameMs) {
// Reduce soft drop speed multiplier from 10.0 to 3.0 to make it less aggressive // This method is now deprecated - soft drop is handled in tickGravity
if (!paused) fallAcc += frameMs * 3.0; // Kept for API compatibility but does nothing
(void)frameMs;
} }
void Game::hardDrop() { void Game::hardDrop() {

View File

@ -7,6 +7,7 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <SDL3/SDL.h>
#include "../../core/GravityManager.h" #include "../../core/GravityManager.h"
enum PieceType { I, O, T, S, Z, J, L, PIECE_COUNT }; enum PieceType { I, O, T, S, Z, J, L, PIECE_COUNT };
@ -40,13 +41,14 @@ public:
bool canHoldPiece() const { return canHold; } bool canHoldPiece() const { return canHold; }
bool isGameOver() const { return gameOver; } bool isGameOver() const { return gameOver; }
bool isPaused() const { return paused; } bool isPaused() const { return paused; }
void setPaused(bool p) { paused = p; } void setPaused(bool p);
int score() const { return _score; } int score() const { return _score; }
int lines() const { return _lines; } int lines() const { return _lines; }
int level() const { return _level; } int level() const { return _level; }
int startLevelBase() const { return startLevel; } int startLevelBase() const { return startLevel; }
double elapsed() const { return _elapsedSec; } double elapsed() const; // Now calculated from start time
void addElapsed(double frameMs) { if (!paused) _elapsedSec += frameMs/1000.0; } void updateElapsedTime(); // Update elapsed time from system clock
bool isSoftDropping() const { return softDropping; }
// Block statistics // Block statistics
const std::array<int, PIECE_COUNT>& getBlockCounts() const { return blockCounts; } const std::array<int, PIECE_COUNT>& getBlockCounts() const { return blockCounts; }
@ -69,6 +71,7 @@ public:
void setGravityGlobalMultiplier(double m) { gravityGlobalMultiplier = m; } void setGravityGlobalMultiplier(double m) { gravityGlobalMultiplier = m; }
double getGravityGlobalMultiplier() const; double getGravityGlobalMultiplier() const;
double getGravityMs() const; double getGravityMs() const;
double getFallAccumulator() const { return fallAcc; } // Debug: time accumulated toward next drop
void setLevelGravityMultiplier(int level, double m); void setLevelGravityMultiplier(int level, double m);
private: private:
@ -85,7 +88,9 @@ private:
int _level{1}; int _level{1};
double gravityMs{800.0}; double gravityMs{800.0};
double fallAcc{0.0}; double fallAcc{0.0};
double _elapsedSec{0.0}; Uint64 _startTime{0}; // Performance counter at game start
Uint64 _pausedTime{0}; // Time spent paused (in performance counter ticks)
Uint64 _lastPauseStart{0}; // When the current pause started
bool gameOver{false}; bool gameOver{false};
int startLevel{0}; int startLevel{0};
bool softDropping{false}; // true while player holds Down key bool softDropping{false}; // true while player holds Down key

View File

@ -412,6 +412,25 @@ void GameRenderer::renderPlayingState(
snprintf(timeStr, sizeof(timeStr), "%02d:%02d", mins, secs); snprintf(timeStr, sizeof(timeStr), "%02d:%02d", mins, secs);
pixelFont->draw(renderer, scoreX, baseY + 290, timeStr, 0.9f, {255, 255, 255, 255}); pixelFont->draw(renderer, scoreX, baseY + 290, timeStr, 0.9f, {255, 255, 255, 255});
// Debug: Gravity timing info
pixelFont->draw(renderer, scoreX, baseY + 330, "GRAVITY", 0.8f, {150, 150, 150, 255});
double gravityMs = game->getGravityMs();
double fallAcc = game->getFallAccumulator();
// Calculate effective gravity (accounting for soft drop)
bool isSoftDrop = game->isSoftDropping();
double effectiveGravityMs = isSoftDrop ? (gravityMs / 2.0) : gravityMs;
double timeUntilDrop = std::max(0.0, effectiveGravityMs - fallAcc);
char gravityStr[32];
snprintf(gravityStr, sizeof(gravityStr), "%.0f ms%s", gravityMs, isSoftDrop ? " (SD)" : "");
pixelFont->draw(renderer, scoreX, baseY + 350, gravityStr, 0.7f, {180, 180, 180, 255});
char dropStr[32];
snprintf(dropStr, sizeof(dropStr), "Drop: %.0f", timeUntilDrop);
SDL_Color dropColor = isSoftDrop ? SDL_Color{255, 200, 100, 255} : SDL_Color{100, 255, 100, 255};
pixelFont->draw(renderer, scoreX, baseY + 370, dropStr, 0.7f, dropColor);
// Gravity HUD // Gravity HUD
char gms[64]; char gms[64];
double gms_val = game->getGravityMs(); double gms_val = game->getGravityMs();

View File

@ -33,6 +33,7 @@
#include "audio/MenuWrappers.h" #include "audio/MenuWrappers.h"
#include "utils/ImagePathResolver.h" #include "utils/ImagePathResolver.h"
#include "graphics/renderers/GameRenderer.h" #include "graphics/renderers/GameRenderer.h"
#include "core/Config.h"
// Debug logging removed: no-op in this build (previously LOG_DEBUG) // Debug logging removed: no-op in this build (previously LOG_DEBUG)
@ -629,6 +630,9 @@ int main(int, char **)
} }
Game game(startLevelSelection); Game game(startLevelSelection);
// Apply global gravity speed multiplier from config
game.setGravityGlobalMultiplier(Config::Gameplay::GRAVITY_SPEED_MULTIPLIER);
game.reset(startLevelSelection);
// Initialize sound effects system // Initialize sound effects system
SoundEffectManager::instance().init(); SoundEffectManager::instance().init();
@ -713,7 +717,7 @@ int main(int, char **)
const double DAS = 170.0, ARR = 40.0; const double DAS = 170.0, ARR = 40.0;
SDL_Rect logicalVP{0, 0, LOGICAL_W, LOGICAL_H}; SDL_Rect logicalVP{0, 0, LOGICAL_W, LOGICAL_H};
float logicalScale = 1.f; float logicalScale = 1.f;
Uint64 lastMs = SDL_GetTicks(); Uint64 lastMs = SDL_GetPerformanceCounter();
bool musicStarted = false; bool musicStarted = false;
bool musicLoaded = false; bool musicLoaded = false;
int currentTrackLoading = 0; int currentTrackLoading = 0;
@ -1084,9 +1088,12 @@ int main(int, char **)
} }
// --- Timing --- // --- Timing ---
Uint64 now = SDL_GetTicks(); Uint64 now = SDL_GetPerformanceCounter();
double frameMs = double(now - lastMs); double frameMs = double(now - lastMs) * 1000.0 / double(SDL_GetPerformanceFrequency());
lastMs = now; lastMs = now;
// Cap frame time to avoid spiral of death (max 100ms)
if (frameMs > 100.0) frameMs = 100.0;
const bool *ks = SDL_GetKeyboardState(nullptr); const bool *ks = SDL_GetKeyboardState(nullptr);
bool left = state == AppState::Playing && ks[SDL_SCANCODE_LEFT]; bool left = state == AppState::Playing && ks[SDL_SCANCODE_LEFT];
bool right = state == AppState::Playing && ks[SDL_SCANCODE_RIGHT]; bool right = state == AppState::Playing && ks[SDL_SCANCODE_RIGHT];
@ -1132,7 +1139,7 @@ int main(int, char **)
{ {
if (!game.isPaused()) { if (!game.isPaused()) {
game.tickGravity(frameMs); game.tickGravity(frameMs);
game.addElapsed(frameMs); game.updateElapsedTime();
// Update line effect and clear lines when animation completes // Update line effect and clear lines when animation completes
if (lineEffect.isActive()) { if (lineEffect.isActive()) {

View File

@ -128,16 +128,10 @@ void PlayingState::handleEvent(const SDL_Event& e) {
void PlayingState::update(double frameMs) { void PlayingState::update(double frameMs) {
if (!ctx.game) return; if (!ctx.game) return;
static bool debugPrinted = false; // forward per-frame gameplay updates (gravity, line effects)
if (!debugPrinted) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[PLAYING] Starting updates, frameMs=%.2f, paused=%d", frameMs, ctx.game->isPaused());
debugPrinted = true;
}
// forward per-frame gameplay updates (gravity, elapsed)
if (!ctx.game->isPaused()) { if (!ctx.game->isPaused()) {
ctx.game->tickGravity(frameMs); ctx.game->tickGravity(frameMs);
ctx.game->addElapsed(frameMs); ctx.game->updateElapsedTime();
if (ctx.lineEffect && ctx.lineEffect->isActive()) { if (ctx.lineEffect && ctx.lineEffect->isActive()) {
if (ctx.lineEffect->update(frameMs / 1000.0f)) { if (ctx.lineEffect->update(frameMs / 1000.0f)) {