补全Rogue侧边HUD与局内结算反馈提示

This commit is contained in:
2026-04-24 20:02:43 +08:00
parent bbaa89e21d
commit 8ee470b059
5 changed files with 272 additions and 45 deletions
+3 -3
View File
@@ -178,8 +178,8 @@
目标:让系统信息足够清楚,不靠猜测理解机制。
- [ ] 右侧面板补充等级、EXP 条、强化列表、当前倍率
- [ ] 增加本次结算浮动提示:`+Score``+EXP``Level Up`
- [x] 右侧面板补充等级、EXP 条、强化列表、当前倍率
- [x] 增加本次结算浮动提示:`+Score``+EXP``Level Up`
- [ ] 为特殊强化增加简短提示文案
- [x] 增加暂停、升级、失败三种不同遮罩样式
- [ ] 增加占位图标、占位按钮、占位边框资源
@@ -187,7 +187,7 @@
完成标准:
- [ ] 玩家能看懂自己为什么升级、为什么得分变化
- [x] 玩家能看懂自己为什么升级、为什么得分变化
## 阶段 10:平衡与稳定性
+8
View File
@@ -79,6 +79,13 @@ struct UpgradeUiState
UpgradeOption options[3];
};
struct FeedbackState
{
int visibleTicks;
TCHAR title[64];
TCHAR detail[128];
};
enum ScreenState
{
SCREEN_MENU = 0,
@@ -107,6 +114,7 @@ extern MenuState menuState;
extern PlayerStats classicStats;
extern PlayerStats rogueStats;
extern UpgradeUiState upgradeUiState;
extern FeedbackState feedbackState;
extern int currentScreen;
extern int currentMode;
extern int currentFallInterval;
+17 -2
View File
@@ -145,8 +145,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
}
break;
case WM_TIMER:
if (wParam == GAME_TIMER_ID &&
currentScreen == SCREEN_PLAYING &&
if (wParam == GAME_TIMER_ID)
{
bool shouldRefresh = false;
if (feedbackState.visibleTicks > 0)
{
feedbackState.visibleTicks--;
shouldRefresh = true;
}
if (currentScreen == SCREEN_PLAYING &&
!suspendFlag &&
!gameOverFlag)
{
@@ -168,8 +177,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
ComputeTarget();
}
shouldRefresh = true;
}
if (shouldRefresh)
{
InvalidateRect(hWnd, nullptr, FALSE);
}
}
break;
case WM_SIZE:
InvalidateRect(hWnd, nullptr, FALSE);
+38 -2
View File
@@ -15,6 +15,7 @@ MenuState menuState = { 0, 2 };
PlayerStats classicStats = { 0, 1, 0, 0, 0 };
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
UpgradeUiState upgradeUiState = { 0, 0, 0, 0, {} };
FeedbackState feedbackState = { 0, _T(""), _T("") };
int currentScreen = SCREEN_MENU;
int currentMode = MODE_CLASSIC;
int currentFallInterval = 500;
@@ -246,6 +247,13 @@ static bool IsUpgradeSelectable(const UpgradeEntry& entry)
return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel;
}
static void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks)
{
feedbackState.visibleTicks = ticks;
lstrcpyn(feedbackState.title, title, sizeof(feedbackState.title) / sizeof(TCHAR));
lstrcpyn(feedbackState.detail, detail, sizeof(feedbackState.detail) / sizeof(TCHAR));
}
static void ResetNextQueue()
{
for (int i = 0; i < 3; i++)
@@ -410,9 +418,23 @@ static void ApplyLineClearResult(int linesCleared)
rogueStats.totalLinesCleared += linesCleared;
rogueStats.score += scoreGain;
rogueStats.exp += expGain;
upgradeUiState.pendingCount += ApplyLevelProgress(rogueStats);
int levelUps = ApplyLevelProgress(rogueStats);
upgradeUiState.pendingCount += levelUps;
tScore = rogueStats.score;
TCHAR feedbackTitle[64];
TCHAR feedbackDetail[128];
_stprintf_s(feedbackTitle, _T("+%d Score +%d EXP"), scoreGain, expGain);
if (levelUps > 0)
{
_stprintf_s(feedbackDetail, _T("Level Up x%d \u5f53\u524d Lv.%d"), levelUps, rogueStats.level);
}
else
{
_stprintf_s(feedbackDetail, _T("\u6d88\u884c %d \u8fde\u51fb %d"), linesCleared, rogueStats.comboChain);
}
SetFeedbackMessage(feedbackTitle, feedbackDetail, 10);
if (upgradeUiState.pendingCount > 0)
{
OpenUpgradeMenu();
@@ -677,6 +699,11 @@ void Fixing()
{
DeleteOneLine(nGameHeight - 1);
}
SetFeedbackMessage(
_T("\u6700\u540e\u4e00\u640f\u89e6\u53d1"),
_T("\u81ea\u52a8\u6e05\u9664\u5e95\u90e8 3 \u884c\uff0c\u672c\u5c40\u7ee7\u7eed\u3002"),
14);
}
else
{
@@ -810,6 +837,9 @@ void Restart()
upgradeUiState.optionCount = 0;
upgradeUiState.pendingCount = 0;
upgradeUiState.totalChosenCount = 0;
feedbackState.visibleTicks = 0;
feedbackState.title[0] = _T('\0');
feedbackState.detail[0] = _T('\0');
tScore = 0;
ResetNextQueue();
@@ -869,8 +899,14 @@ void ConfirmUpgradeSelection()
return;
}
ApplyUpgradeById(upgradeUiState.options[upgradeUiState.selectedIndex].id);
UpgradeOption selectedOption = upgradeUiState.options[upgradeUiState.selectedIndex];
ApplyUpgradeById(selectedOption.id);
upgradeUiState.totalChosenCount++;
TCHAR feedbackTitle[64];
TCHAR feedbackDetail[128];
_stprintf_s(feedbackTitle, _T("\u5df2\u83b7\u5f97\uff1a%s"), selectedOption.name);
_stprintf_s(feedbackDetail, _T("%s"), selectedOption.description);
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
if (upgradeUiState.pendingCount > 0)
{
+195 -27
View File
@@ -113,6 +113,19 @@ void TDrawScreen(HDC hdc, HWND hWnd)
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, textColor);
auto DrawPanelCard = [&](const RECT& rect, COLORREF fillColor, COLORREF borderColor, int radius)
{
HBRUSH cardBrush = CreateSolidBrush(fillColor);
HPEN cardPen = CreatePen(PS_SOLID, 1, borderColor);
HGDIOBJ savedPen = SelectObject(hdc, cardPen);
HGDIOBJ savedBrush = SelectObject(hdc, cardBrush);
RoundRect(hdc, rect.left, rect.top, rect.right, rect.bottom, SS(radius), SS(radius));
SelectObject(hdc, savedBrush);
SelectObject(hdc, savedPen);
DeleteObject(cardBrush);
DeleteObject(cardPen);
};
if (currentScreen == SCREEN_MENU)
{
RECT menuCard =
@@ -543,56 +556,173 @@ void TDrawScreen(HDC hdc, HWND hWnd)
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT overviewRect =
{
panelRect.left + SS(20),
panelRect.top + SS(92),
panelRect.right - SS(20),
panelRect.top + SS(208)
};
DrawPanelCard(overviewRect, RGB(255, 247, 250), RGB(233, 191, 208), 24);
TCHAR scoreText[64];
_stprintf_s(scoreText, _T("\u5f53\u524d\u5f97\u5206 %d"), tScore);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(104), scoreText, lstrlen(scoreText));
TextOut(hdc, overviewRect.left + SS(18), overviewRect.top + SS(16), scoreText, lstrlen(scoreText));
TCHAR modeText[64];
_stprintf_s(modeText, _T("\u5f53\u524d\u6a21\u5f0f %s"), currentMode == MODE_CLASSIC ? _T("\u7ecf\u5178") : _T("Rogue"));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(140), modeText, lstrlen(modeText));
TextOut(hdc, overviewRect.left + SS(18), overviewRect.top + SS(48), modeText, lstrlen(modeText));
TCHAR linesText[64];
int totalLines = (currentMode == MODE_CLASSIC) ? classicStats.totalLinesCleared : rogueStats.totalLinesCleared;
_stprintf_s(linesText, _T("\u7d2f\u8ba1\u6d88\u884c %d"), totalLines);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(176), linesText, lstrlen(linesText));
TextOut(hdc, overviewRect.left + SS(18), overviewRect.top + SS(80), linesText, lstrlen(linesText));
if (currentMode == MODE_ROGUE)
{
RECT progressRect =
{
panelRect.left + SS(20),
panelRect.top + SS(224),
panelRect.right - SS(20),
panelRect.top + SS(364)
};
DrawPanelCard(progressRect, RGB(255, 248, 251), RGB(233, 191, 208), 24);
TCHAR levelText[64];
_stprintf_s(levelText, _T("\u5f53\u524d\u7b49\u7ea7 %d"), rogueStats.level);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(212), levelText, lstrlen(levelText));
TextOut(hdc, progressRect.left + SS(18), progressRect.top + SS(16), levelText, lstrlen(levelText));
TCHAR expText[64];
_stprintf_s(expText, _T("EXP %d / %d"), rogueStats.exp, rogueStats.requiredExp);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(248), expText, lstrlen(expText));
TextOut(hdc, progressRect.left + SS(18), progressRect.top + SS(46), expText, lstrlen(expText));
TCHAR scoreMultiplierText[64];
_stprintf_s(scoreMultiplierText, _T("\u5206\u6570\u500d\u7387 %d%%"), rogueStats.scoreMultiplierPercent);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(284), scoreMultiplierText, lstrlen(scoreMultiplierText));
RECT expBarRect =
{
progressRect.left + SS(18),
progressRect.top + SS(82),
progressRect.right - SS(18),
progressRect.top + SS(106)
};
HBRUSH expTrackBrush = CreateSolidBrush(RGB(240, 220, 229));
FillRect(hdc, &expBarRect, expTrackBrush);
DeleteObject(expTrackBrush);
TCHAR expMultiplierText[64];
_stprintf_s(expMultiplierText, _T("EXP \u500d\u7387 %d%%"), rogueStats.expMultiplierPercent);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(320), expMultiplierText, lstrlen(expMultiplierText));
RECT expFillRect = expBarRect;
int expWidth = expBarRect.right - expBarRect.left;
if (rogueStats.requiredExp > 0)
{
expFillRect.right = expBarRect.left + expWidth * rogueStats.exp / rogueStats.requiredExp;
}
if (expFillRect.right < expFillRect.left)
{
expFillRect.right = expFillRect.left;
}
HBRUSH expFillBrush = CreateSolidBrush(accentColor);
FillRect(hdc, &expFillRect, expFillBrush);
DeleteObject(expFillBrush);
TCHAR fallText[64];
_stprintf_s(fallText, _T("\u4e0b\u843d\u95f4\u9694 %dms"), currentFallInterval);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(356), fallText, lstrlen(fallText));
RECT growthRect =
{
progressRect.left + SS(18),
progressRect.top + SS(118),
progressRect.right - SS(18),
progressRect.bottom - SS(16)
};
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(122, 95, 110));
TCHAR growthText[128];
_stprintf_s(
growthText,
_T("\u5206\u6570 %d%% EXP %d%% \u901f\u5ea6 %dms"),
rogueStats.scoreMultiplierPercent,
rogueStats.expMultiplierPercent,
currentFallInterval);
DrawText(hdc, growthText, -1, &growthRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
RECT combatRect =
{
panelRect.left + SS(20),
panelRect.top + SS(382),
panelRect.right - SS(20),
panelRect.top + SS(492)
};
DrawPanelCard(combatRect, RGB(255, 247, 250), RGB(233, 191, 208), 24);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
TextOut(hdc, combatRect.left + SS(18), combatRect.top + SS(16), _T("\u5f53\u524d\u6218\u6597\u72b6\u6001"), lstrlen(_T("\u5f53\u524d\u6218\u6597\u72b6\u6001")));
SelectObject(hdc, bodyFont);
TCHAR comboText[64];
_stprintf_s(comboText, _T("\u8fde\u51fb\u94fe %d \u5956\u52b1\u5c42\u6570 %d"), rogueStats.comboChain, rogueStats.comboBonusStacks);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(392), comboText, lstrlen(comboText));
TextOut(hdc, combatRect.left + SS(18), combatRect.top + SS(50), comboText, lstrlen(comboText));
TCHAR summaryText[64];
TCHAR summaryText[96];
_stprintf_s(
summaryText,
_T("\u9884\u89c8 %d \u4fdd\u547d %d"),
_T("\u9884\u89c8 %d \u4fdd\u547d %d \u5df2\u9009 %d"),
rogueStats.previewCount,
rogueStats.lastChanceCount);
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(428), summaryText, lstrlen(summaryText));
rogueStats.lastChanceCount,
upgradeUiState.totalChosenCount);
TextOut(hdc, combatRect.left + SS(18), combatRect.top + SS(82), summaryText, lstrlen(summaryText));
RECT upgradeListRect =
{
panelRect.left + SS(20),
panelRect.top + SS(510),
panelRect.right - SS(20),
panelRect.top + SS(676)
};
DrawPanelCard(upgradeListRect, RGB(255, 248, 251), RGB(233, 191, 208), 24);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
TextOut(hdc, upgradeListRect.left + SS(18), upgradeListRect.top + SS(16), _T("\u5df2\u83b7\u5f97\u5f3a\u5316"), lstrlen(_T("\u5df2\u83b7\u5f97\u5f3a\u5316")));
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(464), _T("\u53f3\u4fa7 HUD \u5df2\u5f00\u59cb\u663e\u793a Rogue \u5f53\u524d\u6210\u957f\u72b6\u6001"), lstrlen(_T("\u53f3\u4fa7 HUD \u5df2\u5f00\u59cb\u663e\u793a Rogue \u5f53\u524d\u6210\u957f\u72b6\u6001")));
RECT upgradeBodyRect =
{
upgradeListRect.left + SS(18),
upgradeListRect.top + SS(48),
upgradeListRect.right - SS(18),
upgradeListRect.bottom - SS(14)
};
TCHAR upgradeSummary[512];
upgradeSummary[0] = _T('\0');
if (rogueStats.scoreUpgradeLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u5206\u6570\u500d\u7387 Lv.%d\r\n"), rogueStats.scoreUpgradeLevel);
}
if (rogueStats.expUpgradeLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("EXP \u5f3a\u5316 Lv.%d\r\n"), rogueStats.expUpgradeLevel);
}
if (rogueStats.slowFallStacks > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6162\u901f\u4e0b\u843d Lv.%d\r\n"), rogueStats.slowFallStacks);
}
if (rogueStats.comboBonusStacks > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8fde\u51fb\u52a0\u6210 Lv.%d\r\n"), rogueStats.comboBonusStacks);
}
if (rogueStats.previewUpgradeLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u989d\u5916\u9884\u89c8 Lv.%d\r\n"), rogueStats.previewUpgradeLevel);
}
if (rogueStats.lastChanceUpgradeLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6700\u540e\u4e00\u640f Lv.1 \u5269\u4f59 %d \u6b21\r\n"), rogueStats.lastChanceCount);
}
if (lstrlen(upgradeSummary) == 0)
{
_stprintf_s(upgradeSummary, _T("\u6682\u672a\u9009\u62e9\u4efb\u4f55\u5f3a\u5316\u3002\r\n\u5347\u7ea7\u540e\u4f1a\u5728\u8fd9\u91cc\u7d2f\u79ef\u663e\u793a\u3002"));
}
DrawText(hdc, upgradeSummary, -1, &upgradeBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
}
@@ -600,13 +730,17 @@ void TDrawScreen(HDC hdc, HWND hWnd)
{
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(212), _T("\u7ecf\u5178\u6a21\u5f0f\u4fdd\u6301\u539f\u7248\u7b80\u5355\u8ba1\u5206"), lstrlen(_T("\u7ecf\u5178\u6a21\u5f0f\u4fdd\u6301\u539f\u7248\u7b80\u5355\u8ba1\u5206")));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(240), _T("\u6682\u4e0d\u63a5\u5165\u7b49\u7ea7\u548c\u5f3a\u5316\u7cfb\u7edf"), lstrlen(_T("\u6682\u4e0d\u63a5\u5165\u7b49\u7ea7\u548c\u5f3a\u5316\u7cfb\u7edf")));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(232), _T("\u7ecf\u5178\u6a21\u5f0f\u4fdd\u6301\u539f\u7248\u7b80\u5355\u8ba1\u5206"), lstrlen(_T("\u7ecf\u5178\u6a21\u5f0f\u4fdd\u6301\u539f\u7248\u7b80\u5355\u8ba1\u5206")));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(260), _T("\u6682\u4e0d\u63a5\u5165\u7b49\u7ea7\u548c\u5f3a\u5316\u7cfb\u7edf"), lstrlen(_T("\u6682\u4e0d\u63a5\u5165\u7b49\u7ea7\u548c\u5f3a\u5316\u7cfb\u7edf")));
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
}
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(430), _T("\u4e0b\u4e00\u4e2a\u65b9\u5757"), lstrlen(_T("\u4e0b\u4e00\u4e2a\u65b9\u5757")));
int previewTitleTop = (currentMode == MODE_ROGUE) ? panelRect.top + SS(690) : panelRect.top + SS(430);
int nextCardTop = (currentMode == MODE_ROGUE) ? panelRect.top + SS(724) : panelRect.top + SS(472);
int hintTop = (currentMode == MODE_ROGUE) ? panelRect.top + SS(818) : panelRect.top + SS(656);
TextOut(hdc, panelRect.left + SS(24), previewTitleTop, _T("\u4e0b\u4e00\u4e2a\u65b9\u5757"), lstrlen(_T("\u4e0b\u4e00\u4e2a\u65b9\u5757")));
int previewCount = 1;
if (currentMode == MODE_ROGUE)
@@ -627,9 +761,9 @@ void TDrawScreen(HDC hdc, HWND hWnd)
RECT nextCard =
{
panelRect.left + SS(24) + previewIndex * SS(94),
panelRect.top + SS(472),
nextCardTop,
panelRect.left + SS(24) + previewIndex * SS(94) + grid * 2 + SS(40),
panelRect.top + SS(472) + grid * 2 + SS(40)
nextCardTop + grid * 2 + SS(40)
};
HBRUSH nextCardBrush = CreateSolidBrush(RGB(255, 238, 244));
@@ -674,8 +808,42 @@ void TDrawScreen(HDC hdc, HWND hWnd)
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(656), _T("M \uff1a\u8fd4\u56de\u83dc\u5355"), lstrlen(_T("M \uff1a\u8fd4\u56de\u83dc\u5355")));
TextOut(hdc, panelRect.left + SS(24), panelRect.top + SS(688), _T("\u89c4\u5219\u8bf4\u660e\u8bf7\u5728\u4e3b\u83dc\u5355\u8fdb\u5165"), lstrlen(_T("\u89c4\u5219\u8bf4\u660e\u8bf7\u5728\u4e3b\u83dc\u5355\u8fdb\u5165")));
TextOut(hdc, panelRect.left + SS(24), hintTop, _T("M \uff1a\u8fd4\u56de\u83dc\u5355"), lstrlen(_T("M \uff1a\u8fd4\u56de\u83dc\u5355")));
TextOut(hdc, panelRect.left + SS(24), hintTop + SS(32), _T("\u89c4\u5219\u8bf4\u660e\u8bf7\u5728\u4e3b\u83dc\u5355\u8fdb\u5165"), lstrlen(_T("\u89c4\u5219\u8bf4\u660e\u8bf7\u5728\u4e3b\u83dc\u5355\u8fdb\u5165")));
if (feedbackState.visibleTicks > 0)
{
RECT feedbackRect =
{
gameRect.left + SS(16),
gameRect.top + SS(16),
gameRect.right - SS(16),
gameRect.top + SS(98)
};
DrawPanelCard(feedbackRect, RGB(255, 245, 249), RGB(232, 170, 194), 22);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, titleColor);
RECT titleRect =
{
feedbackRect.left + SS(16),
feedbackRect.top + SS(12),
feedbackRect.right - SS(16),
feedbackRect.top + SS(40)
};
DrawText(hdc, feedbackState.title, -1, &titleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(116, 90, 104));
RECT detailRect =
{
feedbackRect.left + SS(16),
feedbackRect.top + SS(42),
feedbackRect.right - SS(16),
feedbackRect.bottom - SS(12)
};
DrawText(hdc, feedbackState.detail, -1, &detailRect, DT_LEFT | DT_WORDBREAK);
}
if (suspendFlag || gameOverFlag)
{