converted from bmp to jpg
@ -25,6 +25,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
# Homebrew: brew install sdl3
|
# Homebrew: brew install sdl3
|
||||||
find_package(SDL3 CONFIG REQUIRED)
|
find_package(SDL3 CONFIG REQUIRED)
|
||||||
find_package(SDL3_ttf CONFIG REQUIRED)
|
find_package(SDL3_ttf CONFIG REQUIRED)
|
||||||
|
find_package(SDL3_image CONFIG REQUIRED)
|
||||||
find_package(cpr CONFIG REQUIRED)
|
find_package(cpr CONFIG REQUIRED)
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
|
||||||
@ -85,7 +86,7 @@ if (WIN32)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(tetris PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf cpr::cpr nlohmann_json::nlohmann_json)
|
target_link_libraries(tetris PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image cpr::cpr nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(tetris PRIVATE mfplat mfreadwrite mfuuid)
|
target_link_libraries(tetris PRIVATE mfplat mfreadwrite mfuuid)
|
||||||
@ -160,7 +161,7 @@ if (WIN32)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(tetris_refactored PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf cpr::cpr nlohmann_json::nlohmann_json)
|
target_link_libraries(tetris_refactored PRIVATE SDL3::SDL3 SDL3_ttf::SDL3_ttf SDL3_image::SDL3_image cpr::cpr nlohmann_json::nlohmann_json)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(tetris_refactored PRIVATE mfplat mfreadwrite mfuuid)
|
target_link_libraries(tetris_refactored PRIVATE mfplat mfreadwrite mfuuid)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 14 MiB |
|
Before Width: | Height: | Size: 8.7 MiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
|
Before Width: | Height: | Size: 24 MiB |
@ -212,7 +212,7 @@ Tetris SDL3 Game - Release $Version
|
|||||||
|
|
||||||
## Files Included
|
## Files Included
|
||||||
- tetris.exe - Main game executable
|
- tetris.exe - Main game executable
|
||||||
- SDL3.dll, SDL3_ttf.dll - Required libraries
|
- SDL3.dll, SDL3_ttf.dll, SDL3_image.dll - Required libraries
|
||||||
- assets/ - Game assets (images, music, fonts)
|
- assets/ - Game assets (images, music, fonts)
|
||||||
- FreeSans.ttf - Main font file
|
- FreeSans.ttf - Main font file
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,9 @@
|
|||||||
#include "../../gameplay/core/Game.h"
|
#include "../../gameplay/core/Game.h"
|
||||||
#include "../../gameplay/effects/LineEffect.h"
|
#include "../../gameplay/effects/LineEffect.h"
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
|
#include "../../utils/ImagePathResolver.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -1044,17 +1046,20 @@ void ApplicationManager::setupStateHandlers() {
|
|||||||
if (m_cachedBgLevel != bgLevel) {
|
if (m_cachedBgLevel != bgLevel) {
|
||||||
if (m_nextLevelBackgroundTex) { SDL_DestroyTexture(m_nextLevelBackgroundTex); m_nextLevelBackgroundTex = nullptr; }
|
if (m_nextLevelBackgroundTex) { SDL_DestroyTexture(m_nextLevelBackgroundTex); m_nextLevelBackgroundTex = nullptr; }
|
||||||
char bgPath[256];
|
char bgPath[256];
|
||||||
std::snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.bmp", bgLevel);
|
std::snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", bgLevel);
|
||||||
SDL_Surface* s = SDL_LoadBMP(bgPath);
|
const std::string resolvedBgPath = AssetPath::resolveImagePath(bgPath);
|
||||||
|
SDL_Surface* s = IMG_Load(resolvedBgPath.c_str());
|
||||||
if (s && renderer.getSDLRenderer()) {
|
if (s && renderer.getSDLRenderer()) {
|
||||||
m_nextLevelBackgroundTex = SDL_CreateTextureFromSurface(renderer.getSDLRenderer(), s);
|
m_nextLevelBackgroundTex = SDL_CreateTextureFromSurface(renderer.getSDLRenderer(), s);
|
||||||
SDL_DestroySurface(s);
|
SDL_DestroySurface(s);
|
||||||
m_levelFadeAlpha = 0.0f;
|
m_levelFadeAlpha = 0.0f;
|
||||||
m_levelFadeElapsed = 0.0f;
|
m_levelFadeElapsed = 0.0f;
|
||||||
m_cachedBgLevel = bgLevel;
|
m_cachedBgLevel = bgLevel;
|
||||||
|
if (resolvedBgPath != bgPath) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded level %d background via %s", bgLevel, resolvedBgPath.c_str());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_cachedBgLevel = -1; // don’t change if missing
|
m_cachedBgLevel = -1; // don't change if missing
|
||||||
m_cachedBgLevel = -1; // don't change if missing
|
|
||||||
if (s) SDL_DestroySurface(s);
|
if (s) SDL_DestroySurface(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,10 @@
|
|||||||
#include "../../audio/Audio.h"
|
#include "../../audio/Audio.h"
|
||||||
#include "../../audio/SoundEffect.h"
|
#include "../../audio/SoundEffect.h"
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include "../../utils/ImagePathResolver.h"
|
||||||
|
|
||||||
AssetManager::AssetManager()
|
AssetManager::AssetManager()
|
||||||
: m_renderer(nullptr)
|
: m_renderer(nullptr)
|
||||||
@ -379,19 +381,25 @@ SDL_Texture* AssetManager::loadTextureFromFile(const std::string& filepath) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load using SDL_LoadBMP (matching main.cpp pattern)
|
const std::string resolvedPath = AssetPath::resolveImagePath(filepath);
|
||||||
SDL_Surface* surface = SDL_LoadBMP(filepath.c_str());
|
SDL_Texture* texture = IMG_LoadTexture(m_renderer, resolvedPath.c_str());
|
||||||
if (!surface) {
|
if (!texture) {
|
||||||
setError("Failed to load surface from: " + filepath + " - " + SDL_GetError());
|
std::string message = "Failed to load texture from: ";
|
||||||
|
message += filepath;
|
||||||
|
message += " (resolved: ";
|
||||||
|
message += resolvedPath;
|
||||||
|
message += ") - ";
|
||||||
|
message += SDL_GetError();
|
||||||
|
setError(message);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(m_renderer, surface);
|
if (resolvedPath != filepath) {
|
||||||
SDL_DestroySurface(surface);
|
std::string message = "Loaded alternative image path for ";
|
||||||
|
message += filepath;
|
||||||
if (!texture) {
|
message += ": ";
|
||||||
setError("Failed to create texture from surface: " + filepath + " - " + SDL_GetError());
|
message += resolvedPath;
|
||||||
return nullptr;
|
logInfo(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
|
|||||||
101
src/main.cpp
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_main.h>
|
#include <SDL3/SDL_main.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -30,6 +31,7 @@
|
|||||||
#include "states/LevelSelectorState.h"
|
#include "states/LevelSelectorState.h"
|
||||||
#include "states/PlayingState.h"
|
#include "states/PlayingState.h"
|
||||||
#include "audio/MenuWrappers.h"
|
#include "audio/MenuWrappers.h"
|
||||||
|
#include "utils/ImagePathResolver.h"
|
||||||
#include "graphics/renderers/GameRenderer.h"
|
#include "graphics/renderers/GameRenderer.h"
|
||||||
|
|
||||||
// Debug logging removed: no-op in this build (previously LOG_DEBUG)
|
// Debug logging removed: no-op in this build (previously LOG_DEBUG)
|
||||||
@ -74,6 +76,36 @@ static void drawRect(SDL_Renderer *r, float x, float y, float w, float h, SDL_Co
|
|||||||
SDL_RenderFillRect(r, &fr);
|
SDL_RenderFillRect(r, &fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Texture* loadTextureFromImage(SDL_Renderer* renderer, const std::string& path, int* outW = nullptr, int* outH = nullptr) {
|
||||||
|
if (!renderer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string resolvedPath = AssetPath::resolveImagePath(path);
|
||||||
|
SDL_Surface* surface = IMG_Load(resolvedPath.c_str());
|
||||||
|
if (!surface) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image %s (resolved: %s): %s", path.c_str(), resolvedPath.c_str(), SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outW) { *outW = surface->w; }
|
||||||
|
if (outH) { *outH = surface->h; }
|
||||||
|
|
||||||
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
|
||||||
|
if (!texture) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from %s: %s", resolvedPath.c_str(), SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedPath != path) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded %s via %s", path.c_str(), resolvedPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
||||||
// Now managed by LevelSelectorState
|
// Now managed by LevelSelectorState
|
||||||
|
|
||||||
@ -455,41 +487,15 @@ int main(int, char **)
|
|||||||
LineEffect lineEffect;
|
LineEffect lineEffect;
|
||||||
lineEffect.init(renderer);
|
lineEffect.init(renderer);
|
||||||
|
|
||||||
// Load logo using native SDL BMP loading
|
// Load logo assets via SDL_image so we can use compressed formats
|
||||||
SDL_Texture *logoTex = nullptr;
|
SDL_Texture* logoTex = loadTextureFromImage(renderer, "assets/images/logo.bmp");
|
||||||
SDL_Surface* logoSurface = SDL_LoadBMP("assets/images/logo.bmp");
|
|
||||||
if (logoSurface) {
|
|
||||||
(void)0;
|
|
||||||
logoTex = SDL_CreateTextureFromSurface(renderer, logoSurface);
|
|
||||||
SDL_DestroySurface(logoSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
// Load small logo (used by Menu to show whole logo)
|
|
||||||
SDL_Texture *logoSmallTex = nullptr;
|
|
||||||
SDL_Surface* logoSmallSurface = SDL_LoadBMP("assets/images/logo_small.bmp");
|
|
||||||
int logoSmallW = 0, logoSmallH = 0;
|
|
||||||
if (logoSmallSurface) {
|
|
||||||
// capture surface size before creating the texture (avoids SDL_QueryTexture)
|
|
||||||
logoSmallW = logoSmallSurface->w;
|
|
||||||
logoSmallH = logoSmallSurface->h;
|
|
||||||
logoSmallTex = SDL_CreateTextureFromSurface(renderer, logoSmallSurface);
|
|
||||||
SDL_DestroySurface(logoSmallSurface);
|
|
||||||
} else {
|
|
||||||
// fallback: leave logoSmallTex null so MenuState will use large logo
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load background using native SDL BMP loading
|
// Load small logo (used by Menu to show whole logo)
|
||||||
SDL_Texture *backgroundTex = nullptr;
|
int logoSmallW = 0, logoSmallH = 0;
|
||||||
SDL_Surface* backgroundSurface = SDL_LoadBMP("assets/images/main_background.bmp");
|
SDL_Texture* logoSmallTex = loadTextureFromImage(renderer, "assets/images/logo_small.bmp", &logoSmallW, &logoSmallH);
|
||||||
if (backgroundSurface) {
|
|
||||||
(void)0;
|
// Load menu background using SDL_image (prefers JPEG)
|
||||||
backgroundTex = SDL_CreateTextureFromSurface(renderer, backgroundSurface);
|
SDL_Texture* backgroundTex = loadTextureFromImage(renderer, "assets/images/main_background.bmp");
|
||||||
SDL_DestroySurface(backgroundSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: `backgroundTex` is owned by main and passed into `StateContext::backgroundTex` below.
|
// Note: `backgroundTex` is owned by main and passed into `StateContext::backgroundTex` below.
|
||||||
// States should render using `ctx.backgroundTex` rather than accessing globals.
|
// States should render using `ctx.backgroundTex` rather than accessing globals.
|
||||||
@ -505,16 +511,8 @@ int main(int, char **)
|
|||||||
// Default start level selection: 0 (declare here so it's in scope for all handlers)
|
// Default start level selection: 0 (declare here so it's in scope for all handlers)
|
||||||
int startLevelSelection = 0;
|
int startLevelSelection = 0;
|
||||||
|
|
||||||
// Load blocks texture using native SDL BMP loading
|
// Load blocks texture via SDL_image (falls back to procedural blocks if missing)
|
||||||
SDL_Texture *blocksTex = nullptr;
|
SDL_Texture* blocksTex = loadTextureFromImage(renderer, "assets/images/blocks90px_001.bmp");
|
||||||
SDL_Surface* blocksSurface = SDL_LoadBMP("assets/images/blocks90px_001.bmp");
|
|
||||||
if (blocksSurface) {
|
|
||||||
(void)0;
|
|
||||||
blocksTex = SDL_CreateTextureFromSurface(renderer, blocksSurface);
|
|
||||||
SDL_DestroySurface(blocksSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
// No global exposure of blocksTex; states receive textures via StateContext.
|
// No global exposure of blocksTex; states receive textures via StateContext.
|
||||||
|
|
||||||
if (!blocksTex) {
|
if (!blocksTex) {
|
||||||
@ -1287,18 +1285,17 @@ int main(int, char **)
|
|||||||
// Load new level background into nextLevelBackgroundTex
|
// Load new level background into nextLevelBackgroundTex
|
||||||
if (nextLevelBackgroundTex) { SDL_DestroyTexture(nextLevelBackgroundTex); nextLevelBackgroundTex = nullptr; }
|
if (nextLevelBackgroundTex) { SDL_DestroyTexture(nextLevelBackgroundTex); nextLevelBackgroundTex = nullptr; }
|
||||||
char bgPath[256];
|
char bgPath[256];
|
||||||
snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.bmp", bgLevel);
|
snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", bgLevel);
|
||||||
SDL_Surface* levelBgSurface = SDL_LoadBMP(bgPath);
|
SDL_Texture* newLevelTex = loadTextureFromImage(renderer, bgPath);
|
||||||
if (levelBgSurface) {
|
if (newLevelTex) {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded background for level %d: %s", bgLevel, bgPath);
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded background for level %d: %s", bgLevel, bgPath);
|
||||||
nextLevelBackgroundTex = SDL_CreateTextureFromSurface(renderer, levelBgSurface);
|
nextLevelBackgroundTex = newLevelTex;
|
||||||
SDL_DestroySurface(levelBgSurface);
|
|
||||||
// start fade transition
|
// start fade transition
|
||||||
levelFadeAlpha = 0.0f;
|
levelFadeAlpha = 0.0f;
|
||||||
levelFadeElapsed = 0.0f;
|
levelFadeElapsed = 0.0f;
|
||||||
cachedLevel = bgLevel;
|
cachedLevel = bgLevel;
|
||||||
} else {
|
} else {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load background for level %d: %s (Error: %s)", bgLevel, bgPath, SDL_GetError());
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load background for level %d: %s", bgLevel, bgPath);
|
||||||
// don't change textures if file missing
|
// don't change textures if file missing
|
||||||
cachedLevel = -1;
|
cachedLevel = -1;
|
||||||
}
|
}
|
||||||
@ -1651,10 +1648,14 @@ int main(int, char **)
|
|||||||
SDL_DestroyTexture(logoTex);
|
SDL_DestroyTexture(logoTex);
|
||||||
if (backgroundTex)
|
if (backgroundTex)
|
||||||
SDL_DestroyTexture(backgroundTex);
|
SDL_DestroyTexture(backgroundTex);
|
||||||
|
if (nextLevelBackgroundTex)
|
||||||
|
SDL_DestroyTexture(nextLevelBackgroundTex);
|
||||||
if (levelBackgroundTex)
|
if (levelBackgroundTex)
|
||||||
SDL_DestroyTexture(levelBackgroundTex);
|
SDL_DestroyTexture(levelBackgroundTex);
|
||||||
if (blocksTex)
|
if (blocksTex)
|
||||||
SDL_DestroyTexture(blocksTex);
|
SDL_DestroyTexture(blocksTex);
|
||||||
|
if (logoSmallTex)
|
||||||
|
SDL_DestroyTexture(logoSmallTex);
|
||||||
lineEffect.shutdown();
|
lineEffect.shutdown();
|
||||||
Audio::instance().shutdown();
|
Audio::instance().shutdown();
|
||||||
SoundEffectManager::instance().shutdown();
|
SoundEffectManager::instance().shutdown();
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_main.h>
|
#include <SDL3/SDL_main.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -30,6 +31,7 @@
|
|||||||
#include "states/LevelSelectorState.h"
|
#include "states/LevelSelectorState.h"
|
||||||
#include "states/PlayingState.h"
|
#include "states/PlayingState.h"
|
||||||
#include "audio/MenuWrappers.h"
|
#include "audio/MenuWrappers.h"
|
||||||
|
#include "utils/ImagePathResolver.h"
|
||||||
|
|
||||||
// Debug logging removed: no-op in this build (previously LOG_DEBUG)
|
// Debug logging removed: no-op in this build (previously LOG_DEBUG)
|
||||||
|
|
||||||
@ -73,6 +75,36 @@ static void drawRect(SDL_Renderer *r, float x, float y, float w, float h, SDL_Co
|
|||||||
SDL_RenderFillRect(r, &fr);
|
SDL_RenderFillRect(r, &fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDL_Texture* loadTextureFromImage(SDL_Renderer* renderer, const std::string& path, int* outW = nullptr, int* outH = nullptr) {
|
||||||
|
if (!renderer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string resolvedPath = AssetPath::resolveImagePath(path);
|
||||||
|
SDL_Surface* surface = IMG_Load(resolvedPath.c_str());
|
||||||
|
if (!surface) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image %s (resolved: %s): %s", path.c_str(), resolvedPath.c_str(), SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outW) { *outW = surface->w; }
|
||||||
|
if (outH) { *outH = surface->h; }
|
||||||
|
|
||||||
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
|
||||||
|
if (!texture) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from %s: %s", resolvedPath.c_str(), SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedPath != path) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded %s via %s", path.c_str(), resolvedPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
// Hover state for level popup ( -1 = none, 0..19 = hovered level )
|
||||||
// Now managed by LevelSelectorState
|
// Now managed by LevelSelectorState
|
||||||
|
|
||||||
@ -450,41 +482,14 @@ int main(int, char **)
|
|||||||
LineEffect lineEffect;
|
LineEffect lineEffect;
|
||||||
lineEffect.init(renderer);
|
lineEffect.init(renderer);
|
||||||
|
|
||||||
// Load logo using native SDL BMP loading
|
// Load logo assets via SDL_image so we can use compressed formats
|
||||||
SDL_Texture *logoTex = nullptr;
|
SDL_Texture* logoTex = loadTextureFromImage(renderer, "assets/images/logo.bmp");
|
||||||
SDL_Surface* logoSurface = SDL_LoadBMP("assets/images/logo.bmp");
|
|
||||||
if (logoSurface) {
|
|
||||||
(void)0;
|
|
||||||
logoTex = SDL_CreateTextureFromSurface(renderer, logoSurface);
|
|
||||||
SDL_DestroySurface(logoSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
// Load small logo (used by Menu to show whole logo)
|
// Load small logo (used by Menu to show whole logo)
|
||||||
SDL_Texture *logoSmallTex = nullptr;
|
|
||||||
SDL_Surface* logoSmallSurface = SDL_LoadBMP("assets/images/logo_small.bmp");
|
|
||||||
int logoSmallW = 0, logoSmallH = 0;
|
int logoSmallW = 0, logoSmallH = 0;
|
||||||
if (logoSmallSurface) {
|
SDL_Texture* logoSmallTex = loadTextureFromImage(renderer, "assets/images/logo_small.bmp", &logoSmallW, &logoSmallH);
|
||||||
// capture surface size before creating the texture (avoids SDL_QueryTexture)
|
|
||||||
logoSmallW = logoSmallSurface->w;
|
|
||||||
logoSmallH = logoSmallSurface->h;
|
|
||||||
logoSmallTex = SDL_CreateTextureFromSurface(renderer, logoSmallSurface);
|
|
||||||
SDL_DestroySurface(logoSmallSurface);
|
|
||||||
} else {
|
|
||||||
// fallback: leave logoSmallTex null so MenuState will use large logo
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load background using native SDL BMP loading
|
// Load background using SDL_image (prefers JPEG)
|
||||||
SDL_Texture *backgroundTex = nullptr;
|
SDL_Texture* backgroundTex = loadTextureFromImage(renderer, "assets/images/main_background.bmp");
|
||||||
SDL_Surface* backgroundSurface = SDL_LoadBMP("assets/images/main_background.bmp");
|
|
||||||
if (backgroundSurface) {
|
|
||||||
(void)0;
|
|
||||||
backgroundTex = SDL_CreateTextureFromSurface(renderer, backgroundSurface);
|
|
||||||
SDL_DestroySurface(backgroundSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: `backgroundTex` is owned by main and passed into `StateContext::backgroundTex` below.
|
// Note: `backgroundTex` is owned by main and passed into `StateContext::backgroundTex` below.
|
||||||
// States should render using `ctx.backgroundTex` rather than accessing globals.
|
// States should render using `ctx.backgroundTex` rather than accessing globals.
|
||||||
@ -500,16 +505,8 @@ int main(int, char **)
|
|||||||
// Default start level selection: 0 (declare here so it's in scope for all handlers)
|
// Default start level selection: 0 (declare here so it's in scope for all handlers)
|
||||||
int startLevelSelection = 0;
|
int startLevelSelection = 0;
|
||||||
|
|
||||||
// Load blocks texture using native SDL BMP loading
|
// Load blocks texture using SDL_image (falls back to procedural blocks if necessary)
|
||||||
SDL_Texture *blocksTex = nullptr;
|
SDL_Texture* blocksTex = loadTextureFromImage(renderer, "assets/images/blocks90px_001.bmp");
|
||||||
SDL_Surface* blocksSurface = SDL_LoadBMP("assets/images/blocks90px_001.bmp");
|
|
||||||
if (blocksSurface) {
|
|
||||||
(void)0;
|
|
||||||
blocksTex = SDL_CreateTextureFromSurface(renderer, blocksSurface);
|
|
||||||
SDL_DestroySurface(blocksSurface);
|
|
||||||
} else {
|
|
||||||
(void)0;
|
|
||||||
}
|
|
||||||
// No global exposure of blocksTex; states receive textures via StateContext.
|
// No global exposure of blocksTex; states receive textures via StateContext.
|
||||||
|
|
||||||
if (!blocksTex) {
|
if (!blocksTex) {
|
||||||
@ -1136,11 +1133,10 @@ int main(int, char **)
|
|||||||
// Load new level background into nextLevelBackgroundTex
|
// Load new level background into nextLevelBackgroundTex
|
||||||
if (nextLevelBackgroundTex) { SDL_DestroyTexture(nextLevelBackgroundTex); nextLevelBackgroundTex = nullptr; }
|
if (nextLevelBackgroundTex) { SDL_DestroyTexture(nextLevelBackgroundTex); nextLevelBackgroundTex = nullptr; }
|
||||||
char bgPath[256];
|
char bgPath[256];
|
||||||
snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.bmp", bgLevel);
|
snprintf(bgPath, sizeof(bgPath), "assets/images/tetris_main_back_level%d.jpg", bgLevel);
|
||||||
SDL_Surface* levelBgSurface = SDL_LoadBMP(bgPath);
|
SDL_Texture* newLevelTex = loadTextureFromImage(renderer, bgPath);
|
||||||
if (levelBgSurface) {
|
if (newLevelTex) {
|
||||||
nextLevelBackgroundTex = SDL_CreateTextureFromSurface(renderer, levelBgSurface);
|
nextLevelBackgroundTex = newLevelTex;
|
||||||
SDL_DestroySurface(levelBgSurface);
|
|
||||||
// start fade transition
|
// start fade transition
|
||||||
levelFadeAlpha = 0.0f;
|
levelFadeAlpha = 0.0f;
|
||||||
levelFadeElapsed = 0.0f;
|
levelFadeElapsed = 0.0f;
|
||||||
@ -1718,10 +1714,14 @@ int main(int, char **)
|
|||||||
SDL_DestroyTexture(logoTex);
|
SDL_DestroyTexture(logoTex);
|
||||||
if (backgroundTex)
|
if (backgroundTex)
|
||||||
SDL_DestroyTexture(backgroundTex);
|
SDL_DestroyTexture(backgroundTex);
|
||||||
|
if (nextLevelBackgroundTex)
|
||||||
|
SDL_DestroyTexture(nextLevelBackgroundTex);
|
||||||
if (levelBackgroundTex)
|
if (levelBackgroundTex)
|
||||||
SDL_DestroyTexture(levelBackgroundTex);
|
SDL_DestroyTexture(levelBackgroundTex);
|
||||||
if (blocksTex)
|
if (blocksTex)
|
||||||
SDL_DestroyTexture(blocksTex);
|
SDL_DestroyTexture(blocksTex);
|
||||||
|
if (logoSmallTex)
|
||||||
|
SDL_DestroyTexture(logoSmallTex);
|
||||||
lineEffect.shutdown();
|
lineEffect.shutdown();
|
||||||
Audio::instance().shutdown();
|
Audio::instance().shutdown();
|
||||||
SoundEffectManager::instance().shutdown();
|
SoundEffectManager::instance().shutdown();
|
||||||
|
|||||||
56
src/utils/ImagePathResolver.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
namespace AssetPath {
|
||||||
|
|
||||||
|
inline bool fileExists(const std::string& path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_IOStream* file = SDL_IOFromFile(path.c_str(), "rb");
|
||||||
|
if (file) {
|
||||||
|
SDL_CloseIO(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string resolveImagePath(const std::string& originalPath) {
|
||||||
|
if (originalPath.empty()) {
|
||||||
|
return originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExists(originalPath)) {
|
||||||
|
return originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::size_t dot = originalPath.find_last_of('.');
|
||||||
|
const std::string base = (dot == std::string::npos) ? originalPath : originalPath.substr(0, dot);
|
||||||
|
|
||||||
|
static constexpr std::array<const char*, 5> preferredExtensions{
|
||||||
|
".jpg",
|
||||||
|
".jpeg",
|
||||||
|
".png",
|
||||||
|
".webp",
|
||||||
|
".bmp"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* ext : preferredExtensions) {
|
||||||
|
std::string candidate = base + ext;
|
||||||
|
if (candidate == originalPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fileExists(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AssetPath
|
||||||
@ -2,6 +2,10 @@
|
|||||||
"dependencies": [
|
"dependencies": [
|
||||||
"sdl3",
|
"sdl3",
|
||||||
"sdl3-ttf",
|
"sdl3-ttf",
|
||||||
|
{
|
||||||
|
"name": "sdl3-image",
|
||||||
|
"features": ["jpeg", "png", "webp"]
|
||||||
|
},
|
||||||
"catch2",
|
"catch2",
|
||||||
"cpr",
|
"cpr",
|
||||||
"nlohmann-json"
|
"nlohmann-json"
|
||||||
|
|||||||