添加粒子效果
This commit is contained in:
@@ -143,6 +143,35 @@ struct FeedbackState
|
||||
TCHAR detail[128];
|
||||
};
|
||||
|
||||
struct ClearEffectState
|
||||
{
|
||||
int ticks;
|
||||
int totalTicks;
|
||||
int rowCount;
|
||||
int rows[8];
|
||||
};
|
||||
|
||||
struct FloatingTextEffect
|
||||
{
|
||||
int ticks;
|
||||
int totalTicks;
|
||||
int boardX;
|
||||
int boardY;
|
||||
TCHAR text[64];
|
||||
COLORREF color;
|
||||
};
|
||||
|
||||
struct ParticleEffect
|
||||
{
|
||||
int ticks;
|
||||
int totalTicks;
|
||||
int boardX;
|
||||
int boardY;
|
||||
int velocityX;
|
||||
int velocityY;
|
||||
COLORREF color;
|
||||
};
|
||||
|
||||
enum ScreenState
|
||||
{
|
||||
SCREEN_MENU = 0,
|
||||
@@ -180,6 +209,9 @@ extern PlayerStats classicStats;
|
||||
extern PlayerStats rogueStats;
|
||||
extern UpgradeUiState upgradeUiState;
|
||||
extern FeedbackState feedbackState;
|
||||
extern ClearEffectState clearEffectState;
|
||||
extern FloatingTextEffect floatingTextEffects[8];
|
||||
extern ParticleEffect particleEffects[24];
|
||||
extern int currentScreen;
|
||||
extern int currentMode;
|
||||
extern int currentFallInterval;
|
||||
@@ -215,6 +247,9 @@ void HoldCurrentPiece();
|
||||
void UseScreenBomb();
|
||||
void UseBlackHole();
|
||||
void UseAirReshape();
|
||||
void ResetVisualEffects();
|
||||
bool TickVisualEffects();
|
||||
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared);
|
||||
int GetRogueFallInterval();
|
||||
int GetRoguePlayableHeight();
|
||||
int GetRogueLockedRows();
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
#define MAX_LOADSTRING 100
|
||||
#define GAME_TIMER_ID 1
|
||||
#define EFFECT_TIMER_ID 2
|
||||
#define GAME_TIMER_INTERVAL 500
|
||||
#define EFFECT_TIMER_INTERVAL 33
|
||||
|
||||
HINSTANCE hInst;
|
||||
TCHAR szTitle[MAX_LOADSTRING];
|
||||
@@ -355,6 +357,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
ReturnToMainMenu();
|
||||
StartBackgroundMusic();
|
||||
ResetGameTimer(hWnd);
|
||||
SetTimer(hWnd, EFFECT_TIMER_ID, EFFECT_TIMER_INTERVAL, nullptr);
|
||||
InvalidateRect(hWnd, nullptr, FALSE);
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
@@ -375,6 +378,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
}
|
||||
break;
|
||||
case WM_TIMER:
|
||||
if (wParam == EFFECT_TIMER_ID)
|
||||
{
|
||||
if (TickVisualEffects())
|
||||
{
|
||||
InvalidateRect(hWnd, nullptr, FALSE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wParam == GAME_TIMER_ID)
|
||||
{
|
||||
bool shouldRefresh = false;
|
||||
@@ -823,6 +835,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
KillTimer(hWnd, GAME_TIMER_ID);
|
||||
KillTimer(hWnd, EFFECT_TIMER_ID);
|
||||
StopBackgroundMusic();
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
@@ -17,6 +17,9 @@ PlayerStats classicStats = { 0, 1, 0, 0, 0 };
|
||||
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
|
||||
UpgradeUiState upgradeUiState = { 0, 0, 0, 0, {} };
|
||||
FeedbackState feedbackState = { 0, _T(""), _T("") };
|
||||
ClearEffectState clearEffectState = { 0, 0, 0, {} };
|
||||
FloatingTextEffect floatingTextEffects[8] = {};
|
||||
ParticleEffect particleEffects[24] = {};
|
||||
int currentScreen = SCREEN_MENU;
|
||||
int currentMode = MODE_CLASSIC;
|
||||
int currentFallInterval = 500;
|
||||
@@ -239,6 +242,129 @@ void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks)
|
||||
lstrcpyn(feedbackState.detail, detail, sizeof(feedbackState.detail) / sizeof(TCHAR));
|
||||
}
|
||||
|
||||
void ResetVisualEffects()
|
||||
{
|
||||
clearEffectState.ticks = 0;
|
||||
clearEffectState.totalTicks = 0;
|
||||
clearEffectState.rowCount = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
floatingTextEffects[i].ticks = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
particleEffects[i].ticks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool TickVisualEffects()
|
||||
{
|
||||
bool active = false;
|
||||
|
||||
if (clearEffectState.ticks > 0)
|
||||
{
|
||||
clearEffectState.ticks--;
|
||||
active = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (floatingTextEffects[i].ticks > 0)
|
||||
{
|
||||
floatingTextEffects[i].ticks--;
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (particleEffects[i].ticks > 0)
|
||||
{
|
||||
particleEffects[i].ticks--;
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void AddFloatingText(int boardX, int boardY, const TCHAR* text, COLORREF color)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (floatingTextEffects[i].ticks <= 0)
|
||||
{
|
||||
floatingTextEffects[i].ticks = 22;
|
||||
floatingTextEffects[i].totalTicks = 22;
|
||||
floatingTextEffects[i].boardX = boardX;
|
||||
floatingTextEffects[i].boardY = boardY;
|
||||
floatingTextEffects[i].color = color;
|
||||
lstrcpyn(floatingTextEffects[i].text, text, sizeof(floatingTextEffects[i].text) / sizeof(TCHAR));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddParticle(int boardX, int boardY, COLORREF color)
|
||||
{
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (particleEffects[i].ticks <= 0)
|
||||
{
|
||||
particleEffects[i].ticks = 10 + rand() % 5;
|
||||
particleEffects[i].totalTicks = particleEffects[i].ticks;
|
||||
particleEffects[i].boardX = boardX;
|
||||
particleEffects[i].boardY = boardY;
|
||||
particleEffects[i].velocityX = (rand() % 11) - 5;
|
||||
particleEffects[i].velocityY = -8 + (rand() % 5);
|
||||
particleEffects[i].color = color;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared)
|
||||
{
|
||||
if (rows == nullptr || rowCount <= 0 || linesCleared <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (rowCount > 8)
|
||||
{
|
||||
rowCount = 8;
|
||||
}
|
||||
|
||||
clearEffectState.ticks = 16;
|
||||
clearEffectState.totalTicks = 16;
|
||||
clearEffectState.rowCount = rowCount;
|
||||
|
||||
int rowSum = 0;
|
||||
for (int i = 0; i < rowCount; i++)
|
||||
{
|
||||
clearEffectState.rows[i] = rows[i];
|
||||
rowSum += rows[i];
|
||||
for (int x = 0; x < nGameWidth; x += 3)
|
||||
{
|
||||
COLORREF particleColor = BrickColor[(x + rows[i]) % 7];
|
||||
AddParticle(x * 100 + 50, rows[i] * 100 + 50, particleColor);
|
||||
}
|
||||
}
|
||||
|
||||
TCHAR text[64];
|
||||
if (linesCleared >= 4)
|
||||
{
|
||||
_stprintf_s(text, _T("TETRIS"));
|
||||
}
|
||||
else
|
||||
{
|
||||
_stprintf_s(text, _T("%d LINE%s"), linesCleared, linesCleared > 1 ? _T("S") : _T(""));
|
||||
}
|
||||
AddFloatingText(nGameWidth * 50, (rowSum * 100 / rowCount) - 20, text, linesCleared >= 4 ? RGB(255, 232, 120) : RGB(255, 250, 252));
|
||||
}
|
||||
|
||||
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
@@ -741,6 +867,8 @@ int DeleteLines()
|
||||
{
|
||||
int clearedLines = 0;
|
||||
bool clearedWithRainbow = false;
|
||||
int clearedRows[8] = {};
|
||||
int clearedRowCount = 0;
|
||||
|
||||
int playableHeight = GetRoguePlayableHeight();
|
||||
for (int i = playableHeight - 1; i >= 0; i--)
|
||||
@@ -758,6 +886,12 @@ int DeleteLines()
|
||||
|
||||
if (fullLine)
|
||||
{
|
||||
if (clearedRowCount < 8)
|
||||
{
|
||||
clearedRows[clearedRowCount] = i;
|
||||
clearedRowCount++;
|
||||
}
|
||||
|
||||
for (int j = 0; j < nGameWidth; j++)
|
||||
{
|
||||
if (IsRainbowBoardCell(workRegion[i][j]))
|
||||
@@ -773,6 +907,7 @@ int DeleteLines()
|
||||
}
|
||||
|
||||
ApplyLineClearResult(clearedLines);
|
||||
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||
|
||||
if (pendingChainBombFollowup && clearedLines > 0)
|
||||
{
|
||||
@@ -898,6 +1033,7 @@ void Restart()
|
||||
feedbackState.visibleTicks = 0;
|
||||
feedbackState.title[0] = _T('\0');
|
||||
feedbackState.detail[0] = _T('\0');
|
||||
ResetVisualEffects();
|
||||
holdType = -1;
|
||||
holdUsedThisTurn = false;
|
||||
RollCurrentPieceSpecialFlags(false);
|
||||
@@ -929,6 +1065,7 @@ void ReturnToMainMenu()
|
||||
currentScreen = SCREEN_MENU;
|
||||
suspendFlag = false;
|
||||
gameOverFlag = false;
|
||||
ResetVisualEffects();
|
||||
menuState.optionCount = 3;
|
||||
upgradeUiState.pendingCount = 0;
|
||||
upgradeUiState.picksRemaining = 0;
|
||||
|
||||
@@ -839,6 +839,87 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
||||
}
|
||||
}
|
||||
|
||||
if (clearEffectState.ticks > 0 && clearEffectState.totalTicks > 0)
|
||||
{
|
||||
int elapsed = clearEffectState.totalTicks - clearEffectState.ticks;
|
||||
int alpha = 42 + clearEffectState.ticks * 150 / clearEffectState.totalTicks;
|
||||
int inset = SS(elapsed * 2);
|
||||
Graphics flashGraphics(hdc);
|
||||
|
||||
for (int i = 0; i < clearEffectState.rowCount; i++)
|
||||
{
|
||||
int row = clearEffectState.rows[i];
|
||||
if (row < 0 || row >= nGameHeight)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int top = gameRect.top + row * grid + inset;
|
||||
int height = grid - inset * 2;
|
||||
if (height < SS(4))
|
||||
{
|
||||
height = SS(4);
|
||||
}
|
||||
|
||||
SolidBrush flashBrush(Color(alpha, 255, 248, 174));
|
||||
flashGraphics.FillRectangle(
|
||||
&flashBrush,
|
||||
static_cast<INT>(gameRect.left + SS(2)),
|
||||
static_cast<INT>(top),
|
||||
static_cast<INT>(gameRect.right - gameRect.left - SS(4)),
|
||||
static_cast<INT>(height));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
if (particleEffects[i].ticks <= 0 || particleEffects[i].totalTicks <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int elapsed = particleEffects[i].totalTicks - particleEffects[i].ticks;
|
||||
int particleX = gameRect.left + particleEffects[i].boardX * grid / 100 + SS(particleEffects[i].velocityX * elapsed / 2);
|
||||
int particleY = gameRect.top + particleEffects[i].boardY * grid / 100 + SS(particleEffects[i].velocityY * elapsed / 2 + elapsed * elapsed / 10);
|
||||
int particleSize = SS(3 + (elapsed % 2));
|
||||
|
||||
RECT particleRect =
|
||||
{
|
||||
particleX - particleSize,
|
||||
particleY - particleSize,
|
||||
particleX + particleSize,
|
||||
particleY + particleSize
|
||||
};
|
||||
|
||||
HBRUSH particleBrush = CreateSolidBrush(particleEffects[i].color);
|
||||
FillRect(hdc, &particleRect, particleBrush);
|
||||
DeleteObject(particleBrush);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (floatingTextEffects[i].ticks <= 0 || floatingTextEffects[i].totalTicks <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int elapsed = floatingTextEffects[i].totalTicks - floatingTextEffects[i].ticks;
|
||||
int textX = gameRect.left + floatingTextEffects[i].boardX * grid / 100;
|
||||
int textY = gameRect.top + floatingTextEffects[i].boardY * grid / 100 - SS(elapsed * 4);
|
||||
|
||||
HFONT oldFloatFont = (HFONT)SelectObject(hdc, sectionFont);
|
||||
SetTextColor(hdc, floatingTextEffects[i].color);
|
||||
RECT floatRect =
|
||||
{
|
||||
textX - SS(160),
|
||||
textY - SS(24),
|
||||
textX + SS(160),
|
||||
textY + SS(28)
|
||||
};
|
||||
DrawText(hdc, floatingTextEffects[i].text, -1, &floatRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||
SelectObject(hdc, oldFloatFont);
|
||||
}
|
||||
|
||||
HFONT oldFont = (HFONT)SelectObject(hdc, titleFont);
|
||||
DrawPanelHeader(leftPanelRect, _T("战局信息"), 120);
|
||||
DrawPanelHeader(rightPanelRect, _T("预览与战术"), 148);
|
||||
|
||||
Reference in New Issue
Block a user