Immediate Code Cleanup

This commit is contained in:
2025-08-17 10:58:06 +02:00
parent 56bdc61cc4
commit e591aaba45
8 changed files with 679 additions and 22 deletions

View File

@ -2,6 +2,8 @@
#include "StateManager.h"
#include "InputManager.h"
#include "AssetManager.h"
#include "Config.h"
#include "GlobalState.h"
#include "../graphics/RenderManager.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
@ -23,6 +25,9 @@ bool ApplicationManager::initialize(int argc, char* argv[]) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Initializing ApplicationManager...");
// Initialize GlobalState
GlobalState::instance().initialize();
// Initialize SDL first
if (!initializeSDL()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize SDL");
@ -67,8 +72,8 @@ void ApplicationManager::run() {
m_lastFrameTime = currentTime;
// Limit delta time to prevent spiral of death
if (deltaTime > 0.05f) { // Cap at 20 FPS minimum
deltaTime = 0.05f;
if (deltaTime > Config::Performance::MIN_FRAME_TIME) {
deltaTime = Config::Performance::MIN_FRAME_TIME;
}
// Main loop phases
@ -95,6 +100,9 @@ void ApplicationManager::shutdown() {
// Cleanup in reverse order of initialization
cleanupManagers();
cleanupSDL();
// Shutdown GlobalState last
GlobalState::instance().shutdown();
m_initialized = false;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "ApplicationManager shutdown complete");
@ -154,11 +162,11 @@ bool ApplicationManager::initializeGame() {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading essential assets...");
// Set up asset loading tasks
AssetManager::LoadingTask logoTask{AssetManager::LoadingTask::TEXTURE, "logo", "assets/images/logo.bmp"};
AssetManager::LoadingTask backgroundTask{AssetManager::LoadingTask::TEXTURE, "background", "assets/images/main_background.bmp"};
AssetManager::LoadingTask blocksTask{AssetManager::LoadingTask::TEXTURE, "blocks", "assets/images/blocks90px_001.bmp"};
AssetManager::LoadingTask fontTask{AssetManager::LoadingTask::FONT, "main_font", "FreeSans.ttf", 24};
AssetManager::LoadingTask pixelFontTask{AssetManager::LoadingTask::FONT, "pixel_font", "assets/fonts/PressStart2P-Regular.ttf", 16};
AssetManager::LoadingTask logoTask{AssetManager::LoadingTask::TEXTURE, "logo", Config::Assets::LOGO_BMP};
AssetManager::LoadingTask backgroundTask{AssetManager::LoadingTask::TEXTURE, "background", Config::Assets::BACKGROUND_BMP};
AssetManager::LoadingTask blocksTask{AssetManager::LoadingTask::TEXTURE, "blocks", Config::Assets::BLOCKS_BMP};
AssetManager::LoadingTask fontTask{AssetManager::LoadingTask::FONT, "main_font", Config::Fonts::DEFAULT_FONT_PATH, Config::Fonts::DEFAULT_FONT_SIZE};
AssetManager::LoadingTask pixelFontTask{AssetManager::LoadingTask::FONT, "pixel_font", Config::Fonts::PIXEL_FONT_PATH, Config::Fonts::PIXEL_FONT_SIZE};
// Add tasks to AssetManager
m_assetManager->addLoadingTask(logoTask);
@ -189,7 +197,7 @@ bool ApplicationManager::initializeGame() {
// Try to render background if loaded
SDL_Texture* background = m_assetManager->getTexture("background");
if (background) {
SDL_FRect bgRect = { 0, 0, 1200, 1000 };
SDL_FRect bgRect = { 0, 0, Config::Logical::WIDTH, Config::Logical::HEIGHT };
renderer.renderTexture(background, nullptr, &bgRect);
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "Config.h"
#include <memory>
#include <string>
@ -73,7 +74,7 @@ private:
uint64_t m_lastFrameTime = 0;
// Configuration
int m_windowWidth = 1200;
int m_windowHeight = 1000;
std::string m_windowTitle = "Tetris (SDL3)";
int m_windowWidth = Config::Window::DEFAULT_WIDTH;
int m_windowHeight = Config::Window::DEFAULT_HEIGHT;
std::string m_windowTitle = Config::Window::DEFAULT_TITLE;
};

153
src/core/Config.h Normal file
View File

@ -0,0 +1,153 @@
#pragma once
#include <SDL3/SDL.h>
/**
* Config - Centralized configuration constants
*
* Replaces magic numbers and scattered constants throughout main.cpp
* Organized by functional area for easy maintenance and modification
*/
namespace Config {
// Window and Display Settings
namespace Window {
constexpr int DEFAULT_WIDTH = 1200;
constexpr int DEFAULT_HEIGHT = 1000;
constexpr const char* DEFAULT_TITLE = "Tetris (SDL3)";
constexpr bool DEFAULT_VSYNC = true;
}
// Logical rendering dimensions (internal coordinate system)
namespace Logical {
constexpr int WIDTH = 1200;
constexpr int HEIGHT = 1000;
}
// Gameplay constants
namespace Gameplay {
constexpr double DAS_DELAY = 170.0; // Delayed Auto Shift delay 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 int MAX_LEVELS = 20; // Maximum selectable starting level
}
// UI Layout constants
namespace UI {
constexpr float MIN_MARGIN = 40.0f;
constexpr float PANEL_WIDTH = 180.0f;
constexpr float PANEL_SPACING = 30.0f;
constexpr float BUTTON_HEIGHT_SMALL = 60.0f;
constexpr float BUTTON_HEIGHT_NORMAL = 70.0f;
constexpr float BUTTON_WIDTH_SMALL = 0.4f; // Fraction of screen width
constexpr float BUTTON_WIDTH_NORMAL = 300.0f;
constexpr float SETTINGS_GEAR_SIZE = 50.0f;
constexpr float SETTINGS_GEAR_MARGIN = 10.0f;
// Screen size breakpoints
constexpr float SMALL_SCREEN_BREAKPOINT = 700.0f;
// Menu positioning
constexpr float MENU_BUTTON_Y_OFFSET = 40.0f;
constexpr float MENU_BUTTON_Y_BASE = 0.86f; // Fraction of screen height
}
// Loading screen constants
namespace Loading {
constexpr float LOGO_HEIGHT_FACTOR_LIMITED = 0.25f; // When height < 450
constexpr float LOGO_HEIGHT_FACTOR_NORMAL = 0.4f;
constexpr float LOGO_MAX_WIDTH_FACTOR = 0.9f; // Fraction of screen width
constexpr float LOGO_MAX_WIDTH_ABSOLUTE = 600.0f;
constexpr int LOGO_ORIGINAL_WIDTH = 872;
constexpr int LOGO_ORIGINAL_HEIGHT = 273;
constexpr float LOADING_TEXT_HEIGHT = 20.0f;
constexpr float LOADING_BAR_HEIGHT = 20.0f;
constexpr float LOADING_BAR_PADDING_LIMITED = 15.0f;
constexpr float LOADING_BAR_PADDING_NORMAL = 35.0f;
constexpr float LOADING_PERCENTAGE_HEIGHT = 24.0f;
constexpr float LOADING_ELEMENT_SPACING_LIMITED = 5.0f;
constexpr float LOADING_ELEMENT_SPACING_NORMAL = 15.0f;
constexpr int LIMITED_HEIGHT_THRESHOLD = 450;
}
// Animation constants
namespace Animation {
constexpr double LOGO_ANIM_SPEED = 0.0008; // Logo animation speed multiplier
constexpr float STARFIELD_UPDATE_DIVISOR = 1000.0f; // Convert ms to seconds
}
// HUD and Game Display
namespace HUD {
constexpr float GRAVITY_DISPLAY_X = 260.0f; // Distance from right edge
constexpr float GRAVITY_DISPLAY_Y = 10.0f;
constexpr float SCORE_PANEL_X_OFFSET = 120.0f; // Distance from center
constexpr float SCORE_PANEL_BASE_Y = 220.0f;
constexpr float CONTROLS_HINT_Y_OFFSET = 30.0f; // Distance from bottom
}
// Popup and Modal constants
namespace Popup {
constexpr float EXIT_CONFIRM_WIDTH = 400.0f;
constexpr float EXIT_CONFIRM_HEIGHT = 200.0f;
constexpr float SETTINGS_POPUP_WIDTH = 300.0f;
constexpr float SETTINGS_POPUP_HEIGHT = 250.0f;
}
// Color definitions (commonly used colors)
namespace Colors {
constexpr SDL_Color WHITE = {255, 255, 255, 255};
constexpr SDL_Color BLACK = {0, 0, 0, 255};
constexpr SDL_Color YELLOW_TITLE = {255, 220, 0, 255};
constexpr SDL_Color GRAY_TEXT = {220, 220, 230, 255};
constexpr SDL_Color BLUE_HIGHLIGHT = {200, 240, 255, 255};
constexpr SDL_Color RED_ERROR = {255, 80, 60, 255};
constexpr SDL_Color GREEN_SUCCESS = {0, 255, 0, 255};
constexpr SDL_Color RED_DISABLED = {255, 0, 0, 255};
constexpr SDL_Color CONTROL_HINT = {150, 150, 170, 255};
constexpr SDL_Color PAUSED_TEXT = {255, 255, 255, 255};
constexpr SDL_Color PAUSED_HINT = {200, 200, 220, 255};
constexpr SDL_Color SHADOW = {0, 0, 0, 200};
}
// Font configuration
namespace Fonts {
constexpr int DEFAULT_FONT_SIZE = 24;
constexpr int PIXEL_FONT_SIZE = 16;
constexpr const char* DEFAULT_FONT_PATH = "FreeSans.ttf";
constexpr const char* PIXEL_FONT_PATH = "assets/fonts/PressStart2P-Regular.ttf";
}
// Asset paths
namespace Assets {
constexpr const char* IMAGES_DIR = "assets/images/";
constexpr const char* MUSIC_DIR = "assets/music/";
constexpr const char* FONTS_DIR = "assets/fonts/";
// Specific asset files
constexpr const char* LOGO_BMP = "assets/images/logo.bmp";
constexpr const char* LOGO_SMALL_BMP = "assets/images/logo_small.bmp";
constexpr const char* BACKGROUND_BMP = "assets/images/main_background.bmp";
constexpr const char* BLOCKS_BMP = "assets/images/blocks90px_001.bmp";
}
// Audio settings
namespace Audio {
constexpr float DEFAULT_VOLUME = 1.0f;
constexpr float LETS_GO_VOLUME = 1.0f;
constexpr int MAX_MUSIC_TRACKS = 100; // Maximum number of music files to scan
}
// Input settings
namespace Input {
constexpr int MOUSE_BUTTON_PRIMARY = SDL_BUTTON_LEFT;
constexpr Uint32 MODIFIER_SHIFT = SDL_KMOD_SHIFT;
constexpr Uint32 MODIFIER_CTRL = SDL_KMOD_CTRL;
}
// Performance settings
namespace Performance {
constexpr float MIN_FRAME_TIME = 0.05f; // Cap at 20 FPS minimum (prevent spiral of death)
constexpr int STARFIELD_PARTICLE_COUNT = 200;
constexpr int STARFIELD_3D_PARTICLE_COUNT = 200;
}
}

179
src/core/GlobalState.cpp Normal file
View File

@ -0,0 +1,179 @@
#include "GlobalState.h"
#include "Config.h"
#include <SDL3/SDL.h>
#include <algorithm>
#include <random>
GlobalState& GlobalState::instance() {
static GlobalState instance;
return instance;
}
void GlobalState::initialize() {
if (m_initialized) {
return;
}
// Initialize timing
lastMs = SDL_GetTicks();
loadStart = SDL_GetTicks();
// Initialize viewport to logical dimensions
logicalVP = {0, 0, Config::Logical::WIDTH, Config::Logical::HEIGHT};
// Initialize fireworks system
fireworks.clear();
lastFireworkTime = 0;
m_initialized = true;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[GlobalState] Initialized");
}
void GlobalState::shutdown() {
if (!m_initialized) {
return;
}
// Clear fireworks
fireworks.clear();
m_initialized = false;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[GlobalState] Shutdown complete");
}
void GlobalState::updateFireworks(double frameMs) {
const Uint64 currentTime = SDL_GetTicks();
// Create new fireworks occasionally
if (currentTime - lastFireworkTime > 800 + (rand() % 1200)) {
float x = Config::Logical::WIDTH * 0.2f + (rand() % (int)(Config::Logical::WIDTH * 0.6f));
float y = Config::Logical::HEIGHT * 0.3f + (rand() % (int)(Config::Logical::HEIGHT * 0.4f));
createFirework(x, y);
lastFireworkTime = currentTime;
}
// Update existing fireworks
for (auto& firework : fireworks) {
if (!firework.active) continue;
bool hasActiveParticles = false;
for (auto& particle : firework.particles) {
if (particle.life <= 0) continue;
// Update physics
particle.x += particle.vx * (frameMs / 1000.0f);
particle.y += particle.vy * (frameMs / 1000.0f);
particle.vy += 150.0f * (frameMs / 1000.0f); // Gravity
particle.life -= frameMs;
// Fade size over time
float lifeRatio = particle.life / particle.maxLife;
particle.size = 20.0f + 10.0f * lifeRatio;
if (particle.life > 0) {
hasActiveParticles = true;
}
}
firework.active = hasActiveParticles;
}
}
void GlobalState::createFirework(float x, float y) {
// Find an inactive firework to reuse
TetrisFirework* firework = nullptr;
for (auto& fw : fireworks) {
if (!fw.active) {
firework = &fw;
break;
}
}
// If no inactive firework found, create a new one
if (!firework) {
fireworks.emplace_back();
firework = &fireworks.back();
}
// Initialize firework
firework->active = true;
firework->particles.clear();
// Create particles
const int particleCount = 12 + (rand() % 8);
for (int i = 0; i < particleCount; ++i) {
BlockParticle particle;
particle.x = x;
particle.y = y;
// Random velocity in all directions
float angle = (float)(rand() % 360) * 3.14159f / 180.0f;
float speed = 80.0f + (rand() % 120);
particle.vx = cos(angle) * speed;
particle.vy = sin(angle) * speed - 50.0f; // Slight upward bias
particle.type = 1 + (rand() % 7); // Random tetris piece color
particle.maxLife = 1500.0f + (rand() % 1000); // 1.5-2.5 seconds
particle.life = particle.maxLife;
particle.size = 15.0f + (rand() % 15);
firework->particles.push_back(particle);
}
}
void GlobalState::drawFireworks(SDL_Renderer* renderer, SDL_Texture* blocksTex) {
if (!blocksTex) return;
for (const auto& firework : fireworks) {
if (!firework.active) continue;
for (const auto& particle : firework.particles) {
if (particle.life <= 0) continue;
// Calculate alpha based on remaining life
float lifeRatio = particle.life / particle.maxLife;
Uint8 alpha = (Uint8)(255 * std::min(1.0f, lifeRatio * 2.0f));
// Set texture alpha
SDL_SetTextureAlphaMod(blocksTex, alpha);
SDL_SetTextureBlendMode(blocksTex, SDL_BLENDMODE_BLEND);
// Draw particle as a small block
SDL_FRect srcRect = {(particle.type - 1) * 90.0f, 0, 90.0f, 90.0f};
SDL_FRect dstRect = {
particle.x - particle.size / 2,
particle.y - particle.size / 2,
particle.size,
particle.size
};
SDL_RenderTexture(renderer, blocksTex, &srcRect, &dstRect);
}
}
// Reset texture alpha
SDL_SetTextureAlphaMod(blocksTex, 255);
SDL_SetTextureBlendMode(blocksTex, SDL_BLENDMODE_BLEND);
}
void GlobalState::resetGameState() {
// Reset game-related state
leftHeld = false;
rightHeld = false;
moveTimerMs = 0.0;
startLevelSelection = 0;
}
void GlobalState::resetUIState() {
// Reset UI state
showSettingsPopup = false;
showExitConfirmPopup = false;
hoveredButton = -1;
}
void GlobalState::resetAnimationState() {
// Reset animation state
logoAnimCounter = 0.0;
fireworks.clear();
lastFireworkTime = 0;
}

115
src/core/GlobalState.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#include <vector>
#include <memory>
#include <SDL3/SDL.h>
// Forward declarations
class FontAtlas;
class Game;
class ScoreManager;
class Starfield;
class Starfield3D;
class LineEffect;
/**
* GlobalState - Centralized management of application-wide state
*
* Replaces global variables scattered throughout main.cpp
* Provides controlled access to shared state between systems
* Will eventually be replaced by proper dependency injection
*/
class GlobalState {
public:
// Singleton access (temporary until dependency injection is implemented)
static GlobalState& instance();
// Initialization and cleanup
void initialize();
void shutdown();
// Application state flags
bool running = true;
bool isFullscreen = false;
bool musicEnabled = true;
bool musicStarted = false;
bool musicLoaded = false;
// UI state flags
bool showSettingsPopup = false;
bool showExitConfirmPopup = false;
int hoveredButton = -1; // -1 = none, 0 = play, 1 = level, 2 = settings
// Input state (will be migrated to InputManager)
bool leftHeld = false;
bool rightHeld = false;
double moveTimerMs = 0.0;
// Loading state
double loadingProgress = 0.0;
int currentTrackLoading = 0;
int totalTracks = 0;
int startLevelSelection = 0;
// Timing
Uint64 lastMs = 0;
Uint64 loadStart = 0;
// Animation state
double logoAnimCounter = 0.0;
// Level background caching
int cachedLevel = -1;
float levelFadeAlpha = 0.0f;
float levelFadeElapsed = 0.0f;
// Viewport and scaling
SDL_Rect logicalVP{0, 0, 1200, 1000}; // Will use Config::Logical constants
float logicalScale = 1.0f;
// Fireworks system (for menu animation)
struct BlockParticle {
float x, y, vx, vy;
int type;
float life, maxLife;
float size;
};
struct TetrisFirework {
std::vector<BlockParticle> particles;
bool active = false;
};
std::vector<TetrisFirework> fireworks;
Uint64 lastFireworkTime = 0;
// Fireworks management methods
void updateFireworks(double frameMs);
void createFirework(float x, float y);
void drawFireworks(SDL_Renderer* renderer, SDL_Texture* blocksTex);
// Reset methods for different states
void resetGameState();
void resetUIState();
void resetAnimationState();
private:
GlobalState() = default;
~GlobalState() = default;
GlobalState(const GlobalState&) = delete;
GlobalState& operator=(const GlobalState&) = delete;
bool m_initialized = false;
};
// Convenience accessors (temporary until proper dependency injection)
namespace Globals {
inline GlobalState& state() { return GlobalState::instance(); }
// Quick access to commonly used flags
inline bool& running() { return state().running; }
inline bool& musicEnabled() { return state().musicEnabled; }
inline bool& showSettingsPopup() { return state().showSettingsPopup; }
inline int& hoveredButton() { return state().hoveredButton; }
inline double& logoAnimCounter() { return state().logoAnimCounter; }
}