325 lines
7.7 KiB
C++
325 lines
7.7 KiB
C++
#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<HWND>(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<DWORD_PTR>(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);
|
|
}
|
|
}
|