new background for main screen

This commit is contained in:
2025-12-01 20:52:26 +01:00
parent cfbb4e4c86
commit 383b2e48ec
11 changed files with 417 additions and 85 deletions

View File

@ -19,6 +19,8 @@
// `ctx.musicEnabled` and `ctx.hoveredButton` instead to avoid globals.
// Menu helper wrappers are declared in a shared header implemented in main.cpp
#include "../audio/MenuWrappers.h"
#include "../utils/ImagePathResolver.h"
#include <SDL3_image/SDL_image.h>
MenuState::MenuState(StateContext& ctx) : State(ctx) {}
@ -159,9 +161,6 @@ void MenuState::handleEvent(const SDL_Event& e) {
void MenuState::update(double frameMs) {
// Update logo animation counter
GlobalState::instance().logoAnimCounter += frameMs;
// Update fireworks particles
GlobalState::instance().updateFireworks(frameMs);
}
void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) {
@ -184,67 +183,32 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
float contentOffsetY = (winH - contentH) * 0.5f / logicalScale;
// Background is drawn by main (stretched to the full window) to avoid double-draw.
// Draw the animated logo and fireworks using the small logo if available (show whole image)
SDL_Texture* logoToUse = ctx.logoSmallTex ? ctx.logoSmallTex : ctx.logoTex;
// Trace logo texture pointer
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render logoToUse=%llu\n", (unsigned long long)(uintptr_t)logoToUse); fclose(f); }
}
if (logoToUse) {
// Use dimensions provided by the shared context when available
int texW = (logoToUse == ctx.logoSmallTex && ctx.logoSmallW > 0) ? ctx.logoSmallW : 872;
int texH = (logoToUse == ctx.logoSmallTex && ctx.logoSmallH > 0) ? ctx.logoSmallH : 273;
float maxW = LOGICAL_W * 0.6f;
float scale = std::min(1.0f, maxW / float(texW));
float dw = texW * scale;
float dh = texH * scale;
float logoX = (LOGICAL_W - dw) / 2.f + contentOffsetX;
float logoY = LOGICAL_H * 0.05f + contentOffsetY;
SDL_FRect dst{logoX, logoY, dw, dh};
// Trace before rendering logo
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render before SDL_RenderTexture logo ptr=%llu\n", (unsigned long long)(uintptr_t)logoToUse); fclose(f); }
}
SDL_RenderTexture(renderer, logoToUse, nullptr, &dst);
// Trace after rendering logo
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render after SDL_RenderTexture logo\n"); fclose(f); }
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render ctx.mainScreenTex=%llu (w=%d h=%d)\n",
(unsigned long long)(uintptr_t)ctx.mainScreenTex,
ctx.mainScreenW,
ctx.mainScreenH);
fclose(f);
}
}
// Fireworks (draw above high scores / near buttons)
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render before drawFireworks blocksTex=%llu\n", (unsigned long long)(uintptr_t)ctx.blocksTex); fclose(f); }
}
GlobalState::instance().drawFireworks(renderer, ctx.blocksTex);
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render after drawFireworks\n"); fclose(f); }
}
// Score list and top players with a sine-wave vertical animation (use pixelFont for retro look)
float topPlayersY = LOGICAL_H * 0.30f + contentOffsetY; // more top padding
FontAtlas* useFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
float topPlayersY = LOGICAL_H * 0.24f + contentOffsetY;
if (useFont) {
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render before useFont->draw TOP PLAYERS ptr=%llu\n", (unsigned long long)(uintptr_t)useFont); fclose(f); }
}
const std::string title = "TOP PLAYERS";
int tW = 0, tH = 0; useFont->measure(title, 1.8f, tW, tH);
int tW = 0, tH = 0;
useFont->measure(title, 1.8f, tW, tH);
float titleX = (LOGICAL_W - (float)tW) * 0.5f + contentOffsetX;
useFont->draw(renderer, titleX, topPlayersY, title, 1.8f, SDL_Color{255, 220, 0, 255});
{
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render after useFont->draw TOP PLAYERS\n"); fclose(f); }
}
}
// High scores table with wave offset
float scoresStartY = topPlayersY + 70; // more spacing under title
float scoresStartY = topPlayersY + 70.0f;
static const std::vector<ScoreEntry> EMPTY_SCORES;
const auto& hs = ctx.scores ? ctx.scores->all() : EMPTY_SCORES;
size_t maxDisplay = std::min(hs.size(), size_t(12));
// Draw table header
if (useFont) {
float cx = LOGICAL_W * 0.5f + contentOffsetX;
float colX[] = { cx - 280, cx - 180, cx - 20, cx + 90, cx + 200, cx + 300 };
@ -254,36 +218,126 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
useFont->draw(renderer, colX[3], scoresStartY - 28, "LINES", 1.1f, SDL_Color{200,200,220,255});
useFont->draw(renderer, colX[4], scoresStartY - 28, "LEVEL", 1.1f, SDL_Color{200,200,220,255});
useFont->draw(renderer, colX[5], scoresStartY - 28, "TIME", 1.1f, SDL_Color{200,200,220,255});
for (size_t i = 0; i < maxDisplay; ++i) {
float baseY = scoresStartY + i * 25.0f;
float wave = std::sin((float)GlobalState::instance().logoAnimCounter * 0.006f + i * 0.25f) * 6.0f;
float y = baseY + wave;
char rankStr[8];
std::snprintf(rankStr, sizeof(rankStr), "%zu.", i + 1);
useFont->draw(renderer, colX[0], y, rankStr, 1.0f, SDL_Color{220, 220, 230, 255});
useFont->draw(renderer, colX[1], y, hs[i].name, 1.0f, SDL_Color{220, 220, 230, 255});
char scoreStr[16];
std::snprintf(scoreStr, sizeof(scoreStr), "%d", hs[i].score);
useFont->draw(renderer, colX[2], y, scoreStr, 1.0f, SDL_Color{220, 220, 230, 255});
char linesStr[8];
std::snprintf(linesStr, sizeof(linesStr), "%d", hs[i].lines);
useFont->draw(renderer, colX[3], y, linesStr, 1.0f, SDL_Color{220, 220, 230, 255});
char levelStr[8];
std::snprintf(levelStr, sizeof(levelStr), "%d", hs[i].level);
useFont->draw(renderer, colX[4], y, levelStr, 1.0f, SDL_Color{220, 220, 230, 255});
char timeStr[16];
int mins = int(hs[i].timeSec) / 60;
int secs = int(hs[i].timeSec) % 60;
std::snprintf(timeStr, sizeof(timeStr), "%d:%02d", mins, secs);
useFont->draw(renderer, colX[5], y, timeStr, 1.0f, SDL_Color{220, 220, 230, 255});
}
}
// Center columns around mid X, wider
float cx = LOGICAL_W * 0.5f + contentOffsetX;
float colX[] = { cx - 280, cx - 180, cx - 20, cx + 90, cx + 200, cx + 300 };
for (size_t i = 0; i < maxDisplay; ++i)
{
float baseY = scoresStartY + i * 25;
float wave = std::sin((float)GlobalState::instance().logoAnimCounter * 0.006f + i * 0.25f) * 6.0f; // subtle wave
float y = baseY + wave;
char rankStr[8];
std::snprintf(rankStr, sizeof(rankStr), "%zu.", i + 1);
if (useFont) useFont->draw(renderer, colX[0], y, rankStr, 1.0f, SDL_Color{220, 220, 230, 255});
// Draw the sci-fi overlay that sits above the scoreboard but below the buttons
SDL_Texture* overlayTex = ctx.mainScreenTex;
int overlayW = ctx.mainScreenW;
int overlayH = ctx.mainScreenH;
if (useFont) useFont->draw(renderer, colX[1], y, hs[i].name, 1.0f, SDL_Color{220, 220, 230, 255});
static SDL_Texture* fallbackOverlay = nullptr;
static int fallbackW = 0;
static int fallbackH = 0;
char scoreStr[16]; std::snprintf(scoreStr, sizeof(scoreStr), "%d", hs[i].score);
if (useFont) useFont->draw(renderer, colX[2], y, scoreStr, 1.0f, SDL_Color{220, 220, 230, 255});
if (!overlayTex) {
if (!fallbackOverlay) {
const std::string resolvedOverlay = AssetPath::resolveImagePath("assets/images/main_screen.bmp");
fallbackOverlay = IMG_LoadTexture(renderer, resolvedOverlay.c_str());
if (fallbackOverlay) {
SDL_SetTextureBlendMode(fallbackOverlay, SDL_BLENDMODE_BLEND);
float tmpW = 0.0f;
float tmpH = 0.0f;
SDL_GetTextureSize(fallbackOverlay, &tmpW, &tmpH);
fallbackW = static_cast<int>(tmpW);
fallbackH = static_cast<int>(tmpH);
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render loaded fallback overlay texture %p path=%s size=%dx%d\n",
(void*)fallbackOverlay, resolvedOverlay.c_str(), fallbackW, fallbackH);
fclose(f);
}
} else {
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render failed to load fallback overlay: %s\n", SDL_GetError());
fclose(f);
}
}
}
overlayTex = fallbackOverlay;
overlayW = fallbackW;
overlayH = fallbackH;
}
char linesStr[8]; std::snprintf(linesStr, sizeof(linesStr), "%d", hs[i].lines);
if (useFont) useFont->draw(renderer, colX[3], y, linesStr, 1.0f, SDL_Color{220, 220, 230, 255});
char levelStr[8]; std::snprintf(levelStr, sizeof(levelStr), "%d", hs[i].level);
if (useFont) useFont->draw(renderer, colX[4], y, levelStr, 1.0f, SDL_Color{220, 220, 230, 255});
char timeStr[16]; int mins = int(hs[i].timeSec) / 60; int secs = int(hs[i].timeSec) % 60;
std::snprintf(timeStr, sizeof(timeStr), "%d:%02d", mins, secs);
if (useFont) useFont->draw(renderer, colX[5], y, timeStr, 1.0f, SDL_Color{220, 220, 230, 255});
if (overlayTex) {
{
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render overlay tex=%llu dims=%dx%d\n",
(unsigned long long)(uintptr_t)overlayTex,
overlayW,
overlayH);
fclose(f);
}
}
float texW = overlayW > 0 ? static_cast<float>(overlayW) : 0.0f;
float texH = overlayH > 0 ? static_cast<float>(overlayH) : 0.0f;
if (texW <= 0.0f || texH <= 0.0f) {
if (!SDL_GetTextureSize(overlayTex, &texW, &texH)) {
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render failed to query overlay size: %s\n", SDL_GetError());
fclose(f);
}
texW = 0.0f;
texH = 0.0f;
}
}
if (texW > 0.0f && texH > 0.0f) {
const float drawH = LOGICAL_H;
const float scale = drawH / texH;
const float drawW = texW * scale;
SDL_FRect dst{
(LOGICAL_W - drawW) * 0.5f + contentOffsetX,
contentOffsetY,
drawW,
drawH
};
int renderResult = SDL_RenderTexture(renderer, overlayTex, nullptr, &dst);
if (renderResult < 0) {
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render failed to draw overlay: %s\n", SDL_GetError());
fclose(f);
}
}
}
} else {
FILE* f = fopen("tetris_trace.log", "a");
if (f) {
fprintf(f, "MenuState::render no overlay texture available\n");
fclose(f);
}
}
// Draw bottom action buttons with responsive sizing (reduced to match main mouse hit-test)

View File

@ -39,6 +39,9 @@ struct StateContext {
// backgroundTex is set once in `main.cpp` and passed to states via this context.
// Prefer reading this field instead of relying on any `extern SDL_Texture*` globals.
SDL_Texture* blocksTex = nullptr;
SDL_Texture* mainScreenTex = nullptr;
int mainScreenW = 0;
int mainScreenH = 0;
// Audio / SFX - forward declared types in main
// Pointers to booleans/flags used by multiple states