Compare commits

...

10 Commits

Author SHA1 Message Date
1355ce49fe visual makeover of backgrounds for each level 2025-12-06 20:07:04 +01:00
8a549d14dc Fixed menu 2025-12-06 18:18:27 +01:00
12110bd8b4 fixed 2025-12-06 17:51:54 +01:00
f086ed3021 fixed main menu background 2025-12-06 15:44:05 +01:00
f24d484496 fixed highscore 2025-12-06 15:10:41 +01:00
26b4454eea fixed highscore 2025-12-06 14:54:56 +01:00
b531bbc798 fix 2025-12-06 14:08:24 +01:00
cb8293175b new main screen 2025-12-06 12:42:29 +01:00
fff14fe3e1 New fonts 2025-12-06 11:09:12 +01:00
ffdb67ce9b Fixed button text 2025-12-06 10:48:59 +01:00
85 changed files with 1085 additions and 182 deletions

BIN
assets/fonts/Exo2.ttf Normal file

Binary file not shown.

BIN
assets/fonts/Orbitron.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 957 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 805 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 947 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 970 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 995 KiB

View File

@ -6,7 +6,7 @@ Fullscreen=1
[Audio]
Music=1
Sound=0
Sound=1
[Gameplay]
SmoothScroll=1

View File

@ -77,13 +77,28 @@ void SpaceWarp::setAutoPilotEnabled(bool enabled) {
}
void SpaceWarp::scheduleNewAutoTarget() {
motionTarget.forwardScale = randomRange(0.82f, 1.28f);
if (randomRange(0.0f, 1.0f) < 0.12f) {
motionTarget.forwardScale = -randomRange(0.35f, 0.85f);
// Autopilot behavior:
// - 90% of the time: gentle forward flight with small lateral/vertical drift
// - 10% of the time: short lateral "bank" burst (stronger lateral speed) for a while
float choice = randomRange(0.0f, 1.0f);
if (choice < 0.90f) {
// Normal forward flight
motionTarget.forwardScale = randomRange(0.95f, 1.12f);
motionTarget.lateralSpeed = randomRange(-0.18f, 0.18f);
motionTarget.verticalSpeed = randomRange(-0.12f, 0.12f);
// Longer interval between aggressive maneuvers
autoTimer = randomRange(autoMinInterval, autoMaxInterval);
} else {
// Occasional lateral bank burst
motionTarget.forwardScale = randomRange(0.90f, 1.10f);
// Pick left or right burst
float dir = (randomRange(0.0f, 1.0f) < 0.5f) ? -1.0f : 1.0f;
motionTarget.lateralSpeed = dir * randomRange(0.70f, 1.35f);
// Allow modest vertical bias during a bank
motionTarget.verticalSpeed = randomRange(-0.35f, 0.35f);
// Shorter duration for the burst so it feels like a brief maneuver
autoTimer = randomRange(1.0f, 3.0f);
}
motionTarget.lateralSpeed = randomRange(-1.35f, 1.35f);
motionTarget.verticalSpeed = randomRange(-0.75f, 0.75f);
autoTimer = randomRange(autoMinInterval, autoMaxInterval);
}
void SpaceWarp::spawnComet() {
@ -105,10 +120,22 @@ void SpaceWarp::spawnComet() {
float shade = randomRange(0.85f, 1.0f);
Uint8 c = static_cast<Uint8>(std::clamp(220.0f + shade * 35.0f, 0.0f, 255.0f));
comet.color = SDL_Color{c, Uint8(std::min(255.0f, c * 0.95f)), 255, 255};
comet.prevScreenX = centerX;
comet.prevScreenY = centerY;
comet.screenX = centerX;
comet.screenY = centerY;
// Initialize screen positions based on projection so the comet is not stuck at center
float sx = 0.0f, sy = 0.0f;
if (projectPoint(comet.x, comet.y, comet.z, sx, sy)) {
comet.screenX = sx;
comet.screenY = sy;
// Place prev slightly behind the head so the first frame shows motion/trail
float jitter = std::max(4.0f, comet.trailLength * 0.08f);
float ang = randomRange(0.0f, 6.28318530718f);
comet.prevScreenX = comet.screenX - std::cos(ang) * jitter;
comet.prevScreenY = comet.screenY - std::sin(ang) * jitter;
} else {
comet.prevScreenX = centerX;
comet.prevScreenY = centerY;
comet.screenX = centerX;
comet.screenY = centerY;
}
comets.push_back(comet);
}
@ -135,10 +162,22 @@ void SpaceWarp::respawn(WarpStar& star, bool randomDepth) {
static constexpr Uint8 GRAY_SHADES[] = {160, 180, 200, 220, 240};
int idx = randomIntInclusive(rng, 0, int(std::size(GRAY_SHADES)) - 1);
star.baseShade = GRAY_SHADES[idx];
star.prevScreenX = centerX;
star.prevScreenY = centerY;
star.screenX = centerX;
star.screenY = centerY;
// Compute initial projected screen position so newly spawned stars aren't frozen at center
float sx = 0.0f, sy = 0.0f;
if (projectPoint(star.x, star.y, star.z, sx, sy)) {
star.screenX = sx;
star.screenY = sy;
// give a small previous offset so trails and motion are visible immediately
float jitter = std::max(1.0f, settings.maxTrailLength * 0.06f);
float ang = randomRange(0.0f, 6.28318530718f);
star.prevScreenX = star.screenX - std::cos(ang) * jitter;
star.prevScreenY = star.screenY - std::sin(ang) * jitter;
} else {
star.prevScreenX = centerX;
star.prevScreenY = centerY;
star.screenX = centerX;
star.screenY = centerY;
}
}
bool SpaceWarp::project(const WarpStar& star, float& outX, float& outY) const {

View File

@ -53,6 +53,16 @@ void UIRenderer::drawButton(SDL_Renderer* renderer, FontAtlas* font, float cx, f
bgColor.a};
}
// Neon glow aura around the button to increase visibility (subtle)
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
for (int gi = 0; gi < 3; ++gi) {
float grow = 6.0f + gi * 3.0f;
Uint8 glowA = static_cast<Uint8>(std::max(0, (int)borderColor.a / (3 - gi)));
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, glowA);
SDL_FRect glowRect{x - grow, y - grow, w + grow * 2.0f, h + grow * 2.0f};
SDL_RenderRect(renderer, &glowRect);
}
// Draw button background with border
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, borderColor.a);
SDL_FRect borderRect{x - 2, y - 2, w + 4, h + 4};
@ -92,13 +102,17 @@ void UIRenderer::drawButton(SDL_Renderer* renderer, FontAtlas* font, float cx, f
// Reset color mod
SDL_SetTextureColorMod(icon, 255, 255, 255);
} else if (font) {
// Draw text
float textScale = 1.5f;
// Draw text (smaller scale for tighter buttons)
float textScale = 1.2f;
int textW = 0, textH = 0;
font->measure(label, textScale, textW, textH);
float tx = x + (w - static_cast<float>(textW)) * 0.5f;
// Adjust vertical position for better alignment with background buttons
float ty = y + (h - static_cast<float>(textH)) * 0.5f + 2.0f;
// Vertically center text precisely within the button
// Vertically center text precisely within the button, then nudge down slightly
// to improve optical balance relative to icons and button art.
const float textNudge = 3.0f; // tweak this value to move labels up/down
float ty = y + (h - static_cast<float>(textH)) * 0.5f + textNudge;
// Choose text color based on selection state
SDL_Color textColor = {255, 255, 255, 255}; // Default white

View File

@ -160,7 +160,7 @@ static bool queueLevelBackground(LevelBackgroundFader& fader, SDL_Renderer* rend
}
char bgPath[256];
std::snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", level);
std::snprintf(bgPath, sizeof(bgPath), "assets/images/levels/level%d.jpg", level);
SDL_Texture* newTexture = loadTextureFromImage(renderer, bgPath);
if (!newTexture) {
@ -580,12 +580,13 @@ int main(int, char **)
SDL_GetError());
}
FontAtlas font;
font.init("FreeSans.ttf", 24);
// Load PressStart2P font for loading screen and retro UI elements
// Primary UI font (Orbitron) used for major UI text: buttons, loading, HUD
FontAtlas pixelFont;
pixelFont.init("assets/fonts/PressStart2P-Regular.ttf", 16);
pixelFont.init("assets/fonts/Orbitron.ttf", 22);
// Secondary font (Exo2) used for longer descriptions, settings, credits
FontAtlas font;
font.init("assets/fonts/Exo2.ttf", 20);
ScoreManager scores;
std::atomic<bool> scoresLoadComplete{false};
@ -623,7 +624,7 @@ int main(int, char **)
// Load the new main screen overlay that sits above the background but below buttons
int mainScreenW = 0;
int mainScreenH = 0;
SDL_Texture* mainScreenTex = loadTextureFromImage(renderer, "assets/images/main_screen_004.png", &mainScreenW, &mainScreenH);
SDL_Texture* mainScreenTex = loadTextureFromImage(renderer, "assets/images/main_screen.png", &mainScreenW, &mainScreenH);
if (mainScreenTex) {
SDL_SetTextureBlendMode(mainScreenTex, SDL_BLENDMODE_BLEND);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded main_screen overlay %dx%d (tex=%p)", mainScreenW, mainScreenH, (void*)mainScreenTex);
@ -1573,28 +1574,8 @@ int main(int, char **)
} else if (state == AppState::Menu) {
// Space flyover backdrop for the main screen
spaceWarp.draw(renderer, 1.0f);
if (mainScreenTex) {
float texW = mainScreenW > 0 ? static_cast<float>(mainScreenW) : 0.0f;
float texH = mainScreenH > 0 ? static_cast<float>(mainScreenH) : 0.0f;
if (texW <= 0.0f || texH <= 0.0f) {
if (!SDL_GetTextureSize(mainScreenTex, &texW, &texH)) {
texW = texH = 0.0f;
}
}
if (texW > 0.0f && texH > 0.0f) {
const float drawH = static_cast<float>(winH);
const float scale = drawH / texH;
const float drawW = texW * scale;
SDL_FRect dst{
(winW - drawW) * 0.5f,
0.0f,
drawW,
drawH
};
SDL_RenderTexture(renderer, mainScreenTex, nullptr, &dst);
}
}
// `mainScreenTex` is rendered as a top layer just before presenting
// so we don't draw it here. Keep the space warp background only.
} else if (state == AppState::LevelSelector || state == AppState::Options) {
if (backgroundTex) {
SDL_FRect fullRect = { 0, 0, (float)winW, (float)winH };
@ -1926,6 +1907,43 @@ int main(int, char **)
HelpOverlay::Render(renderer, pixelFont, LOGICAL_W, LOGICAL_H, contentOffsetX, contentOffsetY);
}
// Top-layer overlay: render `mainScreenTex` above all other layers when in Menu
if (state == AppState::Menu && mainScreenTex) {
SDL_SetRenderViewport(renderer, nullptr);
SDL_SetRenderScale(renderer, 1.f, 1.f);
float texW = mainScreenW > 0 ? static_cast<float>(mainScreenW) : 0.0f;
float texH = mainScreenH > 0 ? static_cast<float>(mainScreenH) : 0.0f;
if (texW <= 0.0f || texH <= 0.0f) {
float iwf = 0.0f, ihf = 0.0f;
if (SDL_GetTextureSize(mainScreenTex, &iwf, &ihf) != 0) {
iwf = ihf = 0.0f;
}
texW = iwf;
texH = ihf;
}
if (texW > 0.0f && texH > 0.0f) {
const float drawH = static_cast<float>(winH);
const float scale = drawH / texH;
const float drawW = texW * scale;
SDL_FRect dst{
(winW - drawW) * 0.5f,
0.0f,
drawW,
drawH
};
SDL_SetTextureBlendMode(mainScreenTex, SDL_BLENDMODE_BLEND);
SDL_RenderTexture(renderer, mainScreenTex, nullptr, &dst);
}
// Restore logical viewport/scale and draw the main PLAY button above the overlay
SDL_SetRenderViewport(renderer, &logicalVP);
SDL_SetRenderScale(renderer, logicalScale, logicalScale);
if (menuState) {
menuState->drawMainButtonNormally = false; // ensure it isn't double-drawn
menuState->renderMainButtonTop(renderer, logicalScale, logicalVP);
menuState->drawMainButtonNormally = true;
}
}
SDL_RenderPresent(renderer);
SDL_SetRenderScale(renderer, 1.f, 1.f);
}

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,11 @@ public:
void handleEvent(const SDL_Event& e) override;
void update(double frameMs) override;
void render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP) override;
// When false, the main PLAY button is not drawn by `render()` and can be
// rendered separately with `renderMainButtonTop` (useful for layer ordering).
bool drawMainButtonNormally = true;
// Draw only the main PLAY button on top of other layers (expects logical coords).
void renderMainButtonTop(SDL_Renderer* renderer, float logicalScale, SDL_Rect logicalVP);
private:
int selectedButton = 0; // 0 = PLAY, 1 = LEVEL, 2 = OPTIONS, 3 = EXIT
@ -19,4 +24,54 @@ private:
SDL_Texture* levelIcon = nullptr;
SDL_Texture* optionsIcon = nullptr;
SDL_Texture* exitIcon = nullptr;
// Options panel animation state
bool optionsVisible = false;
bool optionsAnimating = false;
double optionsTransition = 0.0; // 0..1
double optionsTransitionDurationMs = 400.0;
int optionsDirection = 1; // 1 show, -1 hide
// Which row in the inline options panel is currently selected (0..4)
int optionsSelectedRow = 0;
// Inline level selector HUD state
bool levelPanelVisible = false;
bool levelPanelAnimating = false;
double levelTransition = 0.0; // 0..1
double levelTransitionDurationMs = 400.0;
int levelDirection = 1; // 1 show, -1 hide
int levelHovered = -1; // hovered cell
int levelSelected = 0; // current selected level
// Cache logical viewport/scale for input conversion when needed
float lastLogicalScale = 1.0f;
SDL_Rect lastLogicalVP{0,0,0,0};
// Animated highlight position (world/logical coordinates)
double levelHighlightX = 0.0;
double levelHighlightY = 0.0;
bool levelHighlightInitialized = false;
// Highlight tuning parameters
double levelHighlightSpeed = 0.018; // smoothing constant - higher = snappier
double levelHighlightGlowAlpha = 0.70; // 0..1 base glow alpha
int levelHighlightThickness = 3; // inner outline thickness (px)
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
};

View File

@ -113,7 +113,8 @@ void OptionsState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l
UIRenderer::drawSciFiPanel(renderer, panel);
FontAtlas* retroFont = ctx.pixelFont ? ctx.pixelFont : ctx.font;
// For options/settings we prefer the secondary (Exo2) font for longer descriptions.
FontAtlas* retroFont = ctx.font ? ctx.font : ctx.pixelFont;
if (!logoTexture && retroFont) {
retroFont->draw(renderer, panel.x + 24.0f, panel.y + 24.0f, "OPTIONS", 2.0f, {255, 230, 120, 255});
@ -252,9 +253,12 @@ void OptionsState::toggleSmoothScroll() {
}
void OptionsState::exitToMenu() {
// Try a graceful fade transition if available, but always ensure we
// return to the Menu state so the UI is responsive to the user's action.
if (ctx.requestFadeTransition) {
ctx.requestFadeTransition(AppState::Menu);
} else if (ctx.stateManager) {
}
if (ctx.stateManager) {
ctx.stateManager->setState(AppState::Menu);
}
}

View File

@ -43,6 +43,10 @@ struct StateContext {
SDL_Texture* mainScreenTex = nullptr;
int mainScreenW = 0;
int mainScreenH = 0;
// Captured full-scene texture (used by menu for backdrop blur effects)
SDL_Texture* sceneTex = nullptr;
int sceneW = 0;
int sceneH = 0;
// Audio / SFX - forward declared types in main
// Pointers to booleans/flags used by multiple states