添加粒子效果
This commit is contained in:
@@ -143,6 +143,35 @@ struct FeedbackState
|
|||||||
TCHAR detail[128];
|
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
|
enum ScreenState
|
||||||
{
|
{
|
||||||
SCREEN_MENU = 0,
|
SCREEN_MENU = 0,
|
||||||
@@ -180,6 +209,9 @@ extern PlayerStats classicStats;
|
|||||||
extern PlayerStats rogueStats;
|
extern PlayerStats rogueStats;
|
||||||
extern UpgradeUiState upgradeUiState;
|
extern UpgradeUiState upgradeUiState;
|
||||||
extern FeedbackState feedbackState;
|
extern FeedbackState feedbackState;
|
||||||
|
extern ClearEffectState clearEffectState;
|
||||||
|
extern FloatingTextEffect floatingTextEffects[8];
|
||||||
|
extern ParticleEffect particleEffects[24];
|
||||||
extern int currentScreen;
|
extern int currentScreen;
|
||||||
extern int currentMode;
|
extern int currentMode;
|
||||||
extern int currentFallInterval;
|
extern int currentFallInterval;
|
||||||
@@ -215,6 +247,9 @@ void HoldCurrentPiece();
|
|||||||
void UseScreenBomb();
|
void UseScreenBomb();
|
||||||
void UseBlackHole();
|
void UseBlackHole();
|
||||||
void UseAirReshape();
|
void UseAirReshape();
|
||||||
|
void ResetVisualEffects();
|
||||||
|
bool TickVisualEffects();
|
||||||
|
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared);
|
||||||
int GetRogueFallInterval();
|
int GetRogueFallInterval();
|
||||||
int GetRoguePlayableHeight();
|
int GetRoguePlayableHeight();
|
||||||
int GetRogueLockedRows();
|
int GetRogueLockedRows();
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
#define MAX_LOADSTRING 100
|
#define MAX_LOADSTRING 100
|
||||||
#define GAME_TIMER_ID 1
|
#define GAME_TIMER_ID 1
|
||||||
|
#define EFFECT_TIMER_ID 2
|
||||||
#define GAME_TIMER_INTERVAL 500
|
#define GAME_TIMER_INTERVAL 500
|
||||||
|
#define EFFECT_TIMER_INTERVAL 33
|
||||||
|
|
||||||
HINSTANCE hInst;
|
HINSTANCE hInst;
|
||||||
TCHAR szTitle[MAX_LOADSTRING];
|
TCHAR szTitle[MAX_LOADSTRING];
|
||||||
@@ -355,6 +357,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
ReturnToMainMenu();
|
ReturnToMainMenu();
|
||||||
StartBackgroundMusic();
|
StartBackgroundMusic();
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
|
SetTimer(hWnd, EFFECT_TIMER_ID, EFFECT_TIMER_INTERVAL, nullptr);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
@@ -375,6 +378,15 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
|
if (wParam == EFFECT_TIMER_ID)
|
||||||
|
{
|
||||||
|
if (TickVisualEffects())
|
||||||
|
{
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (wParam == GAME_TIMER_ID)
|
if (wParam == GAME_TIMER_ID)
|
||||||
{
|
{
|
||||||
bool shouldRefresh = false;
|
bool shouldRefresh = false;
|
||||||
@@ -823,6 +835,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
break;
|
break;
|
||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
KillTimer(hWnd, GAME_TIMER_ID);
|
KillTimer(hWnd, GAME_TIMER_ID);
|
||||||
|
KillTimer(hWnd, EFFECT_TIMER_ID);
|
||||||
StopBackgroundMusic();
|
StopBackgroundMusic();
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ PlayerStats classicStats = { 0, 1, 0, 0, 0 };
|
|||||||
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
|
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
|
||||||
UpgradeUiState upgradeUiState = { 0, 0, 0, 0, {} };
|
UpgradeUiState upgradeUiState = { 0, 0, 0, 0, {} };
|
||||||
FeedbackState feedbackState = { 0, _T(""), _T("") };
|
FeedbackState feedbackState = { 0, _T(""), _T("") };
|
||||||
|
ClearEffectState clearEffectState = { 0, 0, 0, {} };
|
||||||
|
FloatingTextEffect floatingTextEffects[8] = {};
|
||||||
|
ParticleEffect particleEffects[24] = {};
|
||||||
int currentScreen = SCREEN_MENU;
|
int currentScreen = SCREEN_MENU;
|
||||||
int currentMode = MODE_CLASSIC;
|
int currentMode = MODE_CLASSIC;
|
||||||
int currentFallInterval = 500;
|
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));
|
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)
|
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
@@ -741,6 +867,8 @@ int DeleteLines()
|
|||||||
{
|
{
|
||||||
int clearedLines = 0;
|
int clearedLines = 0;
|
||||||
bool clearedWithRainbow = false;
|
bool clearedWithRainbow = false;
|
||||||
|
int clearedRows[8] = {};
|
||||||
|
int clearedRowCount = 0;
|
||||||
|
|
||||||
int playableHeight = GetRoguePlayableHeight();
|
int playableHeight = GetRoguePlayableHeight();
|
||||||
for (int i = playableHeight - 1; i >= 0; i--)
|
for (int i = playableHeight - 1; i >= 0; i--)
|
||||||
@@ -758,6 +886,12 @@ int DeleteLines()
|
|||||||
|
|
||||||
if (fullLine)
|
if (fullLine)
|
||||||
{
|
{
|
||||||
|
if (clearedRowCount < 8)
|
||||||
|
{
|
||||||
|
clearedRows[clearedRowCount] = i;
|
||||||
|
clearedRowCount++;
|
||||||
|
}
|
||||||
|
|
||||||
for (int j = 0; j < nGameWidth; j++)
|
for (int j = 0; j < nGameWidth; j++)
|
||||||
{
|
{
|
||||||
if (IsRainbowBoardCell(workRegion[i][j]))
|
if (IsRainbowBoardCell(workRegion[i][j]))
|
||||||
@@ -773,6 +907,7 @@ int DeleteLines()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApplyLineClearResult(clearedLines);
|
ApplyLineClearResult(clearedLines);
|
||||||
|
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
|
|
||||||
if (pendingChainBombFollowup && clearedLines > 0)
|
if (pendingChainBombFollowup && clearedLines > 0)
|
||||||
{
|
{
|
||||||
@@ -898,6 +1033,7 @@ void Restart()
|
|||||||
feedbackState.visibleTicks = 0;
|
feedbackState.visibleTicks = 0;
|
||||||
feedbackState.title[0] = _T('\0');
|
feedbackState.title[0] = _T('\0');
|
||||||
feedbackState.detail[0] = _T('\0');
|
feedbackState.detail[0] = _T('\0');
|
||||||
|
ResetVisualEffects();
|
||||||
holdType = -1;
|
holdType = -1;
|
||||||
holdUsedThisTurn = false;
|
holdUsedThisTurn = false;
|
||||||
RollCurrentPieceSpecialFlags(false);
|
RollCurrentPieceSpecialFlags(false);
|
||||||
@@ -929,6 +1065,7 @@ void ReturnToMainMenu()
|
|||||||
currentScreen = SCREEN_MENU;
|
currentScreen = SCREEN_MENU;
|
||||||
suspendFlag = false;
|
suspendFlag = false;
|
||||||
gameOverFlag = false;
|
gameOverFlag = false;
|
||||||
|
ResetVisualEffects();
|
||||||
menuState.optionCount = 3;
|
menuState.optionCount = 3;
|
||||||
upgradeUiState.pendingCount = 0;
|
upgradeUiState.pendingCount = 0;
|
||||||
upgradeUiState.picksRemaining = 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);
|
HFONT oldFont = (HFONT)SelectObject(hdc, titleFont);
|
||||||
DrawPanelHeader(leftPanelRect, _T("战局信息"), 120);
|
DrawPanelHeader(leftPanelRect, _T("战局信息"), 120);
|
||||||
DrawPanelHeader(rightPanelRect, _T("预览与战术"), 148);
|
DrawPanelHeader(rightPanelRect, _T("预览与战术"), 148);
|
||||||
|
|||||||
Reference in New Issue
Block a user