优化选择强化逻辑

This commit is contained in:
2026-04-26 19:30:32 +08:00
parent 34c36306fe
commit a5747ff55c
6 changed files with 280 additions and 72 deletions
+2
View File
@@ -226,6 +226,8 @@ extern Point point;
extern Point target; extern Point target;
extern MenuState menuState; extern MenuState menuState;
extern HelpState helpState; extern HelpState helpState;
extern int helpScrollOffset;
extern int upgradeListScrollOffset;
extern PlayerStats classicStats; extern PlayerStats classicStats;
extern PlayerStats rogueStats; extern PlayerStats rogueStats;
extern UpgradeUiState upgradeUiState; extern UpgradeUiState upgradeUiState;
+68
View File
@@ -29,6 +29,43 @@ static bool FileExists(const std::wstring& path);
static void StopBackgroundMusic(); static void StopBackgroundMusic();
static void StartBackgroundMusic(); static void StartBackgroundMusic();
/**
* @brief 将指定滚动偏移按步长调整,并限制在非负范围内。
*/
static void AdjustScrollOffset(int& scrollOffset, int delta)
{
scrollOffset += delta;
if (scrollOffset < 0)
{
scrollOffset = 0;
}
if (scrollOffset > 2400)
{
scrollOffset = 2400;
}
}
/**
* @brief 按当前窗口缩放返回一次滚动操作的像素距离。
*/
static int GetScrollStep(HWND hWnd, int baseStep)
{
RECT clientRect;
GetClientRect(hWnd, &clientRect);
int clientWidth = clientRect.right - clientRect.left;
int clientHeight = clientRect.bottom - clientRect.top;
int scaleX = MulDiv(clientWidth, 1000, WINDOW_CLIENT_WIDTH);
int scaleY = MulDiv(clientHeight, 1000, WINDOW_CLIENT_HEIGHT);
int scale = (scaleX < scaleY) ? scaleX : scaleY;
if (scale < 500)
{
scale = 500;
}
return MulDiv(baseStep, scale, 1000);
}
struct LayoutMetrics struct LayoutMetrics
{ {
int scale; int scale;
@@ -886,6 +923,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
helpState.selectedIndex = i; helpState.selectedIndex = i;
helpState.currentPage = i + 1; helpState.currentPage = i + 1;
helpScrollOffset = 0;
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
} }
@@ -899,6 +937,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
else if (IsPointInRect(GetHelpBackHintRect(hWnd), mouseX, mouseY)) else if (IsPointInRect(GetHelpBackHintRect(hWnd), mouseX, mouseY))
{ {
helpState.currentPage = 0; helpState.currentPage = 0;
helpScrollOffset = 0;
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
break; break;
@@ -1013,6 +1052,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return DefWindowProc(hWnd, message, wParam, lParam); return DefWindowProc(hWnd, message, wParam, lParam);
} }
case WM_MOUSEWHEEL:
{
int wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
int direction = (wheelDelta > 0) ? -1 : 1;
int scrollStep = GetScrollStep(hWnd, 64);
if (currentScreen == SCREEN_RULES && helpState.currentPage != 0)
{
AdjustScrollOffset(helpScrollOffset, direction * scrollStep);
InvalidateRect(hWnd, nullptr, FALSE);
break;
}
if (currentScreen == SCREEN_PLAYING && currentMode == MODE_ROGUE)
{
AdjustScrollOffset(upgradeListScrollOffset, direction * scrollStep);
InvalidateRect(hWnd, nullptr, FALSE);
break;
}
break;
}
case WM_KEYDOWN: case WM_KEYDOWN:
if (currentScreen == SCREEN_MENU) if (currentScreen == SCREEN_MENU)
{ {
@@ -1103,6 +1161,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (helpState.currentPage == 0) if (helpState.currentPage == 0)
{ {
helpState.currentPage = helpState.selectedIndex + 1; helpState.currentPage = helpState.selectedIndex + 1;
helpScrollOffset = 0;
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
} }
break; break;
@@ -1116,6 +1175,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
else else
{ {
helpState.currentPage = 0; helpState.currentPage = 0;
helpScrollOffset = 0;
} }
InvalidateRect(hWnd, nullptr, FALSE); InvalidateRect(hWnd, nullptr, FALSE);
break; break;
@@ -1280,6 +1340,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break; break;
} }
if (currentMode == MODE_ROGUE && (wParam == 'J' || wParam == 'K'))
{
int direction = (wParam == 'J') ? 1 : -1;
AdjustScrollOffset(upgradeListScrollOffset, direction * GetScrollStep(hWnd, 52));
InvalidateRect(hWnd, nullptr, FALSE);
break;
}
switch (wParam) switch (wParam)
{ {
case VK_LEFT: case VK_LEFT:
+2
View File
@@ -15,6 +15,8 @@ Point point = { 0, 0 };
Point target = { 0, 0 }; Point target = { 0, 0 };
MenuState menuState = { 0, 2 }; MenuState menuState = { 0, 2 };
HelpState helpState = { 0, 3, 0 }; HelpState helpState = { 0, 3, 0 };
int helpScrollOffset = 0;
int upgradeListScrollOffset = 0;
PlayerStats classicStats = { 0, 1, 0, 0, 0 }; PlayerStats classicStats = { 0, 1, 0, 0, 0 };
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 }; PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
UpgradeUiState upgradeUiState = { 0, 0, 0, 0, 0, 0, {}, {} }; UpgradeUiState upgradeUiState = { 0, 0, 0, 0, 0, 0, {}, {} };
+4
View File
@@ -506,6 +506,7 @@ void StartGameWithMode(int mode)
{ {
currentMode = mode; currentMode = mode;
currentScreen = SCREEN_PLAYING; currentScreen = SCREEN_PLAYING;
upgradeListScrollOffset = 0;
Restart(); Restart();
currentFallInterval = (currentMode == MODE_ROGUE) ? GetRogueFallInterval() : 500; currentFallInterval = (currentMode == MODE_ROGUE) ? GetRogueFallInterval() : 500;
tScore = (currentMode == MODE_CLASSIC) ? classicStats.score : rogueStats.score; tScore = (currentMode == MODE_CLASSIC) ? classicStats.score : rogueStats.score;
@@ -521,6 +522,8 @@ void ReturnToMainMenu()
gameOverFlag = false; gameOverFlag = false;
ResetVisualEffects(); ResetVisualEffects();
ResetPendingRogueVisualEvents(); ResetPendingRogueVisualEvents();
helpScrollOffset = 0;
upgradeListScrollOffset = 0;
pendingLineClearEffectTicks = 0; pendingLineClearEffectTicks = 0;
pendingLineClearEffectRowCount = 0; pendingLineClearEffectRowCount = 0;
pendingLineClearEffectLineCount = 0; pendingLineClearEffectLineCount = 0;
@@ -545,4 +548,5 @@ void OpenRulesScreen()
helpState.selectedIndex = 0; helpState.selectedIndex = 0;
helpState.optionCount = 3; helpState.optionCount = 3;
helpState.currentPage = 0; helpState.currentPage = 0;
helpScrollOffset = 0;
} }
+183 -51
View File
@@ -695,68 +695,152 @@ void TDrawScreen(HDC hdc, HWND hWnd)
}; };
const TCHAR* categoryTexts[8] = const TCHAR* categoryTexts[8] =
{ {
_T("赏金纹章:得分+20%,可叠加\r\n成长印记:EXP+25%,可叠加\r\n缓坠羽翼:自然下落变慢\r\n连击律动:连续消行追加奖励\r\n先见之眼:下一方块预览+1"), _T("赏金纹章:所有得分收益提高 20%,可重复叠加,是最直接的分数成长。\r\n成长印记:所有 EXP 收益提高 25%,可重复叠加,用来更快进入后续构筑。\r\n缓坠羽翼:降低自然下落速度,最多叠加 4 次。\r\n连击律动:连续消行追加得分和 EXP,断连后重新累计。\r\n先见之眼:额外显示 1 个后续方块;第三个预览由操控大师解锁。"),
_T("最后一搏:首次濒死清底3行\r\n备用仓:C / Shift 暂存方块\r\n完美旋转:旋转受阻时左右修正\r\n时间缓流:堆叠自动减速\r\n空中换形:V 将当前方块变 I"), _T("最后一搏:首次濒死时自动清理底部 3 行,并保留本局继续机会。\r\n备用仓:解锁 C / Shift,将当前方块放入 Hold 仓或取出备用方块\r\n完美旋转:旋转被阻挡时尝试左右修正,提高贴墙和缝隙旋转成功率。\r\n时间缓流:堆叠过高时自动减速,给危险局面留出处理时间。\r\n空中换形:V 将当前下落方块变 I 块,次数有限。"),
_T("卸压清场:立刻清最高占用行\r\n底线清道夫:消行充能后清底\r\n清屏炸弹:X 清底5行,消行充能\r\n黑洞奇点:Z 吞噬数量最多的方块"), _T("卸压清场:获得时立刻清最高的一条占用行,直接降低顶部压力。\r\n底线清道夫:通过消行充能,充满后自动清理底部行。\r\n清屏炸弹:按 X 主动清理底部 5 行,需要通过消行充能后使用。\r\n黑洞奇点:Z 吞噬当前场上数量最多的一种颜色方块"),
_T("爆破核心:橙红边框,落地清 3x3\r\n棱镜激光:青色边框,按落地中心清整\r\n十字方块:绿色边框,按落地中心清行列\r\n彩虹方块:清中心行主色覆盖行染场上主色\r\n方块改造:提高 I 块生成概率"), _T("爆破核心:一次性解锁橙红边框方块,落地后以落点为中心清除 3x3 区域。\r\n棱镜激光:一次性解锁青色边框方块,按落地中心列清除整列固定方块。\r\n十字方块:一次性解锁绿色边框方块,按落地中心同时清除一行一列。\r\n彩虹方块:一次性解锁紫色边框方块,清中心行主色并把覆盖行染场上主色\r\n方块改造:提高指定方块出现概率,目前主要强化 I 块生成"),
_T("连锁火花:消行后追加随机破坏\r\n连环炸弹:爆破扩大为 5x5\r\n雷霆四消:三消/四消额外轰击\r\n雷霆棱镜:三消/四消追加激光"), _T("连锁火花:完成消行后追加随机破坏,适合配合稳定堆叠扩大收益。\r\n连环炸弹:强化爆破核心,将爆破范围从 3x3 扩大为 5x5\r\n雷霆四消:三消四消后追加雷击,额外清除局部方块。\r\n雷霆棱镜:三消四消追加激光清列,强化四消后的清场能力。"),
_T("狂热节拍:累计12行进入狂热\r\n怒火连段:连击越高倍率越高\r\n无尽狂热:狂热中消行延时\r\n高压悬赏:更快但收益更高\r\n豪赌四消 / 极限玩家:偏向四消爆发"), _T("狂热节拍:累计清除 12 行进入狂热,狂热期间收益更高。\r\n怒火连段:连击越高倍率越高,适合连续小消和稳定节奏。\r\n无尽狂热:狂热期间继续消行会延长狂热时间。\r\n高压悬赏:游戏速度更快,但高压下收益更高\r\n豪赌四消:四消收益更高,但非四消表现更不稳定。\r\n极限玩家:危险高度下获得更高收益,同时承担更高操作压力。\r\n赌徒契约:后续强化有概率翻倍或落空,每级提高概率,最多 4 级。"),
_T("双重抉择:升级可多选1个\r\n命运轮盘:6选2但含诅咒\r\n升级冲击波:升级后清底2行\r\n进化冲击:升级后清底3行\r\n成长核心:永久得分/EXP +15%"), _T("双重抉择:升级时可额外选择 1 个强化,加快构筑成型。\r\n命运轮盘:升级变为 6 选 2,但选项中可能混入诅咒\r\n升级冲击波:每次升级后清理底部 2 行。\r\n进化冲击:升级后清理底部 3 行,是升级冲击波的进阶路线。\r\n成长核心:永久提高得分和 EXP 收益 15%"),
_T("操控大师:Hold 后减速并预览+1\r\n方块风暴:接下来5个全 I 块\r\n稳定结构:概率填局部小洞\r\n虚空核心:黑洞生成彩虹,彩虹生效追加小黑洞\r\n赌徒契约:强化20%翻倍/20%落空,收益波动") _T("操控大师:使用 Hold 后短暂减速,并额外增加预览能力。\r\n方块风暴:接下来 5 个方块都变为 I 块,适合冲四消或紧急清场。\r\n稳定结构:落地后概率填局部小洞,让场地更平整。\r\n虚空核心:黑洞生效后生成彩虹方块,彩虹生效时会追加小黑洞")
}; };
int columnGap = SS(18); int sectionGap = SS(18);
int rowGap = SS(14); int titleHeight = SS(34);
int columnWidth = (contentRect.right - contentRect.left - columnGap) / 2; int bodyPadding = SS(16);
int rowHeight = (contentRect.bottom - contentRect.top - rowGap * 3) / 4; int contentHeight = contentRect.bottom - contentRect.top;
int virtualHeight = 0;
SelectObject(hdc, bodyFont);
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
int column = i % 2; RECT calculateBodyRect =
int row = i / 2; {
contentRect.left + bodyPadding,
0,
contentRect.right - bodyPadding,
0
};
DrawText(hdc, categoryTexts[i], -1, &calculateBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
virtualHeight += titleHeight + (calculateBodyRect.bottom - calculateBodyRect.top) + sectionGap;
}
int maxHelpScroll = virtualHeight - contentHeight;
if (maxHelpScroll < 0)
{
maxHelpScroll = 0;
}
if (helpScrollOffset > maxHelpScroll)
{
helpScrollOffset = maxHelpScroll;
}
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
SelectClipRgn(hdc, contentClipRegion);
int sectionTop = contentRect.top - helpScrollOffset;
for (int i = 0; i < 8; ++i)
{
RECT bodyCalculateRect =
{
contentRect.left + bodyPadding,
0,
contentRect.right - bodyPadding,
0
};
SelectObject(hdc, bodyFont);
DrawText(hdc, categoryTexts[i], -1, &bodyCalculateRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
int bodyHeight = bodyCalculateRect.bottom - bodyCalculateRect.top;
RECT categoryRect = RECT categoryRect =
{ {
contentRect.left + column * (columnWidth + columnGap), contentRect.left,
contentRect.top + row * (rowHeight + rowGap), sectionTop,
contentRect.left + column * (columnWidth + columnGap) + columnWidth, contentRect.right,
contentRect.top + row * (rowHeight + rowGap) + rowHeight sectionTop + titleHeight + bodyHeight + SS(12)
}; };
HPEN categoryPen = CreatePen(PS_SOLID, 1, RGB(232, 202, 215)); if (categoryRect.bottom < contentRect.top || categoryRect.top > contentRect.bottom)
HBRUSH categoryBrush = CreateSolidBrush(RGB(255, 252, 250)); {
oldPen = (HPEN)SelectObject(hdc, categoryPen); sectionTop += titleHeight + bodyHeight + sectionGap;
oldBrush = (HBRUSH)SelectObject(hdc, categoryBrush); continue;
RoundRect(hdc, categoryRect.left, categoryRect.top, categoryRect.right, categoryRect.bottom, SS(16), SS(16)); }
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(categoryBrush);
DeleteObject(categoryPen);
SetTextColor(hdc, titleColor); SetTextColor(hdc, titleColor);
SelectObject(hdc, bodyFont); SelectObject(hdc, sectionFont);
RECT categoryTitleRect = RECT categoryTitleRect =
{ {
categoryRect.left + SS(16), categoryRect.left,
categoryRect.top + SS(10), categoryRect.top,
categoryRect.right - SS(16), categoryRect.right,
categoryRect.top + SS(34) categoryRect.top + titleHeight
}; };
DrawText(hdc, categoryNames[i], -1, &categoryTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); DrawText(hdc, categoryNames[i], -1, &categoryTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
HPEN categoryLinePen = CreatePen(PS_SOLID, 1, RGB(232, 202, 215));
oldPen = (HPEN)SelectObject(hdc, categoryLinePen);
MoveToEx(hdc, categoryRect.left, categoryTitleRect.bottom, nullptr);
LineTo(hdc, categoryRect.right, categoryTitleRect.bottom);
SelectObject(hdc, oldPen);
DeleteObject(categoryLinePen);
SetTextColor(hdc, RGB(86, 66, 80)); SetTextColor(hdc, RGB(86, 66, 80));
SelectObject(hdc, smallFont); SelectObject(hdc, bodyFont);
RECT categoryBodyRect = RECT categoryBodyRect =
{ {
categoryRect.left + SS(16), categoryRect.left + bodyPadding,
categoryRect.top + SS(40), categoryTitleRect.bottom + SS(10),
categoryRect.right - SS(16), categoryRect.right - bodyPadding,
categoryRect.bottom - SS(10) categoryTitleRect.bottom + SS(10) + bodyHeight
}; };
DrawText(hdc, categoryTexts[i], -1, &categoryBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK); DrawText(hdc, categoryTexts[i], -1, &categoryBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
sectionTop += titleHeight + bodyHeight + sectionGap;
} }
if (hasOldClipRegion == 1)
{
SelectClipRgn(hdc, oldClipRegion);
}
else
{
SelectClipRgn(hdc, nullptr);
}
DeleteObject(contentClipRegion);
DeleteObject(oldClipRegion);
} }
if (helpState.currentPage != 3) if (helpState.currentPage != 3)
{ {
DrawText(hdc, pageText, -1, &contentRect, pageFlags); RECT calculateRect = { contentRect.left, contentRect.top, contentRect.right, contentRect.top };
DrawText(hdc, pageText, -1, &calculateRect, pageFlags | DT_CALCRECT);
int maxHelpScroll = (calculateRect.bottom - calculateRect.top) - (contentRect.bottom - contentRect.top);
if (maxHelpScroll < 0)
{
maxHelpScroll = 0;
}
if (helpScrollOffset > maxHelpScroll)
{
helpScrollOffset = maxHelpScroll;
}
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
SelectClipRgn(hdc, contentClipRegion);
RECT scrolledContentRect = contentRect;
scrolledContentRect.top -= helpScrollOffset;
scrolledContentRect.bottom += maxHelpScroll;
DrawText(hdc, pageText, -1, &scrolledContentRect, pageFlags);
if (hasOldClipRegion == 1)
{
SelectClipRgn(hdc, oldClipRegion);
}
else
{
SelectClipRgn(hdc, nullptr);
}
DeleteObject(contentClipRegion);
DeleteObject(oldClipRegion);
} }
} }
@@ -771,7 +855,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
}; };
const TCHAR* helpHint = helpState.currentPage == 0 const TCHAR* helpHint = helpState.currentPage == 0
? _T("\u65b9\u5411\u952e / WASD \u5207\u6362\uff0cEnter / Space \u786e\u8ba4\uff0cEsc / M \u8fd4\u56de\u4e3b\u83dc\u5355") ? _T("\u65b9\u5411\u952e / WASD \u5207\u6362\uff0cEnter / Space \u786e\u8ba4\uff0cEsc / M \u8fd4\u56de\u4e3b\u83dc\u5355")
: _T("Esc / 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); DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
DrawBackButton(); DrawBackButton();
@@ -1317,6 +1401,17 @@ void TDrawScreen(HDC hdc, HWND hWnd)
SetTextColor(hdc, textColor); SetTextColor(hdc, textColor);
TextOut(hdc, upgradeListRect.left + SS(18), upgradeListRect.top + SS(16), _T("已掌握强化"), lstrlen(_T("已掌握强化"))); TextOut(hdc, upgradeListRect.left + SS(18), upgradeListRect.top + SS(16), _T("已掌握强化"), lstrlen(_T("已掌握强化")));
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(148, 118, 132));
RECT upgradeScrollHintRect =
{
upgradeListRect.left + SS(116),
upgradeListRect.top + SS(17),
upgradeListRect.right - SS(18),
upgradeListRect.top + SS(42)
};
DrawText(hdc, _T("J/K / 滚轮"), -1, &upgradeScrollHintRect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, smallFont); SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118)); SetTextColor(hdc, RGB(128, 104, 118));
RECT upgradeBodyRect = RECT upgradeBodyRect =
@@ -1495,7 +1590,44 @@ void TDrawScreen(HDC hdc, HWND hWnd)
_stprintf_s(upgradeSummary, _T("尚未掌握强化。\r\n下一次升级将开启构筑。")); _stprintf_s(upgradeSummary, _T("尚未掌握强化。\r\n下一次升级将开启构筑。"));
} }
DrawText(hdc, upgradeSummary, -1, &upgradeBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK); RECT upgradeCalculateRect =
{
upgradeBodyRect.left,
upgradeBodyRect.top,
upgradeBodyRect.right,
upgradeBodyRect.top
};
DrawText(hdc, upgradeSummary, -1, &upgradeCalculateRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
int maxUpgradeScroll = (upgradeCalculateRect.bottom - upgradeCalculateRect.top) - (upgradeBodyRect.bottom - upgradeBodyRect.top);
if (maxUpgradeScroll < 0)
{
maxUpgradeScroll = 0;
}
if (upgradeListScrollOffset > maxUpgradeScroll)
{
upgradeListScrollOffset = maxUpgradeScroll;
}
HRGN oldUpgradeClipRegion = CreateRectRgn(0, 0, 0, 0);
int hasOldUpgradeClipRegion = GetClipRgn(hdc, oldUpgradeClipRegion);
HRGN upgradeClipRegion = CreateRectRgn(upgradeBodyRect.left, upgradeBodyRect.top, upgradeBodyRect.right, upgradeBodyRect.bottom);
SelectClipRgn(hdc, upgradeClipRegion);
RECT scrolledUpgradeBodyRect = upgradeBodyRect;
scrolledUpgradeBodyRect.top -= upgradeListScrollOffset;
scrolledUpgradeBodyRect.bottom += maxUpgradeScroll;
DrawText(hdc, upgradeSummary, -1, &scrolledUpgradeBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
if (hasOldUpgradeClipRegion == 1)
{
SelectClipRgn(hdc, oldUpgradeClipRegion);
}
else
{
SelectClipRgn(hdc, nullptr);
}
DeleteObject(upgradeClipRegion);
DeleteObject(oldUpgradeClipRegion);
SelectObject(hdc, sectionFont); SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor); SetTextColor(hdc, textColor);
} }
@@ -2150,7 +2282,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
cardRect.left + SS(20), cardRect.left + SS(20),
cardRect.top + SS(18), cardRect.top + SS(18),
cardRect.right - SS(116), cardRect.right - SS(116),
cardRect.top + SS(44) cardRect.top + SS(40)
}; };
DrawText(hdc, synthesisPath, -1, &synthesisRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); DrawText(hdc, synthesisPath, -1, &synthesisRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
} }
@@ -2160,30 +2292,30 @@ void TDrawScreen(HDC hdc, HWND hWnd)
RECT nameRect = RECT nameRect =
{ {
cardRect.left + SS(20), cardRect.left + SS(20),
cardRect.top + (hasSynthesisPath ? SS(54) : SS(44)), cardRect.top + (hasSynthesisPath ? SS(44) : SS(38)),
cardRect.right - SS(96), cardRect.right - SS(20),
cardRect.top + (hasSynthesisPath ? SS(106) : SS(98)) cardRect.top + (hasSynthesisPath ? SS(76) : SS(70))
}; };
DrawText(hdc, upgradeUiState.options[i].name, -1, &nameRect, DT_LEFT | DT_TOP | DT_WORDBREAK); DrawText(hdc, upgradeUiState.options[i].name, -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont); SelectObject(hdc, smallFont);
SetTextColor(hdc, descColor); SetTextColor(hdc, descColor);
RECT descRect = RECT descRect =
{ {
cardRect.left + SS(20), cardRect.left + SS(20),
cardRect.top + (hasSynthesisPath ? SS(124) : SS(116)), cardRect.top + (hasSynthesisPath ? SS(84) : SS(78)),
cardRect.right - SS(20), cardRect.right - SS(20),
cardRect.bottom - SS(64) cardRect.bottom - SS(48)
}; };
DrawText(hdc, upgradeUiState.options[i].description, -1, &descRect, DT_LEFT | DT_WORDBREAK); DrawText(hdc, upgradeUiState.options[i].description, -1, &descRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_END_ELLIPSIS);
SelectObject(hdc, smallFont); SelectObject(hdc, smallFont);
RECT footerRect = RECT footerRect =
{ {
cardRect.left + SS(20), cardRect.left + SS(20),
cardRect.bottom - SS(42), cardRect.bottom - SS(34),
cardRect.right - SS(20), cardRect.right - SS(20),
cardRect.bottom - SS(14) cardRect.bottom - SS(10)
}; };
SetTextColor(hdc, footerColor); SetTextColor(hdc, footerColor);
DrawText( DrawText(
+21 -21
View File
@@ -57,17 +57,17 @@ static const UpgradeEntry kUpgradePool[] =
{ {
{ UPGRADE_SCORE_MULTIPLIER, -1, 100, true, _T("赏金纹章"), _T("得分"), _T("所有得分提高 20%,每次选择都会继续叠加。") }, { UPGRADE_SCORE_MULTIPLIER, -1, 100, true, _T("赏金纹章"), _T("得分"), _T("所有得分提高 20%,每次选择都会继续叠加。") },
{ UPGRADE_COMBO_BONUS, -1, 95, true, _T("连击律动"), _T("节奏"), _T("连续消行会追加连击奖励,节奏越稳收益越高。") }, { UPGRADE_COMBO_BONUS, -1, 95, true, _T("连击律动"), _T("节奏"), _T("连续消行会追加连击奖励,节奏越稳收益越高。") },
{ UPGRADE_SLOW_FALL, -1, 90, true, _T("缓坠羽翼"), _T("操作"), _T("自然下落延缓 80ms为摆放和补洞争取更多时间") }, { UPGRADE_SLOW_FALL, 4, 90, true, _T("缓坠羽翼"), _T("操作"), _T("自然下落延缓 80ms最多叠加 4 次") },
{ UPGRADE_PREVIEW_PLUS_ONE, 3, 85, false, _T("先见之眼"), _T("视野"), _T("下一个方块预览 +1,最多可同时看见 3 个") }, { UPGRADE_PREVIEW_PLUS_ONE, 1, 85, false, _T("先见之眼"), _T("视野"), _T("下一个方块预览 +1;第三个预览由操控大师解锁") },
{ UPGRADE_EXP_MULTIPLIER, -1, 100, true, _T("成长印记"), _T("成长"), _T("消行获得的 EXP 提高 25%,更快迎来下一次强化。") }, { UPGRADE_EXP_MULTIPLIER, -1, 100, true, _T("成长印记"), _T("成长"), _T("消行获得的 EXP 提高 25%,更快迎来下一次强化。") },
{ UPGRADE_LAST_CHANCE, 1, 72, false, _T("最后一搏"), _T("保命"), _T("首次濒临失败时清除底部 3 行,让本局继续战斗。") }, { UPGRADE_LAST_CHANCE, 1, 72, false, _T("最后一搏"), _T("保命"), _T("首次濒临失败时清除底部 3 行,让本局继续战斗。") },
{ UPGRADE_HOLD_UNLOCK, 1, 78, false, _T("备用仓"), _T("特殊"), _T("解锁 Hold。按 C 或 Shift 暂存下落方块,每个方块落地前限用一次。") }, { UPGRADE_HOLD_UNLOCK, 1, 78, false, _T("备用仓"), _T("特殊"), _T("解锁 Hold。按 C 或 Shift 暂存下落方块,每个方块落地前限用一次。") },
{ UPGRADE_PRESSURE_RELIEF, -1, 82, true, _T("卸压清场"), _T("特殊"), _T("立刻清除最高占用行,为棋盘腾出一段喘息空间。") }, { UPGRADE_PRESSURE_RELIEF, -1, 82, true, _T("卸压清场"), _T("特殊"), _T("立刻清除最高占用行,为棋盘腾出一段喘息空间。") },
{ UPGRADE_SWEEPER, -1, 74, true, _T("底线清道夫"), _T("特殊"), _T("消行会为清道夫充能,充满后自动清除底部 1 行。") }, { UPGRADE_SWEEPER, -1, 74, true, _T("底线清道夫"), _T("特殊"), _T("消行会为清道夫充能,充满后自动清除底部 1 行。") },
{ UPGRADE_EXPLOSIVE_PIECE, -1, 86, true, _T("爆破核心"), _T("特殊"), _T("大幅提高爆破方块出现率。爆破方块落地时清除 3x3 区域。") }, { UPGRADE_EXPLOSIVE_PIECE, 1, 86, false, _T("爆破核心"), _T("特殊"), _T("解锁爆破方块生成。爆破方块落地时清除 3x3 区域。") },
{ UPGRADE_CHAIN_BLAST, 1, 92, false, _T("连锁火花"), _T("进阶"), _T("每次消行后,在被清除行附近追加随机破坏。") }, { UPGRADE_CHAIN_BLAST, 1, 92, false, _T("连锁火花"), _T("进阶"), _T("每次消行后,在被清除行附近追加随机破坏。") },
{ UPGRADE_CHAIN_BOMB, 1, 110, false, _T("连环炸弹"), _T("进化"), _T("爆破范围扩大为 5x5;若引发消行,再追加一次小爆炸。") }, { UPGRADE_CHAIN_BOMB, 1, 110, false, _T("连环炸弹"), _T("进化"), _T("爆破范围扩大为 5x5;若引发消行,再追加一次小爆炸。") },
{ UPGRADE_LASER_PIECE, -1, 84, true, _T("棱镜激光"), _T("特殊"), _T("大幅提高激光方块出现率。激光方块落地后清除所在整列。") }, { UPGRADE_LASER_PIECE, 1, 84, false, _T("棱镜激光"), _T("特殊"), _T("解锁激光方块生成。激光方块落地后清除所在整列。") },
{ UPGRADE_THUNDER_TETRIS, 1, 94, false, _T("雷霆四消"), _T("进阶"), _T("完成三消或四消时,额外轰击随机 2 行。") }, { UPGRADE_THUNDER_TETRIS, 1, 94, false, _T("雷霆四消"), _T("进阶"), _T("完成三消或四消时,额外轰击随机 2 行。") },
{ UPGRADE_THUNDER_LASER, 1, 112, false, _T("雷霆棱镜"), _T("进化"), _T("三消或四消时额外发射 2 道激光,随机清除 2 列并获得 EXP。") }, { UPGRADE_THUNDER_LASER, 1, 112, false, _T("雷霆棱镜"), _T("进化"), _T("三消或四消时额外发射 2 道激光,随机清除 2 列并获得 EXP。") },
{ UPGRADE_FEVER_MODE, 1, 92, false, _T("狂热节拍"), _T("进阶"), _T("累计消行 12 行后进入 12 秒狂热:得分与 EXP 翻倍。") }, { UPGRADE_FEVER_MODE, 1, 92, false, _T("狂热节拍"), _T("进阶"), _T("累计消行 12 行后进入 12 秒狂热:得分与 EXP 翻倍。") },
@@ -86,15 +86,15 @@ static const UpgradeEntry kUpgradePool[] =
{ UPGRADE_EVOLUTION_IMPACT, 1, 118, false, _T("进化冲击"), _T("进化"), _T("升级时清除底部 3 行,并获得 10 秒双倍 EXP。") }, { UPGRADE_EVOLUTION_IMPACT, 1, 118, false, _T("进化冲击"), _T("进化"), _T("升级时清除底部 3 行,并获得 10 秒双倍 EXP。") },
{ UPGRADE_CONTROL_MASTER, 1, 112, false, _T("操控大师"), _T("进化"), _T("使用备用仓后短暂降低下落速度,并额外增加 1 个预览方块。") }, { UPGRADE_CONTROL_MASTER, 1, 112, false, _T("操控大师"), _T("进化"), _T("使用备用仓后短暂降低下落速度,并额外增加 1 个预览方块。") },
{ UPGRADE_BLOCK_STORM, 1, 82, false, _T("方块风暴"), _T("爆发"), _T("接下来 5 个方块全部变为 I 块,快速制造四消机会。") }, { UPGRADE_BLOCK_STORM, 1, 82, false, _T("方块风暴"), _T("爆发"), _T("接下来 5 个方块全部变为 I 块,快速制造四消机会。") },
{ UPGRADE_CROSS_PIECE, -1, 84, true, _T("十字方块"), _T("爆发"), _T("大幅提高十字方块出现率。十字方块落地后清除所在行与所在列。") }, { UPGRADE_CROSS_PIECE, 1, 84, false, _T("十字方块"), _T("爆发"), _T("解锁十字方块生成。十字方块落地后清除所在行与所在列。") },
{ UPGRADE_BLACK_HOLE, 1, 88, false, _T("黑洞奇点"), _T("特殊"), _T("获得 2 次黑洞。按 Z 吞噬棋盘上数量最多的一种固定方块。") }, { UPGRADE_BLACK_HOLE, 1, 88, false, _T("黑洞奇点"), _T("特殊"), _T("获得 2 次黑洞。按 Z 吞噬棋盘上数量最多的一种固定方块。") },
{ UPGRADE_AIR_RESHAPE, 1, 82, false, _T("空中换形"), _T("操作"), _T("获得 2 次换形。按 V 将正在下落的方块重塑为 I 块。") }, { UPGRADE_AIR_RESHAPE, 1, 82, false, _T("空中换形"), _T("操作"), _T("获得 2 次换形。按 V 将正在下落的方块重塑为 I 块。") },
{ UPGRADE_RAINBOW_PIECE, 1, 84, false, _T("彩虹方块"), _T("爆发"), _T("更高概率生成彩虹方块。落地后清除自身中心行最多的颜色,并把覆盖行染成场上最多的颜色。") }, { UPGRADE_RAINBOW_PIECE, 1, 84, false, _T("彩虹方块"), _T("爆发"), _T("解锁彩虹方块生成。落地后清中心行主色,并把覆盖行染成场上色。") },
{ UPGRADE_VOID_CORE, 1, 112, false, _T("虚空核心"), _T("进化"), _T("黑洞后额外生成 1 个彩虹方块;彩虹生效后追加一次小型黑洞。") }, { UPGRADE_VOID_CORE, 1, 112, false, _T("虚空核心"), _T("进化"), _T("黑洞后额外生成 1 个彩虹方块;彩虹生效后追加一次小型黑洞。") },
{ UPGRADE_STABLE_STRUCTURE, -1, 72, true, _T("稳定结构"), _T("特殊"), _T("落地后有小概率填补邻近空洞,让阵型更加稳固。") }, { UPGRADE_STABLE_STRUCTURE, -1, 72, true, _T("稳定结构"), _T("特殊"), _T("落地后有小概率填补邻近空洞,让阵型更加稳固。") },
{ UPGRADE_DOUBLE_GROWTH, 1, 84, false, _T("成长核心"), _T("成长"), _T("永久获得 +15% 得分与 +15% EXP;每局只能选择一次。") }, { UPGRADE_DOUBLE_GROWTH, 1, 84, false, _T("成长核心"), _T("成长"), _T("永久获得 +15% 得分与 +15% EXP;每局只能选择一次。") },
{ UPGRADE_PIECE_TUNING, -1, 64, true, _T("方块改造"), _T("特殊"), _T("固定提高 I 方块的生成概率。") }, { UPGRADE_PIECE_TUNING, -1, 64, true, _T("方块改造"), _T("特殊"), _T("固定提高 I 方块的生成概率。") },
{ UPGRADE_GAMBLER, -1, 64, true, _T("赌徒契约"), _T("风险"), _T("后续强化 20% 翻倍 / 20% 落空,每级+5%;消行收益随机波动。") } { UPGRADE_GAMBLER, 4, 64, true, _T("赌徒契约"), _T("风险"), _T("后续强化 20% 翻倍 / 20% 落空,每级+5%,最多 4 级;消行收益随机波动。") }
}; };
static constexpr int kUpgradePoolSize = sizeof(kUpgradePool) / sizeof(kUpgradePool[0]); static constexpr int kUpgradePoolSize = sizeof(kUpgradePool) / sizeof(kUpgradePool[0]);
@@ -613,14 +613,9 @@ static bool IsUpgradeSelectable(const UpgradeEntry& entry)
rogueStats.evolutionImpactLevel == 0; rogueStats.evolutionImpactLevel == 0;
} }
if (entry.repeatable)
{
return true;
}
if (entry.maxLevel <= 0) if (entry.maxLevel <= 0)
{ {
return GetUpgradeCurrentLevel(entry.id) == 0; return entry.repeatable || GetUpgradeCurrentLevel(entry.id) == 0;
} }
return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel; return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel;
@@ -1616,15 +1611,16 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
break; break;
case UPGRADE_SLOW_FALL: case UPGRADE_SLOW_FALL:
rogueStats.slowFallStacks += applyCount; rogueStats.slowFallStacks += applyCount;
if (rogueStats.slowFallStacks > 4)
{
rogueStats.slowFallStacks = 4;
}
currentFallInterval = GetRogueFallInterval(); currentFallInterval = GetRogueFallInterval();
break; break;
case UPGRADE_PREVIEW_PLUS_ONE: case UPGRADE_PREVIEW_PLUS_ONE:
for (int i = 0; i < applyCount; i++) if (rogueStats.previewCount < 2)
{ {
if (rogueStats.previewCount < 3) rogueStats.previewCount = 2;
{
rogueStats.previewCount++;
}
} }
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1; rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
break; break;
@@ -1653,7 +1649,7 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
rogueStats.sweeperLevel += applyCount; rogueStats.sweeperLevel += applyCount;
break; break;
case UPGRADE_EXPLOSIVE_PIECE: case UPGRADE_EXPLOSIVE_PIECE:
rogueStats.explosiveLevel += applyCount; rogueStats.explosiveLevel = 1;
break; break;
case UPGRADE_CHAIN_BLAST: case UPGRADE_CHAIN_BLAST:
rogueStats.chainBlastLevel = 1; rogueStats.chainBlastLevel = 1;
@@ -1662,7 +1658,7 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
rogueStats.chainBombLevel = 1; rogueStats.chainBombLevel = 1;
break; break;
case UPGRADE_LASER_PIECE: case UPGRADE_LASER_PIECE:
rogueStats.laserLevel += applyCount; rogueStats.laserLevel = 1;
break; break;
case UPGRADE_THUNDER_TETRIS: case UPGRADE_THUNDER_TETRIS:
rogueStats.thunderTetrisLevel = 1; rogueStats.thunderTetrisLevel = 1;
@@ -1741,7 +1737,7 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
nextTypes[2] = 0; nextTypes[2] = 0;
break; break;
case UPGRADE_CROSS_PIECE: case UPGRADE_CROSS_PIECE:
rogueStats.crossPieceLevel += applyCount; rogueStats.crossPieceLevel = 1;
break; break;
case UPGRADE_BLACK_HOLE: case UPGRADE_BLACK_HOLE:
rogueStats.blackHoleLevel = 1; rogueStats.blackHoleLevel = 1;
@@ -1770,6 +1766,10 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
break; break;
case UPGRADE_GAMBLER: case UPGRADE_GAMBLER:
rogueStats.gamblerLevel += applyCount; rogueStats.gamblerLevel += applyCount;
if (rogueStats.gamblerLevel > 4)
{
rogueStats.gamblerLevel = 4;
}
break; break;
default: default:
break; break;