new background for main screen
This commit is contained in:
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user