diff --git a/src/include/Tetris.h b/src/include/Tetris.h index 5cd1d96..3a6577d 100644 --- a/src/include/Tetris.h +++ b/src/include/Tetris.h @@ -282,6 +282,7 @@ void ReturnToMainMenu(); void ReviveAfterVideo(); void StartRogueSkillDemo(); void StartRogueSkillDemoAt(int demoIndex); +void RestartCurrentRogueSkillDemo(); bool IsRogueSkillDemoMode(); bool TickRogueSkillDemo(); void AdvanceRogueSkillDemo(); diff --git a/src/source/Tetris.cpp b/src/source/Tetris.cpp index b14dd74..839f332 100644 --- a/src/source/Tetris.cpp +++ b/src/source/Tetris.cpp @@ -1523,33 +1523,35 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (IsRogueSkillDemoMode()) { - if (wParam == VK_RETURN || wParam == VK_SPACE) + if (wParam == 'N') { AdvanceRogueSkillDemo(); InvalidateRect(hWnd, nullptr, FALSE); + break; } else if (wParam == 'R') { - StartRogueSkillDemo(); + RestartCurrentRogueSkillDemo(); ResetGameTimer(hWnd); InvalidateRect(hWnd, nullptr, FALSE); + break; } else if (wParam == VK_ESCAPE || wParam == VK_BACK || wParam == 'M') { ReturnToMainMenu(); InvalidateRect(hWnd, nullptr, FALSE); + break; } - break; } - if (wParam == 'M') + if (!IsRogueSkillDemoMode() && wParam == 'M') { ReturnToMainMenu(); InvalidateRect(hWnd, nullptr, FALSE); break; } - if (wParam == 'R') + if (!IsRogueSkillDemoMode() && wParam == 'R') { StartGameWithMode(currentMode); ResetGameTimer(hWnd); @@ -1557,7 +1559,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; } - if (wParam == 'P') + if (!IsRogueSkillDemoMode() && wParam == 'P') { suspendFlag = !suspendFlag; InvalidateRect(hWnd, nullptr, FALSE); diff --git a/src/source/TetrisRender.cpp b/src/source/TetrisRender.cpp index f50daf8..b439c24 100644 --- a/src/source/TetrisRender.cpp +++ b/src/source/TetrisRender.cpp @@ -683,7 +683,7 @@ void TDrawScreen(HDC hdc, HWND hWnd) _T("\u7ecf\u5178\u6a21\u5f0f\u3001Rogue \u6a21\u5f0f\u548c\u590d\u6d3b\u89c4\u5219\u6982\u89c8\u3002"), _T("\u79fb\u52a8\u3001\u65cb\u8f6c\u3001\u786c\u964d\u3001Hold \u4e0e\u6280\u80fd\u5feb\u6377\u952e\u3002"), _T("\u67e5\u770b Rogue \u6a21\u5f0f\u5168\u90e8\u5f3a\u5316\u7684\u7b80\u8981\u6548\u679c\u3002"), - _T("\u8fdb\u5165\u81ea\u52a8\u8f6e\u64ad\u6f14\u793a\uff0c\u4f9d\u6b21\u5c55\u793a Rogue \u4e3b\u8981\u6280\u80fd\u548c\u68cb\u76d8\u6548\u679c\u3002") + _T("\u9009\u62e9\u4e00\u4e2a\u9884\u8bbe\u573a\u666f\uff0c\u4eb2\u81ea\u64cd\u4f5c\u89e6\u53d1 Rogue \u6280\u80fd\u6548\u679c\u3002") }; int optionHeight = SS(100); @@ -1232,7 +1232,7 @@ void TDrawScreen(HDC hdc, HWND hWnd) : (helpState.currentPage == 4 ? _T("\u5de6\u53f3\u65b9\u5411\u952e / A D \u5207\u6362\uff0cEsc / Backspace / M \u8fd4\u56de\u4e3b\u83dc\u5355") : (helpState.currentPage == 5 - ? _T("\u70b9\u51fb\u6280\u80fd\u540d\u79f0\u6216 Enter \u6f14\u793a\uff0c\u6eda\u8f6e\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9") + ? _T("\u70b9\u51fb\u6280\u80fd\u540d\u79f0\u6216 Enter \u8fdb\u5165\u64cd\u4f5c\u573a\u666f\uff0c\u6eda\u8f6e\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9") : _T("\u9f20\u6807\u6eda\u8f6e\u4e0a\u4e0b\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9"))); DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); @@ -2426,15 +2426,30 @@ void TDrawScreen(HDC hdc, HWND hWnd) }; if (currentMode == MODE_ROGUE) { - DrawText( - hdc, - _T("\u79fb\u52a8\uff1a\u2190/\u2192 \u6216 A/D\r\n") - _T("\u65cb\u8f6c/\u4e0b\u843d\uff1a\u2191/W\u3001\u2193/S\u3001Space\r\n") - _T("战局:P 暂停 R 重开 M 菜单\r\n") - _T("技能:C 备用仓 Z 黑洞 X 炸弹 V 换形"), - -1, - &controlBodyRect, - DT_LEFT | DT_TOP | DT_WORDBREAK); + if (IsRogueSkillDemoMode()) + { + DrawText( + hdc, + _T("\u79fb\u52a8\uff1a\u2190/\u2192 \u6216 A/D\r\n") + _T("\u65cb\u8f6c/\u4e0b\u843d\uff1a\u2191/W\u3001\u2193/S\u3001Space\r\n") + _T("演示:R 重置当前 N 下一个 M 菜单\r\n") + _T("技能:C 备用仓 Z 黑洞 X 炸弹 V 换形"), + -1, + &controlBodyRect, + DT_LEFT | DT_TOP | DT_WORDBREAK); + } + else + { + DrawText( + hdc, + _T("\u79fb\u52a8\uff1a\u2190/\u2192 \u6216 A/D\r\n") + _T("\u65cb\u8f6c/\u4e0b\u843d\uff1a\u2191/W\u3001\u2193/S\u3001Space\r\n") + _T("战局:P 暂停 R 重开 M 菜单\r\n") + _T("技能:C 备用仓 Z 黑洞 X 炸弹 V 换形"), + -1, + &controlBodyRect, + DT_LEFT | DT_TOP | DT_WORDBREAK); + } } else { diff --git a/src/source/TetrisRogue.cpp b/src/source/TetrisRogue.cpp index c7aaf77..348bd36 100644 --- a/src/source/TetrisRogue.cpp +++ b/src/source/TetrisRogue.cpp @@ -142,22 +142,22 @@ struct RogueDemoStep static const RogueDemoStep kRogueDemoSteps[] = { - { DEMO_HOLD, _T("备用仓"), _T("演示 C / Shift 暂存当前方块,并换出备用方块。") }, - { DEMO_BLACK_HOLE, _T("黑洞奇点"), _T("吞噬棋盘中数量最多的一种固定方块。") }, - { DEMO_SCREEN_BOMB, _T("清屏炸弹"), _T("主动引爆后清理可玩区域底部 5 行。") }, - { DEMO_AIR_RESHAPE, _T("空中换形"), _T("把正在下落的方块重塑为 I 块。") }, - { DEMO_EXPLOSIVE, _T("爆破核心"), _T("爆破方块落地后清除 3x3 区域。") }, - { DEMO_CHAIN_BOMB, _T("连环炸弹"), _T("爆破范围进化为 5x5。") }, - { DEMO_LASER, _T("棱镜激光"), _T("激光方块落地后清除整列。") }, - { DEMO_CROSS, _T("十字方块"), _T("十字方块同时清除所在行与所在列。") }, - { DEMO_RAINBOW, _T("彩虹方块"), _T("按中心行主色清除,并把覆盖行染成主色。") }, - { DEMO_VOID_CORE, _T("虚空核心"), _T("黑洞生效后追加召来彩虹方块。") }, - { DEMO_SWEEPER, _T("底线清道夫"), _T("消行充能后自动清扫底部。") }, - { DEMO_LAST_CHANCE, _T("最后一搏"), _T("濒死时自动清理底部 3 行。") }, - { DEMO_TIME_DILATION, _T("时间缓流"), _T("堆叠过高时临时降低下落速度。") }, - { DEMO_UPGRADE_SHOCKWAVE, _T("升级冲击波"), _T("升级后清除底部多行。") }, - { DEMO_BLOCK_STORM, _T("方块风暴"), _T("接下来多个方块固定变为 I 块。") }, - { DEMO_STABLE_STRUCTURE, _T("稳定结构"), _T("落地后填补局部空洞,让结构更平整。") } + { DEMO_HOLD, _T("备用仓"), _T("按 C / Shift 换出备用 I 块,再用 Space 填平底行缺口。") }, + { DEMO_BLACK_HOLE, _T("黑洞奇点"), _T("按 Z 吞噬场上最多的粉色方块,观察棋盘重力补位。") }, + { DEMO_SCREEN_BOMB, _T("清屏炸弹"), _T("按 X 主动引爆,清理底部 5 行高压区。") }, + { DEMO_AIR_RESHAPE, _T("空中换形"), _T("按 V 把当前方块变为 I 块,再用 Space 完成四消。") }, + { DEMO_EXPLOSIVE, _T("爆破核心"), _T("用方向键微调红框方块,Space 落地后清除 3x3 区域。") }, + { DEMO_CHAIN_BOMB, _T("连环炸弹"), _T("Space 落下红框方块,演示更大的连环爆破范围。") }, + { DEMO_LASER, _T("棱镜激光"), _T("把青色边框方块落到中列,落地后贯穿整列。") }, + { DEMO_CROSS, _T("十字方块"), _T("把绿色边框方块落到交叉点,清除一行一列。") }, + { DEMO_RAINBOW, _T("彩虹方块"), _T("把紫色边框方块落到彩色层,按中心行主色触发清除。") }, + { DEMO_VOID_CORE, _T("虚空核心"), _T("按 Z 释放黑洞后,下一枚方块会被召唤成彩虹方块。") }, + { DEMO_SWEEPER, _T("底线清道夫"), _T("Space 消去底行,充能满后自动再清扫底部。") }, + { DEMO_LAST_CHANCE, _T("最后一搏"), _T("Space 固定触顶方块,濒死时自动清理底部 3 行。") }, + { DEMO_TIME_DILATION, _T("时间缓流"), _T("高堆叠开局会自动减速,玩家可用慢速窗口整理局面。") }, + { DEMO_UPGRADE_SHOCKWAVE, _T("升级冲击波"), _T("Space 消一行立刻升级,不弹强化菜单并清除底部 2 行。") }, + { DEMO_BLOCK_STORM, _T("方块风暴"), _T("连续用 I 块清理竖井,后续预览也固定为 I 块。") }, + { DEMO_STABLE_STRUCTURE, _T("稳定结构"), _T("Space 落地后只触发稳定结构,自动填补局部空洞。") } }; static constexpr int kRogueDemoStepCount = sizeof(kRogueDemoSteps) / sizeof(kRogueDemoSteps[0]); @@ -194,6 +194,7 @@ static void ResetRogueDemoBoard(); static void ShowRogueDemoFloatingName(const TCHAR* name); static void FillRogueDemoCell(int y, int x, int value); static void FillRogueDemoRows(int firstRow, int lastRow, int baseValue); +static void FillRogueDemoRowExcept(int row, int gapStart, int gapWidth, int baseValue); static void SetRogueDemoCurrentPiece(int pieceType, int pieceState, int x, int y); static void StartRogueSkillDemoInternal(int demoIndex, bool autoAdvance); @@ -1379,6 +1380,10 @@ int TryStabilizeBoard() { triggerChance = 70; } + if (rogueDemoMode && kRogueDemoSteps[rogueDemoStepIndex].kind == DEMO_STABLE_STRUCTURE) + { + triggerChance = 100; + } if ((rand() % 100) >= triggerChance) { @@ -1891,7 +1896,7 @@ void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain, } } - if (allowLevelProgress) + if (allowLevelProgress && !rogueDemoMode) { int levelUps = ApplyLevelProgress(rogueStats); if (levelUps > 0) @@ -1911,6 +1916,10 @@ void CheckRogueLevelProgress() { return; } + if (rogueDemoMode) + { + return; + } int levelUps = ApplyLevelProgress(rogueStats); if (levelUps <= 0) @@ -2227,7 +2236,6 @@ void ApplyLineClearResult(int linesCleared) } int levelUps = ApplyLevelProgress(rogueStats); - upgradeUiState.pendingCount += levelUps; tScore = rogueStats.score; if (levelUps > 0) @@ -2253,15 +2261,34 @@ void ApplyLineClearResult(int linesCleared) TCHAR feedbackTitle[64]; TCHAR feedbackDetail[128]; _stprintf_s(feedbackTitle, _T("灵感涌现 x%d"), levelUps); - _stprintf_s( - feedbackDetail, - _T("等级 Lv.%d EXP %d/%d 选择新的强化"), - rogueStats.level, - rogueStats.exp, - rogueStats.requiredExp); + if (rogueDemoMode) + { + _stprintf_s( + feedbackDetail, + _T("演示升级 Lv.%d,普通强化菜单已关闭。"), + rogueStats.level); + } + else + { + _stprintf_s( + feedbackDetail, + _T("等级 Lv.%d EXP %d/%d 选择新的强化"), + rogueStats.level, + rogueStats.exp, + rogueStats.requiredExp); + } SetFeedbackMessage(feedbackTitle, feedbackDetail, 10); - OpenUpgradeMenu(); + if (rogueDemoMode) + { + ResolvePendingUpgradeShockwave(); + PlayPendingLineClearEffect(); + } + else + { + upgradeUiState.pendingCount += levelUps; + OpenUpgradeMenu(); + } } currentFallInterval = GetRogueFallInterval(); @@ -2830,7 +2857,7 @@ const TCHAR* GetCurrentRogueSkillDemoName() */ void StartRogueSkillDemo() { - StartRogueSkillDemoInternal(0, true); + StartRogueSkillDemoInternal(0, false); } /** @@ -2841,6 +2868,20 @@ void StartRogueSkillDemoAt(int demoIndex) StartRogueSkillDemoInternal(demoIndex, false); } +/** + * @brief 重置当前 Rogue 技能演示场景,保留当前选中的技能条目。 + */ +void RestartCurrentRogueSkillDemo() +{ + if (!rogueDemoMode) + { + StartRogueSkillDemoAt(0); + return; + } + + StartRogueSkillDemoInternal(rogueDemoStepIndex, false); +} + /** * @brief 进入 Rogue 技能演示并配置起始条目和是否自动轮播。 */ @@ -2996,6 +3037,29 @@ static void FillRogueDemoRows(int firstRow, int lastRow, int baseValue) } } +/** + * @brief 填满演示棋盘中的一行,并在指定范围留下空位。 + */ +static void FillRogueDemoRowExcept(int row, int gapStart, int gapWidth, int baseValue) +{ + if (row < 0 || row >= nGameHeight) + { + return; + } + + for (int x = 0; x < nGameWidth; x++) + { + if (x >= gapStart && x < gapStart + gapWidth) + { + workRegion[row][x] = 0; + } + else + { + workRegion[row][x] = 1 + ((baseValue + row + x) % 7); + } + } +} + /** * @brief 设置演示模式下当前下落方块的类型、旋转和位置。 */ @@ -3030,54 +3094,62 @@ static void ApplyRogueSkillDemoStep() rogueStats.holdUnlocked = 1; rogueStats.controlMasterLevel = 1; holdType = 0; - SetRogueDemoCurrentPiece(2, 0, 3, 1); - HoldCurrentPiece(); + FillRogueDemoRows(16, 18, 1); + FillRogueDemoRowExcept(19, 3, 4, 1); + SetRogueDemoCurrentPiece(2, 0, 3, 0); break; case DEMO_BLACK_HOLE: rogueStats.blackHoleLevel = 1; rogueStats.blackHoleCharges = 1; - FillRogueDemoRows(8, 19, 2); + FillRogueDemoRows(9, 19, 2); for (int y = 8; y < nGameHeight; y++) { - for (int x = 0; x < nGameWidth; x += 3) + for (int x = 1; x < nGameWidth; x += 3) { workRegion[y][x] = 2; } } - UseBlackHole(); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_SCREEN_BOMB: rogueStats.screenBombLevel = 1; rogueStats.screenBombCount = 1; - FillRogueDemoRows(12, 19, 0); - UseScreenBomb(); + FillRogueDemoRows(11, 19, 0); + SetRogueDemoCurrentPiece(4, 0, 4, 0); break; case DEMO_AIR_RESHAPE: rogueStats.reshapeLevel = 1; rogueStats.reshapeCharges = 1; - FillRogueDemoRows(15, 19, 3); - SetRogueDemoCurrentPiece(1, 0, 3, 4); - UseAirReshape(); + FillRogueDemoRows(16, 19, 3); + for (int y = 10; y < nGameHeight; y++) + { + FillRogueDemoCell(y, 9, 0); + } + SetRogueDemoCurrentPiece(1, 0, 6, 0); break; case DEMO_EXPLOSIVE: - FillRogueDemoRows(11, 17, 1); + FillRogueDemoRows(12, 19, 1); + FillRogueDemoCell(14, 4, 0); + FillRogueDemoCell(15, 5, 0); + FillRogueDemoCell(16, 4, 0); currentPieceIsExplosive = true; - SetRogueDemoCurrentPiece(5, 0, 3, 8); - ClearExplosiveAreaAt(14, 5); - ApplyBoardGravity(); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_CHAIN_BOMB: rogueStats.chainBombLevel = 1; - FillRogueDemoRows(10, 18, 4); + FillRogueDemoRows(10, 19, 4); + for (int y = 13; y <= 17; y++) + { + FillRogueDemoCell(y, 3, 0); + FillRogueDemoCell(y, 6, 0); + } currentPieceIsExplosive = true; - SetRogueDemoCurrentPiece(5, 0, 3, 8); - ClearExplosiveAreaAt(14, 5); - ApplyBoardGravity(); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_LASER: @@ -3091,9 +3163,7 @@ static void ApplyRogueSkillDemoStep() } } currentPieceIsLaser = true; - SetRogueDemoCurrentPiece(0, 1, 3, 4); - ClearColumnAt(5); - ApplyBoardGravity(); + SetRogueDemoCurrentPiece(0, 1, 3, 0); break; case DEMO_CROSS: @@ -3103,10 +3173,7 @@ static void ApplyRogueSkillDemoStep() FillRogueDemoCell(y, 4, 3); } currentPieceIsCross = true; - SetRogueDemoCurrentPiece(1, 0, 3, 8); - ClearRowAt(14); - ClearColumnAtWithColor(4, RGB(196, 255, 132)); - ApplyBoardGravity(); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_RAINBOW: @@ -3116,12 +3183,7 @@ static void ApplyRogueSkillDemoStep() workRegion[13][x] = (x < 6) ? 3 : 5; } currentPieceIsRainbow = true; - SetRogueDemoCurrentPiece(1, 0, 3, 8); - { - int recoloredCount = 0; - TriggerRainbowColorShift(13, 11, 15, recoloredCount); - } - ApplyBoardGravity(); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_VOID_CORE: @@ -3136,29 +3198,22 @@ static void ApplyRogueSkillDemoStep() workRegion[y][x] = 6; } } - UseBlackHole(); - SetFeedbackMessage(_T("虚空核心"), _T("黑洞吞噬后,下一枚特殊方块将变为彩虹方块。"), 12); + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; case DEMO_SWEEPER: rogueStats.sweeperLevel = 4; - FillRogueDemoRows(13, 19, 1); - ClearRowAt(GetRoguePlayableHeight() - 1); - ApplyBoardGravity(); - SetFeedbackMessage(_T("底线清道夫"), _T("清道夫完成充能,自动扫掉底部压力行。"), 12); + FillRogueDemoRows(15, 18, 1); + FillRogueDemoRowExcept(19, 3, 4, 3); + SetRogueDemoCurrentPiece(0, 0, 3, 0); break; case DEMO_LAST_CHANCE: rogueStats.lastChanceUpgradeLevel = 1; rogueStats.lastChanceCount = 1; - FillRogueDemoRows(2, 7, 5); + FillRogueDemoRows(0, 5, 5); FillRogueDemoRows(14, 19, 3); - for (int i = 0; i < 3; i++) - { - DeleteOneLine(GetRoguePlayableHeight() - 1); - } - TriggerLineClearEffect(nullptr, 0, 3); - SetFeedbackMessage(_T("最后一搏"), _T("触顶前自动清除底部 3 行,保留继续操作空间。"), 12); + SetRogueDemoCurrentPiece(4, 0, 3, -1); break; case DEMO_TIME_DILATION: @@ -3166,22 +3221,28 @@ static void ApplyRogueSkillDemoStep() rogueStats.timeDilationTicks = 8; FillRogueDemoRows(3, 19, 4); currentFallInterval = GetRogueFallInterval(); + SetRogueDemoCurrentPiece(0, 1, 3, 0); SetFeedbackMessage(_T("时间缓流"), _T("堆叠高度危险,短时间内下落速度降低。"), 12); break; case DEMO_UPGRADE_SHOCKWAVE: rogueStats.upgradeShockwaveLevel = 1; - FillRogueDemoRows(12, 19, 2); - TriggerUpgradeShockwave(2); - ApplyBoardGravity(); - SetFeedbackMessage(_T("升级冲击波"), _T("升级完成后,冲击波清除底部 2 行。"), 12); + rogueStats.exp = 0; + rogueStats.requiredExp = 12; + FillRogueDemoRows(12, 18, 2); + FillRogueDemoRowExcept(19, 3, 4, 6); + SetRogueDemoCurrentPiece(0, 0, 3, 0); break; case DEMO_BLOCK_STORM: rogueStats.blockStormLevel = 1; rogueStats.blockStormPiecesRemaining = 5; FillRogueDemoRows(15, 19, 0); - SetRogueDemoCurrentPiece(0, 0, 3, 2); + for (int y = 8; y < nGameHeight; y++) + { + FillRogueDemoCell(y, 5, 0); + } + SetRogueDemoCurrentPiece(0, 1, 3, 0); nextTypes[0] = 0; nextTypes[1] = 0; nextTypes[2] = 0; @@ -3191,11 +3252,10 @@ static void ApplyRogueSkillDemoStep() case DEMO_STABLE_STRUCTURE: rogueStats.stableStructureLevel = 4; FillRogueDemoRows(14, 19, 1); - workRegion[13][3] = 2; workRegion[15][4] = 0; workRegion[16][6] = 0; - TryStabilizeBoard(); - SetFeedbackMessage(_T("稳定结构"), _T("自动填补局部空洞,让堆叠更稳。"), 12); + workRegion[17][5] = 0; + SetRogueDemoCurrentPiece(1, 0, 3, 0); break; default: diff --git a/微信图片_20260428171006_303_11.jpg b/微信图片_20260428171006_303_11.jpg deleted file mode 100644 index 765764b..0000000 Binary files a/微信图片_20260428171006_303_11.jpg and /dev/null differ