fixed menu
This commit is contained in:
@ -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.
|
||||
|
||||
Reference in New Issue
Block a user