asteroid explode

This commit is contained in:
2025-12-20 19:49:05 +01:00
parent ad014e1de0
commit b69b090e45
5 changed files with 45 additions and 2 deletions

View File

@ -460,6 +460,7 @@ int TetrisApp::Impl::init()
ctx.exitPopupSelectedButton = &exitPopupSelectedButton;
ctx.gameplayCountdownActive = &gameplayCountdownActive;
ctx.menuPlayCountdownArmed = &menuPlayCountdownArmed;
ctx.skipNextLevelUpJingle = &skipNextLevelUpJingle;
ctx.challengeClearFxActive = &challengeClearFxActive;
ctx.challengeClearFxElapsedMs = &challengeClearFxElapsedMs;
ctx.challengeClearFxDurationMs = &challengeClearFxDurationMs;
@ -1090,7 +1091,13 @@ void TetrisApp::Impl::runLoop()
const std::vector<std::string> audioIds = {"clear_line","nice_combo","you_fire","well_played","keep_that_ryhtm","great_move","smooth_clear","impressive","triple_strike","amazing","you_re_unstoppable","boom_tetris","wonderful","lets_go","hard_drop","new_level","asteroid_destroy","challenge_clear"};
for (const auto &id : audioIds) {
std::string basePath = "assets/music/" + (id == "hard_drop" ? "hard_drop_001" : (id == "challenge_clear" ? "GONG0" : id));
std::string basePath = "assets/music/" + (id == "hard_drop"
? "hard_drop_001"
: (id == "challenge_clear"
? "GONG0"
: (id == "asteroid_destroy"
? "asteroid-destroy"
: id)));
{
std::lock_guard<std::mutex> lk(currentLoadingMutex);
currentLoadingFile = basePath;

View File

@ -510,6 +510,31 @@ int Game::checkLines() {
completedLines.push_back(y);
}
}
// Pre-play asteroid destroy SFX immediately when a clearing line contains asteroids (reduces latency)
if (!completedLines.empty() && mode == GameMode::Challenge) {
std::optional<AsteroidType> foundType;
for (int y : completedLines) {
for (int x = 0; x < COLS; ++x) {
int idx = y * COLS + x;
if (board[idx] >= ASTEROID_BASE) {
int typeIdx = board[idx] - ASTEROID_BASE;
if (typeIdx >= 0 && typeIdx <= static_cast<int>(AsteroidType::Core)) {
foundType = static_cast<AsteroidType>(typeIdx);
}
} else if (idx >= 0 && idx < static_cast<int>(asteroidGrid.size()) && asteroidGrid[idx].has_value()) {
foundType = asteroidGrid[idx]->type;
}
}
}
if (foundType.has_value()) {
pendingAsteroidDestroyType = foundType;
if (!asteroidDestroySoundPreplayed && asteroidDestroyedCallback) {
asteroidDestroySoundPreplayed = true;
asteroidDestroyedCallback(*foundType);
}
}
}
return static_cast<int>(completedLines.size());
}
@ -537,6 +562,10 @@ void Game::actualClearLines() {
// Apply asteroid-specific gravity after the board collapses
applyAsteroidGravity();
// Reset preplay latch so future destroys can fire again
pendingAsteroidDestroyType.reset();
asteroidDestroySoundPreplayed = false;
if (mode == GameMode::Challenge) {
if (asteroidsRemainingCount <= 0) {
int nextLevel = challengeLevelIndex + 1;
@ -629,7 +658,7 @@ void Game::handleAsteroidsOnClearedRows(const std::vector<int>& clearedRows,
if (destroyedThisPass > 0) {
asteroidsRemainingCount = std::max(0, asteroidsRemainingCount - destroyedThisPass);
if (asteroidDestroyedCallback && lastDestroyedType.has_value()) {
if (!asteroidDestroySoundPreplayed && asteroidDestroyedCallback && lastDestroyedType.has_value()) {
asteroidDestroyedCallback(*lastDestroyedType);
}
}

View File

@ -177,6 +177,9 @@ private:
bool challengeLevelActive{false};
bool challengeAdvanceQueued{false};
int challengeQueuedLevel{0};
// Asteroid SFX latency mitigation
std::optional<AsteroidType> pendingAsteroidDestroyType;
bool asteroidDestroySoundPreplayed{false};
// Internal helpers ----------------------------------------------------
void refillBag();

View File

@ -473,6 +473,9 @@ void MenuState::handleEvent(const SDL_Event& e) {
// Start challenge run at level 1
if (ctx.game) {
ctx.game->setMode(GameMode::Challenge);
if (ctx.skipNextLevelUpJingle) {
*ctx.skipNextLevelUpJingle = true;
}
ctx.game->startChallengeRun(1);
}
triggerPlay();

View File

@ -67,6 +67,7 @@ struct StateContext {
int* exitPopupSelectedButton = nullptr; // 0 = YES, 1 = NO (default)
bool* gameplayCountdownActive = nullptr; // True if start-of-game countdown is running
bool* menuPlayCountdownArmed = nullptr; // True if we are transitioning to play and countdown is pending
bool* skipNextLevelUpJingle = nullptr; // Allows states to silence initial level-up SFX
// Challenge clear FX (slow block-by-block explosion before next level)
bool* challengeClearFxActive = nullptr;
double* challengeClearFxElapsedMs = nullptr;