fixes for mac music
This commit is contained in:
BIN
assets/music/Every Block You Take.ogg
Normal file
BIN
assets/music/Every Block You Take.ogg
Normal file
Binary file not shown.
BIN
assets/music/amazing.ogg
Normal file
BIN
assets/music/amazing.ogg
Normal file
Binary file not shown.
BIN
assets/music/boom_tetris.ogg
Normal file
BIN
assets/music/boom_tetris.ogg
Normal file
Binary file not shown.
BIN
assets/music/great_move.ogg
Normal file
BIN
assets/music/great_move.ogg
Normal file
Binary file not shown.
BIN
assets/music/hard_drop_001.ogg
Normal file
BIN
assets/music/hard_drop_001.ogg
Normal file
Binary file not shown.
BIN
assets/music/impressive.ogg
Normal file
BIN
assets/music/impressive.ogg
Normal file
Binary file not shown.
BIN
assets/music/keep_that_ryhtm.ogg
Normal file
BIN
assets/music/keep_that_ryhtm.ogg
Normal file
Binary file not shown.
BIN
assets/music/lets_go.ogg
Normal file
BIN
assets/music/lets_go.ogg
Normal file
Binary file not shown.
BIN
assets/music/new_level.ogg
Normal file
BIN
assets/music/new_level.ogg
Normal file
Binary file not shown.
BIN
assets/music/nice_combo.ogg
Normal file
BIN
assets/music/nice_combo.ogg
Normal file
Binary file not shown.
BIN
assets/music/smooth_clear.ogg
Normal file
BIN
assets/music/smooth_clear.ogg
Normal file
Binary file not shown.
BIN
assets/music/triple_strike.ogg
Normal file
BIN
assets/music/triple_strike.ogg
Normal file
Binary file not shown.
BIN
assets/music/well_played.ogg
Normal file
BIN
assets/music/well_played.ogg
Normal file
Binary file not shown.
BIN
assets/music/wonderful.ogg
Normal file
BIN
assets/music/wonderful.ogg
Normal file
Binary file not shown.
BIN
assets/music/you_fire.ogg
Normal file
BIN
assets/music/you_fire.ogg
Normal file
Binary file not shown.
BIN
assets/music/you_re_unstoppable.ogg
Normal file
BIN
assets/music/you_re_unstoppable.ogg
Normal file
Binary file not shown.
@ -1,14 +1,14 @@
|
||||
@echo off
|
||||
echo Converting MP3 files to WAV using Windows Media Player...
|
||||
echo Convert MP3 files to OGG (preferred) for cross-platform playback...
|
||||
echo.
|
||||
|
||||
REM Check if we have access to Windows Media Format SDK
|
||||
set MUSIC_DIR=assets\music
|
||||
|
||||
REM List of MP3 files to convert
|
||||
set FILES=amazing.mp3 boom_tetris.mp3 great_move.mp3 impressive.mp3 keep_that_ryhtm.mp3 lets_go.mp3 nice_combo.mp3 smooth_clear.mp3 triple_strike.mp3 well_played.mp3 wonderful.mp3 you_fire.mp3 you_re_unstoppable.mp3
|
||||
set FILES=amazing.mp3 boom_tetris.mp3 great_move.mp3 impressive.mp3 keep_that_ryhtm.mp3 lets_go.mp3 nice_combo.mp3 smooth_clear.mp3 triple_strike.mp3 well_played.mp3 wonderful.mp3 you_fire.mp3 you_re_unstoppable.mp3 hard_drop_001.mp3 new_level.mp3
|
||||
|
||||
echo Please convert these MP3 files to WAV format manually:
|
||||
echo Please convert these MP3 files to OGG Vorbis manually (or run convert_to_ogg.ps1 on Windows):
|
||||
echo.
|
||||
for %%f in (%FILES%) do (
|
||||
echo - %MUSIC_DIR%\%%f
|
||||
@ -17,13 +17,12 @@ for %%f in (%FILES%) do (
|
||||
echo.
|
||||
echo Recommended settings for conversion:
|
||||
echo - Sample Rate: 44100 Hz
|
||||
echo - Bit Depth: 16-bit
|
||||
echo - Channels: Stereo (2)
|
||||
echo - Format: PCM WAV
|
||||
echo - Use OGG Vorbis quality ~4 (or convert to FLAC if you prefer lossless)
|
||||
echo.
|
||||
echo You can use:
|
||||
echo - Audacity (free): https://www.audacityteam.org/
|
||||
echo - VLC Media Player (free): Media ^> Convert/Save
|
||||
echo - Any audio converter software
|
||||
echo - ffmpeg (CLI): ffmpeg -i input.mp3 -c:a libvorbis -qscale:a 4 output.ogg
|
||||
echo.
|
||||
pause
|
||||
|
||||
45
convert_to_ogg.ps1
Normal file
45
convert_to_ogg.ps1
Normal file
@ -0,0 +1,45 @@
|
||||
# Convert MP3 sound effects to OGG Vorbis format for cross-platform playback
|
||||
# Requires ffmpeg (https://ffmpeg.org/). OGG keeps files small while SDL's decoders
|
||||
# work everywhere the game ships.
|
||||
|
||||
$musicDir = "assets\music"
|
||||
$sourceFiles = @(
|
||||
"amazing.mp3",
|
||||
"boom_tetris.mp3",
|
||||
"great_move.mp3",
|
||||
"impressive.mp3",
|
||||
"keep_that_ryhtm.mp3",
|
||||
"lets_go.mp3",
|
||||
"nice_combo.mp3",
|
||||
"smooth_clear.mp3",
|
||||
"triple_strike.mp3",
|
||||
"well_played.mp3",
|
||||
"wonderful.mp3",
|
||||
"you_fire.mp3",
|
||||
"you_re_unstoppable.mp3",
|
||||
"hard_drop_001.mp3",
|
||||
"new_level.mp3",
|
||||
"Every Block You Take.mp3"
|
||||
)
|
||||
|
||||
if (!(Get-Command ffmpeg -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "ffmpeg is required. Install via https://ffmpeg.org/ or winget install Gyan.FFmpeg" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Converting MP3 sound effects to OGG..." -ForegroundColor Cyan
|
||||
foreach ($file in $sourceFiles) {
|
||||
$src = Join-Path $musicDir $file
|
||||
if (!(Test-Path $src)) {
|
||||
Write-Host "Skipping (missing): $src" -ForegroundColor Yellow
|
||||
continue
|
||||
}
|
||||
$ogg = ($file -replace ".mp3$", ".ogg")
|
||||
$dest = Join-Path $musicDir $ogg
|
||||
Write-Host "-> $ogg" -ForegroundColor Green
|
||||
& ffmpeg -y -i $src -c:a libvorbis -qscale:a 4 $dest > $null 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to convert $file" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
Write-Host "Conversion complete." -ForegroundColor Green
|
||||
@ -1,63 +1,11 @@
|
||||
# Convert MP3 sound effects to WAV format
|
||||
# This script converts all MP3 sound effect files to WAV for better compatibility
|
||||
# Deprecated shim: point developers to the OGG conversion workflow
|
||||
Write-Host "convert_to_wav.ps1 is deprecated." -ForegroundColor Yellow
|
||||
Write-Host "Use convert_to_ogg.ps1 for generating assets with OGG Vorbis." -ForegroundColor Yellow
|
||||
|
||||
$musicDir = "assets\music"
|
||||
$mp3Files = @(
|
||||
"amazing.mp3",
|
||||
"boom_tetris.mp3",
|
||||
"great_move.mp3",
|
||||
"impressive.mp3",
|
||||
"keep_that_ryhtm.mp3",
|
||||
"lets_go.mp3",
|
||||
"nice_combo.mp3",
|
||||
"smooth_clear.mp3",
|
||||
"triple_strike.mp3",
|
||||
"well_played.mp3",
|
||||
"wonderful.mp3",
|
||||
"you_fire.mp3",
|
||||
"you_re_unstoppable.mp3"
|
||||
)
|
||||
|
||||
Write-Host "Converting MP3 sound effects to WAV format..." -ForegroundColor Green
|
||||
|
||||
foreach ($mp3File in $mp3Files) {
|
||||
$mp3Path = Join-Path $musicDir $mp3File
|
||||
$wavFile = $mp3File -replace "\.mp3$", ".wav"
|
||||
$wavPath = Join-Path $musicDir $wavFile
|
||||
|
||||
if (Test-Path $mp3Path) {
|
||||
Write-Host "Converting $mp3File to $wavFile..." -ForegroundColor Yellow
|
||||
|
||||
# Try ffmpeg first (most common)
|
||||
$ffmpegResult = $null
|
||||
try {
|
||||
$ffmpegResult = & ffmpeg -i $mp3Path -acodec pcm_s16le -ar 44100 -ac 2 $wavPath -y 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "✓ Successfully converted $mp3File" -ForegroundColor Green
|
||||
continue
|
||||
}
|
||||
} catch {
|
||||
# FFmpeg not available, try other methods
|
||||
}
|
||||
|
||||
# Try Windows Media Format SDK (if available)
|
||||
try {
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
Add-Type -AssemblyName Microsoft.VisualBasic
|
||||
|
||||
# Use Windows built-in audio conversion
|
||||
$shell = New-Object -ComObject Shell.Application
|
||||
# This is a fallback method - may not work on all systems
|
||||
Write-Host "⚠ FFmpeg not found. Please install FFmpeg or convert manually." -ForegroundColor Red
|
||||
} catch {
|
||||
Write-Host "⚠ Could not convert $mp3File automatically." -ForegroundColor Red
|
||||
}
|
||||
} else {
|
||||
Write-Host "⚠ File not found: $mp3Path" -ForegroundColor Red
|
||||
}
|
||||
$oggScript = Join-Path $PSScriptRoot "convert_to_ogg.ps1"
|
||||
if (Test-Path $oggScript) {
|
||||
& $oggScript
|
||||
} else {
|
||||
Write-Host "Missing convert_to_ogg.ps1" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "`nConversion complete! If FFmpeg was not found, please:" -ForegroundColor Cyan
|
||||
Write-Host "1. Install FFmpeg: https://ffmpeg.org/download.html" -ForegroundColor White
|
||||
Write-Host "2. Or use an audio converter like Audacity" -ForegroundColor White
|
||||
Write-Host "3. Convert to: 44.1kHz, 16-bit, Stereo WAV" -ForegroundColor White
|
||||
|
||||
@ -12,7 +12,7 @@ Sound=1
|
||||
SmoothScroll=1
|
||||
|
||||
[Player]
|
||||
Name=GREGOR
|
||||
Name=PLAYER
|
||||
|
||||
[Debug]
|
||||
Enabled=1
|
||||
|
||||
@ -558,16 +558,20 @@ bool ApplicationManager::initializeGame() {
|
||||
Audio::instance().init();
|
||||
// Discover available tracks (up to 100) and queue for background loading
|
||||
m_totalTracks = 0;
|
||||
std::vector<std::string> trackPaths;
|
||||
trackPaths.reserve(100);
|
||||
for (int i = 1; i <= 100; ++i) {
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf), "assets/music/music%03d.mp3", i);
|
||||
// Use simple file existence check via std::filesystem
|
||||
if (std::filesystem::exists(buf)) {
|
||||
Audio::instance().addTrackAsync(buf);
|
||||
++m_totalTracks;
|
||||
} else {
|
||||
char base[128];
|
||||
std::snprintf(base, sizeof(base), "assets/music/music%03d", i);
|
||||
std::string path = AssetPath::resolveWithExtensions(base, { ".mp3" });
|
||||
if (path.empty()) {
|
||||
break;
|
||||
}
|
||||
trackPaths.push_back(path);
|
||||
}
|
||||
m_totalTracks = static_cast<int>(trackPaths.size());
|
||||
for (const auto& path : trackPaths) {
|
||||
Audio::instance().addTrackAsync(path);
|
||||
}
|
||||
if (m_totalTracks > 0) {
|
||||
Audio::instance().startBackgroundLoading();
|
||||
|
||||
@ -208,27 +208,14 @@ bool AssetManager::loadSoundEffectWithFallback(const std::string& id, const std:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try WAV first, then MP3 fallback (matching main.cpp pattern)
|
||||
std::string wavPath = "assets/music/" + baseName + ".wav";
|
||||
std::string mp3Path = "assets/music/" + baseName + ".mp3";
|
||||
|
||||
// Check WAV first
|
||||
if (fileExists(wavPath)) {
|
||||
if (m_soundSystem->loadSound(id, wavPath)) {
|
||||
logInfo("Loaded sound effect: " + id + " from " + wavPath + " (WAV)");
|
||||
return true;
|
||||
}
|
||||
const std::string basePath = "assets/music/" + baseName;
|
||||
std::string resolved = AssetPath::resolveWithExtensions(basePath, { ".wav", ".mp3" });
|
||||
if (!resolved.empty() && m_soundSystem->loadSound(id, resolved)) {
|
||||
logInfo("Loaded sound effect: " + id + " from " + resolved);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback to MP3
|
||||
if (fileExists(mp3Path)) {
|
||||
if (m_soundSystem->loadSound(id, mp3Path)) {
|
||||
logInfo("Loaded sound effect: " + id + " from " + mp3Path + " (MP3 fallback)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
setError("Failed to load sound effect: " + id + " (tried both WAV and MP3)");
|
||||
setError("Failed to load sound effect: " + id + " (no supported audio extension found)");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
137
src/main.cpp
137
src/main.cpp
@ -15,6 +15,7 @@
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include "audio/Audio.h"
|
||||
#include "audio/SoundEffect.h"
|
||||
@ -682,11 +683,11 @@ int main(int, char **)
|
||||
}
|
||||
SDL_SetRenderVSync(renderer, 1);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// On macOS bundles launched from Finder start in /, so re-root relative paths.
|
||||
if (const char* basePathRaw = SDL_GetBasePath()) {
|
||||
std::filesystem::path exeDir(basePathRaw);
|
||||
SDL_free(const_cast<char*>(basePathRaw));
|
||||
AssetPath::setBasePath(exeDir.string());
|
||||
#if defined(__APPLE__)
|
||||
// On macOS bundles launched from Finder start in /, so re-root relative paths.
|
||||
std::error_code ec;
|
||||
std::filesystem::current_path(exeDir, ec);
|
||||
if (ec) {
|
||||
@ -694,12 +695,13 @@ int main(int, char **)
|
||||
"Failed to set working directory to %s: %s",
|
||||
exeDir.string().c_str(), ec.message().c_str());
|
||||
}
|
||||
#endif
|
||||
SDL_free(const_cast<char*>(basePathRaw));
|
||||
} else {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"SDL_GetBasePath() failed; asset lookups rely on current directory: %s",
|
||||
SDL_GetError());
|
||||
}
|
||||
#endif
|
||||
|
||||
FontAtlas font;
|
||||
font.init("FreeSans.ttf", 24);
|
||||
@ -709,10 +711,15 @@ int main(int, char **)
|
||||
pixelFont.init("assets/fonts/PressStart2P-Regular.ttf", 16);
|
||||
|
||||
ScoreManager scores;
|
||||
// Load scores asynchronously to prevent startup hang due to network request
|
||||
std::thread([&scores]() {
|
||||
// Load scores asynchronously but keep the worker alive until shutdown to avoid lifetime issues
|
||||
std::jthread scoreLoader([&scores]() {
|
||||
scores.load();
|
||||
}).detach();
|
||||
});
|
||||
const auto ensureScoresLoaded = [&]() {
|
||||
if (scoreLoader.joinable()) {
|
||||
scoreLoader.join();
|
||||
}
|
||||
};
|
||||
Starfield starfield;
|
||||
starfield.init(200, LOGICAL_W, LOGICAL_H);
|
||||
Starfield3D starfield3D;
|
||||
@ -771,9 +778,19 @@ int main(int, char **)
|
||||
|
||||
// Initialize sound effects system
|
||||
SoundEffectManager::instance().init();
|
||||
|
||||
// Load sound effects
|
||||
SoundEffectManager::instance().loadSound("clear_line", "assets/music/clear_line.wav");
|
||||
|
||||
auto loadAudioAsset = [](const std::string& basePath, const std::string& id) {
|
||||
std::string resolved = AssetPath::resolveWithExtensions(basePath, { ".wav", ".mp3" });
|
||||
if (resolved.empty()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Missing audio asset for %s (base %s)", id.c_str(), basePath.c_str());
|
||||
return;
|
||||
}
|
||||
if (!SoundEffectManager::instance().loadSound(id, resolved)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load %s from %s", id.c_str(), resolved.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
loadAudioAsset("assets/music/clear_line", "clear_line");
|
||||
|
||||
// Load voice lines for line clears using WAV files (with MP3 fallback)
|
||||
std::vector<std::string> singleSounds = {"well_played", "smooth_clear", "great_move"};
|
||||
@ -789,49 +806,25 @@ int main(int, char **)
|
||||
appendVoices(tripleSounds);
|
||||
appendVoices(tetrisSounds);
|
||||
|
||||
// Helper function to load sound with WAV/MP3 fallback and file existence check
|
||||
auto loadSoundWithFallback = [&](const std::string& id, const std::string& baseName) {
|
||||
std::string wavPath = "assets/music/" + baseName + ".wav";
|
||||
std::string mp3Path = "assets/music/" + baseName + ".mp3";
|
||||
|
||||
// Check if WAV file exists first
|
||||
SDL_IOStream* wavFile = SDL_IOFromFile(wavPath.c_str(), "rb");
|
||||
if (wavFile) {
|
||||
SDL_CloseIO(wavFile);
|
||||
if (SoundEffectManager::instance().loadSound(id, wavPath)) {
|
||||
(void)0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to MP3 if WAV doesn't exist or fails to load
|
||||
SDL_IOStream* mp3File = SDL_IOFromFile(mp3Path.c_str(), "rb");
|
||||
if (mp3File) {
|
||||
SDL_CloseIO(mp3File);
|
||||
if (SoundEffectManager::instance().loadSound(id, mp3Path)) {
|
||||
(void)0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load sound: %s (tried both WAV and MP3)", id.c_str());
|
||||
auto loadVoice = [&](const std::string& id, const std::string& baseName) {
|
||||
loadAudioAsset("assets/music/" + baseName, id);
|
||||
};
|
||||
|
||||
loadSoundWithFallback("nice_combo", "nice_combo");
|
||||
loadSoundWithFallback("you_fire", "you_fire");
|
||||
loadSoundWithFallback("well_played", "well_played");
|
||||
loadSoundWithFallback("keep_that_ryhtm", "keep_that_ryhtm");
|
||||
loadSoundWithFallback("great_move", "great_move");
|
||||
loadSoundWithFallback("smooth_clear", "smooth_clear");
|
||||
loadSoundWithFallback("impressive", "impressive");
|
||||
loadSoundWithFallback("triple_strike", "triple_strike");
|
||||
loadSoundWithFallback("amazing", "amazing");
|
||||
loadSoundWithFallback("you_re_unstoppable", "you_re_unstoppable");
|
||||
loadSoundWithFallback("boom_tetris", "boom_tetris");
|
||||
loadSoundWithFallback("wonderful", "wonderful");
|
||||
loadSoundWithFallback("lets_go", "lets_go"); // For level up
|
||||
loadSoundWithFallback("hard_drop", "hard_drop_001");
|
||||
loadSoundWithFallback("new_level", "new_level");
|
||||
|
||||
loadVoice("nice_combo", "nice_combo");
|
||||
loadVoice("you_fire", "you_fire");
|
||||
loadVoice("well_played", "well_played");
|
||||
loadVoice("keep_that_ryhtm", "keep_that_ryhtm");
|
||||
loadVoice("great_move", "great_move");
|
||||
loadVoice("smooth_clear", "smooth_clear");
|
||||
loadVoice("impressive", "impressive");
|
||||
loadVoice("triple_strike", "triple_strike");
|
||||
loadVoice("amazing", "amazing");
|
||||
loadVoice("you_re_unstoppable", "you_re_unstoppable");
|
||||
loadVoice("boom_tetris", "boom_tetris");
|
||||
loadVoice("wonderful", "wonderful");
|
||||
loadVoice("lets_go", "lets_go");
|
||||
loadVoice("hard_drop", "hard_drop_001");
|
||||
loadVoice("new_level", "new_level");
|
||||
|
||||
bool suppressLineVoiceForLevelUp = false;
|
||||
|
||||
@ -1122,6 +1115,7 @@ int main(int, char **)
|
||||
playerName.pop_back();
|
||||
} else if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER) {
|
||||
if (playerName.empty()) playerName = "PLAYER";
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game.score(), game.lines(), game.level(), game.elapsed(), playerName);
|
||||
Settings::instance().setPlayerName(playerName);
|
||||
isNewHighScore = false;
|
||||
@ -1379,6 +1373,7 @@ int main(int, char **)
|
||||
SDL_StartTextInput(window);
|
||||
} else {
|
||||
isNewHighScore = false;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game.score(), game.lines(), game.level(), game.elapsed());
|
||||
}
|
||||
state = AppState::GameOver;
|
||||
@ -1396,25 +1391,21 @@ int main(int, char **)
|
||||
|
||||
// Count actual music files first
|
||||
totalTracks = 0;
|
||||
for (int i = 1; i <= 100; ++i) { // Check up to 100 files
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "assets/music/music%03d.mp3", i);
|
||||
|
||||
// Check if file exists
|
||||
SDL_IOStream* file = SDL_IOFromFile(buf, "rb");
|
||||
if (file) {
|
||||
SDL_CloseIO(file);
|
||||
totalTracks++;
|
||||
} else {
|
||||
break; // No more consecutive files
|
||||
std::vector<std::string> trackPaths;
|
||||
trackPaths.reserve(100);
|
||||
for (int i = 1; i <= 100; ++i) {
|
||||
char base[64];
|
||||
std::snprintf(base, sizeof(base), "assets/music/music%03d", i);
|
||||
std::string path = AssetPath::resolveWithExtensions(base, { ".mp3" });
|
||||
if (path.empty()) {
|
||||
break;
|
||||
}
|
||||
trackPaths.push_back(path);
|
||||
}
|
||||
|
||||
// Add all found tracks to the background loading queue
|
||||
for (int i = 1; i <= totalTracks; ++i) {
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "assets/music/music%03d.mp3", i);
|
||||
Audio::instance().addTrackAsync(buf);
|
||||
totalTracks = static_cast<int>(trackPaths.size());
|
||||
|
||||
for (const auto& track : trackPaths) {
|
||||
Audio::instance().addTrackAsync(track);
|
||||
}
|
||||
|
||||
// Start background loading thread
|
||||
@ -1470,7 +1461,12 @@ int main(int, char **)
|
||||
static bool menuTrackLoaded = false;
|
||||
if (!menuTrackLoaded) {
|
||||
std::thread([]() {
|
||||
Audio::instance().setMenuTrack("assets/music/Every Block You Take.mp3");
|
||||
std::string menuTrack = AssetPath::resolveWithExtensions("assets/music/Every Block You Take", { ".mp3" });
|
||||
if (!menuTrack.empty()) {
|
||||
Audio::instance().setMenuTrack(menuTrack);
|
||||
} else {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Menu track not found (Every Block You Take)");
|
||||
}
|
||||
}).detach();
|
||||
menuTrackLoaded = true;
|
||||
}
|
||||
@ -1804,6 +1800,7 @@ int main(int, char **)
|
||||
// 4. Draw Text
|
||||
// 4. Draw Text
|
||||
// Title
|
||||
ensureScoresLoaded();
|
||||
bool realHighScore = scores.isHighScore(game.score());
|
||||
const char* title = realHighScore ? "NEW HIGH SCORE!" : "GAME OVER";
|
||||
int tW=0, tH=0; pixelFont.measure(title, 2.0f, tW, tH);
|
||||
|
||||
@ -2,17 +2,27 @@
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace AssetPath {
|
||||
|
||||
inline bool fileExists(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
inline std::string& baseDirectory() {
|
||||
static std::string base;
|
||||
return base;
|
||||
}
|
||||
|
||||
inline void setBasePath(std::string path) {
|
||||
baseDirectory() = std::move(path);
|
||||
}
|
||||
|
||||
inline bool tryOpenFile(const std::string& candidate) {
|
||||
if (candidate.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_IOStream* file = SDL_IOFromFile(path.c_str(), "rb");
|
||||
SDL_IOStream* file = SDL_IOFromFile(candidate.c_str(), "rb");
|
||||
if (file) {
|
||||
SDL_CloseIO(file);
|
||||
return true;
|
||||
@ -20,13 +30,58 @@ inline bool fileExists(const std::string& path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool fileExists(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tryOpenFile(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::filesystem::path p(path);
|
||||
if (!p.is_absolute()) {
|
||||
const std::string& base = baseDirectory();
|
||||
if (!base.empty()) {
|
||||
std::filesystem::path combined = std::filesystem::path(base) / p;
|
||||
if (tryOpenFile(combined.string())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string resolveWithBase(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (tryOpenFile(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::filesystem::path p(path);
|
||||
if (!p.is_absolute()) {
|
||||
const std::string& base = baseDirectory();
|
||||
if (!base.empty()) {
|
||||
std::filesystem::path combined = std::filesystem::path(base) / p;
|
||||
std::string combinedStr = combined.string();
|
||||
if (tryOpenFile(combinedStr)) {
|
||||
return combinedStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
inline std::string resolveImagePath(const std::string& originalPath) {
|
||||
if (originalPath.empty()) {
|
||||
return originalPath;
|
||||
}
|
||||
|
||||
if (fileExists(originalPath)) {
|
||||
return originalPath;
|
||||
if (auto resolved = resolveWithBase(originalPath); resolved != originalPath || fileExists(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const std::size_t dot = originalPath.find_last_of('.');
|
||||
@ -45,12 +100,41 @@ inline std::string resolveImagePath(const std::string& originalPath) {
|
||||
if (candidate == originalPath) {
|
||||
continue;
|
||||
}
|
||||
if (fileExists(candidate)) {
|
||||
return candidate;
|
||||
std::string resolvedCandidate = resolveWithBase(candidate);
|
||||
if (resolvedCandidate != candidate || fileExists(resolvedCandidate)) {
|
||||
return resolvedCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
return originalPath;
|
||||
}
|
||||
|
||||
inline std::string resolveWithExtensions(const std::string& basePathWithoutExt, std::initializer_list<const char*> extensions) {
|
||||
for (const char* ext : extensions) {
|
||||
std::string candidate = basePathWithoutExt + ext;
|
||||
std::string resolved = resolveWithBase(candidate);
|
||||
if (resolved != candidate || fileExists(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::string resolveAudioPath(const std::string& basePathWithoutExt) {
|
||||
static constexpr std::array<const char*, 4> AUDIO_EXTENSIONS = {
|
||||
".ogg",
|
||||
".flac",
|
||||
".wav",
|
||||
".mp3"
|
||||
};
|
||||
for (const char* ext : AUDIO_EXTENSIONS) {
|
||||
std::string candidate = basePathWithoutExt + ext;
|
||||
std::string resolved = resolveWithBase(candidate);
|
||||
if (resolved != candidate || fileExists(resolved)) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace AssetPath
|
||||
|
||||
Reference in New Issue
Block a user