fix
This commit is contained in:
@ -85,9 +85,15 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale,
|
|||||||
if (i == 3) extra = -44.0f;
|
if (i == 3) extra = -44.0f;
|
||||||
cxCenter = btnX + offset + extra;
|
cxCenter = btnX + offset + extra;
|
||||||
}
|
}
|
||||||
|
// Apply group alpha and transient flash to button colors
|
||||||
|
double aMul = std::clamp(buttonGroupAlpha + buttonFlash * buttonFlashAmount, 0.0, 1.0);
|
||||||
|
SDL_Color bgCol = buttons[i].bg;
|
||||||
|
SDL_Color bdCol = buttons[i].border;
|
||||||
|
bgCol.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(bgCol.a)));
|
||||||
|
bdCol.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(bdCol.a)));
|
||||||
UIRenderer::drawButton(renderer, ctx.pixelFont, cxCenter, cyCenter, btnW, btnH,
|
UIRenderer::drawButton(renderer, ctx.pixelFont, cxCenter, cyCenter, btnW, btnH,
|
||||||
buttons[i].label, false, selectedButton == i,
|
buttons[i].label, false, selectedButton == i,
|
||||||
buttons[i].bg, buttons[i].border, true, icons[i]);
|
bgCol, bdCol, true, icons[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,36 +137,49 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isExitPromptVisible()) {
|
// Inline exit HUD handling (replaces the old modal popup)
|
||||||
|
if (exitPanelVisible && !exitPanelAnimating) {
|
||||||
switch (e.key.scancode) {
|
switch (e.key.scancode) {
|
||||||
case SDL_SCANCODE_LEFT:
|
case SDL_SCANCODE_LEFT:
|
||||||
case SDL_SCANCODE_UP:
|
// Move selection to YES
|
||||||
setExitSelection(0);
|
exitSelectedButton = 0;
|
||||||
|
if (ctx.exitPopupSelectedButton) *ctx.exitPopupSelectedButton = exitSelectedButton;
|
||||||
return;
|
return;
|
||||||
case SDL_SCANCODE_RIGHT:
|
case SDL_SCANCODE_RIGHT:
|
||||||
case SDL_SCANCODE_DOWN:
|
// Move selection to NO
|
||||||
setExitSelection(1);
|
exitSelectedButton = 1;
|
||||||
|
if (ctx.exitPopupSelectedButton) *ctx.exitPopupSelectedButton = exitSelectedButton;
|
||||||
return;
|
return;
|
||||||
case SDL_SCANCODE_RETURN:
|
case SDL_SCANCODE_RETURN:
|
||||||
case SDL_SCANCODE_KP_ENTER:
|
case SDL_SCANCODE_KP_ENTER:
|
||||||
case SDL_SCANCODE_SPACE:
|
case SDL_SCANCODE_SPACE:
|
||||||
if (getExitSelection() == 0) {
|
if (exitSelectedButton == 0) {
|
||||||
setExitPrompt(false);
|
// Confirm quit
|
||||||
if (ctx.requestQuit) {
|
if (ctx.requestQuit) {
|
||||||
ctx.requestQuit();
|
ctx.requestQuit();
|
||||||
} else {
|
} else {
|
||||||
SDL_Event quit{};
|
SDL_Event quit{}; quit.type = SDL_EVENT_QUIT; SDL_PushEvent(&quit);
|
||||||
quit.type = SDL_EVENT_QUIT;
|
|
||||||
SDL_PushEvent(&quit);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setExitPrompt(false);
|
// Close HUD
|
||||||
|
exitPanelAnimating = true; exitDirection = -1;
|
||||||
|
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = false;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDL_SCANCODE_ESCAPE:
|
case SDL_SCANCODE_ESCAPE:
|
||||||
setExitPrompt(false);
|
// Close HUD
|
||||||
setExitSelection(1);
|
exitPanelAnimating = true; exitDirection = -1;
|
||||||
|
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = false;
|
||||||
return;
|
return;
|
||||||
|
case SDL_SCANCODE_PAGEDOWN:
|
||||||
|
case SDL_SCANCODE_DOWN: {
|
||||||
|
// scroll content down
|
||||||
|
exitScroll += 40.0; return;
|
||||||
|
}
|
||||||
|
case SDL_SCANCODE_PAGEUP:
|
||||||
|
case SDL_SCANCODE_UP: {
|
||||||
|
exitScroll -= 40.0; if (exitScroll < 0.0) exitScroll = 0.0; return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -168,15 +187,17 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
|
|
||||||
// If the inline options HUD is visible and not animating, capture navigation
|
// If the inline options HUD is visible and not animating, capture navigation
|
||||||
if (optionsVisible && !optionsAnimating) {
|
if (optionsVisible && !optionsAnimating) {
|
||||||
|
// Options now has more rows; use OPTIONS_ROW_COUNT
|
||||||
|
constexpr int OPTIONS_ROW_COUNT = 8;
|
||||||
switch (e.key.scancode) {
|
switch (e.key.scancode) {
|
||||||
case SDL_SCANCODE_UP:
|
case SDL_SCANCODE_UP:
|
||||||
{
|
{
|
||||||
optionsSelectedRow = (optionsSelectedRow + 5 - 1) % 5;
|
optionsSelectedRow = (optionsSelectedRow + OPTIONS_ROW_COUNT - 1) % OPTIONS_ROW_COUNT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SDL_SCANCODE_DOWN:
|
case SDL_SCANCODE_DOWN:
|
||||||
{
|
{
|
||||||
optionsSelectedRow = (optionsSelectedRow + 1) % 5;
|
optionsSelectedRow = (optionsSelectedRow + 1) % OPTIONS_ROW_COUNT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SDL_SCANCODE_LEFT:
|
case SDL_SCANCODE_LEFT:
|
||||||
@ -220,6 +241,25 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
|
// PULSE ENABLE
|
||||||
|
buttonPulseEnabled = !buttonPulseEnabled;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
// PULSE SPEED (Enter toggles to default)
|
||||||
|
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER || e.key.scancode == SDL_SCANCODE_SPACE) {
|
||||||
|
buttonPulseSpeed = 1.0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
// PULSE MIN ALPHA (Enter resets)
|
||||||
|
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER || e.key.scancode == SDL_SCANCODE_SPACE) {
|
||||||
|
buttonPulseMinAlpha = 0.6;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
// RETURN TO MENU -> hide panel
|
// RETURN TO MENU -> hide panel
|
||||||
optionsAnimating = true;
|
optionsAnimating = true;
|
||||||
optionsDirection = -1;
|
optionsDirection = -1;
|
||||||
@ -234,7 +274,8 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
|
|
||||||
// If inline level HUD visible and not animating, capture navigation
|
// If inline level HUD visible and not animating, capture navigation
|
||||||
if (levelPanelVisible && !levelPanelAnimating) {
|
if (levelPanelVisible && !levelPanelAnimating) {
|
||||||
int c = levelSelected < 0 ? 0 : levelSelected;
|
// Start navigation from tentative hover if present, otherwise from committed selection
|
||||||
|
int c = (levelHovered >= 0) ? levelHovered : (levelSelected < 0 ? 0 : levelSelected);
|
||||||
switch (e.key.scancode) {
|
switch (e.key.scancode) {
|
||||||
case SDL_SCANCODE_LEFT: if (c % 4 > 0) c--; break;
|
case SDL_SCANCODE_LEFT: if (c % 4 > 0) c--; break;
|
||||||
case SDL_SCANCODE_RIGHT: if (c % 4 < 3) c++; break;
|
case SDL_SCANCODE_RIGHT: if (c % 4 < 3) c++; break;
|
||||||
@ -243,18 +284,22 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
case SDL_SCANCODE_RETURN:
|
case SDL_SCANCODE_RETURN:
|
||||||
case SDL_SCANCODE_KP_ENTER:
|
case SDL_SCANCODE_KP_ENTER:
|
||||||
case SDL_SCANCODE_SPACE:
|
case SDL_SCANCODE_SPACE:
|
||||||
|
// Confirm tentative selection
|
||||||
levelSelected = c;
|
levelSelected = c;
|
||||||
if (ctx.startLevelSelection) *ctx.startLevelSelection = levelSelected;
|
if (ctx.startLevelSelection) *ctx.startLevelSelection = levelSelected;
|
||||||
// close HUD
|
// close HUD
|
||||||
levelPanelAnimating = true; levelDirection = -1;
|
levelPanelAnimating = true; levelDirection = -1;
|
||||||
|
// clear hover candidate
|
||||||
|
levelHovered = -1;
|
||||||
return;
|
return;
|
||||||
case SDL_SCANCODE_ESCAPE:
|
case SDL_SCANCODE_ESCAPE:
|
||||||
levelPanelAnimating = true; levelDirection = -1;
|
levelPanelAnimating = true; levelDirection = -1;
|
||||||
|
levelHovered = -1;
|
||||||
return;
|
return;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
levelSelected = c;
|
// Move tentative cursor (don't commit to startLevelSelection yet)
|
||||||
if (ctx.startLevelSelection) *ctx.startLevelSelection = levelSelected;
|
levelHovered = c;
|
||||||
// Consume the event so main menu navigation does not also run
|
// Consume the event so main menu navigation does not also run
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -265,6 +310,8 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
{
|
{
|
||||||
const int total = 4;
|
const int total = 4;
|
||||||
selectedButton = (selectedButton + total - 1) % total;
|
selectedButton = (selectedButton + total - 1) % total;
|
||||||
|
// brief bright flash on navigation
|
||||||
|
buttonFlash = 1.0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_SCANCODE_RIGHT:
|
case SDL_SCANCODE_RIGHT:
|
||||||
@ -272,6 +319,8 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
{
|
{
|
||||||
const int total = 4;
|
const int total = 4;
|
||||||
selectedButton = (selectedButton + 1) % total;
|
selectedButton = (selectedButton + 1) % total;
|
||||||
|
// brief bright flash on navigation
|
||||||
|
buttonFlash = 1.0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_SCANCODE_RETURN:
|
case SDL_SCANCODE_RETURN:
|
||||||
@ -289,6 +338,8 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
if (!levelPanelVisible && !levelPanelAnimating) {
|
if (!levelPanelVisible && !levelPanelAnimating) {
|
||||||
levelPanelAnimating = true;
|
levelPanelAnimating = true;
|
||||||
levelDirection = 1; // show
|
levelDirection = 1; // show
|
||||||
|
// initialize tentative cursor to current selected level
|
||||||
|
levelHovered = levelSelected < 0 ? 0 : levelSelected;
|
||||||
} else if (levelPanelVisible && !levelPanelAnimating) {
|
} else if (levelPanelVisible && !levelPanelAnimating) {
|
||||||
levelPanelAnimating = true;
|
levelPanelAnimating = true;
|
||||||
levelDirection = -1; // hide
|
levelDirection = -1; // hide
|
||||||
@ -305,8 +356,14 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
setExitPrompt(true);
|
// Show the inline exit HUD
|
||||||
setExitSelection(1);
|
if (!exitPanelVisible && !exitPanelAnimating) {
|
||||||
|
exitPanelAnimating = true;
|
||||||
|
exitDirection = 1;
|
||||||
|
exitSelectedButton = 1;
|
||||||
|
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = true;
|
||||||
|
if (ctx.exitPopupSelectedButton) *ctx.exitPopupSelectedButton = exitSelectedButton;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -317,8 +374,14 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
optionsDirection = -1;
|
optionsDirection = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setExitPrompt(true);
|
// Show inline exit HUD on ESC
|
||||||
setExitSelection(1);
|
if (!exitPanelVisible && !exitPanelAnimating) {
|
||||||
|
exitPanelAnimating = true;
|
||||||
|
exitDirection = 1;
|
||||||
|
exitSelectedButton = 1;
|
||||||
|
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = true;
|
||||||
|
if (ctx.exitPopupSelectedButton) *ctx.exitPopupSelectedButton = exitSelectedButton;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -359,6 +422,21 @@ void MenuState::update(double frameMs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advance exit panel animation if active
|
||||||
|
if (exitPanelAnimating) {
|
||||||
|
double delta = (frameMs / exitTransitionDurationMs) * static_cast<double>(exitDirection);
|
||||||
|
exitTransition += delta;
|
||||||
|
if (exitTransition >= 1.0) {
|
||||||
|
exitTransition = 1.0;
|
||||||
|
exitPanelVisible = true;
|
||||||
|
exitPanelAnimating = false;
|
||||||
|
} else if (exitTransition <= 0.0) {
|
||||||
|
exitTransition = 0.0;
|
||||||
|
exitPanelVisible = false;
|
||||||
|
exitPanelAnimating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Animate level selection highlight position toward the selected cell center
|
// Animate level selection highlight position toward the selected cell center
|
||||||
if (levelTransition > 0.0 && (lastLogicalScale > 0.0f)) {
|
if (levelTransition > 0.0 && (lastLogicalScale > 0.0f)) {
|
||||||
// Recompute same grid geometry used in render to find target center
|
// Recompute same grid geometry used in render to find target center
|
||||||
@ -381,7 +459,7 @@ void MenuState::update(double frameMs) {
|
|||||||
float cellW = (area.w - (cols - 1) * gapX) / cols;
|
float cellW = (area.w - (cols - 1) * gapX) / cols;
|
||||||
float cellH = (area.h - (rows - 1) * gapY) / rows;
|
float cellH = (area.h - (rows - 1) * gapY) / rows;
|
||||||
|
|
||||||
int targetIdx = std::clamp(levelSelected, 0, 19);
|
int targetIdx = std::clamp((levelHovered >= 0 ? levelHovered : levelSelected), 0, 19);
|
||||||
int tr = targetIdx / cols, tc = targetIdx % cols;
|
int tr = targetIdx / cols, tc = targetIdx % cols;
|
||||||
double targetX = area.x + tc * (cellW + gapX) + cellW * 0.5f;
|
double targetX = area.x + tc * (cellW + gapX) + cellW * 0.5f;
|
||||||
double targetY = area.y + tr * (cellH + gapY) + cellH * 0.5f;
|
double targetY = area.y + tr * (cellH + gapY) + cellH * 0.5f;
|
||||||
@ -399,6 +477,47 @@ void MenuState::update(double frameMs) {
|
|||||||
levelHighlightY += (targetY - levelHighlightY) * alpha;
|
levelHighlightY += (targetY - levelHighlightY) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update button group pulsing animation
|
||||||
|
if (buttonPulseEnabled) {
|
||||||
|
buttonPulseTime += frameMs;
|
||||||
|
double t = (buttonPulseTime * 0.001) * buttonPulseSpeed; // seconds * speed
|
||||||
|
double s = 0.0;
|
||||||
|
switch (buttonPulseEasing) {
|
||||||
|
case 0: // sin
|
||||||
|
s = (std::sin(t * 2.0 * 3.14159265358979323846) * 0.5) + 0.5;
|
||||||
|
break;
|
||||||
|
case 1: // triangle
|
||||||
|
{
|
||||||
|
double ft = t - std::floor(t);
|
||||||
|
s = (ft < 0.5) ? (ft * 2.0) : (2.0 - ft * 2.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: // exponential ease-in-out (normalized)
|
||||||
|
{
|
||||||
|
double ft = t - std::floor(t);
|
||||||
|
if (ft < 0.5) {
|
||||||
|
s = 0.5 * std::pow(2.0, 20.0 * ft - 10.0);
|
||||||
|
} else {
|
||||||
|
s = 1.0 - 0.5 * std::pow(2.0, -20.0 * ft + 10.0);
|
||||||
|
}
|
||||||
|
// Clamp to 0..1 in case of numeric issues
|
||||||
|
if (s < 0.0) s = 0.0; if (s > 1.0) s = 1.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
s = (std::sin(t * 2.0 * 3.14159265358979323846) * 0.5) + 0.5;
|
||||||
|
}
|
||||||
|
buttonGroupAlpha = buttonPulseMinAlpha + s * (buttonPulseMaxAlpha - buttonPulseMinAlpha);
|
||||||
|
} else {
|
||||||
|
buttonGroupAlpha = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update flash decay
|
||||||
|
if (buttonFlash > 0.0) {
|
||||||
|
buttonFlash -= frameMs * buttonFlashDecay;
|
||||||
|
if (buttonFlash < 0.0) buttonFlash = 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) {
|
void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) {
|
||||||
@ -439,8 +558,8 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
|||||||
// Slide-space amount for the options HUD (how much highscores move)
|
// Slide-space amount for the options HUD (how much highscores move)
|
||||||
const float moveAmount = 420.0f; // increased so lower score rows slide further up
|
const float moveAmount = 420.0f; // increased so lower score rows slide further up
|
||||||
|
|
||||||
// Compute eased transition and delta to shift highscores when either options or level HUD is shown.
|
// Compute eased transition and delta to shift highscores when either options, level, or exit HUD is shown.
|
||||||
float combinedTransition = static_cast<float>(std::max(optionsTransition, levelTransition));
|
float combinedTransition = static_cast<float>(std::max(std::max(optionsTransition, levelTransition), exitTransition));
|
||||||
float eased = combinedTransition * combinedTransition * (3.0f - 2.0f * combinedTransition); // cubic smoothstep
|
float eased = combinedTransition * combinedTransition * (3.0f - 2.0f * combinedTransition); // cubic smoothstep
|
||||||
float panelDelta = eased * moveAmount;
|
float panelDelta = eased * moveAmount;
|
||||||
|
|
||||||
@ -617,15 +736,59 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.showExitConfirmPopup && *ctx.showExitConfirmPopup) {
|
// Inline exit HUD (no opaque background) - slides into the highscores area
|
||||||
GameRenderer::renderExitPopup(
|
if (exitTransition > 0.0) {
|
||||||
renderer,
|
float easedE = static_cast<float>(exitTransition);
|
||||||
ctx.pixelFont,
|
easedE = easedE * easedE * (3.0f - 2.0f * easedE);
|
||||||
winW,
|
const float panelW = 520.0f;
|
||||||
winH,
|
const float panelH = 360.0f;
|
||||||
logicalScale,
|
float panelBaseX = (LOGICAL_W - panelW) * 0.5f + contentOffsetX;
|
||||||
ctx.exitPopupSelectedButton ? *ctx.exitPopupSelectedButton : 1
|
float panelBaseY = (LOGICAL_H - panelH) * 0.5f + contentOffsetY - (LOGICAL_H * 0.10f);
|
||||||
);
|
float slideAmount = LOGICAL_H * 0.42f;
|
||||||
|
float panelY = panelBaseY + (1.0f - easedE) * slideAmount;
|
||||||
|
|
||||||
|
FontAtlas* titleFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
|
||||||
|
if (titleFont) titleFont->draw(renderer, panelBaseX + 12.0f, panelY + 6.0f, "EXIT GAME?", 1.6f, SDL_Color{255,200,80,220});
|
||||||
|
|
||||||
|
SDL_FRect area{ panelBaseX + 12.0f, panelY + 56.0f, panelW - 24.0f, panelH - 120.0f };
|
||||||
|
// Sample long message (scrollable)
|
||||||
|
// Paragraph-style lines for a nicer exit confirmation message
|
||||||
|
std::vector<std::string> lines = {
|
||||||
|
"Quit now to return to your desktop. Your current session will end.",
|
||||||
|
"Press YES to quit immediately, or NO to return to the menu and continue playing.",
|
||||||
|
"Adjust audio, controls and other settings anytime from the Options menu.",
|
||||||
|
"Thanks for playing SPACETRIS — we hope to see you again!"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw scrollable text (no background box; increased line spacing)
|
||||||
|
FontAtlas* f = ctx.font ? ctx.font : ctx.pixelFont;
|
||||||
|
float y = area.y - (float)exitScroll;
|
||||||
|
const float lineSpacing = 34.0f; // increased spacing for readability
|
||||||
|
if (f) {
|
||||||
|
for (size_t i = 0; i < lines.size(); ++i) {
|
||||||
|
f->draw(renderer, area.x + 6.0f, y + i * lineSpacing, lines[i], 1.0f, SDL_Color{200,220,240,220});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw buttons at bottom of panel
|
||||||
|
float btnW2 = 160.0f, btnH2 = 48.0f;
|
||||||
|
float bx = panelBaseX + (panelW - (btnW2 * 2.0f + 12.0f)) * 0.5f;
|
||||||
|
float by = panelY + panelH - 56.0f;
|
||||||
|
// YES button
|
||||||
|
SDL_Color yesBg{220,80,60, 200}; SDL_Color yesBorder{160,40,40,200};
|
||||||
|
SDL_Color noBg{60,140,200,200}; SDL_Color noBorder{30,90,160,200};
|
||||||
|
// Apply pulse alpha to buttons
|
||||||
|
double aMul = std::clamp(buttonGroupAlpha + buttonFlash * buttonFlashAmount, 0.0, 1.0);
|
||||||
|
yesBg.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(yesBg.a)));
|
||||||
|
yesBorder.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(yesBorder.a)));
|
||||||
|
noBg.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(noBg.a)));
|
||||||
|
noBorder.a = static_cast<Uint8>(std::round(aMul * static_cast<double>(noBorder.a)));
|
||||||
|
|
||||||
|
UIRenderer::drawButton(renderer, ctx.pixelFont, bx + btnW2*0.5f, by, btnW2, btnH2, "YES", false, exitSelectedButton == 0, yesBg, yesBorder, true, nullptr);
|
||||||
|
UIRenderer::drawButton(renderer, ctx.pixelFont, bx + btnW2*1.5f + 12.0f, by, btnW2, btnH2, "NO", false, exitSelectedButton == 1, noBg, noBorder, true, nullptr);
|
||||||
|
|
||||||
|
// Ensure ctx mirrors selection for any other code
|
||||||
|
if (ctx.exitPopupSelectedButton) *ctx.exitPopupSelectedButton = exitSelectedButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popups (settings only - level popup is now a separate state)
|
// Popups (settings only - level popup is now a separate state)
|
||||||
|
|||||||
@ -53,4 +53,25 @@ private:
|
|||||||
double levelHighlightGlowAlpha = 0.70; // 0..1 base glow alpha
|
double levelHighlightGlowAlpha = 0.70; // 0..1 base glow alpha
|
||||||
int levelHighlightThickness = 3; // inner outline thickness (px)
|
int levelHighlightThickness = 3; // inner outline thickness (px)
|
||||||
SDL_Color levelHighlightColor = SDL_Color{80, 200, 255, 200};
|
SDL_Color levelHighlightColor = SDL_Color{80, 200, 255, 200};
|
||||||
|
// Button group pulsing/fade parameters (applies to all four main buttons)
|
||||||
|
double buttonGroupAlpha = 1.0; // current computed alpha (0..1)
|
||||||
|
double buttonPulseTime = 0.0; // accumulator in ms
|
||||||
|
bool buttonPulseEnabled = true; // enable/disable pulsing
|
||||||
|
double buttonPulseSpeed = 1.0; // multiplier for pulse frequency
|
||||||
|
double buttonPulseMinAlpha = 0.60; // minimum alpha during pulse
|
||||||
|
double buttonPulseMaxAlpha = 1.00; // maximum alpha during pulse
|
||||||
|
// Pulse easing mode: 0=sin,1=triangle,2=exponential
|
||||||
|
int buttonPulseEasing = 1;
|
||||||
|
// Short bright flash when navigating buttons
|
||||||
|
double buttonFlash = 0.0; // transient flash amount (0..1)
|
||||||
|
double buttonFlashAmount = 0.45; // how much flash adds to group alpha
|
||||||
|
double buttonFlashDecay = 0.0025; // linear decay per ms
|
||||||
|
// Exit confirmation HUD state (inline HUD like Options/Level)
|
||||||
|
bool exitPanelVisible = false;
|
||||||
|
bool exitPanelAnimating = false;
|
||||||
|
double exitTransition = 0.0; // 0..1
|
||||||
|
double exitTransitionDurationMs = 360.0;
|
||||||
|
int exitDirection = 1; // 1 show, -1 hide
|
||||||
|
int exitSelectedButton = 0; // 0 = YES (quit), 1 = NO (cancel)
|
||||||
|
double exitScroll = 0.0; // vertical scroll offset for content
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user