fixed timer functions
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
15
src/main.cpp
15
src/main.cpp
@ -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()) {
|
||||||
|
|||||||
@ -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)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user