diff --git a/src/include/Tetris.h b/src/include/Tetris.h index 3c750e3..5b21b2a 100644 --- a/src/include/Tetris.h +++ b/src/include/Tetris.h @@ -248,9 +248,11 @@ void HoldCurrentPiece(); void UseScreenBomb(); void UseBlackHole(); void UseAirReshape(); +void ResetPendingRogueVisualEvents(); void ResetVisualEffects(); bool TickVisualEffects(); void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared); +void PlayPendingLineClearEffect(); int GetRogueFallInterval(); int GetRoguePlayableHeight(); int GetRogueLockedRows(); diff --git a/src/source/TetrisLogic.cpp b/src/source/TetrisLogic.cpp index 25113a5..936ebd8 100644 --- a/src/source/TetrisLogic.cpp +++ b/src/source/TetrisLogic.cpp @@ -32,6 +32,10 @@ bool currentPieceIsCross = false; bool currentPieceIsRainbow = false; Point pendingChainBombCenter = { 0, 0 }; 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] = { @@ -386,6 +390,43 @@ static void AddBurstParticles(int boardX, int boardY, COLORREF baseColor, bool s } } +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) @@ -981,7 +1022,14 @@ int DeleteLines() } ApplyLineClearResult(clearedLines); - TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines); + if (currentScreen == SCREEN_UPGRADE) + { + QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines); + } + else + { + TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines); + } if (pendingChainBombFollowup && clearedLines > 0) { @@ -1107,7 +1155,11 @@ void Restart() feedbackState.visibleTicks = 0; feedbackState.title[0] = _T('\0'); feedbackState.detail[0] = _T('\0'); + ResetPendingRogueVisualEvents(); ResetVisualEffects(); + pendingLineClearEffectTicks = 0; + pendingLineClearEffectRowCount = 0; + pendingLineClearEffectLineCount = 0; holdType = -1; holdUsedThisTurn = false; RollCurrentPieceSpecialFlags(false); @@ -1140,6 +1192,10 @@ void ReturnToMainMenu() suspendFlag = false; gameOverFlag = false; ResetVisualEffects(); + ResetPendingRogueVisualEvents(); + pendingLineClearEffectTicks = 0; + pendingLineClearEffectRowCount = 0; + pendingLineClearEffectLineCount = 0; menuState.optionCount = 3; upgradeUiState.pendingCount = 0; upgradeUiState.picksRemaining = 0; diff --git a/src/source/TetrisRender.cpp b/src/source/TetrisRender.cpp index db6708c..6a126e3 100644 --- a/src/source/TetrisRender.cpp +++ b/src/source/TetrisRender.cpp @@ -1251,24 +1251,13 @@ void TDrawScreen(HDC hdc, HWND hWnd) { _stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6210\u957f\u6838\u5fc3 Lv.1\r\n")); } - int tunedPieceCount = 0; - for (int pieceType = 0; pieceType < 7; pieceType++) + if (rogueStats.pieceTuningLevels[0] > 0) { - if (rogueStats.pieceTuningLevels[pieceType] > 0) - { - tunedPieceCount++; - _stprintf_s( - upgradeSummary + lstrlen(upgradeSummary), - 512 - lstrlen(upgradeSummary), - _T("%s \u65b9\u5757\u6539\u9020 Lv.%d\r\n"), - (pieceType == 0 ? _T("I") : - pieceType == 1 ? _T("T") : - pieceType == 2 ? _T("L") : - pieceType == 3 ? _T("J") : - pieceType == 4 ? _T("O") : - pieceType == 5 ? _T("S") : _T("Z")), - rogueStats.pieceTuningLevels[pieceType]); - } + _stprintf_s( + upgradeSummary + lstrlen(upgradeSummary), + 512 - lstrlen(upgradeSummary), + _T("I \u65b9\u5757\u6539\u9020 Lv.%d\r\n"), + rogueStats.pieceTuningLevels[0]); } if (rogueStats.gamblerLevel > 0) { diff --git a/src/source/TetrisRogue.cpp b/src/source/TetrisRogue.cpp index fadb387..f5cf9f3 100644 --- a/src/source/TetrisRogue.cpp +++ b/src/source/TetrisRogue.cpp @@ -85,7 +85,7 @@ static const UpgradeEntry kUpgradePool[] = { UPGRADE_VOID_CORE, 1, 112, false, _T("虚空核心"), _T("进化"), _T("黑洞后额外生成 1 个彩虹方块;彩虹消行时撕开小型黑洞。") }, { UPGRADE_STABLE_STRUCTURE, -1, 72, true, _T("稳定结构"), _T("特殊"), _T("落地后有小概率填补邻近空洞,让阵型更加稳固。") }, { UPGRADE_DOUBLE_GROWTH, 1, 84, false, _T("成长核心"), _T("成长"), _T("永久获得 +15% 得分与 +15% EXP;每局只能选择一次。") }, - { UPGRADE_PIECE_TUNING, -1, 64, true, _T("方块改造"), _T("特殊"), _T("选择一种方块,降低它之后的出现率。") }, + { UPGRADE_PIECE_TUNING, -1, 64, true, _T("方块改造"), _T("特殊"), _T("固定提高 I 方块的生成概率。") }, { UPGRADE_GAMBLER, -1, 64, true, _T("赌徒契约"), _T("特殊"), _T("选择强化时,有概率效果翻倍,也有概率本次落空。") } }; @@ -94,6 +94,8 @@ static constexpr int kDifficultyStepMs = 30000; static constexpr int kDifficultySpeedStepMs = 18; static constexpr int kMaxRogueLockedRows = 4; static constexpr int kDifficultyLevelsPerLockedRow = 3; +static int pendingUpgradeShockwaveRows = 0; +static bool pendingEvolutionImpactShockwave = false; static int GetUpgradeCurrentLevel(int upgradeId); static bool IsUpgradePrerequisiteConsumed(int upgradeId); @@ -114,6 +116,7 @@ static int GetRogueScoreByLines(int linesCleared); static int GetRogueExpByLines(int linesCleared); static int ApplyLevelProgress(PlayerStats& stats); static int TriggerUpgradeShockwave(int rowsToClear); +static void ResolvePendingUpgradeShockwave(); static void FillUpgradeOptions(); static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount); static void ApplyDestinyCurse(); @@ -988,13 +991,10 @@ static int RollNextPieceType() if (currentMode == MODE_ROGUE) { - for (int i = 0; i < 7; i++) + weights[0] += rogueStats.pieceTuningLevels[0] * 35; + if (weights[0] > 320) { - weights[i] -= rogueStats.pieceTuningLevels[i] * 20; - if (weights[i] < 20) - { - weights[i] = 20; - } + weights[0] = 320; } } @@ -1194,35 +1194,16 @@ static void FillUpgradeOptions() if (pickedEntry.id == UPGRADE_PIECE_TUNING) { - int targetPieceType = rand() % 7; - bool duplicatedPiece = true; - int guard = 0; - - while (duplicatedPiece && guard < 16) - { - duplicatedPiece = false; - for (int optionIndex = 0; optionIndex < i; optionIndex++) - { - if (upgradeUiState.options[optionIndex].id == UPGRADE_PIECE_TUNING && - upgradeUiState.options[optionIndex].targetPieceType == targetPieceType) - { - duplicatedPiece = true; - targetPieceType = (targetPieceType + 1) % 7; - break; - } - } - guard++; - } - + int targetPieceType = 0; upgradeUiState.options[i].targetPieceType = targetPieceType; - upgradeUiState.options[i].currentLevel = rogueStats.pieceTuningLevels[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(targetPieceType)); + _T("%s 块的生成概率提高。"), + GetPieceShortName(0)); upgradeUiState.options[i].description = tuningDescriptions[i]; } @@ -1456,10 +1437,7 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount) rogueStats.expMultiplierPercent += 15; break; case UPGRADE_PIECE_TUNING: - if (targetPieceType >= 0 && targetPieceType < 7) - { - rogueStats.pieceTuningLevels[targetPieceType] += applyCount; - } + rogueStats.pieceTuningLevels[0] += applyCount; break; case UPGRADE_GAMBLER: rogueStats.gamblerLevel += applyCount; @@ -1737,22 +1715,8 @@ void ApplyLineClearResult(int linesCleared) if (shockwaveRows > 0) { - int clearedRows = TriggerUpgradeShockwave(shockwaveRows); - TCHAR shockwaveDetail[128]; - - if (rogueStats.evolutionImpactLevel > 0) - { - _stprintf_s( - shockwaveDetail, - _T("进化能量爆发,清除底部 %d 行,并进入 10 秒双倍 EXP。"), - clearedRows); - SetFeedbackMessage(_T("进化冲击"), shockwaveDetail, 14); - } - else - { - _stprintf_s(shockwaveDetail, _T("灵感涌现前,冲击波清除底部 %d 行。"), clearedRows); - SetFeedbackMessage(_T("升级冲击波"), shockwaveDetail, 12); - } + pendingUpgradeShockwaveRows = shockwaveRows; + pendingEvolutionImpactShockwave = rogueStats.evolutionImpactLevel > 0; } TCHAR feedbackTitle[64]; @@ -1781,6 +1745,53 @@ static void ApplyDestinyCurse() } } +static void ResolvePendingUpgradeShockwave() +{ + if (pendingUpgradeShockwaveRows <= 0) + { + return; + } + + int shockwaveRows = pendingUpgradeShockwaveRows; + bool evolutionImpact = pendingEvolutionImpactShockwave; + pendingUpgradeShockwaveRows = 0; + pendingEvolutionImpactShockwave = false; + + int clearedRows = TriggerUpgradeShockwave(shockwaveRows); + int effectRows[4] = {}; + int effectRowCount = clearedRows; + if (effectRowCount > 4) + { + effectRowCount = 4; + } + for (int i = 0; i < effectRowCount; i++) + { + effectRows[i] = GetRoguePlayableHeight() - 1 - i; + } + TriggerLineClearEffect(effectRows, effectRowCount, clearedRows); + + TCHAR shockwaveDetail[128]; + if (evolutionImpact) + { + _stprintf_s( + shockwaveDetail, + _T("进化能量爆发,清除底部 %d 行,并进入 10 秒双倍 EXP。"), + clearedRows); + SetFeedbackMessage(_T("进化冲击"), shockwaveDetail, 14); + } + else + { + _stprintf_s(shockwaveDetail, _T("灵感涌现后,冲击波清除底部 %d 行。"), clearedRows); + SetFeedbackMessage(_T("升级冲击波"), shockwaveDetail, 12); + } +} + +void ResetPendingRogueVisualEvents() +{ + pendingUpgradeShockwaveRows = 0; + pendingEvolutionImpactShockwave = false; +} + void OpenUpgradeMenu() { if (currentMode != MODE_ROGUE || upgradeUiState.pendingCount <= 0) @@ -1833,8 +1844,8 @@ void ConfirmUpgradeSelection() { _stprintf_s( feedbackDetail, - _T("%s 块的出现率降低%s"), - GetPieceShortName(selectedOption.targetPieceType), + _T("%s 块的生成概率提高%s"), + GetPieceShortName(0), gamblerSuffix); } else @@ -1894,6 +1905,8 @@ void ConfirmUpgradeSelection() upgradeUiState.picksRemaining = 0; currentScreen = SCREEN_PLAYING; + ResolvePendingUpgradeShockwave(); + PlayPendingLineClearEffect(); } void HoldCurrentPiece()