再次整理文件结构
This commit is contained in:
@@ -329,6 +329,12 @@ void DeleteOneLine(int number);
|
|||||||
*/
|
*/
|
||||||
int DeleteLines();
|
int DeleteLines();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断当前游戏是否已经结束。
|
||||||
|
* @return 游戏结束返回 true,否则返回 false。
|
||||||
|
*/
|
||||||
|
bool GameOver();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算当前活动方块的预测落点。
|
* @brief 计算当前活动方块的预测落点。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -21,6 +21,55 @@ extern int pendingLineClearEffectLineCount;
|
|||||||
*/
|
*/
|
||||||
Point GetSpawnPoint(int brickType);
|
Point GetSpawnPoint(int brickType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 收集当前方块将要固定到棋盘上的格子,并写入工作区。
|
||||||
|
* @param overflowTop 返回是否有方块格位于可视区域顶部之外。
|
||||||
|
* @param fixedCells 返回普通落地格数组。
|
||||||
|
* @param fixedCellCount 返回普通落地格数量。
|
||||||
|
* @param explosiveCells 返回爆破方块落地格数组。
|
||||||
|
* @param explosiveCellCount 返回爆破格数量。
|
||||||
|
*/
|
||||||
|
void CollectAndWriteFixedCells(
|
||||||
|
bool& overflowTop,
|
||||||
|
Point fixedCells[],
|
||||||
|
int& fixedCellCount,
|
||||||
|
Point explosiveCells[],
|
||||||
|
int& explosiveCellCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理方块固定时的顶部溢出、终末清场和最后一搏。
|
||||||
|
* @param overflowTop 是否出现顶部溢出。
|
||||||
|
* @return 游戏可以继续返回 true,需要结束返回 false。
|
||||||
|
*/
|
||||||
|
bool ResolveFixingOverflow(bool overflowTop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 生成下一枚活动方块,并刷新 Hold、特殊方块和预测落点状态。
|
||||||
|
*/
|
||||||
|
void SpawnNextFallingPiece();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从底向上扫描满行并删除,记录本次消除的原始行号。
|
||||||
|
* @param clearedRows 返回最多 8 个被消除行号。
|
||||||
|
* @param clearedRowCount 返回记录的行号数量。
|
||||||
|
* @return 本次标准消行数量。
|
||||||
|
*/
|
||||||
|
int ScanAndDeleteFullLines(int clearedRows[], int& clearedRowCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据当前界面状态立即播放或暂存消行动画。
|
||||||
|
* @param clearedRows 已消除行号数组。
|
||||||
|
* @param clearedRowCount 行号数量。
|
||||||
|
* @param clearedLines 本次消行数量。
|
||||||
|
*/
|
||||||
|
void DispatchLineClearEffect(const int clearedRows[], int clearedRowCount, int clearedLines);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理连环炸弹因消行触发的一次追加爆破。
|
||||||
|
* @param clearedLines 本次标准消行数量。
|
||||||
|
*/
|
||||||
|
void ResolveChainBombFollowup(int clearedLines);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重置经典或 Rogue 模式使用的玩家统计数据。
|
* @brief 重置经典或 Rogue 模式使用的玩家统计数据。
|
||||||
* @param stats 需要重置的统计结构。
|
* @param stats 需要重置的统计结构。
|
||||||
|
|||||||
@@ -21,3 +21,10 @@ Gdiplus::Bitmap* LoadBackgroundImage();
|
|||||||
* @return 成功时返回缓存位图指针,失败或越界时返回 nullptr。
|
* @return 成功时返回缓存位图指针,失败或越界时返回 nullptr。
|
||||||
*/
|
*/
|
||||||
Gdiplus::Bitmap* LoadCreditImage(int index);
|
Gdiplus::Bitmap* LoadCreditImage(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制完整游戏界面,供 TDrawScreen 总入口调用。
|
||||||
|
* @param hdc 目标绘图设备上下文。
|
||||||
|
* @param hWnd 当前窗口句柄。
|
||||||
|
*/
|
||||||
|
void RenderFullScreen(HDC hdc, HWND hWnd);
|
||||||
|
|||||||
+13
-308
@@ -105,77 +105,6 @@ COLORREF BrickColor[7] =
|
|||||||
RGB(197, 170, 255)
|
RGB(197, 170, 255)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 计算指定方块在指定旋转状态下的最小包围盒边界。
|
|
||||||
*
|
|
||||||
* 该函数会遍历 4x4 形状矩阵,找出所有非空单元的上下左右边界,
|
|
||||||
* 供后续统一计算生成位置和对齐方式时使用。
|
|
||||||
*
|
|
||||||
* @param brickType 方块类型编号。
|
|
||||||
* @param brickState 方块旋转状态编号。
|
|
||||||
* @param minRow 返回最上方非空行号。
|
|
||||||
* @param maxRow 返回最下方非空行号。
|
|
||||||
* @param minCol 返回最左侧非空列号。
|
|
||||||
* @param maxCol 返回最右侧非空列号。
|
|
||||||
*/
|
|
||||||
static void GetBrickBounds(int brickType, int brickState, int& minRow, int& maxRow, int& minCol, int& maxCol)
|
|
||||||
{
|
|
||||||
minRow = 4;
|
|
||||||
maxRow = -1;
|
|
||||||
minCol = 4;
|
|
||||||
maxCol = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (bricks[brickType][brickState][i][j] != 0)
|
|
||||||
{
|
|
||||||
if (i < minRow)
|
|
||||||
{
|
|
||||||
minRow = i;
|
|
||||||
}
|
|
||||||
if (i > maxRow)
|
|
||||||
{
|
|
||||||
maxRow = i;
|
|
||||||
}
|
|
||||||
if (j < minCol)
|
|
||||||
{
|
|
||||||
minCol = j;
|
|
||||||
}
|
|
||||||
if (j > maxCol)
|
|
||||||
{
|
|
||||||
maxCol = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 计算指定方块的统一生成位置。
|
|
||||||
*
|
|
||||||
* 该函数会根据方块在初始旋转状态下的最小包围盒,
|
|
||||||
* 自动把方块水平居中到游戏区附近,并将顶部非空行对齐到可视区域顶部。
|
|
||||||
* 这样不同形状的方块在生成时看起来会更加统一。
|
|
||||||
*
|
|
||||||
* @param brickType 方块类型编号。
|
|
||||||
* @return Point 计算得到的生成坐标。
|
|
||||||
*/
|
|
||||||
Point GetSpawnPoint(int brickType)
|
|
||||||
{
|
|
||||||
int minRow, maxRow, minCol, maxCol;
|
|
||||||
GetBrickBounds(brickType, 0, minRow, maxRow, minCol, maxCol);
|
|
||||||
|
|
||||||
int brickWidth = maxCol - minCol + 1;
|
|
||||||
int brickHeight = maxRow - minRow + 1;
|
|
||||||
Point spawnPoint;
|
|
||||||
spawnPoint.x = (nGameWidth - brickWidth) / 2 - minCol;
|
|
||||||
spawnPoint.y = -brickHeight;
|
|
||||||
|
|
||||||
return spawnPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 判断当前方块是否可以继续向下移动。
|
* @brief 判断当前方块是否可以继续向下移动。
|
||||||
*
|
*
|
||||||
@@ -381,129 +310,6 @@ void DropDown()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 收集当前方块将要固定到棋盘上的格子,并标记是否越过顶部。
|
|
||||||
* @param overflowTop 返回是否有方块格位于可视区域顶部之外。
|
|
||||||
* @param fixedCells 返回普通落地格,用于后续特殊效果定位。
|
|
||||||
* @param fixedCellCount 返回普通落地格数量。
|
|
||||||
* @param explosiveCells 返回爆破方块落地格。
|
|
||||||
* @param explosiveCellCount 返回爆破方块落地格数量。
|
|
||||||
*/
|
|
||||||
static void CollectAndWriteFixedCells(
|
|
||||||
bool& overflowTop,
|
|
||||||
Point fixedCells[],
|
|
||||||
int& fixedCellCount,
|
|
||||||
Point explosiveCells[],
|
|
||||||
int& explosiveCellCount)
|
|
||||||
{
|
|
||||||
overflowTop = false;
|
|
||||||
fixedCellCount = 0;
|
|
||||||
explosiveCellCount = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
if (bricks[type][state][i][j] == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fixY = point.y + i;
|
|
||||||
int fixX = point.x + j;
|
|
||||||
|
|
||||||
// 顶部溢出只记录状态,真正的复活或结束逻辑在后续统一处理。
|
|
||||||
if (fixY < 0)
|
|
||||||
{
|
|
||||||
overflowTop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fixY >= 0 && fixY < GetRoguePlayableHeight() && fixX >= 0 && fixX < nGameWidth)
|
|
||||||
{
|
|
||||||
workRegion[fixY][fixX] = currentPieceIsRainbow ? 8 : bricks[type][state][i][j];
|
|
||||||
if (fixedCellCount < 4)
|
|
||||||
{
|
|
||||||
fixedCells[fixedCellCount].x = fixX;
|
|
||||||
fixedCells[fixedCellCount].y = fixY;
|
|
||||||
fixedCellCount++;
|
|
||||||
}
|
|
||||||
if (currentPieceIsExplosive && explosiveCellCount < 4)
|
|
||||||
{
|
|
||||||
explosiveCells[explosiveCellCount].x = fixX;
|
|
||||||
explosiveCells[explosiveCellCount].y = fixY;
|
|
||||||
explosiveCellCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理方块固定时的顶部溢出、终末清场和最后一搏。
|
|
||||||
* @param overflowTop 是否出现顶部溢出。
|
|
||||||
* @return 溢出已被处理且游戏可以继续时返回 true;需要结束游戏时返回 false。
|
|
||||||
*/
|
|
||||||
static bool ResolveFixingOverflow(bool overflowTop)
|
|
||||||
{
|
|
||||||
if (!overflowTop)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMode == MODE_ROGUE && rogueStats.terminalClearLevel > 0 && rogueStats.lastChanceCount > 0 && rogueStats.screenBombCount > 0)
|
|
||||||
{
|
|
||||||
rogueStats.lastChanceCount--;
|
|
||||||
rogueStats.screenBombCount--;
|
|
||||||
|
|
||||||
int clearedByTerminal = TriggerScreenBomb();
|
|
||||||
rogueStats.feverTicks = 10;
|
|
||||||
currentFallInterval = GetRogueFallInterval();
|
|
||||||
|
|
||||||
TCHAR terminalDetail[128];
|
|
||||||
_stprintf_s(
|
|
||||||
terminalDetail,
|
|
||||||
_T("终末清场启动,清除 %d 格,并进入 10 秒狂热。"),
|
|
||||||
clearedByTerminal);
|
|
||||||
SetFeedbackMessage(_T("终末清场"), terminalDetail, 14);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMode == MODE_ROGUE && rogueStats.lastChanceCount > 0)
|
|
||||||
{
|
|
||||||
rogueStats.lastChanceCount--;
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
DeleteOneLine(GetRoguePlayableHeight() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetFeedbackMessage(
|
|
||||||
_T("最后一搏"),
|
|
||||||
_T("底部 3 行被清除,战局得以延续。"),
|
|
||||||
14);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
gameOverFlag = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 生成下一枚活动方块,并刷新 Hold、特殊方块和预测落点状态。
|
|
||||||
*/
|
|
||||||
static void SpawnNextFallingPiece()
|
|
||||||
{
|
|
||||||
// 消耗预览队列后重置本回合状态,确保 Hold 和特殊标记只影响新方块。
|
|
||||||
type = ConsumeNextType();
|
|
||||||
nType = nextTypes[0];
|
|
||||||
state = 0;
|
|
||||||
holdUsedThisTurn = false;
|
|
||||||
RollCurrentPieceSpecialFlags(true);
|
|
||||||
point = GetSpawnPoint(type);
|
|
||||||
target = point;
|
|
||||||
ComputeTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将当前活动方块固定到工作区,并生成下一个活动方块。
|
* @brief 将当前活动方块固定到工作区,并生成下一个活动方块。
|
||||||
*
|
*
|
||||||
@@ -567,120 +373,6 @@ void DeleteOneLine(int number)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 从底向上扫描满行并删除,记录本次消除的原始行号。
|
|
||||||
* @param clearedRows 返回最多 8 个被消除行号,用于播放消行动画。
|
|
||||||
* @param clearedRowCount 返回记录的行号数量。
|
|
||||||
* @return 本次标准消行数量。
|
|
||||||
*/
|
|
||||||
static int ScanAndDeleteFullLines(int clearedRows[], int& clearedRowCount)
|
|
||||||
{
|
|
||||||
int clearedLines = 0;
|
|
||||||
clearedRowCount = 0;
|
|
||||||
|
|
||||||
int playableHeight = GetRoguePlayableHeight();
|
|
||||||
for (int i = playableHeight - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
bool fullLine = true;
|
|
||||||
|
|
||||||
for (int j = 0; j < nGameWidth; j++)
|
|
||||||
{
|
|
||||||
if (workRegion[i][j] == 0)
|
|
||||||
{
|
|
||||||
fullLine = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fullLine)
|
|
||||||
{
|
|
||||||
if (clearedRowCount < 8)
|
|
||||||
{
|
|
||||||
clearedRows[clearedRowCount] = i;
|
|
||||||
clearedRowCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteOneLine(i);
|
|
||||||
clearedLines++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clearedLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 根据当前界面状态立即播放或暂存消行动画。
|
|
||||||
* @param clearedRows 已消除行号数组。
|
|
||||||
* @param clearedRowCount 行号数量。
|
|
||||||
* @param clearedLines 本次消行数量。
|
|
||||||
*/
|
|
||||||
static void DispatchLineClearEffect(const int clearedRows[], int clearedRowCount, int clearedLines)
|
|
||||||
{
|
|
||||||
if (currentScreen == SCREEN_UPGRADE)
|
|
||||||
{
|
|
||||||
QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 处理连环炸弹因消行触发的一次追加 3x3 爆破。
|
|
||||||
* @param clearedLines 本次标准消行数量。
|
|
||||||
*/
|
|
||||||
static void ResolveChainBombFollowup(int clearedLines)
|
|
||||||
{
|
|
||||||
if (!pendingChainBombFollowup || clearedLines <= 0)
|
|
||||||
{
|
|
||||||
pendingChainBombFollowup = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingChainBombFollowup = false;
|
|
||||||
|
|
||||||
int followupCleared = 0;
|
|
||||||
int centerY = pendingChainBombCenter.y;
|
|
||||||
int centerX = pendingChainBombCenter.x;
|
|
||||||
Point followupCells[9] = {};
|
|
||||||
|
|
||||||
for (int y = centerY - 1; y <= centerY + 1; y++)
|
|
||||||
{
|
|
||||||
for (int x = centerX - 1; x <= centerX + 1; x++)
|
|
||||||
{
|
|
||||||
if (y >= 0 && y < GetRoguePlayableHeight() && x >= 0 && x < nGameWidth && workRegion[y][x] != 0)
|
|
||||||
{
|
|
||||||
if (followupCleared < 9)
|
|
||||||
{
|
|
||||||
followupCells[followupCleared].x = x;
|
|
||||||
followupCells[followupCleared].y = y;
|
|
||||||
}
|
|
||||||
workRegion[y][x] = 0;
|
|
||||||
followupCleared++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMode == MODE_ROGUE && followupCleared > 0)
|
|
||||||
{
|
|
||||||
TriggerCellClearEffect(followupCells, followupCleared < 9 ? followupCleared : 9, true);
|
|
||||||
int followupScore = 0;
|
|
||||||
int followupExp = 0;
|
|
||||||
AwardRogueSkillClearRewards(followupCleared, followupScore, followupExp, false);
|
|
||||||
|
|
||||||
TCHAR followupDetail[128];
|
|
||||||
_stprintf_s(
|
|
||||||
followupDetail,
|
|
||||||
_T("追加爆炸清除 %d 格 +%d 分 +%d EXP"),
|
|
||||||
followupCleared,
|
|
||||||
followupScore,
|
|
||||||
followupExp);
|
|
||||||
SetFeedbackMessage(_T("连环炸弹"), followupDetail, 12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查并删除所有已满的行,同时更新当前得分。
|
* @brief 检查并删除所有已满的行,同时更新当前得分。
|
||||||
*
|
*
|
||||||
@@ -707,6 +399,19 @@ int DeleteLines()
|
|||||||
return clearedLines;
|
return clearedLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 判断当前游戏是否已经结束。
|
||||||
|
*
|
||||||
|
* 老师作业框架中保留该函数名,当前项目内部的结束状态统一记录在
|
||||||
|
* gameOverFlag 中,因此这里直接返回该标记,避免改变原有流程。
|
||||||
|
*
|
||||||
|
* @return 游戏结束返回 true,否则返回 false。
|
||||||
|
*/
|
||||||
|
bool GameOver()
|
||||||
|
{
|
||||||
|
return gameOverFlag;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算当前活动方块的预测落点位置。
|
* @brief 计算当前活动方块的预测落点位置。
|
||||||
*
|
*
|
||||||
|
|||||||
+5
-2708
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,316 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
/**
|
||||||
|
* @file TetrisCoreHelpers.cpp
|
||||||
|
* @brief 存放基础逻辑框架函数之外的内部辅助流程。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Tetris.h"
|
||||||
|
#include "TetrisLogicInternal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算指定方块在指定旋转状态下的最小包围盒边界。
|
||||||
|
*
|
||||||
|
* 该函数会遍历 4x4 形状矩阵,找出所有非空单元的上下左右边界,
|
||||||
|
* 供后续统一计算生成位置和对齐方式时使用。
|
||||||
|
*
|
||||||
|
* @param brickType 方块类型编号。
|
||||||
|
* @param brickState 方块旋转状态编号。
|
||||||
|
* @param minRow 返回最上方非空行号。
|
||||||
|
* @param maxRow 返回最下方非空行号。
|
||||||
|
* @param minCol 返回最左侧非空列号。
|
||||||
|
* @param maxCol 返回最右侧非空列号。
|
||||||
|
*/
|
||||||
|
static void GetBrickBounds(int brickType, int brickState, int& minRow, int& maxRow, int& minCol, int& maxCol)
|
||||||
|
{
|
||||||
|
minRow = 4;
|
||||||
|
maxRow = -1;
|
||||||
|
minCol = 4;
|
||||||
|
maxCol = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if (bricks[brickType][brickState][i][j] != 0)
|
||||||
|
{
|
||||||
|
if (i < minRow)
|
||||||
|
{
|
||||||
|
minRow = i;
|
||||||
|
}
|
||||||
|
if (i > maxRow)
|
||||||
|
{
|
||||||
|
maxRow = i;
|
||||||
|
}
|
||||||
|
if (j < minCol)
|
||||||
|
{
|
||||||
|
minCol = j;
|
||||||
|
}
|
||||||
|
if (j > maxCol)
|
||||||
|
{
|
||||||
|
maxCol = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算指定方块的统一生成位置。
|
||||||
|
*
|
||||||
|
* 该函数会根据方块在初始旋转状态下的最小包围盒,
|
||||||
|
* 自动把方块水平居中到游戏区附近,并将顶部非空行对齐到可视区域顶部。
|
||||||
|
* 这样不同形状的方块在生成时看起来会更加统一。
|
||||||
|
*
|
||||||
|
* @param brickType 方块类型编号。
|
||||||
|
* @return Point 计算得到的生成坐标。
|
||||||
|
*/
|
||||||
|
Point GetSpawnPoint(int brickType)
|
||||||
|
{
|
||||||
|
int minRow, maxRow, minCol, maxCol;
|
||||||
|
GetBrickBounds(brickType, 0, minRow, maxRow, minCol, maxCol);
|
||||||
|
|
||||||
|
int brickWidth = maxCol - minCol + 1;
|
||||||
|
int brickHeight = maxRow - minRow + 1;
|
||||||
|
Point spawnPoint;
|
||||||
|
spawnPoint.x = (nGameWidth - brickWidth) / 2 - minCol;
|
||||||
|
spawnPoint.y = -brickHeight;
|
||||||
|
|
||||||
|
return spawnPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 收集当前方块将要固定到棋盘上的格子,并标记是否越过顶部。
|
||||||
|
* @param overflowTop 返回是否有方块格位于可视区域顶部之外。
|
||||||
|
* @param fixedCells 返回普通落地格,用于后续特殊效果定位。
|
||||||
|
* @param fixedCellCount 返回普通落地格数量。
|
||||||
|
* @param explosiveCells 返回爆破方块落地格。
|
||||||
|
* @param explosiveCellCount 返回爆破方块落地格数量。
|
||||||
|
*/
|
||||||
|
void CollectAndWriteFixedCells(
|
||||||
|
bool& overflowTop,
|
||||||
|
Point fixedCells[],
|
||||||
|
int& fixedCellCount,
|
||||||
|
Point explosiveCells[],
|
||||||
|
int& explosiveCellCount)
|
||||||
|
{
|
||||||
|
overflowTop = false;
|
||||||
|
fixedCellCount = 0;
|
||||||
|
explosiveCellCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if (bricks[type][state][i][j] == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fixY = point.y + i;
|
||||||
|
int fixX = point.x + j;
|
||||||
|
|
||||||
|
// 顶部溢出只记录状态,真正的复活或结束逻辑在后续统一处理。
|
||||||
|
if (fixY < 0)
|
||||||
|
{
|
||||||
|
overflowTop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixY >= 0 && fixY < GetRoguePlayableHeight() && fixX >= 0 && fixX < nGameWidth)
|
||||||
|
{
|
||||||
|
workRegion[fixY][fixX] = currentPieceIsRainbow ? 8 : bricks[type][state][i][j];
|
||||||
|
if (fixedCellCount < 4)
|
||||||
|
{
|
||||||
|
fixedCells[fixedCellCount].x = fixX;
|
||||||
|
fixedCells[fixedCellCount].y = fixY;
|
||||||
|
fixedCellCount++;
|
||||||
|
}
|
||||||
|
if (currentPieceIsExplosive && explosiveCellCount < 4)
|
||||||
|
{
|
||||||
|
explosiveCells[explosiveCellCount].x = fixX;
|
||||||
|
explosiveCells[explosiveCellCount].y = fixY;
|
||||||
|
explosiveCellCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理方块固定时的顶部溢出、终末清场和最后一搏。
|
||||||
|
* @param overflowTop 是否出现顶部溢出。
|
||||||
|
* @return 溢出已被处理且游戏可以继续时返回 true;需要结束游戏时返回 false。
|
||||||
|
*/
|
||||||
|
bool ResolveFixingOverflow(bool overflowTop)
|
||||||
|
{
|
||||||
|
if (!overflowTop)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMode == MODE_ROGUE && rogueStats.terminalClearLevel > 0 && rogueStats.lastChanceCount > 0 && rogueStats.screenBombCount > 0)
|
||||||
|
{
|
||||||
|
rogueStats.lastChanceCount--;
|
||||||
|
rogueStats.screenBombCount--;
|
||||||
|
|
||||||
|
int clearedByTerminal = TriggerScreenBomb();
|
||||||
|
rogueStats.feverTicks = 10;
|
||||||
|
currentFallInterval = GetRogueFallInterval();
|
||||||
|
|
||||||
|
TCHAR terminalDetail[128];
|
||||||
|
_stprintf_s(
|
||||||
|
terminalDetail,
|
||||||
|
_T("终末清场启动,清除 %d 格,并进入 10 秒狂热。"),
|
||||||
|
clearedByTerminal);
|
||||||
|
SetFeedbackMessage(_T("终末清场"), terminalDetail, 14);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMode == MODE_ROGUE && rogueStats.lastChanceCount > 0)
|
||||||
|
{
|
||||||
|
rogueStats.lastChanceCount--;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
DeleteOneLine(GetRoguePlayableHeight() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetFeedbackMessage(
|
||||||
|
_T("最后一搏"),
|
||||||
|
_T("底部 3 行被清除,战局得以延续。"),
|
||||||
|
14);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameOverFlag = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 生成下一枚活动方块,并刷新 Hold、特殊方块和预测落点状态。
|
||||||
|
*/
|
||||||
|
void SpawnNextFallingPiece()
|
||||||
|
{
|
||||||
|
// 消耗预览队列后重置本回合状态,确保 Hold 和特殊标记只影响新方块。
|
||||||
|
type = ConsumeNextType();
|
||||||
|
nType = nextTypes[0];
|
||||||
|
state = 0;
|
||||||
|
holdUsedThisTurn = false;
|
||||||
|
RollCurrentPieceSpecialFlags(true);
|
||||||
|
point = GetSpawnPoint(type);
|
||||||
|
target = point;
|
||||||
|
ComputeTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从底向上扫描满行并删除,记录本次消除的原始行号。
|
||||||
|
* @param clearedRows 返回最多 8 个被消除行号,用于播放消行动画。
|
||||||
|
* @param clearedRowCount 返回记录的行号数量。
|
||||||
|
* @return 本次标准消行数量。
|
||||||
|
*/
|
||||||
|
int ScanAndDeleteFullLines(int clearedRows[], int& clearedRowCount)
|
||||||
|
{
|
||||||
|
int clearedLines = 0;
|
||||||
|
clearedRowCount = 0;
|
||||||
|
|
||||||
|
int playableHeight = GetRoguePlayableHeight();
|
||||||
|
for (int i = playableHeight - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
bool fullLine = true;
|
||||||
|
|
||||||
|
for (int j = 0; j < nGameWidth; j++)
|
||||||
|
{
|
||||||
|
if (workRegion[i][j] == 0)
|
||||||
|
{
|
||||||
|
fullLine = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullLine)
|
||||||
|
{
|
||||||
|
if (clearedRowCount < 8)
|
||||||
|
{
|
||||||
|
clearedRows[clearedRowCount] = i;
|
||||||
|
clearedRowCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteOneLine(i);
|
||||||
|
clearedLines++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clearedLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据当前界面状态立即播放或暂存消行动画。
|
||||||
|
* @param clearedRows 已消除行号数组。
|
||||||
|
* @param clearedRowCount 行号数量。
|
||||||
|
* @param clearedLines 本次消行数量。
|
||||||
|
*/
|
||||||
|
void DispatchLineClearEffect(const int clearedRows[], int clearedRowCount, int clearedLines)
|
||||||
|
{
|
||||||
|
if (currentScreen == SCREEN_UPGRADE)
|
||||||
|
{
|
||||||
|
QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理连环炸弹因消行触发的一次追加 3x3 爆破。
|
||||||
|
* @param clearedLines 本次标准消行数量。
|
||||||
|
*/
|
||||||
|
void ResolveChainBombFollowup(int clearedLines)
|
||||||
|
{
|
||||||
|
if (!pendingChainBombFollowup || clearedLines <= 0)
|
||||||
|
{
|
||||||
|
pendingChainBombFollowup = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingChainBombFollowup = false;
|
||||||
|
|
||||||
|
int followupCleared = 0;
|
||||||
|
int centerY = pendingChainBombCenter.y;
|
||||||
|
int centerX = pendingChainBombCenter.x;
|
||||||
|
Point followupCells[9] = {};
|
||||||
|
|
||||||
|
for (int y = centerY - 1; y <= centerY + 1; y++)
|
||||||
|
{
|
||||||
|
for (int x = centerX - 1; x <= centerX + 1; x++)
|
||||||
|
{
|
||||||
|
if (y >= 0 && y < GetRoguePlayableHeight() && x >= 0 && x < nGameWidth && workRegion[y][x] != 0)
|
||||||
|
{
|
||||||
|
if (followupCleared < 9)
|
||||||
|
{
|
||||||
|
followupCells[followupCleared].x = x;
|
||||||
|
followupCells[followupCleared].y = y;
|
||||||
|
}
|
||||||
|
workRegion[y][x] = 0;
|
||||||
|
followupCleared++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMode == MODE_ROGUE && followupCleared > 0)
|
||||||
|
{
|
||||||
|
TriggerCellClearEffect(followupCells, followupCleared < 9 ? followupCleared : 9, true);
|
||||||
|
int followupScore = 0;
|
||||||
|
int followupExp = 0;
|
||||||
|
AwardRogueSkillClearRewards(followupCleared, followupScore, followupExp, false);
|
||||||
|
|
||||||
|
TCHAR followupDetail[128];
|
||||||
|
_stprintf_s(
|
||||||
|
followupDetail,
|
||||||
|
_T("追加爆炸清除 %d 格 +%d 分 +%d EXP"),
|
||||||
|
followupCleared,
|
||||||
|
followupScore,
|
||||||
|
followupExp);
|
||||||
|
SetFeedbackMessage(_T("连环炸弹"), followupDetail, 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user