asteroid explode
This commit is contained in:
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user