added helper menu

This commit is contained in:
2025-11-30 13:30:02 +01:00
parent 588f870b26
commit 55c40f0516
11 changed files with 272 additions and 26 deletions

View File

@ -23,6 +23,7 @@
#include "graphics/effects/Starfield.h"
#include "graphics/effects/Starfield3D.h"
#include "graphics/ui/Font.h"
#include "graphics/ui/HelpOverlay.h"
#include "gameplay/effects/LineEffect.h"
#include "states/State.h"
#include "states/LoadingState.h"
@ -487,10 +488,10 @@ static void drawSettingsPopup(SDL_Renderer* renderer, FontAtlas& font, bool musi
// Instructions
font.draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, {200, 200, 220, 255});
font.draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255});
font.draw(renderer, popupX + 20, popupY + 190, "N = PLAY LETS_GO", 1.0f, {200, 200, 220, 255});
font.draw(renderer, popupX + 20, popupY + 210, "ESC = CLOSE", 1.0f, {200, 200, 220, 255});
font.draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, {200, 200, 220, 255});
}
// -----------------------------------------------------------------------------
// Starfield effect for background
// -----------------------------------------------------------------------------
@ -504,12 +505,14 @@ static void drawSettingsPopup(SDL_Renderer* renderer, FontAtlas& font, bool musi
// -----------------------------------------------------------------------------
static double logoAnimCounter = 0.0;
static bool showSettingsPopup = false;
static bool showHelpOverlay = false;
static bool showExitConfirmPopup = false;
static int exitPopupSelectedButton = 1; // 0 = YES, 1 = NO
static bool musicEnabled = true;
static int hoveredButton = -1; // -1 = none, 0 = play, 1 = level, 2 = settings
static bool isNewHighScore = false;
static std::string playerName = "";
static bool helpOverlayPausedGame = false;
// -----------------------------------------------------------------------------
// Tetris Block Fireworks for intro animation (block particles)
@ -904,6 +907,7 @@ int main(int, char **)
ctx.startLevelSelection = &startLevelSelection;
ctx.hoveredButton = &hoveredButton;
ctx.showSettingsPopup = &showSettingsPopup;
ctx.showHelpOverlay = &showHelpOverlay;
ctx.showExitConfirmPopup = &showExitConfirmPopup;
ctx.exitPopupSelectedButton = &exitPopupSelectedButton;
ctx.gameplayCountdownActive = &gameplayCountdownActive;
@ -1027,12 +1031,22 @@ int main(int, char **)
running = false;
else {
// Route event to state manager handlers for per-state logic
stateMgr.handleEvent(e);
// Keep the local `state` variable in sync with StateManager in case
// a state handler requested a transition (handlers may call
// stateMgr.setState()). Many branches below rely on the local
// `state` variable, so update it immediately after handling.
state = stateMgr.getState();
const bool isUserInputEvent =
e.type == SDL_EVENT_KEY_DOWN ||
e.type == SDL_EVENT_KEY_UP ||
e.type == SDL_EVENT_TEXT_INPUT ||
e.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
e.type == SDL_EVENT_MOUSE_BUTTON_UP ||
e.type == SDL_EVENT_MOUSE_MOTION;
if (!(showHelpOverlay && isUserInputEvent)) {
stateMgr.handleEvent(e);
// Keep the local `state` variable in sync with StateManager in case
// a state handler requested a transition (handlers may call
// stateMgr.setState()). Many branches below rely on the local
// `state` variable, so update it immediately after handling.
state = stateMgr.getState();
}
// Global key toggles (applies regardless of state)
if (e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
@ -1048,11 +1062,23 @@ int main(int, char **)
SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled());
Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled());
}
if (e.key.scancode == SDL_SCANCODE_N)
if (e.key.scancode == SDL_SCANCODE_H && state != AppState::Loading)
{
// Manually trigger a random voice line for quick testing
if (!allVoiceSounds.empty()) {
SoundEffectManager::instance().playRandomSound(allVoiceSounds, 1.0f);
showHelpOverlay = !showHelpOverlay;
if (state == AppState::Playing) {
if (showHelpOverlay) {
if (!game.isPaused()) {
game.setPaused(true);
helpOverlayPausedGame = true;
} else {
helpOverlayPausedGame = false;
}
} else if (helpOverlayPausedGame) {
game.setPaused(false);
helpOverlayPausedGame = false;
}
} else if (!showHelpOverlay) {
helpOverlayPausedGame = false;
}
}
if (e.key.key == SDLK_F11 || (e.key.key == SDLK_RETURN && (e.key.mod & SDL_KMOD_ALT)))
@ -1064,13 +1090,13 @@ int main(int, char **)
}
// Text input for high score
if (state == AppState::GameOver && isNewHighScore && e.type == SDL_EVENT_TEXT_INPUT) {
if (!showHelpOverlay && state == AppState::GameOver && isNewHighScore && e.type == SDL_EVENT_TEXT_INPUT) {
if (playerName.length() < 12) {
playerName += e.text.text;
}
}
if (state == AppState::GameOver && e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
if (!showHelpOverlay && state == AppState::GameOver && e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
if (isNewHighScore) {
if (e.key.scancode == SDL_SCANCODE_BACKSPACE && !playerName.empty()) {
playerName.pop_back();
@ -1096,7 +1122,7 @@ int main(int, char **)
}
// Mouse handling remains in main loop for UI interactions
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
if (!showHelpOverlay && e.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
{
float mx = (float)e.button.x, my = (float)e.button.y;
if (mx >= logicalVP.x && my >= logicalVP.y && mx <= logicalVP.x + logicalVP.w && my <= logicalVP.y + logicalVP.h)
@ -1227,7 +1253,7 @@ int main(int, char **)
}
}
}
else if (e.type == SDL_EVENT_MOUSE_MOTION)
else if (!showHelpOverlay && e.type == SDL_EVENT_MOUSE_MOTION)
{
float mx = (float)e.motion.x, my = (float)e.motion.y;
if (mx >= logicalVP.x && my >= logicalVP.y && mx <= logicalVP.x + logicalVP.w && my <= logicalVP.y + logicalVP.h)
@ -1886,6 +1912,20 @@ int main(int, char **)
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
}
if (showHelpOverlay) {
SDL_SetRenderViewport(renderer, &logicalVP);
SDL_SetRenderScale(renderer, logicalScale, logicalScale);
float contentOffsetX = 0.0f;
float contentOffsetY = 0.0f;
if (logicalScale > 0.0f) {
float scaledW = LOGICAL_W * logicalScale;
float scaledH = LOGICAL_H * logicalScale;
contentOffsetX = (winW - scaledW) * 0.5f / logicalScale;
contentOffsetY = (winH - scaledH) * 0.5f / logicalScale;
}
HelpOverlay::Render(renderer, pixelFont, LOGICAL_W, LOGICAL_H, contentOffsetX, contentOffsetY);
}
SDL_RenderPresent(renderer);
SDL_SetRenderScale(renderer, 1.f, 1.f);
}