代码结构整理
This commit is contained in:
@@ -65,6 +65,7 @@ $Sources = @(
|
|||||||
(Join-Path $SourceDir "stdafx.cpp"),
|
(Join-Path $SourceDir "stdafx.cpp"),
|
||||||
(Join-Path $SourceDir "Tetris.cpp"),
|
(Join-Path $SourceDir "Tetris.cpp"),
|
||||||
(Join-Path $SourceDir "TetrisLogic.cpp"),
|
(Join-Path $SourceDir "TetrisLogic.cpp"),
|
||||||
|
(Join-Path $SourceDir "TetrisLogicInnovation.cpp"),
|
||||||
(Join-Path $SourceDir "TetrisRogue.cpp"),
|
(Join-Path $SourceDir "TetrisRogue.cpp"),
|
||||||
(Join-Path $SourceDir "TetrisRender.cpp")
|
(Join-Path $SourceDir "TetrisRender.cpp")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,20 +4,97 @@
|
|||||||
|
|
||||||
extern Point pendingChainBombCenter;
|
extern Point pendingChainBombCenter;
|
||||||
extern bool pendingChainBombFollowup;
|
extern bool pendingChainBombFollowup;
|
||||||
|
extern int pendingLineClearEffectTicks;
|
||||||
|
extern int pendingLineClearEffectRows[8];
|
||||||
|
extern int pendingLineClearEffectRowCount;
|
||||||
|
extern int pendingLineClearEffectLineCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算指定方块在棋盘顶部的统一生成位置。
|
||||||
|
*/
|
||||||
Point GetSpawnPoint(int brickType);
|
Point GetSpawnPoint(int brickType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置经典或 Rogue 模式使用的玩家统计数据。
|
||||||
|
*/
|
||||||
void ResetPlayerStats(PlayerStats& stats, bool useRogueRules);
|
void ResetPlayerStats(PlayerStats& stats, bool useRogueRules);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置界面右侧显示的即时反馈标题、内容和持续时间。
|
||||||
|
*/
|
||||||
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks);
|
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断指定方块、旋转状态和位置是否可以合法放置。
|
||||||
|
*/
|
||||||
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position);
|
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断棋盘格是否为彩虹特殊方块。
|
||||||
|
*/
|
||||||
bool IsRainbowBoardCell(int cellValue);
|
bool IsRainbowBoardCell(int cellValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发小型黑洞并返回被清除的固定方块数量。
|
||||||
|
*/
|
||||||
int TriggerMiniBlackHole(int maxCellsToClear);
|
int TriggerMiniBlackHole(int maxCellsToClear);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发彩虹方块补洞效果并返回补齐格数。
|
||||||
|
*/
|
||||||
int TriggerRainbowRowCompletion(int minRow, int maxRow);
|
int TriggerRainbowRowCompletion(int minRow, int maxRow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 引爆清屏炸弹并返回清除格数。
|
||||||
|
*/
|
||||||
int TriggerScreenBomb();
|
int TriggerScreenBomb();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除指定中心点周围的爆破范围并返回清除格数。
|
||||||
|
*/
|
||||||
int ClearExplosiveAreaAt(int centerY, int centerX);
|
int ClearExplosiveAreaAt(int centerY, int centerX);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除指定列并返回清除格数。
|
||||||
|
*/
|
||||||
int ClearColumnAt(int column);
|
int ClearColumnAt(int column);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除指定行并返回清除格数。
|
||||||
|
*/
|
||||||
int ClearRowAt(int row);
|
int ClearRowAt(int row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 尝试填补局部空洞以稳定棋盘结构。
|
||||||
|
*/
|
||||||
int TryStabilizeBoard();
|
int TryStabilizeBoard();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 为当前方块刷新 Rogue 特殊方块标记。
|
||||||
|
*/
|
||||||
void RollCurrentPieceSpecialFlags(bool allowRandomSpecials);
|
void RollCurrentPieceSpecialFlags(bool allowRandomSpecials);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 暂存消行动画,等待升级选择结束后再播放。
|
||||||
|
*/
|
||||||
|
void QueueLineClearEffect(const int* rows, int rowCount, int linesCleared);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 尝试把旋转后的方块横向偏移指定格数后放置。
|
||||||
|
*/
|
||||||
|
bool TryRotateWithOffset(int nextState, int offsetX);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置下一方块预览队列。
|
||||||
|
*/
|
||||||
void ResetNextQueue();
|
void ResetNextQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 消费队首下一方块并补充新的预览方块。
|
||||||
|
*/
|
||||||
int ConsumeNextType();
|
int ConsumeNextType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结算一次标准消行带来的 Rogue 玩法效果。
|
||||||
|
*/
|
||||||
void ApplyLineClearResult(int linesCleared);
|
void ApplyLineClearResult(int linesCleared);
|
||||||
|
|||||||
@@ -34,10 +34,6 @@ bool currentPieceIsCross = false;
|
|||||||
bool currentPieceIsRainbow = false;
|
bool currentPieceIsRainbow = false;
|
||||||
Point pendingChainBombCenter = { 0, 0 };
|
Point pendingChainBombCenter = { 0, 0 };
|
||||||
bool pendingChainBombFollowup = false;
|
bool pendingChainBombFollowup = false;
|
||||||
static int pendingLineClearEffectTicks = 0;
|
|
||||||
static int pendingLineClearEffectRows[8] = {};
|
|
||||||
static int pendingLineClearEffectRowCount = 0;
|
|
||||||
static int pendingLineClearEffectLineCount = 0;
|
|
||||||
|
|
||||||
int bricks[7][4][4][4] =
|
int bricks[7][4][4][4] =
|
||||||
{
|
{
|
||||||
@@ -167,369 +163,6 @@ Point GetSpawnPoint(int brickType)
|
|||||||
return spawnPoint;
|
return spawnPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetPlayerStats(PlayerStats& stats, bool useRogueRules)
|
|
||||||
{
|
|
||||||
stats.score = 0;
|
|
||||||
stats.level = 1;
|
|
||||||
stats.exp = 0;
|
|
||||||
stats.requiredExp = useRogueRules ? 10 : 0;
|
|
||||||
stats.totalLinesCleared = 0;
|
|
||||||
stats.scoreMultiplierPercent = 100;
|
|
||||||
stats.expMultiplierPercent = 100;
|
|
||||||
stats.slowFallStacks = 0;
|
|
||||||
stats.comboBonusStacks = 0;
|
|
||||||
stats.comboChain = 0;
|
|
||||||
stats.previewCount = 1;
|
|
||||||
stats.lastChanceCount = 0;
|
|
||||||
stats.scoreUpgradeLevel = 0;
|
|
||||||
stats.expUpgradeLevel = 0;
|
|
||||||
stats.previewUpgradeLevel = 0;
|
|
||||||
stats.lastChanceUpgradeLevel = 0;
|
|
||||||
stats.holdUnlocked = 0;
|
|
||||||
stats.pressureReliefLevel = 0;
|
|
||||||
stats.sweeperLevel = 0;
|
|
||||||
stats.sweeperCharge = 0;
|
|
||||||
stats.explosiveLevel = 0;
|
|
||||||
stats.explosivePieceCounter = 0;
|
|
||||||
stats.chainBlastLevel = 0;
|
|
||||||
stats.chainBombLevel = 0;
|
|
||||||
stats.laserLevel = 0;
|
|
||||||
stats.thunderTetrisLevel = 0;
|
|
||||||
stats.thunderLaserLevel = 0;
|
|
||||||
stats.feverLevel = 0;
|
|
||||||
stats.rageStackLevel = 0;
|
|
||||||
stats.infiniteFeverLevel = 0;
|
|
||||||
stats.feverLineCharge = 0;
|
|
||||||
stats.feverTicks = 0;
|
|
||||||
stats.screenBombLevel = 0;
|
|
||||||
stats.screenBombCharge = 0;
|
|
||||||
stats.screenBombCount = 0;
|
|
||||||
stats.terminalClearLevel = 0;
|
|
||||||
stats.dualChoiceLevel = 0;
|
|
||||||
stats.destinyWheelLevel = 0;
|
|
||||||
stats.perfectRotateLevel = 0;
|
|
||||||
stats.timeDilationLevel = 0;
|
|
||||||
stats.timeDilationTicks = 0;
|
|
||||||
stats.highPressureLevel = 0;
|
|
||||||
stats.tetrisGambleLevel = 0;
|
|
||||||
stats.extremePlayerLevel = 0;
|
|
||||||
stats.extremeSlowTicks = 0;
|
|
||||||
stats.extremeDangerTicks = 30;
|
|
||||||
stats.extremeDangerLevel = 0;
|
|
||||||
stats.upgradeShockwaveLevel = 0;
|
|
||||||
stats.evolutionImpactLevel = 0;
|
|
||||||
stats.controlMasterLevel = 0;
|
|
||||||
stats.holdSlowTicks = 0;
|
|
||||||
stats.blockStormLevel = 0;
|
|
||||||
stats.blockStormPiecesRemaining = 0;
|
|
||||||
stats.blackHoleLevel = 0;
|
|
||||||
stats.blackHoleCharges = 0;
|
|
||||||
stats.reshapeLevel = 0;
|
|
||||||
stats.reshapeCharges = 0;
|
|
||||||
stats.rainbowPieceLevel = 0;
|
|
||||||
stats.voidCoreLevel = 0;
|
|
||||||
stats.pendingRainbowPieceCount = 0;
|
|
||||||
stats.stableStructureLevel = 0;
|
|
||||||
stats.doubleGrowthLevel = 0;
|
|
||||||
stats.gamblerLevel = 0;
|
|
||||||
stats.difficultyElapsedMs = 0;
|
|
||||||
stats.difficultyLevel = 0;
|
|
||||||
stats.lockedRows = 0;
|
|
||||||
for (int i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
stats.pieceTuningLevels[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks)
|
|
||||||
{
|
|
||||||
feedbackState.visibleTicks = ticks;
|
|
||||||
lstrcpyn(feedbackState.title, title, sizeof(feedbackState.title) / 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 < 96; 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 < 96; 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, int velocityX, int velocityY, int size, COLORREF color)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 96; i++)
|
|
||||||
{
|
|
||||||
if (particleEffects[i].ticks <= 0)
|
|
||||||
{
|
|
||||||
particleEffects[i].ticks = 12 + rand() % 7;
|
|
||||||
particleEffects[i].totalTicks = particleEffects[i].ticks;
|
|
||||||
particleEffects[i].boardX = boardX;
|
|
||||||
particleEffects[i].boardY = boardY;
|
|
||||||
particleEffects[i].velocityX = velocityX;
|
|
||||||
particleEffects[i].velocityY = velocityY;
|
|
||||||
particleEffects[i].size = size;
|
|
||||||
particleEffects[i].color = color;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AddBurstParticles(int boardX, int boardY, COLORREF baseColor, bool strongBurst)
|
|
||||||
{
|
|
||||||
int burstCount = strongBurst ? 4 : 2;
|
|
||||||
for (int i = 0; i < burstCount; i++)
|
|
||||||
{
|
|
||||||
int angleSeed = rand() % 8;
|
|
||||||
int speed = strongBurst ? (9 + rand() % 9) : (6 + rand() % 7);
|
|
||||||
int velocityX = 0;
|
|
||||||
int velocityY = 0;
|
|
||||||
|
|
||||||
switch (angleSeed)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
velocityX = speed;
|
|
||||||
velocityY = -rand() % 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
velocityX = -speed;
|
|
||||||
velocityY = -rand() % 4;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
velocityX = (rand() % 5) - 2;
|
|
||||||
velocityY = -speed;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
velocityX = (rand() % 5) - 2;
|
|
||||||
velocityY = speed / 2;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
velocityX = speed;
|
|
||||||
velocityY = -speed;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
velocityX = -speed;
|
|
||||||
velocityY = -speed;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
velocityX = speed;
|
|
||||||
velocityY = speed / 3;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
velocityX = -speed;
|
|
||||||
velocityY = speed / 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
velocityX += (rand() % 7) - 3;
|
|
||||||
velocityY += (rand() % 7) - 3;
|
|
||||||
|
|
||||||
COLORREF color = (i % 3 == 0) ? RGB(255, 248, 220) : baseColor;
|
|
||||||
AddParticle(
|
|
||||||
boardX + (rand() % 31) - 15,
|
|
||||||
boardY + (rand() % 31) - 15,
|
|
||||||
velocityX,
|
|
||||||
velocityY,
|
|
||||||
strongBurst ? (4 + rand() % 5) : (3 + rand() % 4),
|
|
||||||
color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void QueueLineClearEffect(const int* rows, int rowCount, int linesCleared)
|
|
||||||
{
|
|
||||||
if (rows == nullptr || rowCount <= 0 || linesCleared <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rowCount > 8)
|
|
||||||
{
|
|
||||||
rowCount = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingLineClearEffectTicks = 1;
|
|
||||||
pendingLineClearEffectRowCount = rowCount;
|
|
||||||
pendingLineClearEffectLineCount = linesCleared;
|
|
||||||
for (int i = 0; i < rowCount; i++)
|
|
||||||
{
|
|
||||||
pendingLineClearEffectRows[i] = rows[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlayPendingLineClearEffect()
|
|
||||||
{
|
|
||||||
if (pendingLineClearEffectTicks <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingLineClearEffectTicks = 0;
|
|
||||||
TriggerLineClearEffect(
|
|
||||||
pendingLineClearEffectRows,
|
|
||||||
pendingLineClearEffectRowCount,
|
|
||||||
pendingLineClearEffectLineCount);
|
|
||||||
pendingLineClearEffectRowCount = 0;
|
|
||||||
pendingLineClearEffectLineCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
COLORREF particleColor = BrickColor[(x + rows[i]) % 7];
|
|
||||||
int centerX = x * 100 + 50;
|
|
||||||
int centerY = rows[i] * 100 + 50;
|
|
||||||
AddBurstParticles(centerX, centerY, particleColor, linesCleared >= 4);
|
|
||||||
|
|
||||||
if (linesCleared >= 4)
|
|
||||||
{
|
|
||||||
AddParticle(
|
|
||||||
centerX,
|
|
||||||
centerY,
|
|
||||||
((x < nGameWidth / 2) ? -1 : 1) * (16 + rand() % 12),
|
|
||||||
-16 - rand() % 10,
|
|
||||||
4 + rand() % 3,
|
|
||||||
RGB(255, 238, 120));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TriggerCellClearEffect(const Point* cells, int cellCount, bool strongBurst)
|
|
||||||
{
|
|
||||||
if (cells == nullptr || cellCount <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < cellCount; i++)
|
|
||||||
{
|
|
||||||
if (cells[i].x < 0 || cells[i].x >= nGameWidth || cells[i].y < 0 || cells[i].y >= nGameHeight)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
COLORREF particleColor = BrickColor[(cells[i].x + cells[i].y) % 7];
|
|
||||||
AddBurstParticles(cells[i].x * 100 + 50, cells[i].y * 100 + 50, particleColor, strongBurst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (bricks[pieceType][pieceState][i][j] == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int checkY = position.y + i;
|
|
||||||
int checkX = position.x + j;
|
|
||||||
|
|
||||||
if (checkX < 0 || checkX >= nGameWidth || checkY >= GetRoguePlayableHeight())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkY >= 0 && workRegion[checkY][checkX] != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 判断当前方块是否可以继续向下移动。
|
* @brief 判断当前方块是否可以继续向下移动。
|
||||||
*
|
*
|
||||||
@@ -650,13 +283,6 @@ bool CanMoveRight()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TryRotateWithOffset(int nextState, int offsetX)
|
|
||||||
{
|
|
||||||
Point rotatedPoint = point;
|
|
||||||
rotatedPoint.x += offsetX;
|
|
||||||
return IsPiecePlacementValid(type, nextState, rotatedPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将当前活动方块向下移动一格。
|
* @brief 将当前活动方块向下移动一格。
|
||||||
*
|
*
|
||||||
@@ -1198,80 +824,3 @@ void Restart()
|
|||||||
ComputeTarget();
|
ComputeTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReviveAfterVideo()
|
|
||||||
{
|
|
||||||
if (!gameOverFlag || !reviveAvailable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
reviveAvailable = false;
|
|
||||||
gameOverFlag = false;
|
|
||||||
suspendFlag = false;
|
|
||||||
currentScreen = SCREEN_PLAYING;
|
|
||||||
|
|
||||||
int playableHeight = GetRoguePlayableHeight();
|
|
||||||
int rowsToClear = playableHeight / 3;
|
|
||||||
if (rowsToClear < 5)
|
|
||||||
{
|
|
||||||
rowsToClear = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = 0; y < rowsToClear && y < playableHeight; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < nGameWidth; x++)
|
|
||||||
{
|
|
||||||
workRegion[y][x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type = ConsumeNextType();
|
|
||||||
nType = nextTypes[0];
|
|
||||||
state = 0;
|
|
||||||
holdUsedThisTurn = false;
|
|
||||||
RollCurrentPieceSpecialFlags(true);
|
|
||||||
point = GetSpawnPoint(type);
|
|
||||||
target = point;
|
|
||||||
ComputeTarget();
|
|
||||||
|
|
||||||
SetFeedbackMessage(_T("复活成功"), _T("已清理顶部空间,本局复活机会已用完。"), 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartGameWithMode(int mode)
|
|
||||||
{
|
|
||||||
currentMode = mode;
|
|
||||||
currentScreen = SCREEN_PLAYING;
|
|
||||||
Restart();
|
|
||||||
currentFallInterval = (currentMode == MODE_ROGUE) ? GetRogueFallInterval() : 500;
|
|
||||||
tScore = (currentMode == MODE_CLASSIC) ? classicStats.score : rogueStats.score;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReturnToMainMenu()
|
|
||||||
{
|
|
||||||
currentScreen = SCREEN_MENU;
|
|
||||||
suspendFlag = false;
|
|
||||||
gameOverFlag = false;
|
|
||||||
ResetVisualEffects();
|
|
||||||
ResetPendingRogueVisualEvents();
|
|
||||||
pendingLineClearEffectTicks = 0;
|
|
||||||
pendingLineClearEffectRowCount = 0;
|
|
||||||
pendingLineClearEffectLineCount = 0;
|
|
||||||
menuState.optionCount = 3;
|
|
||||||
upgradeUiState.pendingCount = 0;
|
|
||||||
upgradeUiState.picksRemaining = 0;
|
|
||||||
upgradeUiState.markedCount = 0;
|
|
||||||
|
|
||||||
if (menuState.selectedIndex < 0 || menuState.selectedIndex >= menuState.optionCount)
|
|
||||||
{
|
|
||||||
menuState.selectedIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenRulesScreen()
|
|
||||||
{
|
|
||||||
currentScreen = SCREEN_RULES;
|
|
||||||
suspendFlag = false;
|
|
||||||
helpState.selectedIndex = 0;
|
|
||||||
helpState.optionCount = 3;
|
|
||||||
helpState.currentPage = 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,506 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "TetrisLogicInternal.h"
|
||||||
|
|
||||||
|
int pendingLineClearEffectTicks = 0;
|
||||||
|
int pendingLineClearEffectRows[8] = {};
|
||||||
|
int pendingLineClearEffectRowCount = 0;
|
||||||
|
int pendingLineClearEffectLineCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置经典或 Rogue 模式使用的玩家统计数据。
|
||||||
|
*/
|
||||||
|
void ResetPlayerStats(PlayerStats& stats, bool useRogueRules)
|
||||||
|
{
|
||||||
|
stats.score = 0;
|
||||||
|
stats.level = 1;
|
||||||
|
stats.exp = 0;
|
||||||
|
stats.requiredExp = useRogueRules ? 10 : 0;
|
||||||
|
stats.totalLinesCleared = 0;
|
||||||
|
stats.scoreMultiplierPercent = 100;
|
||||||
|
stats.expMultiplierPercent = 100;
|
||||||
|
stats.slowFallStacks = 0;
|
||||||
|
stats.comboBonusStacks = 0;
|
||||||
|
stats.comboChain = 0;
|
||||||
|
stats.previewCount = 1;
|
||||||
|
stats.lastChanceCount = 0;
|
||||||
|
stats.scoreUpgradeLevel = 0;
|
||||||
|
stats.expUpgradeLevel = 0;
|
||||||
|
stats.previewUpgradeLevel = 0;
|
||||||
|
stats.lastChanceUpgradeLevel = 0;
|
||||||
|
stats.holdUnlocked = 0;
|
||||||
|
stats.pressureReliefLevel = 0;
|
||||||
|
stats.sweeperLevel = 0;
|
||||||
|
stats.sweeperCharge = 0;
|
||||||
|
stats.explosiveLevel = 0;
|
||||||
|
stats.explosivePieceCounter = 0;
|
||||||
|
stats.chainBlastLevel = 0;
|
||||||
|
stats.chainBombLevel = 0;
|
||||||
|
stats.laserLevel = 0;
|
||||||
|
stats.thunderTetrisLevel = 0;
|
||||||
|
stats.thunderLaserLevel = 0;
|
||||||
|
stats.feverLevel = 0;
|
||||||
|
stats.rageStackLevel = 0;
|
||||||
|
stats.infiniteFeverLevel = 0;
|
||||||
|
stats.feverLineCharge = 0;
|
||||||
|
stats.feverTicks = 0;
|
||||||
|
stats.screenBombLevel = 0;
|
||||||
|
stats.screenBombCharge = 0;
|
||||||
|
stats.screenBombCount = 0;
|
||||||
|
stats.terminalClearLevel = 0;
|
||||||
|
stats.dualChoiceLevel = 0;
|
||||||
|
stats.destinyWheelLevel = 0;
|
||||||
|
stats.perfectRotateLevel = 0;
|
||||||
|
stats.timeDilationLevel = 0;
|
||||||
|
stats.timeDilationTicks = 0;
|
||||||
|
stats.highPressureLevel = 0;
|
||||||
|
stats.tetrisGambleLevel = 0;
|
||||||
|
stats.extremePlayerLevel = 0;
|
||||||
|
stats.extremeSlowTicks = 0;
|
||||||
|
stats.extremeDangerTicks = 30;
|
||||||
|
stats.extremeDangerLevel = 0;
|
||||||
|
stats.upgradeShockwaveLevel = 0;
|
||||||
|
stats.evolutionImpactLevel = 0;
|
||||||
|
stats.controlMasterLevel = 0;
|
||||||
|
stats.holdSlowTicks = 0;
|
||||||
|
stats.blockStormLevel = 0;
|
||||||
|
stats.blockStormPiecesRemaining = 0;
|
||||||
|
stats.blackHoleLevel = 0;
|
||||||
|
stats.blackHoleCharges = 0;
|
||||||
|
stats.reshapeLevel = 0;
|
||||||
|
stats.reshapeCharges = 0;
|
||||||
|
stats.rainbowPieceLevel = 0;
|
||||||
|
stats.voidCoreLevel = 0;
|
||||||
|
stats.pendingRainbowPieceCount = 0;
|
||||||
|
stats.stableStructureLevel = 0;
|
||||||
|
stats.doubleGrowthLevel = 0;
|
||||||
|
stats.gamblerLevel = 0;
|
||||||
|
stats.difficultyElapsedMs = 0;
|
||||||
|
stats.difficultyLevel = 0;
|
||||||
|
stats.lockedRows = 0;
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
stats.pieceTuningLevels[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置界面右侧显示的即时反馈标题、内容和持续时间。
|
||||||
|
*/
|
||||||
|
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks)
|
||||||
|
{
|
||||||
|
feedbackState.visibleTicks = ticks;
|
||||||
|
lstrcpyn(feedbackState.title, title, sizeof(feedbackState.title) / sizeof(TCHAR));
|
||||||
|
lstrcpyn(feedbackState.detail, detail, sizeof(feedbackState.detail) / sizeof(TCHAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清空所有消行、浮动文字和粒子视觉效果。
|
||||||
|
*/
|
||||||
|
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 < 96; i++)
|
||||||
|
{
|
||||||
|
particleEffects[i].ticks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 推进视觉效果计时,并返回是否仍有动画需要刷新。
|
||||||
|
*/
|
||||||
|
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 < 96; i++)
|
||||||
|
{
|
||||||
|
if (particleEffects[i].ticks > 0)
|
||||||
|
{
|
||||||
|
particleEffects[i].ticks--;
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 添加一段棋盘坐标系中的浮动文字效果。
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 添加一个棋盘坐标系中的粒子效果。
|
||||||
|
*/
|
||||||
|
static void AddParticle(int boardX, int boardY, int velocityX, int velocityY, int size, COLORREF color)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 96; i++)
|
||||||
|
{
|
||||||
|
if (particleEffects[i].ticks <= 0)
|
||||||
|
{
|
||||||
|
particleEffects[i].ticks = 12 + rand() % 7;
|
||||||
|
particleEffects[i].totalTicks = particleEffects[i].ticks;
|
||||||
|
particleEffects[i].boardX = boardX;
|
||||||
|
particleEffects[i].boardY = boardY;
|
||||||
|
particleEffects[i].velocityX = velocityX;
|
||||||
|
particleEffects[i].velocityY = velocityY;
|
||||||
|
particleEffects[i].size = size;
|
||||||
|
particleEffects[i].color = color;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在指定棋盘坐标周围生成一组爆裂粒子。
|
||||||
|
*/
|
||||||
|
static void AddBurstParticles(int boardX, int boardY, COLORREF baseColor, bool strongBurst)
|
||||||
|
{
|
||||||
|
int burstCount = strongBurst ? 4 : 2;
|
||||||
|
for (int i = 0; i < burstCount; i++)
|
||||||
|
{
|
||||||
|
int angleSeed = rand() % 8;
|
||||||
|
int speed = strongBurst ? (9 + rand() % 9) : (6 + rand() % 7);
|
||||||
|
int velocityX = 0;
|
||||||
|
int velocityY = 0;
|
||||||
|
|
||||||
|
switch (angleSeed)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
velocityX = speed;
|
||||||
|
velocityY = -rand() % 4;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
velocityX = -speed;
|
||||||
|
velocityY = -rand() % 4;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
velocityX = (rand() % 5) - 2;
|
||||||
|
velocityY = -speed;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
velocityX = (rand() % 5) - 2;
|
||||||
|
velocityY = speed / 2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
velocityX = speed;
|
||||||
|
velocityY = -speed;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
velocityX = -speed;
|
||||||
|
velocityY = -speed;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
velocityX = speed;
|
||||||
|
velocityY = speed / 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
velocityX = -speed;
|
||||||
|
velocityY = speed / 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
velocityX += (rand() % 7) - 3;
|
||||||
|
velocityY += (rand() % 7) - 3;
|
||||||
|
|
||||||
|
COLORREF color = (i % 3 == 0) ? RGB(255, 248, 220) : baseColor;
|
||||||
|
AddParticle(
|
||||||
|
boardX + (rand() % 31) - 15,
|
||||||
|
boardY + (rand() % 31) - 15,
|
||||||
|
velocityX,
|
||||||
|
velocityY,
|
||||||
|
strongBurst ? (4 + rand() % 5) : (3 + rand() % 4),
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 暂存消行动画,等待升级选择结束后再播放。
|
||||||
|
*/
|
||||||
|
void QueueLineClearEffect(const int* rows, int rowCount, int linesCleared)
|
||||||
|
{
|
||||||
|
if (rows == nullptr || rowCount <= 0 || linesCleared <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowCount > 8)
|
||||||
|
{
|
||||||
|
rowCount = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingLineClearEffectTicks = 1;
|
||||||
|
pendingLineClearEffectRowCount = rowCount;
|
||||||
|
pendingLineClearEffectLineCount = linesCleared;
|
||||||
|
for (int i = 0; i < rowCount; i++)
|
||||||
|
{
|
||||||
|
pendingLineClearEffectRows[i] = rows[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 播放之前暂存的消行动画。
|
||||||
|
*/
|
||||||
|
void PlayPendingLineClearEffect()
|
||||||
|
{
|
||||||
|
if (pendingLineClearEffectTicks <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingLineClearEffectTicks = 0;
|
||||||
|
TriggerLineClearEffect(
|
||||||
|
pendingLineClearEffectRows,
|
||||||
|
pendingLineClearEffectRowCount,
|
||||||
|
pendingLineClearEffectLineCount);
|
||||||
|
pendingLineClearEffectRowCount = 0;
|
||||||
|
pendingLineClearEffectLineCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发标准消行动画和浮动文字。
|
||||||
|
*/
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
COLORREF particleColor = BrickColor[(x + rows[i]) % 7];
|
||||||
|
int centerX = x * 100 + 50;
|
||||||
|
int centerY = rows[i] * 100 + 50;
|
||||||
|
AddBurstParticles(centerX, centerY, particleColor, linesCleared >= 4);
|
||||||
|
|
||||||
|
if (linesCleared >= 4)
|
||||||
|
{
|
||||||
|
AddParticle(
|
||||||
|
centerX,
|
||||||
|
centerY,
|
||||||
|
((x < nGameWidth / 2) ? -1 : 1) * (16 + rand() % 12),
|
||||||
|
-16 - rand() % 10,
|
||||||
|
4 + rand() % 3,
|
||||||
|
RGB(255, 238, 120));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 为指定棋盘格集合触发清除粒子效果。
|
||||||
|
*/
|
||||||
|
void TriggerCellClearEffect(const Point* cells, int cellCount, bool strongBurst)
|
||||||
|
{
|
||||||
|
if (cells == nullptr || cellCount <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cellCount; i++)
|
||||||
|
{
|
||||||
|
if (cells[i].x < 0 || cells[i].x >= nGameWidth || cells[i].y < 0 || cells[i].y >= nGameHeight)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
COLORREF particleColor = BrickColor[(cells[i].x + cells[i].y) % 7];
|
||||||
|
AddBurstParticles(cells[i].x * 100 + 50, cells[i].y * 100 + 50, particleColor, strongBurst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断指定方块、旋转状态和位置是否可以合法放置。
|
||||||
|
*/
|
||||||
|
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if (bricks[pieceType][pieceState][i][j] == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int checkY = position.y + i;
|
||||||
|
int checkX = position.x + j;
|
||||||
|
|
||||||
|
if (checkX < 0 || checkX >= nGameWidth || checkY >= GetRoguePlayableHeight())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkY >= 0 && workRegion[checkY][checkX] != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 尝试把旋转后的方块横向偏移指定格数后放置。
|
||||||
|
*/
|
||||||
|
bool TryRotateWithOffset(int nextState, int offsetX)
|
||||||
|
{
|
||||||
|
Point rotatedPoint = point;
|
||||||
|
rotatedPoint.x += offsetX;
|
||||||
|
return IsPiecePlacementValid(type, nextState, rotatedPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 视频复活后清理顶部空间并恢复一局游戏。
|
||||||
|
*/
|
||||||
|
void ReviveAfterVideo()
|
||||||
|
{
|
||||||
|
if (!gameOverFlag || !reviveAvailable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reviveAvailable = false;
|
||||||
|
gameOverFlag = false;
|
||||||
|
suspendFlag = false;
|
||||||
|
currentScreen = SCREEN_PLAYING;
|
||||||
|
|
||||||
|
int playableHeight = GetRoguePlayableHeight();
|
||||||
|
int rowsToClear = playableHeight / 3;
|
||||||
|
if (rowsToClear < 5)
|
||||||
|
{
|
||||||
|
rowsToClear = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < rowsToClear && y < playableHeight; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < nGameWidth; x++)
|
||||||
|
{
|
||||||
|
workRegion[y][x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type = ConsumeNextType();
|
||||||
|
nType = nextTypes[0];
|
||||||
|
state = 0;
|
||||||
|
holdUsedThisTurn = false;
|
||||||
|
RollCurrentPieceSpecialFlags(true);
|
||||||
|
point = GetSpawnPoint(type);
|
||||||
|
target = point;
|
||||||
|
ComputeTarget();
|
||||||
|
|
||||||
|
SetFeedbackMessage(_T("复活成功"), _T("已清理顶部空间,本局复活机会已用完。"), 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按指定模式开始新游戏。
|
||||||
|
*/
|
||||||
|
void StartGameWithMode(int mode)
|
||||||
|
{
|
||||||
|
currentMode = mode;
|
||||||
|
currentScreen = SCREEN_PLAYING;
|
||||||
|
Restart();
|
||||||
|
currentFallInterval = (currentMode == MODE_ROGUE) ? GetRogueFallInterval() : 500;
|
||||||
|
tScore = (currentMode == MODE_CLASSIC) ? classicStats.score : rogueStats.score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回主菜单并清理游戏中的临时界面状态。
|
||||||
|
*/
|
||||||
|
void ReturnToMainMenu()
|
||||||
|
{
|
||||||
|
currentScreen = SCREEN_MENU;
|
||||||
|
suspendFlag = false;
|
||||||
|
gameOverFlag = false;
|
||||||
|
ResetVisualEffects();
|
||||||
|
ResetPendingRogueVisualEvents();
|
||||||
|
pendingLineClearEffectTicks = 0;
|
||||||
|
pendingLineClearEffectRowCount = 0;
|
||||||
|
pendingLineClearEffectLineCount = 0;
|
||||||
|
menuState.optionCount = 3;
|
||||||
|
upgradeUiState.pendingCount = 0;
|
||||||
|
upgradeUiState.picksRemaining = 0;
|
||||||
|
upgradeUiState.markedCount = 0;
|
||||||
|
|
||||||
|
if (menuState.selectedIndex < 0 || menuState.selectedIndex >= menuState.optionCount)
|
||||||
|
{
|
||||||
|
menuState.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 打开规则说明界面并重置说明页状态。
|
||||||
|
*/
|
||||||
|
void OpenRulesScreen()
|
||||||
|
{
|
||||||
|
currentScreen = SCREEN_RULES;
|
||||||
|
suspendFlag = false;
|
||||||
|
helpState.selectedIndex = 0;
|
||||||
|
helpState.optionCount = 3;
|
||||||
|
helpState.currentPage = 0;
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "TetrisLogicInternal.h"
|
#include "TetrisLogicInternal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rogue 创新玩法独立实现文件。
|
||||||
|
*
|
||||||
|
* 本文件集中放置从基础俄罗斯方块版本扩展出的创新设计,包括强化池、
|
||||||
|
* 等级成长、特殊方块、主动技能、难度收缩、连锁奖励和升级选择流程。
|
||||||
|
* 代码保持过程式函数组织,不使用 class、继承或多态。
|
||||||
|
*/
|
||||||
|
|
||||||
enum UpgradeId
|
enum UpgradeId
|
||||||
{
|
{
|
||||||
UPGRADE_SCORE_MULTIPLIER = 0,
|
UPGRADE_SCORE_MULTIPLIER = 0,
|
||||||
@@ -127,6 +135,9 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
|
|||||||
static void ApplyDestinyCurse();
|
static void ApplyDestinyCurse();
|
||||||
static void ClearLockedRows();
|
static void ClearLockedRows();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 限制 Rogue 模式的下一方块预览数量。
|
||||||
|
*/
|
||||||
static int GetNextPreviewLimit()
|
static int GetNextPreviewLimit()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE)
|
if (currentMode != MODE_ROGUE)
|
||||||
@@ -147,6 +158,9 @@ static int GetNextPreviewLimit()
|
|||||||
return rogueStats.previewCount;
|
return rogueStats.previewCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Rogue 难度系统当前封锁的底部行数。
|
||||||
|
*/
|
||||||
int GetRogueLockedRows()
|
int GetRogueLockedRows()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE)
|
if (currentMode != MODE_ROGUE)
|
||||||
@@ -162,11 +176,17 @@ int GetRogueLockedRows()
|
|||||||
return rogueStats.lockedRows > kMaxRogueLockedRows ? kMaxRogueLockedRows : rogueStats.lockedRows;
|
return rogueStats.lockedRows > kMaxRogueLockedRows ? kMaxRogueLockedRows : rogueStats.lockedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算当前模式下棋盘可操作区域的高度。
|
||||||
|
*/
|
||||||
int GetRoguePlayableHeight()
|
int GetRoguePlayableHeight()
|
||||||
{
|
{
|
||||||
return nGameHeight - GetRogueLockedRows();
|
return nGameHeight - GetRogueLockedRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清空 Rogue 模式中被难度系统封锁的底部区域。
|
||||||
|
*/
|
||||||
static void ClearLockedRows()
|
static void ClearLockedRows()
|
||||||
{
|
{
|
||||||
int lockedRows = GetRogueLockedRows();
|
int lockedRows = GetRogueLockedRows();
|
||||||
@@ -184,6 +204,9 @@ static void ClearLockedRows()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据经过时间推进 Rogue 难度、封锁行数和下落速度。
|
||||||
|
*/
|
||||||
void AdvanceRogueDifficulty(int elapsedMs)
|
void AdvanceRogueDifficulty(int elapsedMs)
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag || elapsedMs <= 0)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag || elapsedMs <= 0)
|
||||||
@@ -232,6 +255,9 @@ void AdvanceRogueDifficulty(int elapsedMs)
|
|||||||
SetFeedbackMessage(lockedRowsChanged ? _T("战场收缩") : _T("压力升高"), difficultyDetail, 12);
|
SetFeedbackMessage(lockedRowsChanged ? _T("战场收缩") : _T("压力升高"), difficultyDetail, 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 读取指定强化在当前 Rogue 属性中的等级或拥有状态。
|
||||||
|
*/
|
||||||
static int GetUpgradeCurrentLevel(int upgradeId)
|
static int GetUpgradeCurrentLevel(int upgradeId)
|
||||||
{
|
{
|
||||||
switch (upgradeId)
|
switch (upgradeId)
|
||||||
@@ -319,6 +345,9 @@ static int GetUpgradeCurrentLevel(int upgradeId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 返回进化类强化在界面中展示的合成路线说明。
|
||||||
|
*/
|
||||||
const TCHAR* GetUpgradeSynthesisPath(int upgradeId)
|
const TCHAR* GetUpgradeSynthesisPath(int upgradeId)
|
||||||
{
|
{
|
||||||
switch (upgradeId)
|
switch (upgradeId)
|
||||||
@@ -346,6 +375,9 @@ const TCHAR* GetUpgradeSynthesisPath(int upgradeId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取指定强化的基础稀有度分类。
|
||||||
|
*/
|
||||||
static int GetUpgradeBaseRarity(int upgradeId)
|
static int GetUpgradeBaseRarity(int upgradeId)
|
||||||
{
|
{
|
||||||
switch (upgradeId)
|
switch (upgradeId)
|
||||||
@@ -390,6 +422,9 @@ static int GetUpgradeBaseRarity(int upgradeId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断指定强化是否已被更高阶合成强化替代。
|
||||||
|
*/
|
||||||
static bool IsUpgradePrerequisiteConsumed(int upgradeId)
|
static bool IsUpgradePrerequisiteConsumed(int upgradeId)
|
||||||
{
|
{
|
||||||
switch (upgradeId)
|
switch (upgradeId)
|
||||||
@@ -425,6 +460,9 @@ static bool IsUpgradePrerequisiteConsumed(int upgradeId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据当前局势动态计算强化选项的抽取权重。
|
||||||
|
*/
|
||||||
static int GetUpgradeDynamicWeight(const UpgradeEntry& entry)
|
static int GetUpgradeDynamicWeight(const UpgradeEntry& entry)
|
||||||
{
|
{
|
||||||
int weight = entry.baseWeight;
|
int weight = entry.baseWeight;
|
||||||
@@ -484,6 +522,9 @@ static int GetUpgradeDynamicWeight(const UpgradeEntry& entry)
|
|||||||
return weight < 1 ? 1 : weight;
|
return weight < 1 ? 1 : weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 把方块类型编号转换为界面展示用的短名称。
|
||||||
|
*/
|
||||||
static const TCHAR* GetPieceShortName(int pieceType)
|
static const TCHAR* GetPieceShortName(int pieceType)
|
||||||
{
|
{
|
||||||
static const TCHAR* kNames[7] =
|
static const TCHAR* kNames[7] =
|
||||||
@@ -499,6 +540,9 @@ static const TCHAR* GetPieceShortName(int pieceType)
|
|||||||
return kNames[pieceType];
|
return kNames[pieceType];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断强化是否满足当前等级、前置和互斥条件。
|
||||||
|
*/
|
||||||
static bool IsUpgradeSelectable(const UpgradeEntry& entry)
|
static bool IsUpgradeSelectable(const UpgradeEntry& entry)
|
||||||
{
|
{
|
||||||
if (IsUpgradePrerequisiteConsumed(entry.id))
|
if (IsUpgradePrerequisiteConsumed(entry.id))
|
||||||
@@ -581,6 +625,9 @@ static bool IsUpgradeSelectable(const UpgradeEntry& entry)
|
|||||||
return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel;
|
return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 查找棋盘中最高的已占用行,用于评估局势压力。
|
||||||
|
*/
|
||||||
static int GetTopOccupiedRow()
|
static int GetTopOccupiedRow()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < GetRoguePlayableHeight(); i++)
|
for (int i = 0; i < GetRoguePlayableHeight(); i++)
|
||||||
@@ -597,6 +644,9 @@ static int GetTopOccupiedRow()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算底线清道夫触发一次自动清底需要的消行充能。
|
||||||
|
*/
|
||||||
static int GetSweeperThreshold()
|
static int GetSweeperThreshold()
|
||||||
{
|
{
|
||||||
int reduction = (rogueStats.sweeperLevel - 1);
|
int reduction = (rogueStats.sweeperLevel - 1);
|
||||||
@@ -604,6 +654,9 @@ static int GetSweeperThreshold()
|
|||||||
return threshold < 2 ? 2 : threshold;
|
return threshold < 2 ? 2 : threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据爆破强化等级随机判定当前方块是否获得爆破特性。
|
||||||
|
*/
|
||||||
static bool RollExplosivePiece()
|
static bool RollExplosivePiece()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.explosiveLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.explosiveLevel <= 0)
|
||||||
@@ -636,6 +689,9 @@ static bool RollExplosivePiece()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据激光强化等级随机判定当前方块是否获得激光特性。
|
||||||
|
*/
|
||||||
static bool RollLaserPiece()
|
static bool RollLaserPiece()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.laserLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.laserLevel <= 0)
|
||||||
@@ -661,6 +717,9 @@ static bool RollLaserPiece()
|
|||||||
return (rand() % 100) < chancePercent;
|
return (rand() % 100) < chancePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据十字强化等级随机判定当前方块是否获得十字清除特性。
|
||||||
|
*/
|
||||||
static bool RollCrossPiece()
|
static bool RollCrossPiece()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.crossPieceLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.crossPieceLevel <= 0)
|
||||||
@@ -686,6 +745,9 @@ static bool RollCrossPiece()
|
|||||||
return (rand() % 100) < chancePercent;
|
return (rand() % 100) < chancePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据彩虹强化等级随机判定当前方块是否获得彩虹补洞特性。
|
||||||
|
*/
|
||||||
static bool RollRainbowPiece()
|
static bool RollRainbowPiece()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.rainbowPieceLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.rainbowPieceLevel <= 0)
|
||||||
@@ -701,11 +763,17 @@ static bool RollRainbowPiece()
|
|||||||
return (rand() % 100) < chancePercent;
|
return (rand() % 100) < chancePercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断棋盘格是否属于彩虹方块固定后的特殊格。
|
||||||
|
*/
|
||||||
bool IsRainbowBoardCell(int cellValue)
|
bool IsRainbowBoardCell(int cellValue)
|
||||||
{
|
{
|
||||||
return cellValue == 8;
|
return cellValue == 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发小型黑洞,随机吞噬限定数量的固定方块。
|
||||||
|
*/
|
||||||
int TriggerMiniBlackHole(int maxCellsToClear)
|
int TriggerMiniBlackHole(int maxCellsToClear)
|
||||||
{
|
{
|
||||||
int blockCounts[8] = { 0 };
|
int blockCounts[8] = { 0 };
|
||||||
@@ -761,6 +829,9 @@ int TriggerMiniBlackHole(int maxCellsToClear)
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 为当前活动方块刷新爆破、激光、十字和彩虹等特殊标记。
|
||||||
|
*/
|
||||||
void RollCurrentPieceSpecialFlags(bool allowRandomSpecials)
|
void RollCurrentPieceSpecialFlags(bool allowRandomSpecials)
|
||||||
{
|
{
|
||||||
if (!allowRandomSpecials)
|
if (!allowRandomSpecials)
|
||||||
@@ -788,6 +859,9 @@ void RollCurrentPieceSpecialFlags(bool allowRandomSpecials)
|
|||||||
currentPieceIsRainbow = !currentPieceIsExplosive && !currentPieceIsLaser && !currentPieceIsCross && RollRainbowPiece();
|
currentPieceIsRainbow = !currentPieceIsExplosive && !currentPieceIsLaser && !currentPieceIsCross && RollRainbowPiece();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 以指定棋盘格为中心清除爆破范围内的固定方块。
|
||||||
|
*/
|
||||||
int ClearExplosiveAreaAt(int centerY, int centerX)
|
int ClearExplosiveAreaAt(int centerY, int centerX)
|
||||||
{
|
{
|
||||||
int radius = (currentMode == MODE_ROGUE && rogueStats.chainBombLevel > 0) ? 2 : 1;
|
int radius = (currentMode == MODE_ROGUE && rogueStats.chainBombLevel > 0) ? 2 : 1;
|
||||||
@@ -818,6 +892,9 @@ int ClearExplosiveAreaAt(int centerY, int centerX)
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除指定列中的所有固定方块并返回清除数量。
|
||||||
|
*/
|
||||||
int ClearColumnAt(int column)
|
int ClearColumnAt(int column)
|
||||||
{
|
{
|
||||||
int clearedCellCount = 0;
|
int clearedCellCount = 0;
|
||||||
@@ -846,6 +923,9 @@ int ClearColumnAt(int column)
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除指定行中的所有固定方块并返回清除数量。
|
||||||
|
*/
|
||||||
int ClearRowAt(int row)
|
int ClearRowAt(int row)
|
||||||
{
|
{
|
||||||
int clearedCellCount = 0;
|
int clearedCellCount = 0;
|
||||||
@@ -874,6 +954,9 @@ int ClearRowAt(int row)
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 让彩虹方块尝试补齐其覆盖行内的空缺格。
|
||||||
|
*/
|
||||||
int TriggerRainbowRowCompletion(int minRow, int maxRow)
|
int TriggerRainbowRowCompletion(int minRow, int maxRow)
|
||||||
{
|
{
|
||||||
int filledCellCount = 0;
|
int filledCellCount = 0;
|
||||||
@@ -916,6 +999,9 @@ int TriggerRainbowRowCompletion(int minRow, int maxRow)
|
|||||||
return filledCellCount;
|
return filledCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发黑洞,吞噬棋盘中数量最多的一类固定方块。
|
||||||
|
*/
|
||||||
static int TriggerBlackHole()
|
static int TriggerBlackHole()
|
||||||
{
|
{
|
||||||
int blockCounts[8] = { 0 };
|
int blockCounts[8] = { 0 };
|
||||||
@@ -970,6 +1056,9 @@ static int TriggerBlackHole()
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 引爆清屏炸弹,清除当前可玩区域底部多行方块。
|
||||||
|
*/
|
||||||
int TriggerScreenBomb()
|
int TriggerScreenBomb()
|
||||||
{
|
{
|
||||||
int clearedCellCount = 0;
|
int clearedCellCount = 0;
|
||||||
@@ -1003,6 +1092,9 @@ int TriggerScreenBomb()
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 围绕消行位置触发连锁火花,随机破坏附近固定方块。
|
||||||
|
*/
|
||||||
static int TriggerChainBlast(int lineAnchor)
|
static int TriggerChainBlast(int lineAnchor)
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.chainBlastLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.chainBlastLevel <= 0)
|
||||||
@@ -1044,6 +1136,9 @@ static int TriggerChainBlast(int lineAnchor)
|
|||||||
return clearedCellCount;
|
return clearedCellCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据 Rogue 强化和权重系统随机生成下一个方块类型。
|
||||||
|
*/
|
||||||
static int RollNextPieceType()
|
static int RollNextPieceType()
|
||||||
{
|
{
|
||||||
if (currentMode == MODE_ROGUE && rogueStats.blockStormPiecesRemaining > 0)
|
if (currentMode == MODE_ROGUE && rogueStats.blockStormPiecesRemaining > 0)
|
||||||
@@ -1082,6 +1177,9 @@ static int RollNextPieceType()
|
|||||||
return rand() % 7;
|
return rand() % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 尝试用稳定结构强化填补局部空洞并返回填补数量。
|
||||||
|
*/
|
||||||
int TryStabilizeBoard()
|
int TryStabilizeBoard()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || rogueStats.stableStructureLevel <= 0)
|
if (currentMode != MODE_ROGUE || rogueStats.stableStructureLevel <= 0)
|
||||||
@@ -1129,6 +1227,9 @@ int TryStabilizeBoard()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置下一方块队列并按当前预览上限填充。
|
||||||
|
*/
|
||||||
void ResetNextQueue()
|
void ResetNextQueue()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
@@ -1137,6 +1238,9 @@ void ResetNextQueue()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 取出队首方块并补充新的下一方块。
|
||||||
|
*/
|
||||||
int ConsumeNextType()
|
int ConsumeNextType()
|
||||||
{
|
{
|
||||||
int nextType = nextTypes[0];
|
int nextType = nextTypes[0];
|
||||||
@@ -1146,6 +1250,9 @@ int ConsumeNextType()
|
|||||||
return nextType;
|
return nextType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按 Rogue 规则计算指定消行数对应的基础得分。
|
||||||
|
*/
|
||||||
static int GetRogueScoreByLines(int linesCleared)
|
static int GetRogueScoreByLines(int linesCleared)
|
||||||
{
|
{
|
||||||
switch (linesCleared)
|
switch (linesCleared)
|
||||||
@@ -1158,6 +1265,9 @@ static int GetRogueScoreByLines(int linesCleared)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按 Rogue 规则计算指定消行数对应的基础经验。
|
||||||
|
*/
|
||||||
static int GetRogueExpByLines(int linesCleared)
|
static int GetRogueExpByLines(int linesCleared)
|
||||||
{
|
{
|
||||||
switch (linesCleared)
|
switch (linesCleared)
|
||||||
@@ -1170,6 +1280,9 @@ static int GetRogueExpByLines(int linesCleared)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结算经验条并返回本次连续升级次数。
|
||||||
|
*/
|
||||||
static int ApplyLevelProgress(PlayerStats& stats)
|
static int ApplyLevelProgress(PlayerStats& stats)
|
||||||
{
|
{
|
||||||
int levelUps = 0;
|
int levelUps = 0;
|
||||||
@@ -1185,6 +1298,9 @@ static int ApplyLevelProgress(PlayerStats& stats)
|
|||||||
return levelUps;
|
return levelUps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 触发升级冲击波,清除底部指定数量的行。
|
||||||
|
*/
|
||||||
static int TriggerUpgradeShockwave(int rowsToClear)
|
static int TriggerUpgradeShockwave(int rowsToClear)
|
||||||
{
|
{
|
||||||
int clearedRows = 0;
|
int clearedRows = 0;
|
||||||
@@ -1198,6 +1314,9 @@ static int TriggerUpgradeShockwave(int rowsToClear)
|
|||||||
return clearedRows;
|
return clearedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按权重和当前局势生成升级菜单中的候选强化。
|
||||||
|
*/
|
||||||
static void FillUpgradeOptions()
|
static void FillUpgradeOptions()
|
||||||
{
|
{
|
||||||
int selectableIndexes[kUpgradePoolSize] = { 0 };
|
int selectableIndexes[kUpgradePoolSize] = { 0 };
|
||||||
@@ -1289,6 +1408,9 @@ static void FillUpgradeOptions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 综合难度、强化和临时状态计算 Rogue 模式下落间隔。
|
||||||
|
*/
|
||||||
int GetRogueFallInterval()
|
int GetRogueFallInterval()
|
||||||
{
|
{
|
||||||
int baseInterval = 500 + rogueStats.slowFallStacks * 80;
|
int baseInterval = 500 + rogueStats.slowFallStacks * 80;
|
||||||
@@ -1337,6 +1459,9 @@ int GetRogueFallInterval()
|
|||||||
return baseInterval;
|
return baseInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据强化编号把对应效果写入 Rogue 属性。
|
||||||
|
*/
|
||||||
static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
|
static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
|
||||||
{
|
{
|
||||||
if (applyCount <= 0)
|
if (applyCount <= 0)
|
||||||
@@ -1518,6 +1643,9 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 为技能清除的格子结算得分和经验奖励。
|
||||||
|
*/
|
||||||
void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain, bool allowLevelProgress)
|
void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain, bool allowLevelProgress)
|
||||||
{
|
{
|
||||||
scoreGain = 0;
|
scoreGain = 0;
|
||||||
@@ -1577,6 +1705,9 @@ void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查 Rogue 经验是否升级,并在需要时打开强化选择界面。
|
||||||
|
*/
|
||||||
void CheckRogueLevelProgress()
|
void CheckRogueLevelProgress()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING)
|
||||||
@@ -1624,6 +1755,9 @@ void CheckRogueLevelProgress()
|
|||||||
OpenUpgradeMenu();
|
OpenUpgradeMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 对棋盘固定方块应用重力,使悬空方块自然下落。
|
||||||
|
*/
|
||||||
void ApplyBoardGravity()
|
void ApplyBoardGravity()
|
||||||
{
|
{
|
||||||
int playableHeight = GetRoguePlayableHeight();
|
int playableHeight = GetRoguePlayableHeight();
|
||||||
@@ -1648,6 +1782,9 @@ void ApplyBoardGravity()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结算一次标准消行带来的 Rogue 得分、经验、连击和派生效果。
|
||||||
|
*/
|
||||||
void ApplyLineClearResult(int linesCleared)
|
void ApplyLineClearResult(int linesCleared)
|
||||||
{
|
{
|
||||||
if (linesCleared <= 0)
|
if (linesCleared <= 0)
|
||||||
@@ -1929,6 +2066,9 @@ void ApplyLineClearResult(int linesCleared)
|
|||||||
currentFallInterval = GetRogueFallInterval();
|
currentFallInterval = GetRogueFallInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用命运轮盘的诅咒,提高下一次升级所需经验。
|
||||||
|
*/
|
||||||
static void ApplyDestinyCurse()
|
static void ApplyDestinyCurse()
|
||||||
{
|
{
|
||||||
rogueStats.requiredExp = rogueStats.requiredExp * 125 / 100;
|
rogueStats.requiredExp = rogueStats.requiredExp * 125 / 100;
|
||||||
@@ -1938,6 +2078,9 @@ static void ApplyDestinyCurse()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在升级选择结束后处理延迟触发的升级冲击波。
|
||||||
|
*/
|
||||||
static void ResolvePendingUpgradeShockwave()
|
static void ResolvePendingUpgradeShockwave()
|
||||||
{
|
{
|
||||||
if (pendingUpgradeShockwaveRows <= 0)
|
if (pendingUpgradeShockwaveRows <= 0)
|
||||||
@@ -1984,12 +2127,18 @@ static void ResolvePendingUpgradeShockwave()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 重置 Rogue 模式中等待播放的特殊视觉事件。
|
||||||
|
*/
|
||||||
void ResetPendingRogueVisualEvents()
|
void ResetPendingRogueVisualEvents()
|
||||||
{
|
{
|
||||||
pendingUpgradeShockwaveRows = 0;
|
pendingUpgradeShockwaveRows = 0;
|
||||||
pendingEvolutionImpactShockwave = false;
|
pendingEvolutionImpactShockwave = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在 Rogue 模式中打开升级强化选择界面。
|
||||||
|
*/
|
||||||
void OpenUpgradeMenu()
|
void OpenUpgradeMenu()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || upgradeUiState.pendingCount <= 0)
|
if (currentMode != MODE_ROGUE || upgradeUiState.pendingCount <= 0)
|
||||||
@@ -2001,6 +2150,9 @@ void OpenUpgradeMenu()
|
|||||||
currentScreen = SCREEN_UPGRADE;
|
currentScreen = SCREEN_UPGRADE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 确认升级菜单中的选择并应用对应强化效果。
|
||||||
|
*/
|
||||||
void ConfirmUpgradeSelection()
|
void ConfirmUpgradeSelection()
|
||||||
{
|
{
|
||||||
if (currentScreen != SCREEN_UPGRADE || upgradeUiState.optionCount <= 0)
|
if (currentScreen != SCREEN_UPGRADE || upgradeUiState.optionCount <= 0)
|
||||||
@@ -2205,6 +2357,9 @@ void ConfirmUpgradeSelection()
|
|||||||
PlayPendingLineClearEffect();
|
PlayPendingLineClearEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 执行 Hold 操作,在备用仓与当前方块之间交换。
|
||||||
|
*/
|
||||||
void HoldCurrentPiece()
|
void HoldCurrentPiece()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
||||||
@@ -2266,6 +2421,9 @@ void HoldCurrentPiece()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 响应玩家主动使用清屏炸弹的操作。
|
||||||
|
*/
|
||||||
void UseScreenBomb()
|
void UseScreenBomb()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
||||||
@@ -2303,6 +2461,9 @@ void UseScreenBomb()
|
|||||||
ComputeTarget();
|
ComputeTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 响应玩家主动释放黑洞的操作。
|
||||||
|
*/
|
||||||
void UseBlackHole()
|
void UseBlackHole()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
||||||
@@ -2364,6 +2525,9 @@ void UseBlackHole()
|
|||||||
ComputeTarget();
|
ComputeTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 响应玩家主动使用空中换形,将当前方块重塑为 I 块。
|
||||||
|
*/
|
||||||
void UseAirReshape()
|
void UseAirReshape()
|
||||||
{
|
{
|
||||||
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag)
|
||||||
|
|||||||
Reference in New Issue
Block a user