#include "stdafx.h" /** * @file TetrisTimers.cpp * @brief 实现游戏下落、视觉特效、致谢动画和 Rogue 限时状态的定时推进。 */ #include "TetrisAppInternal.h" static MMRESULT creditTimerHandle = 0; /** * @brief 多媒体定时器回调,用于高频率请求致谢页动画刷新。 * @param userData 创建定时器时传入的窗口句柄。 */ static void CALLBACK CreditTimerCallback(UINT, UINT, DWORD_PTR userData, DWORD_PTR, DWORD_PTR) { HWND hWnd = reinterpret_cast(userData); if (hWnd != nullptr) { PostMessage(hWnd, WM_CREDIT_TICK, 0, 0); } } /** * @brief 重置主下落定时器。 * @param hWnd 当前窗口句柄。 */ void ResetGameTimer(HWND hWnd) { KillTimer(hWnd, GAME_TIMER_ID); SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr); } /** * @brief 启动游戏、特效和致谢页动画定时器。 * @param hWnd 当前窗口句柄。 */ void StartAppTimers(HWND hWnd) { ResetGameTimer(hWnd); SetTimer(hWnd, EFFECT_TIMER_ID, EFFECT_TIMER_INTERVAL, nullptr); creditTimerHandle = timeSetEvent( CREDIT_TIMER_INTERVAL, 1, CreditTimerCallback, reinterpret_cast(hWnd), TIME_PERIODIC | TIME_CALLBACK_FUNCTION); if (creditTimerHandle == 0) { // 多媒体定时器不可用时退回普通窗口定时器,保证致谢页仍可动画。 SetTimer(hWnd, CREDIT_TIMER_ID, CREDIT_TIMER_INTERVAL, nullptr); } } /** * @brief 停止游戏、特效和致谢页动画定时器。 * @param hWnd 当前窗口句柄。 */ void StopAppTimers(HWND hWnd) { KillTimer(hWnd, GAME_TIMER_ID); KillTimer(hWnd, EFFECT_TIMER_ID); if (creditTimerHandle != 0) { timeKillEvent(creditTimerHandle); creditTimerHandle = 0; } else { KillTimer(hWnd, CREDIT_TIMER_ID); } } /** * @brief 处理致谢页高频动画刷新消息。 * @param hWnd 当前窗口句柄。 */ void HandleCreditTick(HWND hWnd) { if (currentScreen == SCREEN_RULES && helpState.currentPage == 4 && TickCreditAnimation()) { InvalidateRect(hWnd, nullptr, FALSE); } } /** * @brief 推进 Rogue 限时状态并按需要重置下落定时器。 * @param hWnd 当前窗口句柄。 * @return 任意状态变化需要刷新界面时返回 true。 */ static bool TickRogueTimedStates(HWND hWnd) { bool shouldRefresh = false; if (currentMode == MODE_ROGUE && !IsRogueSkillDemoMode() && rogueStats.feverTicks > 0) { rogueStats.feverTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && !IsRogueSkillDemoMode() && rogueStats.timeDilationTicks > 0 && currentScreen == SCREEN_PLAYING && !suspendFlag && !gameOverFlag) { rogueStats.timeDilationTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && !IsRogueSkillDemoMode() && rogueStats.extremeSlowTicks > 0) { rogueStats.extremeSlowTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } if (currentMode == MODE_ROGUE && !IsRogueSkillDemoMode() && rogueStats.holdSlowTicks > 0) { rogueStats.holdSlowTicks--; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); shouldRefresh = true; } return shouldRefresh; } /** * @brief 检查极限玩家的危险等级计时。 * @param hWnd 当前窗口句柄。 * @return 危险等级变化时返回 true。 */ static bool TickExtremeDanger(HWND hWnd) { if (currentMode != MODE_ROGUE || IsRogueSkillDemoMode() || rogueStats.extremePlayerLevel <= 0 || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return false; } if (rogueStats.extremeDangerTicks > 0) { rogueStats.extremeDangerTicks--; return false; } rogueStats.extremeDangerTicks = 30; if (rogueStats.extremeDangerLevel < 5) { rogueStats.extremeDangerLevel++; } currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); SetFeedbackMessage( _T("极限压力升高"), _T("30 秒内没有完成四消,危险等级提升,下落速度进一步加快。"), 10); return true; } /** * @brief 检查高堆叠触发的时间缓流。 * @param hWnd 当前窗口句柄。 * @return 成功触发时间缓流时返回 true。 */ static bool TryStartTimeDilation(HWND hWnd) { if (currentMode != MODE_ROGUE || IsRogueSkillDemoMode() || rogueStats.timeDilationLevel <= 0 || rogueStats.timeDilationTicks > 0) { return false; } int occupiedHeight = 0; int playableHeight = GetRoguePlayableHeight(); for (int y = 0; y < playableHeight; y++) { bool hasCell = false; for (int x = 0; x < nGameWidth; x++) { if (workRegion[y][x] != 0) { hasCell = true; break; } } if (hasCell) { occupiedHeight = playableHeight - y; break; } } if (occupiedHeight <= 15) { return false; } rogueStats.timeDilationTicks = 8; currentFallInterval = GetRogueFallInterval(); ResetGameTimer(hWnd); SetFeedbackMessage( _T("时间缓流"), _T("堆叠高度超过 15 行,接下来 8 秒下落速度降低 30%。"), 10); return true; } /** * @brief 推进一次自动下落逻辑。 * @param hWnd 当前窗口句柄。 * @return 游戏状态推进后需要刷新界面返回 true。 */ static bool TickGameFall(HWND hWnd) { if (currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return false; } if (currentMode == MODE_ROGUE && !IsRogueSkillDemoMode()) { int previousFallInterval = currentFallInterval; AdvanceRogueDifficulty(currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL); if (currentFallInterval != previousFallInterval) { ResetGameTimer(hWnd); } } TryStartTimeDilation(hWnd); if (CanMoveDown()) { MoveDown(); } else { Fixing(); if (!gameOverFlag) { DeleteLines(); CheckRogueLevelProgress(); } } if (!gameOverFlag) { ComputeTarget(); } return true; } /** * @brief 处理窗口定时器消息。 * @param hWnd 当前窗口句柄。 * @param timerId 触发的定时器编号。 */ void HandleTimerMessage(HWND hWnd, WPARAM timerId) { if (timerId == EFFECT_TIMER_ID) { if (TickVisualEffects()) { InvalidateRect(hWnd, nullptr, FALSE); } return; } if (timerId == CREDIT_TIMER_ID && creditTimerHandle == 0) { HandleCreditTick(hWnd); return; } if (timerId != GAME_TIMER_ID) { return; } bool shouldRefresh = false; if (feedbackState.visibleTicks > 0) { feedbackState.visibleTicks--; shouldRefresh = true; } if (IsRogueSkillDemoMode() && TickRogueSkillDemo()) { shouldRefresh = true; } if (TickRogueTimedStates(hWnd)) { shouldRefresh = true; } if (TickExtremeDanger(hWnd)) { shouldRefresh = true; } if (TickGameFall(hWnd)) { shouldRefresh = true; } if (shouldRefresh) { InvalidateRect(hWnd, nullptr, FALSE); } }