fixed menu

This commit is contained in:
2025-12-23 14:49:55 +01:00
parent a65756f298
commit b450e2af21
2 changed files with 112 additions and 19 deletions

View File

@ -112,7 +112,7 @@ static void renderBackdropBlur(SDL_Renderer* renderer, const SDL_Rect& logicalVP
MenuState::MenuState(StateContext& ctx) : State(ctx) {}
void MenuState::showCoopSetupPanel(bool show) {
void MenuState::showCoopSetupPanel(bool show, bool resumeMusic) {
if (show) {
if (!coopSetupVisible && !coopSetupAnimating) {
// Avoid overlapping panels
@ -152,8 +152,8 @@ void MenuState::showCoopSetupPanel(bool show) {
coopSetupAnimating = true;
coopSetupDirection = -1;
coopSetupRectsValid = false;
// Ensure menu music resumes when closing the coop setup panel
if (ctx.musicEnabled && *ctx.musicEnabled) {
// Resume menu music only when requested (ESC should pass resumeMusic=false)
if (resumeMusic && ctx.musicEnabled && *ctx.musicEnabled) {
Audio::instance().playMenuMusic();
}
}
@ -280,9 +280,70 @@ void MenuState::onExit() {
}
void MenuState::handleEvent(const SDL_Event& e) {
// Coop setup panel navigation (modal within the menu)
// Handle this FIRST and consume key events so the main menu navigation doesn't interfere.
// Note: Do not require !repeat here; some keyboards/OS configs may emit Enter with repeat.
if ((coopSetupVisible || coopSetupAnimating) && coopSetupTransition > 0.0 && e.type == SDL_EVENT_KEY_DOWN) {
switch (e.key.scancode) {
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_A:
coopSetupSelected = 0;
buttonFlash = 1.0;
return;
case SDL_SCANCODE_RIGHT:
case SDL_SCANCODE_D:
coopSetupSelected = 1;
buttonFlash = 1.0;
return;
// Do NOT allow up/down to change anything
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
return;
case SDL_SCANCODE_ESCAPE:
showCoopSetupPanel(false, false);
return;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER:
case SDL_SCANCODE_SPACE:
{
const bool useAI = (coopSetupSelected == 1);
if (ctx.coopVsAI) {
*ctx.coopVsAI = useAI;
}
if (ctx.game) {
ctx.game->setMode(GameMode::Cooperate);
ctx.game->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
}
if (ctx.coopGame) {
ctx.coopGame->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
}
// Close the panel without restarting menu music; gameplay will take over.
showCoopSetupPanel(false, false);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: coop start via key, selected=%d, startPlayTransition_present=%d, stateManager=%p", coopSetupSelected, ctx.startPlayTransition ? 1 : 0, (void*)ctx.stateManager);
if (ctx.startPlayTransition) {
ctx.startPlayTransition();
} else if (ctx.stateManager) {
ctx.stateManager->setState(AppState::Playing);
}
return;
}
default:
// Allow all other keys to be pressed, but don't let them affect the main menu while coop is open.
return;
}
}
// Mouse input for COOP setup panel or inline coop buttons
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
if (coopSetupRectsValid) {
// While the coop submenu is active (animating or visible) we disallow
// mouse interaction — only keyboard LEFT/RIGHT/ESC is permitted.
if (coopSetupAnimating || coopSetupVisible) {
return;
}
const float mx = static_cast<float>(e.button.x);
const float my = static_cast<float>(e.button.y);
if (mx >= lastLogicalVP.x && my >= lastLogicalVP.y && mx <= (lastLogicalVP.x + lastLogicalVP.w) && my <= (lastLogicalVP.y + lastLogicalVP.h)) {
@ -384,8 +445,21 @@ void MenuState::handleEvent(const SDL_Event& e) {
}
return;
case SDL_SCANCODE_ESCAPE:
// Close HUD
exitPanelAnimating = true; exitDirection = -1;
showCoopSetupPanel(false, true);
// Cannot print std::function as a pointer; print presence (1/0) instead
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: coop ENTER pressed, selected=%d, startPlayTransition_present=%d, stateManager=%p", coopSetupSelected, ctx.startPlayTransition ? 1 : 0, (void*)ctx.stateManager);
if (ctx.startPlayTransition) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: calling startPlayTransition");
ctx.startPlayTransition();
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: startPlayTransition is null");
}
if (ctx.stateManager) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: setting AppState::Playing on stateManager");
ctx.stateManager->setState(AppState::Playing);
} else {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "MenuState: stateManager is null");
}
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = false;
return;
case SDL_SCANCODE_PAGEDOWN:
@ -555,17 +629,20 @@ void MenuState::handleEvent(const SDL_Event& e) {
if ((coopSetupVisible || coopSetupAnimating) && coopSetupTransition > 0.0) {
switch (e.key.scancode) {
case SDL_SCANCODE_LEFT:
case SDL_SCANCODE_A:
coopSetupSelected = 0;
buttonFlash = 1.0;
return;
case SDL_SCANCODE_RIGHT:
case SDL_SCANCODE_D:
coopSetupSelected = 1;
buttonFlash = 1.0;
return;
// Explicitly consume Up/Down so main menu navigation doesn't trigger
case SDL_SCANCODE_UP:
case SDL_SCANCODE_DOWN:
return;
case SDL_SCANCODE_ESCAPE:
showCoopSetupPanel(false);
// Close coop panel without restarting music
showCoopSetupPanel(false, false);
return;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER:
@ -575,7 +652,6 @@ void MenuState::handleEvent(const SDL_Event& e) {
if (ctx.coopVsAI) {
*ctx.coopVsAI = useAI;
}
// Start cooperative play
if (ctx.game) {
ctx.game->setMode(GameMode::Cooperate);
ctx.game->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
@ -583,7 +659,7 @@ void MenuState::handleEvent(const SDL_Event& e) {
if (ctx.coopGame) {
ctx.coopGame->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
}
showCoopSetupPanel(false);
showCoopSetupPanel(false, false);
triggerPlay();
return;
}
@ -695,7 +771,7 @@ void MenuState::handleEvent(const SDL_Event& e) {
break;
case SDL_SCANCODE_ESCAPE:
if (coopSetupVisible && !coopSetupAnimating) {
showCoopSetupPanel(false);
showCoopSetupPanel(false, false);
return;
}
// If options panel is visible, hide it first.
@ -1317,6 +1393,10 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
}
// If the image loaded, render it centered above the two choice buttons
// Compute fade alpha from the coop transition so it can be used for image, text and buttons
float alphaFactor = static_cast<float>(coopSetupTransition);
if (alphaFactor < 0.0f) alphaFactor = 0.0f;
if (alphaFactor > 1.0f) alphaFactor = 1.0f;
if (coopInfoTexture && coopInfoTexW > 0 && coopInfoTexH > 0) {
float totalW = btnW2 * 2.0f + gap;
// Increase allowed image width by ~15% (was 0.75 of totalW)
@ -1331,8 +1411,8 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
if (imgY < minY) imgY = minY;
SDL_FRect dst{ imgX, imgY, targetW, targetH };
SDL_SetTextureBlendMode(coopInfoTexture, SDL_BLENDMODE_BLEND);
// Make the coop info image slightly transparent
SDL_SetTextureAlphaMod(coopInfoTexture, 200);
// Make the coop info image slightly transparent scaled by transition
SDL_SetTextureAlphaMod(coopInfoTexture, static_cast<Uint8>(std::round(200.0f * alphaFactor)));
SDL_RenderTexture(renderer, coopInfoTexture, nullptr, &dst);
// Draw cooperative instructions inside the panel area (overlayed on the panel background)
@ -1353,6 +1433,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
};
float bulletScale = 0.78f;
SDL_Color bulletCol{200,220,230,220};
bulletCol.a = static_cast<Uint8>(std::round(bulletCol.a * alphaFactor));
int sampleLW = 0, sampleLH = 0;
f->measure(bullets[0], bulletScale, sampleLW, sampleLH);
@ -1362,7 +1443,8 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
int hW=0, hH=0; f->measure(header, headerScale, hW, hH);
float hx = panelBaseX + (panelW - static_cast<float>(hW)) * 0.5f + 40.0f; // nudge header right by 40px
float headerY = textY - static_cast<float>(sampleLH);
f->draw(renderer, hx, headerY, header, headerScale, SDL_Color{220,240,255,230});
SDL_Color headerCol = SDL_Color{220,240,255,230}; headerCol.a = static_cast<Uint8>(std::round(headerCol.a * alphaFactor));
f->draw(renderer, hx, headerY, header, headerScale, headerCol);
// Start body text slightly below header
textY = headerY + static_cast<float>(hH) + 8.0f;
@ -1379,18 +1461,28 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
// GOAL section (aligned with shifted bullets)
textY += 6.0f;
std::string goalTitle = "GOAL:";
f->draw(renderer, bulletX, textY, goalTitle, 0.88f, SDL_Color{255,215,80,230});
SDL_Color goalTitleCol = SDL_Color{255,215,80,230}; goalTitleCol.a = static_cast<Uint8>(std::round(goalTitleCol.a * alphaFactor));
f->draw(renderer, bulletX, textY, goalTitle, 0.88f, goalTitleCol);
int gW=0, gH=0; f->measure(goalTitle, 0.88f, gW, gH);
float goalX = bulletX + static_cast<float>(gW) + 10.0f;
std::string goalText = "Clear lines together and achieve the highest TEAM SCORE";
f->draw(renderer, goalX, textY, goalText, 0.86f, SDL_Color{220,240,255,220});
SDL_Color goalTextCol = SDL_Color{220,240,255,220}; goalTextCol.a = static_cast<Uint8>(std::round(goalTextCol.a * alphaFactor));
f->draw(renderer, goalX, textY, goalText, 0.86f, goalTextCol);
}
}
// Delay + eased fade specifically for the two coop buttons so they appear after the image/text.
const float btnDelay = 0.25f; // fraction of transition to wait before buttons start fading
float rawBtn = (alphaFactor - btnDelay) / (1.0f - btnDelay);
rawBtn = std::clamp(rawBtn, 0.0f, 1.0f);
// ease-in (squared) for a slower, smoother fade
float buttonFade = rawBtn * rawBtn;
SDL_Color bgA = bg; bgA.a = static_cast<Uint8>(std::round(bgA.a * buttonFade));
SDL_Color borderA = border; borderA.a = static_cast<Uint8>(std::round(borderA.a * buttonFade));
UIRenderer::drawButton(renderer, ctx.pixelFont, coopSetupBtnRects[0].x + btnW2 * 0.5f, coopSetupBtnRects[0].y + btnH2 * 0.5f,
btnW2, btnH2, "2 PLAYERS", false, coopSetupSelected == 0, bg, border, false, nullptr);
btnW2, btnH2, "2 PLAYERS", false, coopSetupSelected == 0, bgA, borderA, false, nullptr);
UIRenderer::drawButton(renderer, ctx.pixelFont, coopSetupBtnRects[1].x + btnW2 * 0.5f, coopSetupBtnRects[1].y + btnH2 * 0.5f,
btnW2, btnH2, "COMPUTER (AI)", false, coopSetupSelected == 1, bg, border, false, nullptr);
btnW2, btnH2, "COMPUTER (AI)", false, coopSetupSelected == 1, bgA, borderA, false, nullptr);
}
// NOTE: slide-up COOP panel intentionally removed. Only the inline
// highscores-area choice buttons are shown when coop setup is active.