feat: implement textured line clear effects and refine UI alignment

- **Visual Effects**: Upgraded line clear particles to use the game's block texture instead of simple circles, matching the reference web game's aesthetic.
- **Particle Physics**: Tuned particle velocity, gravity, and fade rates for a more dynamic explosion effect.
- **Rendering Integration**: Updated [main.cpp](cci:7://file:///d:/Sites/Work/tetris/src/main.cpp:0:0-0:0) and `GameRenderer` to pass the block texture to the effect system and correctly trigger animations upon line completion.
- **Menu UI**: Fixed [MenuState](cci:1://file:///d:/Sites/Work/tetris/src/states/MenuState.cpp:19:0-19:55) layout calculations to use fixed logical dimensions (1200x1000), ensuring consistent centering and alignment of the logo, buttons, and settings icon across different window sizes.
- **Code Cleanup**: Refactored `PlayingState` to delegate effect triggering to the rendering layer where correct screen coordinates are available.
This commit is contained in:
2025-11-21 21:19:14 +01:00
parent b5ef9172b3
commit 66099809e0
47 changed files with 5547 additions and 267 deletions

36
src/graphics/ui/Font.cpp Normal file
View File

@ -0,0 +1,36 @@
// Font.cpp - implementation of FontAtlas (copied into src/graphics)
#include "graphics/Font.h"
#include <SDL3/SDL.h>
bool FontAtlas::init(const std::string& path, int basePt) { fontPath = path; baseSize = basePt; return true; }
void FontAtlas::shutdown() { for (auto &kv : cache) if (kv.second) TTF_CloseFont(kv.second); cache.clear(); }
TTF_Font* FontAtlas::getSized(int ptSize) {
auto it = cache.find(ptSize); if (it!=cache.end()) return it->second;
TTF_Font* f = TTF_OpenFont(fontPath.c_str(), ptSize);
if (!f) return nullptr; cache[ptSize] = f; return f;
}
void FontAtlas::draw(SDL_Renderer* r, float x, float y, const std::string& text, float scale, SDL_Color color) {
if (scale <= 0) return; int pt = int(baseSize * scale); if (pt < 8) pt = 8; TTF_Font* f = getSized(pt); if (!f) return;
SDL_Surface* surf = TTF_RenderText_Blended(f, text.c_str(), text.length(), color); if (!surf) return;
SDL_Texture* tex = SDL_CreateTextureFromSurface(r, surf);
if (tex) { SDL_FRect dst{ x, y, (float)surf->w, (float)surf->h }; SDL_RenderTexture(r, tex, nullptr, &dst); SDL_DestroyTexture(tex); }
SDL_DestroySurface(surf);
}
void FontAtlas::measure(const std::string& text, float scale, int& outW, int& outH) {
outW = 0; outH = 0;
if (scale <= 0) return;
int pt = int(baseSize * scale);
if (pt < 1) pt = 1;
TTF_Font* f = getSized(pt);
if (!f) return;
// Use render-to-surface measurement to avoid dependency on specific TTF_* measurement API variants
SDL_Color dummy = {255,255,255,255};
SDL_Surface* surf = TTF_RenderText_Blended(f, text.c_str(), text.length(), dummy);
if (!surf) return;
outW = surf->w; outH = surf->h;
SDL_DestroySurface(surf);
}

20
src/graphics/ui/Font.h Normal file
View File

@ -0,0 +1,20 @@
// Font.h - Font rendering abstraction with simple size cache
#pragma once
#include <SDL3_ttf/SDL_ttf.h>
#include <string>
#include <unordered_map>
struct SDL_Renderer;
class FontAtlas {
public:
bool init(const std::string& path, int basePt);
void shutdown();
void draw(SDL_Renderer* r, float x, float y, const std::string& text, float scale, SDL_Color color);
// Measure rendered text size in pixels for a given scale
void measure(const std::string& text, float scale, int& outW, int& outH);
private:
std::string fontPath;
int baseSize{24};
std::unordered_map<int, TTF_Font*> cache; // point size -> font*
TTF_Font* getSized(int ptSize);
};