进一步拆分与注释补强

This commit is contained in:
2026-04-29 15:21:14 +08:00
parent 58ab400949
commit a331162349
4 changed files with 973 additions and 641 deletions
Submodule .worktrees/ack-syc-swj added at 58ab400949
+208 -128
View File
@@ -381,6 +381,129 @@ 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 将当前活动方块固定到工作区,并生成下一个活动方块。
* *
@@ -400,81 +523,13 @@ void Fixing()
int explosiveCellCount = 0; int explosiveCellCount = 0;
pendingChainBombFollowup = false; pendingChainBombFollowup = false;
for (int i = 0; i < 4; i++) CollectAndWriteFixedCells(overflowTop, fixedCells, fixedCellCount, explosiveCells, explosiveCellCount);
{
for (int j = 0; j < 4; j++)
{
if (bricks[type][state][i][j] != 0)
{
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++;
}
}
}
}
}
ApplyRainbowLandingEffect(overflowTop, fixedCells, fixedCellCount); ApplyRainbowLandingEffect(overflowTop, fixedCells, fixedCellCount);
if (overflowTop) if (!ResolveFixingOverflow(overflowTop))
{ {
if (currentMode == MODE_ROGUE && rogueStats.terminalClearLevel > 0 && rogueStats.lastChanceCount > 0 && rogueStats.screenBombCount > 0) return;
{
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);
}
else if (currentMode == MODE_ROGUE && rogueStats.lastChanceCount > 0)
{
rogueStats.lastChanceCount--;
for (int i = 0; i < 3; i++)
{
DeleteOneLine(GetRoguePlayableHeight() - 1);
}
SetFeedbackMessage(
_T("最后一搏"),
_T("底部 3 行被清除,战局得以延续。"),
14);
}
else
{
gameOverFlag = true;
return;
}
} }
ApplySpecialLandingEffects(fixedCells, fixedCellCount, explosiveCells, explosiveCellCount); ApplySpecialLandingEffects(fixedCells, fixedCellCount, explosiveCells, explosiveCellCount);
@@ -484,15 +539,7 @@ void Fixing()
currentFallInterval = GetRogueFallInterval(); currentFallInterval = GetRogueFallInterval();
} }
// 生成下一个活动方块 SpawnNextFallingPiece();
type = ConsumeNextType();
nType = nextTypes[0];
state = 0;
holdUsedThisTurn = false;
RollCurrentPieceSpecialFlags(true);
point = GetSpawnPoint(type);
target = point;
ComputeTarget();
} }
/** /**
@@ -521,20 +568,15 @@ void DeleteOneLine(int number)
} }
/** /**
* @brief 检查并删除所有已满的行,同时更新当前得分 * @brief 从底向上扫描满行并删除,记录本次消除的原始行号
* * @param clearedRows 返回最多 8 个被消除行号,用于播放消行动画。
* 该函数会从底部向上遍历工作区,判断每一行是否被完全填满 * @param clearedRowCount 返回记录的行号数量
* 如果某一行全部非 0,则调用 DeleteOneLine 删除该行, * @return 本次标准消行数量。
* 并将该行上方的内容整体下移。为了避免连续满行被漏检,
* 删除后会继续检查当前行号。每成功消除 1 行,当前得分增加 100 分。
*
* @return 本次实际消除的行数。
*/ */
int DeleteLines() static int ScanAndDeleteFullLines(int clearedRows[], int& clearedRowCount)
{ {
int clearedLines = 0; int clearedLines = 0;
int clearedRows[8] = {}; clearedRowCount = 0;
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--)
@@ -564,8 +606,17 @@ int DeleteLines()
} }
} }
// 消行数量先进入玩法结算,再根据是否正在升级决定动画立即播放还是暂存。 return clearedLines;
ApplyLineClearResult(clearedLines); }
/**
* @brief 根据当前界面状态立即播放或暂存消行动画。
* @param clearedRows 已消除行号数组。
* @param clearedRowCount 行号数量。
* @param clearedLines 本次消行数量。
*/
static void DispatchLineClearEffect(const int clearedRows[], int clearedRowCount, int clearedLines)
{
if (currentScreen == SCREEN_UPGRADE) if (currentScreen == SCREEN_UPGRADE)
{ {
QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines); QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines);
@@ -574,55 +625,84 @@ int DeleteLines()
{ {
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines); TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
} }
}
// 连环炸弹的追加爆破只在爆破方块导致后续消行时触发一次。 /**
if (pendingChainBombFollowup && clearedLines > 0) * @brief 处理连环炸弹因消行触发的一次追加 3x3 爆破。
* @param clearedLines 本次标准消行数量。
*/
static void ResolveChainBombFollowup(int clearedLines)
{
if (!pendingChainBombFollowup || clearedLines <= 0)
{ {
pendingChainBombFollowup = false; pendingChainBombFollowup = false;
return;
}
int followupCleared = 0; pendingChainBombFollowup = false;
int centerY = pendingChainBombCenter.y;
int centerX = pendingChainBombCenter.x;
Point followupCells[9] = {};
for (int y = centerY - 1; y <= centerY + 1; y++) 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++)
{ {
for (int x = centerX - 1; x <= centerX + 1; x++) if (y >= 0 && y < GetRoguePlayableHeight() && x >= 0 && x < nGameWidth && workRegion[y][x] != 0)
{ {
if (y >= 0 && y < GetRoguePlayableHeight() && x >= 0 && x < nGameWidth && workRegion[y][x] != 0) if (followupCleared < 9)
{ {
if (followupCleared < 9) followupCells[followupCleared].x = x;
{ followupCells[followupCleared].y = y;
followupCells[followupCleared].x = x;
followupCells[followupCleared].y = y;
}
workRegion[y][x] = 0;
followupCleared++;
} }
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);
}
} }
else
if (currentMode == MODE_ROGUE && followupCleared > 0)
{ {
pendingChainBombFollowup = false; 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 检查并删除所有已满的行,同时更新当前得分。
*
* 该函数会从底部向上遍历工作区,判断每一行是否被完全填满。
* 如果某一行全部非 0,则调用 DeleteOneLine 删除该行,
* 并将该行上方的内容整体下移。为了避免连续满行被漏检,
* 删除后会继续检查当前行号。每成功消除 1 行,当前得分增加 100 分。
*
* @return 本次实际消除的行数。
*/
int DeleteLines()
{
int clearedRows[8] = {};
int clearedRowCount = 0;
int clearedLines = ScanAndDeleteFullLines(clearedRows, clearedRowCount);
// 消行数量先进入玩法结算,再根据是否正在升级决定动画立即播放还是暂存。
ApplyLineClearResult(clearedLines);
DispatchLineClearEffect(clearedRows, clearedRowCount, clearedLines);
// 连环炸弹的追加爆破只在爆破方块导致后续消行时触发一次。
ResolveChainBombFollowup(clearedLines);
return clearedLines; return clearedLines;
} }
+305 -178
View File
@@ -417,6 +417,88 @@ static bool HandleMenuKey(HWND hWnd, WPARAM key)
return true; return true;
} }
/**
* @brief 在帮助首页选项之间循环移动。
* @param direction 负数向前,正数向后。
*/
static void MoveHelpHomeSelection(int direction)
{
helpState.selectedIndex += direction;
if (helpState.selectedIndex < 0)
{
helpState.selectedIndex = helpState.optionCount - 1;
}
if (helpState.selectedIndex >= helpState.optionCount)
{
helpState.selectedIndex = 0;
}
}
/**
* @brief 在技能演示列表中移动选中项,并同步滚动偏移。
* @param direction 负数向上,正数向下。
*/
static void MoveSkillDemoSelection(int direction)
{
helpState.selectedIndex += direction;
if (helpState.selectedIndex < 0)
{
helpState.selectedIndex = GetRogueSkillDemoCount() - 1;
}
if (helpState.selectedIndex >= GetRogueSkillDemoCount())
{
helpState.selectedIndex = 0;
helpScrollOffset = 0;
}
else if (direction < 0 && helpState.selectedIndex * 68 < helpScrollOffset)
{
helpScrollOffset = helpState.selectedIndex * 68;
}
else if (direction > 0 && helpState.selectedIndex * 68 > helpScrollOffset + 360)
{
helpScrollOffset = helpState.selectedIndex * 68 - 360;
}
}
/**
* @brief 打开帮助首页当前选中的子页面。
*/
static void ActivateHelpSelection()
{
if (helpState.selectedIndex == 3)
{
helpState.currentPage = 5;
helpState.selectedIndex = 0;
helpScrollOffset = 0;
}
else
{
helpState.currentPage = helpState.selectedIndex + 1;
helpScrollOffset = 0;
}
}
/**
* @brief 从帮助子页返回帮助首页或主菜单。
*/
static void LeaveRulesPage()
{
int previousPage = helpState.currentPage;
if (helpState.currentPage == 0)
{
ReturnToMainMenu();
}
else
{
helpState.currentPage = 0;
if (previousPage == 4 || previousPage == 5)
{
helpState.selectedIndex = 3;
}
helpScrollOffset = 0;
}
}
/** /**
* @brief 处理帮助和致谢页键盘导航。 * @brief 处理帮助和致谢页键盘导航。
* @param hWnd 当前窗口句柄。 * @param hWnd 当前窗口句柄。
@@ -438,11 +520,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
case 'A': case 'A':
if (helpState.currentPage == 0) if (helpState.currentPage == 0)
{ {
helpState.selectedIndex--; MoveHelpHomeSelection(-1);
if (helpState.selectedIndex < 0)
{
helpState.selectedIndex = helpState.optionCount - 1;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
else if (helpState.currentPage == 4) else if (helpState.currentPage == 4)
@@ -452,15 +530,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
} }
else if (helpState.currentPage == 5) else if (helpState.currentPage == 5)
{ {
helpState.selectedIndex--; MoveSkillDemoSelection(-1);
if (helpState.selectedIndex < 0)
{
helpState.selectedIndex = GetRogueSkillDemoCount() - 1;
}
if (helpState.selectedIndex * 68 < helpScrollOffset)
{
helpScrollOffset = helpState.selectedIndex * 68;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
break; break;
@@ -470,11 +540,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
case 'D': case 'D':
if (helpState.currentPage == 0) if (helpState.currentPage == 0)
{ {
helpState.selectedIndex++; MoveHelpHomeSelection(1);
if (helpState.selectedIndex >= helpState.optionCount)
{
helpState.selectedIndex = 0;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
else if (helpState.currentPage == 4) else if (helpState.currentPage == 4)
@@ -484,16 +550,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
} }
else if (helpState.currentPage == 5) else if (helpState.currentPage == 5)
{ {
helpState.selectedIndex++; MoveSkillDemoSelection(1);
if (helpState.selectedIndex >= GetRogueSkillDemoCount())
{
helpState.selectedIndex = 0;
helpScrollOffset = 0;
}
else if (helpState.selectedIndex * 68 > helpScrollOffset + 360)
{
helpScrollOffset = helpState.selectedIndex * 68 - 360;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
break; break;
@@ -501,17 +558,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
case VK_SPACE: case VK_SPACE:
if (helpState.currentPage == 0) if (helpState.currentPage == 0)
{ {
if (helpState.selectedIndex == 3) ActivateHelpSelection();
{
helpState.currentPage = 5;
helpState.selectedIndex = 0;
helpScrollOffset = 0;
}
else
{
helpState.currentPage = helpState.selectedIndex + 1;
helpScrollOffset = 0;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
else if (helpState.currentPage == 5) else if (helpState.currentPage == 5)
@@ -524,24 +571,9 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
case VK_ESCAPE: case VK_ESCAPE:
case VK_BACK: case VK_BACK:
case 'M': case 'M':
{ LeaveRulesPage();
int previousPage = helpState.currentPage;
if (helpState.currentPage == 0)
{
ReturnToMainMenu();
}
else
{
helpState.currentPage = 0;
if (previousPage == 4 || previousPage == 5)
{
helpState.selectedIndex = 3;
}
helpScrollOffset = 0;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
}
default: default:
break; break;
} }
@@ -549,6 +581,92 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
return true; return true;
} }
/**
* @brief 计算升级卡片网格列数。
* @return 当前升级界面使用的列数,至少为 1。
*/
static int GetUpgradeColumnCount()
{
int upgradeColumnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3;
if (upgradeColumnCount < 1)
{
upgradeColumnCount = 1;
}
return upgradeColumnCount;
}
/**
* @brief 在升级卡片网格中横向移动选中项。
* @param direction 负数向左,正数向右。
*/
static void MoveUpgradeSelectionHorizontal(int direction)
{
if (upgradeUiState.optionCount <= 1)
{
return;
}
int upgradeColumnCount = GetUpgradeColumnCount();
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
int rowEnd = rowStart + upgradeColumnCount - 1;
if (rowEnd >= upgradeUiState.optionCount)
{
rowEnd = upgradeUiState.optionCount - 1;
}
if (direction < 0)
{
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex > rowStart) ? upgradeUiState.selectedIndex - 1 : rowEnd;
}
else
{
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex < rowEnd) ? upgradeUiState.selectedIndex + 1 : rowStart;
}
}
/**
* @brief 在升级卡片网格中纵向移动选中项。
* @param direction 负数向上,正数向下。
*/
static void MoveUpgradeSelectionVertical(int direction)
{
int upgradeColumnCount = GetUpgradeColumnCount();
if (direction < 0 && upgradeUiState.selectedIndex >= upgradeColumnCount)
{
upgradeUiState.selectedIndex -= upgradeColumnCount;
}
else if (direction > 0 && upgradeUiState.selectedIndex + upgradeColumnCount < upgradeUiState.optionCount)
{
upgradeUiState.selectedIndex += upgradeColumnCount;
}
}
/**
* @brief 切换多选升级中的当前卡片标记状态。
*/
static void ToggleUpgradeMarkedSelection()
{
if (upgradeUiState.picksRemaining <= 1 || upgradeUiState.optionCount <= 0)
{
return;
}
bool currentlyMarked = upgradeUiState.marked[upgradeUiState.selectedIndex];
if (currentlyMarked)
{
upgradeUiState.marked[upgradeUiState.selectedIndex] = false;
if (upgradeUiState.markedCount > 0)
{
upgradeUiState.markedCount--;
}
}
else if (upgradeUiState.markedCount < upgradeUiState.picksRemaining)
{
upgradeUiState.marked[upgradeUiState.selectedIndex] = true;
upgradeUiState.markedCount++;
}
}
/** /**
* @brief 处理升级选择界面键盘导航。 * @brief 处理升级选择界面键盘导航。
* @param hWnd 当前窗口句柄。 * @param hWnd 当前窗口句柄。
@@ -562,64 +680,26 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
return false; return false;
} }
int upgradeColumnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3;
if (upgradeColumnCount < 1)
{
upgradeColumnCount = 1;
}
switch (key) switch (key)
{ {
case VK_LEFT: case VK_LEFT:
case 'A': case 'A':
if (upgradeUiState.optionCount > 1) MoveUpgradeSelectionHorizontal(-1);
{
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
if (upgradeUiState.selectedIndex > rowStart)
{
upgradeUiState.selectedIndex--;
}
else
{
int rowEnd = rowStart + upgradeColumnCount - 1;
if (rowEnd >= upgradeUiState.optionCount)
{
rowEnd = upgradeUiState.optionCount - 1;
}
upgradeUiState.selectedIndex = rowEnd;
}
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
case VK_RIGHT: case VK_RIGHT:
case 'D': case 'D':
if (upgradeUiState.optionCount > 1) MoveUpgradeSelectionHorizontal(1);
{
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
int rowEnd = rowStart + upgradeColumnCount - 1;
if (rowEnd >= upgradeUiState.optionCount)
{
rowEnd = upgradeUiState.optionCount - 1;
}
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex < rowEnd) ? upgradeUiState.selectedIndex + 1 : rowStart;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
case VK_UP: case VK_UP:
case 'W': case 'W':
if (upgradeUiState.selectedIndex >= upgradeColumnCount) MoveUpgradeSelectionVertical(-1);
{
upgradeUiState.selectedIndex -= upgradeColumnCount;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
case VK_DOWN: case VK_DOWN:
case 'S': case 'S':
if (upgradeUiState.selectedIndex + upgradeColumnCount < upgradeUiState.optionCount) MoveUpgradeSelectionVertical(1);
{
upgradeUiState.selectedIndex += upgradeColumnCount;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
case VK_RETURN: case VK_RETURN:
@@ -630,20 +710,7 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
case VK_SPACE: case VK_SPACE:
if (upgradeUiState.picksRemaining > 1 && upgradeUiState.optionCount > 0) if (upgradeUiState.picksRemaining > 1 && upgradeUiState.optionCount > 0)
{ {
bool currentlyMarked = upgradeUiState.marked[upgradeUiState.selectedIndex]; ToggleUpgradeMarkedSelection();
if (currentlyMarked)
{
upgradeUiState.marked[upgradeUiState.selectedIndex] = false;
if (upgradeUiState.markedCount > 0)
{
upgradeUiState.markedCount--;
}
}
else if (upgradeUiState.markedCount < upgradeUiState.picksRemaining)
{
upgradeUiState.marked[upgradeUiState.selectedIndex] = true;
upgradeUiState.markedCount++;
}
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
else else
@@ -665,40 +732,54 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
} }
/** /**
* @brief 处理游戏过程中的按键。 * @brief 处理 Rogue 技能演示模式的专用按键。
* @param hWnd 当前窗口句柄。 * @param hWnd 当前窗口句柄。
* @param key 按键虚拟键码。 * @param key 按键虚拟键码。
* @return 已处理返回 true。
*/ */
static void HandlePlayingKey(HWND hWnd, WPARAM key) static bool HandleDemoPlayingKey(HWND hWnd, WPARAM key)
{ {
if (IsRogueSkillDemoMode()) if (!IsRogueSkillDemoMode())
{ {
if (key == 'N') return false;
{
AdvanceRogueSkillDemo();
InvalidateRect(hWnd, nullptr, FALSE);
return;
}
if (key == 'R')
{
RestartCurrentRogueSkillDemo();
ResetGameTimer(hWnd);
InvalidateRect(hWnd, nullptr, FALSE);
return;
}
if (key == VK_ESCAPE || key == VK_BACK || key == 'M')
{
OpenSkillDemoScreen();
InvalidateRect(hWnd, nullptr, FALSE);
return;
}
} }
if (key == 'N')
{
AdvanceRogueSkillDemo();
InvalidateRect(hWnd, nullptr, FALSE);
return true;
}
if (key == 'R')
{
RestartCurrentRogueSkillDemo();
ResetGameTimer(hWnd);
InvalidateRect(hWnd, nullptr, FALSE);
return true;
}
if (key == VK_ESCAPE || key == VK_BACK || key == 'M')
{
OpenSkillDemoScreen();
InvalidateRect(hWnd, nullptr, FALSE);
return true;
}
return false;
}
/**
* @brief 处理正常战局控制键,如菜单、重开、暂停、目标提示和复活。
* @param hWnd 当前窗口句柄。
* @param key 按键虚拟键码。
* @return 已处理返回 true。
*/
static bool HandleBattleControlKey(HWND hWnd, WPARAM key)
{
if (!IsRogueSkillDemoMode() && key == 'M') if (!IsRogueSkillDemoMode() && key == 'M')
{ {
ReturnToMainMenu(); ReturnToMainMenu();
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
if (!IsRogueSkillDemoMode() && key == 'R') if (!IsRogueSkillDemoMode() && key == 'R')
@@ -706,21 +787,21 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
StartGameWithMode(currentMode); StartGameWithMode(currentMode);
ResetGameTimer(hWnd); ResetGameTimer(hWnd);
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
if (!IsRogueSkillDemoMode() && key == 'P') if (!IsRogueSkillDemoMode() && key == 'P')
{ {
suspendFlag = !suspendFlag; suspendFlag = !suspendFlag;
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
if (key == 'G') if (key == 'G')
{ {
targetFlag = !targetFlag; targetFlag = !targetFlag;
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
if (gameOverFlag && reviveAvailable && key == 'V') if (gameOverFlag && reviveAvailable && key == 'V')
@@ -735,23 +816,70 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
SetFeedbackMessage(_T("视频播放失败"), _T("无法打开复活视频,复活机会未消耗。"), 14); SetFeedbackMessage(_T("视频播放失败"), _T("无法打开复活视频,复活机会未消耗。"), 14);
} }
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
if (gameOverFlag || suspendFlag) return false;
{ }
return;
}
/**
* @brief 处理 Rogue 侧栏滚动和主动技能按键。
* @param hWnd 当前窗口句柄。
* @param key 按键虚拟键码。
* @return 已处理返回 true。
*/
static bool HandleRogueSkillKey(HWND hWnd, WPARAM key)
{
if (currentMode == MODE_ROGUE && (key == 'J' || key == 'K')) if (currentMode == MODE_ROGUE && (key == 'J' || key == 'K'))
{ {
int direction = (key == 'J') ? 1 : -1; int direction = (key == 'J') ? 1 : -1;
AdjustScrollOffset(upgradeListScrollOffset, direction * GetScrollStep(hWnd, 52)); AdjustScrollOffset(upgradeListScrollOffset, direction * GetScrollStep(hWnd, 52));
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
return; return true;
} }
// 正常游玩按键先改变方块或触发技能,再统一刷新预测落点和界面。 switch (key)
{
case 'C':
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
HoldCurrentPiece();
return true;
case 'Z':
UseBlackHole();
return true;
case 'X':
UseScreenBomb();
return true;
case 'V':
UseAirReshape();
return true;
default:
return false;
}
}
/**
* @brief 固定当前方块后执行消行和 Rogue 升级检查。
*/
static void FixPieceAndResolveLines()
{
Fixing();
if (!gameOverFlag)
{
DeleteLines();
CheckRogueLevelProgress();
}
}
/**
* @brief 处理移动、旋转、软降和硬降等方块操作键。
* @param key 按键虚拟键码。
* @return 已处理返回 true。
*/
static bool HandlePieceMovementKey(WPARAM key)
{
switch (key) switch (key)
{ {
case VK_LEFT: case VK_LEFT:
@@ -760,14 +888,14 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
{ {
MoveLeft(); MoveLeft();
} }
break; return true;
case VK_RIGHT: case VK_RIGHT:
case 'D': case 'D':
if (CanMoveRight()) if (CanMoveRight())
{ {
MoveRight(); MoveRight();
} }
break; return true;
case VK_DOWN: case VK_DOWN:
case 'S': case 'S':
if (CanMoveDown()) if (CanMoveDown())
@@ -776,44 +904,43 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
} }
else else
{ {
Fixing(); FixPieceAndResolveLines();
if (!gameOverFlag)
{
DeleteLines();
CheckRogueLevelProgress();
}
} }
break; return true;
case VK_UP: case VK_UP:
case 'W': case 'W':
Rotate(); Rotate();
break; return true;
case VK_SPACE: case VK_SPACE:
DropDown(); DropDown();
Fixing(); FixPieceAndResolveLines();
if (!gameOverFlag) return true;
{
DeleteLines();
CheckRogueLevelProgress();
}
break;
case 'C':
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
HoldCurrentPiece();
break;
case 'Z':
UseBlackHole();
break;
case 'X':
UseScreenBomb();
break;
case 'V':
UseAirReshape();
break;
default: default:
break; return false;
}
}
/**
* @brief 处理游戏过程中的按键。
* @param hWnd 当前窗口句柄。
* @param key 按键虚拟键码。
*/
static void HandlePlayingKey(HWND hWnd, WPARAM key)
{
if (HandleDemoPlayingKey(hWnd, key) || HandleBattleControlKey(hWnd, key))
{
return;
}
if (gameOverFlag || suspendFlag)
{
return;
}
// 正常游玩按键先改变方块或触发技能,再统一刷新预测落点和界面。
if (!HandlePieceMovementKey(key))
{
HandleRogueSkillKey(hWnd, key);
} }
if (!gameOverFlag) if (!gameOverFlag)
+459 -335
View File
@@ -1555,18 +1555,15 @@ static int TriggerUpgradeShockwave(int rowsToClear)
} }
/** /**
* @brief 按权重和当前局势生成升级菜单中的候选强化 * @brief 收集当前可进入升级池的强化下标和动态权重
* * @param selectableIndexes 返回候选在强化池中的下标。
* 该函数只负责生成 UI 选项,不应用强化效果。候选池会先过滤前置、 * @param selectableWeights 返回候选动态权重。
* 等级上限和互斥条件,再按动态权重不放回抽取,保证同一轮不会重复出现 * @return 候选数量
*/ */
static void FillUpgradeOptions() static int CollectSelectableUpgrades(int selectableIndexes[], int selectableWeights[])
{ {
int selectableIndexes[kUpgradePoolSize] = { 0 };
int selectableWeights[kUpgradePoolSize] = { 0 };
int selectableCount = 0; int selectableCount = 0;
// 第一段:筛出当前真正可选的强化,并记录它们的动态权重。
for (int i = 0; i < kUpgradePoolSize; i++) for (int i = 0; i < kUpgradePoolSize; i++)
{ {
if (IsUpgradeSelectable(kUpgradePool[i])) if (IsUpgradeSelectable(kUpgradePool[i]))
@@ -1577,7 +1574,16 @@ static void FillUpgradeOptions()
} }
} }
// 命运轮盘把候选扩到 6 个;双重抉择和命运轮盘都会允许本轮选 2 个。 return selectableCount;
}
/**
* @brief 初始化本轮升级界面的数量、选中项和多选状态。
* @param selectableCount 当前可选候选数量。
* @return 本轮实际展示的选项数量。
*/
static int PrepareUpgradeOptionState(int selectableCount)
{
int optionLimit = (rogueStats.destinyWheelLevel > 0) ? 6 : 3; int optionLimit = (rogueStats.destinyWheelLevel > 0) ? 6 : 3;
int optionCount = selectableCount < optionLimit ? selectableCount : optionLimit; int optionCount = selectableCount < optionLimit ? selectableCount : optionLimit;
upgradeUiState.optionCount = optionCount; upgradeUiState.optionCount = optionCount;
@@ -1589,71 +1595,114 @@ static void FillUpgradeOptions()
upgradeUiState.marked[i] = false; upgradeUiState.marked[i] = false;
} }
return optionCount;
}
/**
* @brief 按权重从候选数组中抽取一个槽位。
* @param selectableWeights 候选权重数组。
* @param selectableCount 候选数量。
* @return 被抽中的候选槽位。
*/
static int PickUpgradeSlotByWeight(const int selectableWeights[], int selectableCount)
{
int totalWeight = 0;
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
{
totalWeight += selectableWeights[weightIndex];
}
int pickSlot = 0;
if (totalWeight > 0)
{
int roll = rand() % totalWeight;
int accumulatedWeight = 0;
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
{
accumulatedWeight += selectableWeights[weightIndex];
if (roll < accumulatedWeight)
{
pickSlot = weightIndex;
break;
}
}
}
return pickSlot;
}
/**
* @brief 将抽中的强化池条目写入升级 UI 选项。
* @param optionIndex UI 选项下标。
* @param pickedEntry 被抽中的强化池条目。
*/
static void FillUpgradeOptionFromEntry(int optionIndex, const UpgradeEntry& pickedEntry)
{
upgradeUiState.options[optionIndex].id = pickedEntry.id;
upgradeUiState.options[optionIndex].currentLevel = GetUpgradeCurrentLevel(pickedEntry.id);
upgradeUiState.options[optionIndex].targetPieceType = -1;
upgradeUiState.options[optionIndex].rarity = GetUpgradeBaseRarity(pickedEntry.id);
upgradeUiState.options[optionIndex].cursed = false;
upgradeUiState.options[optionIndex].name = pickedEntry.name;
upgradeUiState.options[optionIndex].category = pickedEntry.category;
upgradeUiState.options[optionIndex].description = pickedEntry.description;
if (pickedEntry.id == UPGRADE_PIECE_TUNING)
{
int targetPieceType = 0;
upgradeUiState.options[optionIndex].targetPieceType = targetPieceType;
upgradeUiState.options[optionIndex].currentLevel = rogueStats.pieceTuningLevels[0];
upgradeUiState.options[optionIndex].name = _T("方块改造");
static TCHAR tuningDescriptions[6][64];
_stprintf_s(
tuningDescriptions[optionIndex],
_T("%s 块的生成概率提高。"),
GetPieceShortName(0));
upgradeUiState.options[optionIndex].description = tuningDescriptions[optionIndex];
}
}
/**
* @brief 命运轮盘生效时,为本轮候选随机附加一个诅咒标记。
* @param optionCount 本轮展示选项数量。
*/
static void MarkDestinyCursedOption(int optionCount)
{
if (rogueStats.destinyWheelLevel > 0 && optionCount > 0)
{
int cursedIndex = rand() % optionCount;
upgradeUiState.options[cursedIndex].cursed = true;
}
}
/**
* @brief 按权重和当前局势生成升级菜单中的候选强化。
*
* 该函数只负责生成 UI 选项,不应用强化效果。候选池会先过滤前置、
* 等级上限和互斥条件,再按动态权重不放回抽取,保证同一轮不会重复出现。
*/
static void FillUpgradeOptions()
{
int selectableIndexes[kUpgradePoolSize] = { 0 };
int selectableWeights[kUpgradePoolSize] = { 0 };
int selectableCount = CollectSelectableUpgrades(selectableIndexes, selectableWeights);
int optionCount = PrepareUpgradeOptionState(selectableCount);
// 第二段:按权重不放回抽取候选,抽中后用末尾元素覆盖当前槽位。 // 第二段:按权重不放回抽取候选,抽中后用末尾元素覆盖当前槽位。
for (int i = 0; i < optionCount; i++) for (int i = 0; i < optionCount; i++)
{ {
int totalWeight = 0; int pickSlot = PickUpgradeSlotByWeight(selectableWeights, selectableCount);
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
{
totalWeight += selectableWeights[weightIndex];
}
int pickSlot = 0;
if (totalWeight > 0)
{
int roll = rand() % totalWeight;
int accumulatedWeight = 0;
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
{
accumulatedWeight += selectableWeights[weightIndex];
if (roll < accumulatedWeight)
{
pickSlot = weightIndex;
break;
}
}
}
int pickedIndex = selectableIndexes[pickSlot]; int pickedIndex = selectableIndexes[pickSlot];
const UpgradeEntry& pickedEntry = kUpgradePool[pickedIndex]; FillUpgradeOptionFromEntry(i, kUpgradePool[pickedIndex]);
upgradeUiState.options[i].id = pickedEntry.id;
upgradeUiState.options[i].currentLevel = GetUpgradeCurrentLevel(pickedEntry.id);
upgradeUiState.options[i].targetPieceType = -1;
upgradeUiState.options[i].rarity = GetUpgradeBaseRarity(pickedEntry.id);
upgradeUiState.options[i].cursed = false;
upgradeUiState.options[i].name = pickedEntry.name;
upgradeUiState.options[i].category = pickedEntry.category;
upgradeUiState.options[i].description = pickedEntry.description;
// 方块改造目前固定展示 I 块概率提升,说明文字需要运行时拼接。
if (pickedEntry.id == UPGRADE_PIECE_TUNING)
{
int targetPieceType = 0;
upgradeUiState.options[i].targetPieceType = targetPieceType;
upgradeUiState.options[i].currentLevel = rogueStats.pieceTuningLevels[0];
upgradeUiState.options[i].name = _T("方块改造");
static TCHAR tuningDescriptions[6][64];
_stprintf_s(
tuningDescriptions[i],
_T("%s 块的生成概率提高。"),
GetPieceShortName(0));
upgradeUiState.options[i].description = tuningDescriptions[i];
}
selectableIndexes[pickSlot] = selectableIndexes[selectableCount - 1]; selectableIndexes[pickSlot] = selectableIndexes[selectableCount - 1];
selectableWeights[pickSlot] = selectableWeights[selectableCount - 1]; selectableWeights[pickSlot] = selectableWeights[selectableCount - 1];
selectableCount--; selectableCount--;
} }
// 命运轮盘在候选中随机附加一个诅咒,确认时才提高下一次升级需求。 MarkDestinyCursedOption(optionCount);
if (rogueStats.destinyWheelLevel > 0 && optionCount > 0)
{
int cursedIndex = rand() % optionCount;
upgradeUiState.options[cursedIndex].cursed = true;
}
} }
/** /**
@@ -1708,6 +1757,241 @@ int GetRogueFallInterval()
return baseInterval; return baseInterval;
} }
/**
* @brief 应用成长、保命和基础操作类强化。
* @param upgradeId 强化编号。
* @param applyCount 本次应用次数。
* @return 当前强化已处理返回 true。
*/
static bool ApplyGrowthOrOperationUpgrade(int upgradeId, int applyCount)
{
switch (upgradeId)
{
case UPGRADE_SCORE_MULTIPLIER:
rogueStats.scoreMultiplierPercent += 20 * applyCount;
rogueStats.scoreUpgradeLevel += applyCount;
return true;
case UPGRADE_COMBO_BONUS:
rogueStats.comboBonusStacks += applyCount;
return true;
case UPGRADE_EXP_MULTIPLIER:
rogueStats.expMultiplierPercent += 25 * applyCount;
rogueStats.expUpgradeLevel += applyCount;
return true;
case UPGRADE_SLOW_FALL:
rogueStats.slowFallStacks += applyCount;
if (rogueStats.slowFallStacks > 4)
{
rogueStats.slowFallStacks = 4;
}
currentFallInterval = GetRogueFallInterval();
return true;
case UPGRADE_PREVIEW_PLUS_ONE:
if (rogueStats.previewCount < 2)
{
rogueStats.previewCount = 2;
}
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
return true;
case UPGRADE_LAST_CHANCE:
rogueStats.lastChanceCount += applyCount;
rogueStats.lastChanceUpgradeLevel += applyCount;
return true;
case UPGRADE_HOLD_UNLOCK:
rogueStats.holdUnlocked = 1;
holdUsedThisTurn = false;
return true;
case UPGRADE_PERFECT_ROTATE:
rogueStats.perfectRotateLevel = 1;
return true;
case UPGRADE_TIME_DILATION:
rogueStats.timeDilationLevel = 1;
currentFallInterval = GetRogueFallInterval();
return true;
case UPGRADE_CONTROL_MASTER:
rogueStats.controlMasterLevel = 1;
if (rogueStats.previewCount < 3)
{
rogueStats.previewCount++;
}
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
return true;
case UPGRADE_DOUBLE_GROWTH:
rogueStats.doubleGrowthLevel = 1;
rogueStats.scoreMultiplierPercent += 15;
rogueStats.expMultiplierPercent += 15;
return true;
default:
return false;
}
}
/**
* @brief 应用特殊方块和落地派生类强化。
* @param upgradeId 强化编号。
* @param applyCount 本次应用次数。
* @return 当前强化已处理返回 true。
*/
static bool ApplySpecialPieceUpgrade(int upgradeId, int applyCount)
{
switch (upgradeId)
{
case UPGRADE_PRESSURE_RELIEF:
rogueStats.pressureReliefLevel += applyCount;
for (int i = 0; i < applyCount; i++)
{
int topOccupiedRow = GetTopOccupiedRow();
if (topOccupiedRow >= 0)
{
DeleteOneLine(topOccupiedRow);
}
}
return true;
case UPGRADE_SWEEPER:
rogueStats.sweeperLevel += applyCount;
if (rogueStats.sweeperLevel > 4)
{
rogueStats.sweeperLevel = 4;
}
return true;
case UPGRADE_EXPLOSIVE_PIECE:
rogueStats.explosiveLevel = 1;
return true;
case UPGRADE_CHAIN_BLAST:
rogueStats.chainBlastLevel = 1;
return true;
case UPGRADE_CHAIN_BOMB:
rogueStats.chainBombLevel = 1;
return true;
case UPGRADE_LASER_PIECE:
rogueStats.laserLevel = 1;
return true;
case UPGRADE_THUNDER_TETRIS:
rogueStats.thunderTetrisLevel = 1;
return true;
case UPGRADE_THUNDER_LASER:
rogueStats.thunderLaserLevel = 1;
return true;
case UPGRADE_CROSS_PIECE:
rogueStats.crossPieceLevel = 1;
return true;
case UPGRADE_RAINBOW_PIECE:
rogueStats.rainbowPieceLevel = 1;
return true;
case UPGRADE_STABLE_STRUCTURE:
rogueStats.stableStructureLevel += applyCount;
return true;
case UPGRADE_PIECE_TUNING:
rogueStats.pieceTuningLevels[0] += applyCount;
return true;
default:
return false;
}
}
/**
* @brief 应用主动技能、爆发和资源次数类强化。
* @param upgradeId 强化编号。
* @param applyCount 本次应用次数。
* @return 当前强化已处理返回 true。
*/
static bool ApplyActiveSkillUpgrade(int upgradeId, int applyCount)
{
switch (upgradeId)
{
case UPGRADE_FEVER_MODE:
rogueStats.feverLevel = 1;
return true;
case UPGRADE_RAGE_STACK:
rogueStats.rageStackLevel = 1;
return true;
case UPGRADE_INFINITE_FEVER:
rogueStats.infiniteFeverLevel = 1;
return true;
case UPGRADE_SCREEN_BOMB:
rogueStats.screenBombLevel += applyCount;
rogueStats.screenBombCount += applyCount;
return true;
case UPGRADE_TERMINAL_CLEAR:
rogueStats.terminalClearLevel = 1;
return true;
case UPGRADE_BLOCK_STORM:
rogueStats.blockStormLevel = 1;
rogueStats.blockStormPiecesRemaining = 5;
nextTypes[0] = 0;
nextTypes[1] = 0;
nextTypes[2] = 0;
return true;
case UPGRADE_BLACK_HOLE:
rogueStats.blackHoleLevel = 1;
rogueStats.blackHoleCharges += 2 * applyCount;
return true;
case UPGRADE_AIR_RESHAPE:
rogueStats.reshapeLevel += applyCount;
rogueStats.reshapeCharges += 2 * applyCount;
return true;
case UPGRADE_VOID_CORE:
rogueStats.voidCoreLevel = 1;
return true;
default:
return false;
}
}
/**
* @brief 应用风险、升级规则和进化类强化。
* @param upgradeId 强化编号。
* @param applyCount 本次应用次数。
* @return 当前强化已处理返回 true。
*/
static bool ApplyRiskOrEvolutionUpgrade(int upgradeId, int applyCount)
{
switch (upgradeId)
{
case UPGRADE_DUAL_CHOICE:
rogueStats.dualChoiceLevel = 1;
rogueStats.requiredExp = rogueStats.requiredExp * 130 / 100;
if (rogueStats.requiredExp < 10)
{
rogueStats.requiredExp = 10;
}
return true;
case UPGRADE_DESTINY_WHEEL:
rogueStats.destinyWheelLevel = 1;
return true;
case UPGRADE_HIGH_PRESSURE:
rogueStats.highPressureLevel = 1;
rogueStats.scoreMultiplierPercent += 50;
rogueStats.expMultiplierPercent += 50;
currentFallInterval = GetRogueFallInterval();
return true;
case UPGRADE_TETRIS_GAMBLE:
rogueStats.tetrisGambleLevel = 1;
return true;
case UPGRADE_EXTREME_PLAYER:
rogueStats.extremePlayerLevel = 1;
rogueStats.extremeDangerTicks = 30;
rogueStats.extremeDangerLevel = 0;
currentFallInterval = GetRogueFallInterval();
return true;
case UPGRADE_UPGRADE_SHOCKWAVE:
rogueStats.upgradeShockwaveLevel = 1;
return true;
case UPGRADE_EVOLUTION_IMPACT:
rogueStats.evolutionImpactLevel = 1;
return true;
case UPGRADE_GAMBLER:
rogueStats.gamblerLevel += applyCount;
if (rogueStats.gamblerLevel > 4)
{
rogueStats.gamblerLevel = 4;
}
return true;
default:
return false;
}
}
/** /**
* @brief 根据强化编号把对应效果写入 Rogue 属性。 * @brief 根据强化编号把对应效果写入 Rogue 属性。
* *
@@ -1727,191 +2011,10 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
return; return;
} }
// 基础成长类强化直接叠加倍率或层数。 ApplyGrowthOrOperationUpgrade(upgradeId, applyCount) ||
switch (upgradeId) ApplySpecialPieceUpgrade(upgradeId, applyCount) ||
{ ApplyActiveSkillUpgrade(upgradeId, applyCount) ||
case UPGRADE_SCORE_MULTIPLIER: ApplyRiskOrEvolutionUpgrade(upgradeId, applyCount);
rogueStats.scoreMultiplierPercent += 20 * applyCount;
rogueStats.scoreUpgradeLevel += applyCount;
break;
case UPGRADE_COMBO_BONUS:
rogueStats.comboBonusStacks += applyCount;
break;
case UPGRADE_EXP_MULTIPLIER:
rogueStats.expMultiplierPercent += 25 * applyCount;
rogueStats.expUpgradeLevel += applyCount;
break;
case UPGRADE_SLOW_FALL:
rogueStats.slowFallStacks += applyCount;
if (rogueStats.slowFallStacks > 4)
{
rogueStats.slowFallStacks = 4;
}
currentFallInterval = GetRogueFallInterval();
break;
case UPGRADE_PREVIEW_PLUS_ONE:
if (rogueStats.previewCount < 2)
{
rogueStats.previewCount = 2;
}
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
break;
case UPGRADE_LAST_CHANCE:
rogueStats.lastChanceCount += applyCount;
rogueStats.lastChanceUpgradeLevel += applyCount;
break;
case UPGRADE_HOLD_UNLOCK:
rogueStats.holdUnlocked = 1;
holdUsedThisTurn = false;
break;
case UPGRADE_PRESSURE_RELIEF:
{
// 卸压清场立即从最高占用行开始删除,直接改善当前局面。
rogueStats.pressureReliefLevel += applyCount;
for (int i = 0; i < applyCount; i++)
{
int topOccupiedRow = GetTopOccupiedRow();
if (topOccupiedRow >= 0)
{
DeleteOneLine(topOccupiedRow);
}
}
break;
}
case UPGRADE_SWEEPER:
rogueStats.sweeperLevel += applyCount;
if (rogueStats.sweeperLevel > 4)
{
rogueStats.sweeperLevel = 4;
}
break;
case UPGRADE_EXPLOSIVE_PIECE:
rogueStats.explosiveLevel = 1;
break;
case UPGRADE_CHAIN_BLAST:
rogueStats.chainBlastLevel = 1;
break;
case UPGRADE_CHAIN_BOMB:
rogueStats.chainBombLevel = 1;
break;
case UPGRADE_LASER_PIECE:
rogueStats.laserLevel = 1;
break;
case UPGRADE_THUNDER_TETRIS:
rogueStats.thunderTetrisLevel = 1;
break;
case UPGRADE_THUNDER_LASER:
rogueStats.thunderLaserLevel = 1;
break;
case UPGRADE_FEVER_MODE:
rogueStats.feverLevel = 1;
break;
case UPGRADE_RAGE_STACK:
rogueStats.rageStackLevel = 1;
break;
case UPGRADE_INFINITE_FEVER:
rogueStats.infiniteFeverLevel = 1;
break;
case UPGRADE_SCREEN_BOMB:
rogueStats.screenBombLevel += applyCount;
rogueStats.screenBombCount += applyCount;
break;
case UPGRADE_TERMINAL_CLEAR:
rogueStats.terminalClearLevel = 1;
break;
case UPGRADE_DUAL_CHOICE:
rogueStats.dualChoiceLevel = 1;
rogueStats.requiredExp = rogueStats.requiredExp * 130 / 100;
if (rogueStats.requiredExp < 10)
{
rogueStats.requiredExp = 10;
}
break;
case UPGRADE_DESTINY_WHEEL:
rogueStats.destinyWheelLevel = 1;
break;
case UPGRADE_PERFECT_ROTATE:
rogueStats.perfectRotateLevel = 1;
break;
case UPGRADE_TIME_DILATION:
rogueStats.timeDilationLevel = 1;
currentFallInterval = GetRogueFallInterval();
break;
case UPGRADE_HIGH_PRESSURE:
rogueStats.highPressureLevel = 1;
rogueStats.scoreMultiplierPercent += 50;
rogueStats.expMultiplierPercent += 50;
currentFallInterval = GetRogueFallInterval();
break;
case UPGRADE_TETRIS_GAMBLE:
rogueStats.tetrisGambleLevel = 1;
break;
case UPGRADE_EXTREME_PLAYER:
rogueStats.extremePlayerLevel = 1;
rogueStats.extremeDangerTicks = 30;
rogueStats.extremeDangerLevel = 0;
currentFallInterval = GetRogueFallInterval();
break;
case UPGRADE_UPGRADE_SHOCKWAVE:
rogueStats.upgradeShockwaveLevel = 1;
break;
case UPGRADE_EVOLUTION_IMPACT:
rogueStats.evolutionImpactLevel = 1;
break;
case UPGRADE_CONTROL_MASTER:
rogueStats.controlMasterLevel = 1;
if (rogueStats.previewCount < 3)
{
rogueStats.previewCount++;
}
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
break;
case UPGRADE_BLOCK_STORM:
// 方块风暴会同时改写预览队列,确保玩家马上看到连续 I 块。
rogueStats.blockStormLevel = 1;
rogueStats.blockStormPiecesRemaining = 5;
nextTypes[0] = 0;
nextTypes[1] = 0;
nextTypes[2] = 0;
break;
case UPGRADE_CROSS_PIECE:
rogueStats.crossPieceLevel = 1;
break;
case UPGRADE_BLACK_HOLE:
rogueStats.blackHoleLevel = 1;
rogueStats.blackHoleCharges += 2 * applyCount;
break;
case UPGRADE_AIR_RESHAPE:
rogueStats.reshapeLevel += applyCount;
rogueStats.reshapeCharges += 2 * applyCount;
break;
case UPGRADE_RAINBOW_PIECE:
rogueStats.rainbowPieceLevel = 1;
break;
case UPGRADE_VOID_CORE:
rogueStats.voidCoreLevel = 1;
break;
case UPGRADE_STABLE_STRUCTURE:
rogueStats.stableStructureLevel += applyCount;
break;
case UPGRADE_DOUBLE_GROWTH:
rogueStats.doubleGrowthLevel = 1;
rogueStats.scoreMultiplierPercent += 15;
rogueStats.expMultiplierPercent += 15;
break;
case UPGRADE_PIECE_TUNING:
rogueStats.pieceTuningLevels[0] += applyCount;
break;
case UPGRADE_GAMBLER:
rogueStats.gamblerLevel += applyCount;
if (rogueStats.gamblerLevel > 4)
{
rogueStats.gamblerLevel = 4;
}
break;
default:
break;
}
} }
/** /**
@@ -2473,6 +2576,109 @@ void OpenUpgradeMenu()
currentScreen = SCREEN_UPGRADE; currentScreen = SCREEN_UPGRADE;
} }
/**
* @brief 结束一轮升级选择,如果还有待处理升级则刷新下一轮选项。
* @return 仍停留在升级界面返回 true;已回到游戏返回 false。
*/
static bool FinishUpgradeSelectionRound()
{
if (upgradeUiState.pendingCount > 0)
{
upgradeUiState.pendingCount--;
}
if (upgradeUiState.pendingCount > 0)
{
FillUpgradeOptions();
currentScreen = SCREEN_UPGRADE;
return true;
}
upgradeUiState.optionCount = 0;
upgradeUiState.picksRemaining = 0;
upgradeUiState.markedCount = 0;
currentScreen = SCREEN_PLAYING;
ResolvePendingUpgradeShockwave();
PlayPendingLineClearEffect();
return false;
}
/**
* @brief 构造单选强化的反馈正文。
* @param selectedOption 被选择的强化选项。
* @param gamblerSuffix 赌徒契约附加说明。
* @param feedbackDetail 返回反馈正文。
* @param feedbackDetailCapacity 反馈正文缓冲区长度。
*/
static void BuildSingleUpgradeFeedback(const UpgradeOption& selectedOption, const TCHAR* gamblerSuffix, TCHAR feedbackDetail[], int feedbackDetailCapacity)
{
if (selectedOption.id == UPGRADE_PIECE_TUNING && selectedOption.targetPieceType >= 0)
{
_stprintf_s(
feedbackDetail,
feedbackDetailCapacity,
_T("%s 块的生成概率提高%s"),
GetPieceShortName(0),
gamblerSuffix);
}
else
{
_stprintf_s(feedbackDetail, feedbackDetailCapacity, _T("%s%s"), selectedOption.description, gamblerSuffix);
}
if (selectedOption.cursed)
{
_stprintf_s(
feedbackDetail + lstrlen(feedbackDetail),
feedbackDetailCapacity - lstrlen(feedbackDetail),
_T(" 诅咒缠身:下一次升级所需 EXP 提高 25%。"));
}
}
/**
* @brief 应用多选升级中一个已标记的选项并拼接反馈。
* @param selectedOption 被应用的选项。
* @param appliedSelections 已应用选项数量。
* @param feedbackTitle 返回反馈标题。
* @param feedbackDetail 返回反馈正文。
*/
static void ApplyMarkedUpgradeOption(const UpgradeOption& selectedOption, int& appliedSelections, TCHAR feedbackTitle[], TCHAR feedbackDetail[])
{
TCHAR gamblerSuffix[64] = _T("");
int applyCount = RollGamblerApplyCount(gamblerSuffix, 64, true);
ApplyUpgradeById(selectedOption.id, selectedOption.targetPieceType, applyCount);
upgradeUiState.totalChosenCount++;
if (selectedOption.cursed)
{
ApplyDestinyCurse();
}
if (appliedSelections == 0)
{
_stprintf_s(feedbackTitle, 64, _T("获得强化:%s"), selectedOption.name);
}
else
{
_stprintf_s(feedbackTitle, 64, _T("获得强化 x%d"), appliedSelections + 1);
}
if (lstrlen(feedbackDetail) > 0)
{
_stprintf_s(feedbackDetail + lstrlen(feedbackDetail), 128 - lstrlen(feedbackDetail), _T(""));
}
_stprintf_s(
feedbackDetail + lstrlen(feedbackDetail),
128 - lstrlen(feedbackDetail),
_T("%s%s%s"),
selectedOption.name,
gamblerSuffix,
selectedOption.cursed ? _T(" [诅咒]") : _T(""));
appliedSelections++;
}
/** /**
* @brief 确认升级菜单中的选择并应用对应强化效果。 * @brief 确认升级菜单中的选择并应用对应强化效果。
* *
@@ -2507,59 +2713,11 @@ void ConfirmUpgradeSelection()
} }
UpgradeOption selectedOption = upgradeUiState.options[optionIndex]; UpgradeOption selectedOption = upgradeUiState.options[optionIndex];
TCHAR gamblerSuffix[64] = _T(""); ApplyMarkedUpgradeOption(selectedOption, appliedSelections, feedbackTitle, feedbackDetail);
int applyCount = RollGamblerApplyCount(gamblerSuffix, 64, true);
ApplyUpgradeById(selectedOption.id, selectedOption.targetPieceType, applyCount);
upgradeUiState.totalChosenCount++;
if (selectedOption.cursed)
{
ApplyDestinyCurse();
}
if (appliedSelections == 0)
{
_stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name);
}
else
{
_stprintf_s(feedbackTitle, _T("获得强化 x%d"), appliedSelections + 1);
}
if (lstrlen(feedbackDetail) > 0)
{
_stprintf_s(feedbackDetail + lstrlen(feedbackDetail), 128 - lstrlen(feedbackDetail), _T(""));
}
_stprintf_s(
feedbackDetail + lstrlen(feedbackDetail),
128 - lstrlen(feedbackDetail),
_T("%s%s%s"),
selectedOption.name,
gamblerSuffix,
selectedOption.cursed ? _T(" [诅咒]") : _T(""));
appliedSelections++;
} }
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12); SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
if (upgradeUiState.pendingCount > 0) FinishUpgradeSelectionRound();
{
upgradeUiState.pendingCount--;
}
if (upgradeUiState.pendingCount > 0)
{
FillUpgradeOptions();
currentScreen = SCREEN_UPGRADE;
return;
}
upgradeUiState.optionCount = 0;
upgradeUiState.picksRemaining = 0;
upgradeUiState.markedCount = 0;
currentScreen = SCREEN_PLAYING;
ResolvePendingUpgradeShockwave();
PlayPendingLineClearEffect();
return; return;
} }
@@ -2573,26 +2731,11 @@ void ConfirmUpgradeSelection()
TCHAR feedbackTitle[64]; TCHAR feedbackTitle[64];
TCHAR feedbackDetail[128]; TCHAR feedbackDetail[128];
_stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name); _stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name);
if (selectedOption.id == UPGRADE_PIECE_TUNING && selectedOption.targetPieceType >= 0) BuildSingleUpgradeFeedback(selectedOption, gamblerSuffix, feedbackDetail, 128);
{
_stprintf_s(
feedbackDetail,
_T("%s 块的生成概率提高%s"),
GetPieceShortName(0),
gamblerSuffix);
}
else
{
_stprintf_s(feedbackDetail, _T("%s%s"), selectedOption.description, gamblerSuffix);
}
if (selectedOption.cursed) if (selectedOption.cursed)
{ {
ApplyDestinyCurse(); ApplyDestinyCurse();
_stprintf_s(
feedbackDetail + lstrlen(feedbackDetail),
128 - lstrlen(feedbackDetail),
_T(" 诅咒缠身:下一次升级所需 EXP 提高 25%。"));
} }
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12); SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
@@ -2622,26 +2765,7 @@ void ConfirmUpgradeSelection()
return; return;
} }
// 连续升级时重新生成下一轮强化;全部完成后回到游戏并播放延迟效果。 FinishUpgradeSelectionRound();
if (upgradeUiState.pendingCount > 0)
{
upgradeUiState.pendingCount--;
}
if (upgradeUiState.pendingCount > 0)
{
FillUpgradeOptions();
currentScreen = SCREEN_UPGRADE;
return;
}
upgradeUiState.optionCount = 0;
upgradeUiState.picksRemaining = 0;
upgradeUiState.markedCount = 0;
currentScreen = SCREEN_PLAYING;
ResolvePendingUpgradeShockwave();
PlayPendingLineClearEffect();
} }
/** /**