Files
Tereis/src/source/TetrisRender.cpp
T
2026-04-26 14:50:15 +08:00

2025 lines
77 KiB
C++

#include "stdafx.h"
#include "Tetris.h"
#include <objidl.h>
#include <gdiplus.h>
#include <string>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
static std::wstring BuildAssetPath(const wchar_t* relativePath)
{
wchar_t modulePath[MAX_PATH] = {};
GetModuleFileNameW(nullptr, modulePath, MAX_PATH);
std::wstring basePath(modulePath);
size_t lastSlash = basePath.find_last_of(L"\\/");
if (lastSlash != std::wstring::npos)
{
basePath.resize(lastSlash);
}
std::wstring projectRelative = basePath + L"\\..\\..\\" + relativePath;
wchar_t fullPath[MAX_PATH] = {};
DWORD result = GetFullPathNameW(projectRelative.c_str(), MAX_PATH, fullPath, nullptr);
if (result > 0 && result < MAX_PATH)
{
return fullPath;
}
return projectRelative;
}
static std::wstring BuildWorkingDirAssetPath(const wchar_t* relativePath)
{
wchar_t currentDirectory[MAX_PATH] = {};
DWORD length = GetCurrentDirectoryW(MAX_PATH, currentDirectory);
if (length == 0 || length >= MAX_PATH)
{
return L"";
}
std::wstring candidate = std::wstring(currentDirectory) + L"\\" + relativePath;
wchar_t fullPath[MAX_PATH] = {};
DWORD result = GetFullPathNameW(candidate.c_str(), MAX_PATH, fullPath, nullptr);
if (result > 0 && result < MAX_PATH)
{
return fullPath;
}
return candidate;
}
static Bitmap* LoadBackgroundImage()
{
static ULONG_PTR gdiplusToken = 0;
static Bitmap* backgroundImage = nullptr;
static bool attempted = false;
if (!attempted)
{
attempted = true;
GdiplusStartupInput startupInput;
if (GdiplusStartup(&gdiplusToken, &startupInput, nullptr) == Ok)
{
const std::wstring candidates[] =
{
BuildAssetPath(L"assets\\images\\background.png"),
BuildWorkingDirAssetPath(L"assets\\images\\background.png"),
BuildAssetPath(L"assets\\images\\background.bmp"),
BuildWorkingDirAssetPath(L"assets\\images\\background.bmp")
};
for (const std::wstring& candidate : candidates)
{
if (candidate.empty())
{
continue;
}
DWORD attributes = GetFileAttributesW(candidate.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
{
continue;
}
Bitmap* loadedImage = Bitmap::FromFile(candidate.c_str(), FALSE);
if (loadedImage != nullptr && loadedImage->GetLastStatus() == Ok)
{
backgroundImage = loadedImage;
break;
}
delete loadedImage;
}
}
}
return backgroundImage;
}
void TDrawScreen(HDC hdc, HWND hWnd)
{
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;
}
int layoutWidth = MulDiv(WINDOW_CLIENT_WIDTH, scale, 1000);
int layoutHeight = MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000);
int offsetX = (clientWidth - layoutWidth) / 2;
int offsetY = 0;
auto SX = [offsetX, scale](int value) -> int
{
return offsetX + MulDiv(value, scale, 1000);
};
auto SY = [offsetY, scale](int value) -> int
{
return offsetY + MulDiv(value, scale, 1000);
};
auto SS = [scale](int value) -> int
{
int result = MulDiv(value, scale, 1000);
return result < 1 ? 1 : result;
};
int grid = SS(GRID);
int previewMiniCell = SS(16);
int panelGap = SS(SIDE_PANEL_GAP);
int panelWidth = SS(SIDE_PANEL_WIDTH);
int panelHeight = SS(SIDE_PANEL_HEIGHT - WINDOW_PADDING * 2);
int boardWidth = grid * nGameWidth;
int boardHeight = grid * nGameHeight;
const RECT leftPanelRect =
{
SX(WINDOW_PADDING),
SY(WINDOW_PADDING),
SX(WINDOW_PADDING) + panelWidth,
SY(WINDOW_PADDING) + panelHeight
};
const RECT gameRect =
{
leftPanelRect.right + panelGap,
SY(WINDOW_PADDING),
leftPanelRect.right + panelGap + boardWidth,
SY(WINDOW_PADDING) + boardHeight
};
const RECT rightPanelRect =
{
gameRect.right + panelGap,
SY(WINDOW_PADDING),
gameRect.right + panelGap + panelWidth,
SY(WINDOW_PADDING) + panelHeight
};
const COLORREF pageColor = RGB(255, 244, 248);
const COLORREF blobColorA = RGB(255, 230, 238);
const COLORREF blobColorB = RGB(250, 221, 233);
const COLORREF cardColor = RGB(255, 252, 253);
const COLORREF boardColor = RGB(70, 57, 78);
const COLORREF boardInnerColor = RGB(54, 44, 62);
const COLORREF lineColor = RGB(104, 90, 116);
const COLORREF frameColor = RGB(212, 168, 188);
const COLORREF titleColor = RGB(104, 62, 84);
const COLORREF textColor = RGB(92, 73, 88);
const COLORREF accentColor = RGB(228, 145, 182);
const BYTE shellAlpha = 142;
const BYTE panelAlpha = 150;
const BYTE panelNestedAlpha = 128;
const BYTE panelStrongAlpha = 168;
Bitmap* backgroundImage = LoadBackgroundImage();
if (backgroundImage != nullptr)
{
Graphics graphics(hdc);
graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
graphics.DrawImage(
backgroundImage,
Rect(0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top));
SolidBrush washBrush(Color(76, 255, 250, 252));
graphics.FillRectangle(&washBrush, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
}
else
{
HBRUSH pageBrush = CreateSolidBrush(pageColor);
FillRect(hdc, &clientRect, pageBrush);
DeleteObject(pageBrush);
}
HBRUSH oldBrush = nullptr;
HPEN oldPen = nullptr;
if (backgroundImage == nullptr)
{
HBRUSH blobBrushA = CreateSolidBrush(blobColorA);
HBRUSH blobBrushB = CreateSolidBrush(blobColorB);
oldBrush = (HBRUSH)SelectObject(hdc, blobBrushA);
oldPen = (HPEN)SelectObject(hdc, GetStockObject(NULL_PEN));
Ellipse(hdc, clientRect.right - 260, -20, clientRect.right + 80, 260);
Ellipse(hdc, -80, clientRect.bottom - 200, 220, clientRect.bottom + 70);
SelectObject(hdc, blobBrushB);
Ellipse(hdc, clientRect.right - 180, clientRect.bottom - 220, clientRect.right + 120, clientRect.bottom + 40);
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(blobBrushA);
DeleteObject(blobBrushB);
}
HFONT titleFont = CreateFont(
-SS(36), 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_NATURAL_QUALITY,
VARIABLE_PITCH, _T("Microsoft YaHei UI"));
HFONT sectionFont = CreateFont(
-SS(22), 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_NATURAL_QUALITY,
VARIABLE_PITCH, _T("Microsoft YaHei UI"));
HFONT bodyFont = CreateFont(
-SS(18), 0, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_NATURAL_QUALITY,
VARIABLE_PITCH, _T("Microsoft YaHei UI"));
HFONT smallFont = CreateFont(
-SS(15), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_NATURAL_QUALITY,
VARIABLE_PITCH, _T("Microsoft YaHei UI"));
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);
};
auto DrawPanelCardAlpha = [&](const RECT& rect, COLORREF fillColor, COLORREF borderColor, int radius, BYTE alpha)
{
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
GraphicsPath path;
INT left = static_cast<INT>(rect.left);
INT top = static_cast<INT>(rect.top);
INT width = static_cast<INT>(rect.right - rect.left);
INT height = static_cast<INT>(rect.bottom - rect.top);
INT corner = static_cast<INT>(SS(radius));
path.AddArc(left, top, corner, corner, 180.0f, 90.0f);
path.AddArc(left + width - corner, top, corner, corner, 270.0f, 90.0f);
path.AddArc(left + width - corner, top + height - corner, corner, corner, 0.0f, 90.0f);
path.AddArc(left, top + height - corner, corner, corner, 90.0f, 90.0f);
path.CloseFigure();
SolidBrush brush(Color(alpha, GetRValue(fillColor), GetGValue(fillColor), GetBValue(fillColor)));
Pen pen(Color(232, GetRValue(borderColor), GetGValue(borderColor), GetBValue(borderColor)), 1.0f);
graphics.FillPath(&brush, &path);
graphics.DrawPath(&pen, &path);
};
auto DrawPanelHeader = [&](const RECT& panelRect, const TCHAR* title, int lineWidth)
{
SelectObject(hdc, titleFont);
SetTextColor(hdc, titleColor);
RECT titleRect =
{
panelRect.left + SS(24),
panelRect.top + SS(20),
panelRect.right - SS(24),
panelRect.top + SS(62)
};
DrawText(hdc, title, -1, &titleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
HPEN localAccentPen = CreatePen(PS_SOLID, SS(3), accentColor);
HPEN savedPen = (HPEN)SelectObject(hdc, localAccentPen);
MoveToEx(hdc, panelRect.left + SS(24), panelRect.top + SS(78), nullptr);
LineTo(hdc, panelRect.left + SS(24) + SS(lineWidth), panelRect.top + SS(78));
SelectObject(hdc, savedPen);
DeleteObject(localAccentPen);
};
auto DrawMusicButton = [&]()
{
RECT musicButtonRect =
{
SX(WINDOW_CLIENT_WIDTH - 40),
SY(WINDOW_CLIENT_HEIGHT - 40),
SX(WINDOW_CLIENT_WIDTH - 12),
SY(WINDOW_CLIENT_HEIGHT - 12)
};
DrawPanelCardAlpha(
musicButtonRect,
bgmEnabled ? RGB(255, 238, 246) : RGB(244, 236, 240),
bgmEnabled ? RGB(222, 130, 166) : RGB(170, 148, 158),
12,
bgmEnabled ? 218 : 190);
HPEN musicPen = CreatePen(PS_SOLID, SS(3), bgmEnabled ? RGB(128, 70, 100) : RGB(128, 112, 120));
HPEN oldMusicPen = (HPEN)SelectObject(hdc, musicPen);
HBRUSH oldMusicBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
int noteStemX = musicButtonRect.left + SS(17);
int noteTop = musicButtonRect.top + SS(7);
int noteBottom = musicButtonRect.bottom - SS(9);
MoveToEx(hdc, noteStemX, noteTop, nullptr);
LineTo(hdc, noteStemX, noteBottom);
MoveToEx(hdc, noteStemX, noteTop, nullptr);
LineTo(hdc, musicButtonRect.right - SS(7), noteTop + SS(4));
LineTo(hdc, musicButtonRect.right - SS(7), noteTop + SS(10));
Ellipse(
hdc,
musicButtonRect.left + SS(7),
musicButtonRect.bottom - SS(13),
musicButtonRect.left + SS(19),
musicButtonRect.bottom - SS(4));
if (!bgmEnabled)
{
MoveToEx(hdc, musicButtonRect.left + SS(7), musicButtonRect.bottom - SS(7), nullptr);
LineTo(hdc, musicButtonRect.right - SS(6), musicButtonRect.top + SS(6));
}
SelectObject(hdc, oldMusicBrush);
SelectObject(hdc, oldMusicPen);
DeleteObject(musicPen);
};
if (currentScreen == SCREEN_MENU)
{
RECT menuCard =
{
SX(110),
SY(70),
SX(WINDOW_CLIENT_WIDTH - 110),
SY(WINDOW_CLIENT_HEIGHT - 70)
};
DrawPanelCardAlpha(menuCard, cardColor, frameColor, 34, 214);
HFONT oldFont = (HFONT)SelectObject(hdc, titleFont);
SetTextColor(hdc, titleColor);
RECT titleRect =
{
menuCard.left,
menuCard.top + SS(24),
menuCard.right,
menuCard.top + SS(70)
};
DrawText(hdc, _T("Rogue Tetris"), -1, &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
RECT subtitleRect =
{
menuCard.left,
menuCard.top + SS(72),
menuCard.right,
menuCard.top + SS(110)
};
SelectObject(hdc, bodyFont);
SetTextColor(hdc, textColor);
DrawText(hdc, _T("选择你的战局"), -1, &subtitleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
const TCHAR* modeNames[3] =
{
_T("\u7ecf\u5178\u6a21\u5f0f"),
_T("Rogue \u6a21\u5f0f"),
_T("\u5e2e\u52a9")
};
const TCHAR* modeDescriptions[3] =
{
_T("纯粹方块挑战,专注消行、堆叠与生存。"),
_T("在不断升级的棋盘中收集强化,构筑本局专属流派。"),
_T("查看操作、模式规则与全部强化效果。")
};
for (int i = 0; i < menuState.optionCount; i++)
{
bool isSelected = (i == menuState.selectedIndex);
int top = menuCard.top + SS(140) + i * SS(130);
RECT optionRect =
{
menuCard.left + SS(36),
top,
menuCard.right - SS(36),
top + SS(104)
};
DrawPanelCardAlpha(
optionRect,
isSelected ? RGB(255, 232, 240) : RGB(255, 247, 250),
isSelected ? accentColor : RGB(226, 198, 210),
24,
isSelected ? 208 : 172);
if (isSelected)
{
HPEN optionPen = CreatePen(PS_SOLID, SS(3), accentColor);
oldPen = (HPEN)SelectObject(hdc, optionPen);
oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
RoundRect(hdc, optionRect.left, optionRect.top, optionRect.right, optionRect.bottom, SS(24), SS(24));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(optionPen);
}
RECT modeNameRect =
{
optionRect.left + SS(24),
optionRect.top + SS(14),
optionRect.right - SS(24),
optionRect.top + SS(44)
};
RECT modeDescriptionRect =
{
optionRect.left + SS(24),
optionRect.top + SS(46),
optionRect.right - SS(24),
optionRect.bottom - SS(14)
};
SelectObject(hdc, sectionFont);
SetTextColor(hdc, isSelected ? titleColor : textColor);
DrawText(hdc, modeNames[i], -1, &modeNameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(118, 96, 110));
DrawText(hdc, modeDescriptions[i], -1, &modeDescriptionRect, DT_LEFT | DT_WORDBREAK);
}
RECT hintRect =
{
menuCard.left + SS(36),
menuCard.bottom - SS(92),
menuCard.right - SS(36),
menuCard.bottom - SS(36)
};
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
DrawText(hdc, _T("\u65b9\u5411\u952e / WASD \u5207\u6362\uff0cEnter \u6216 Space \u786e\u8ba4\uff0cEsc \u9000\u51fa"), -1, &hintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
DrawMusicButton();
SelectObject(hdc, oldFont);
DeleteObject(titleFont);
DeleteObject(sectionFont);
DeleteObject(bodyFont);
DeleteObject(smallFont);
return;
}
if (currentScreen == SCREEN_RULES)
{
RECT rulesCard =
{
SX(76),
SY(54),
SX(WINDOW_CLIENT_WIDTH - 76),
SY(WINDOW_CLIENT_HEIGHT - 54)
};
HPEN rulesPen = CreatePen(PS_SOLID, 1, frameColor);
HBRUSH rulesBrush = CreateSolidBrush(cardColor);
HFONT oldFont = (HFONT)SelectObject(hdc, titleFont);
oldPen = (HPEN)SelectObject(hdc, rulesPen);
oldBrush = (HBRUSH)SelectObject(hdc, rulesBrush);
RoundRect(hdc, rulesCard.left, rulesCard.top, rulesCard.right, rulesCard.bottom, SS(34), SS(34));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(rulesBrush);
DeleteObject(rulesPen);
SetTextColor(hdc, titleColor);
RECT rulesTitleRect =
{
rulesCard.left + SS(36),
rulesCard.top + SS(26),
rulesCard.right - SS(36),
rulesCard.top + SS(78)
};
const TCHAR* helpTitle = _T("\u5e2e\u52a9");
if (helpState.currentPage == 1)
{
helpTitle = _T("\u6e38\u620f\u4ecb\u7ecd");
}
else if (helpState.currentPage == 2)
{
helpTitle = _T("\u64cd\u4f5c\u8bf4\u660e");
}
else if (helpState.currentPage == 3)
{
helpTitle = _T("\u5f3a\u5316\u56fe\u9274");
}
DrawText(hdc, helpTitle, -1, &rulesTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
HPEN rulesAccentPen = CreatePen(PS_SOLID, SS(3), accentColor);
oldPen = (HPEN)SelectObject(hdc, rulesAccentPen);
MoveToEx(hdc, rulesCard.left + SS(38), rulesCard.top + SS(92), nullptr);
LineTo(hdc, rulesCard.left + SS(186), rulesCard.top + SS(92));
SelectObject(hdc, oldPen);
DeleteObject(rulesAccentPen);
RECT contentRect =
{
rulesCard.left + SS(36),
rulesCard.top + SS(126),
rulesCard.right - SS(36),
rulesCard.bottom - SS(86)
};
if (helpState.currentPage == 0)
{
const TCHAR* optionTitles[3] =
{
_T("\u6e38\u620f\u4ecb\u7ecd"),
_T("\u64cd\u4f5c\u8bf4\u660e"),
_T("\u5f3a\u5316\u56fe\u9274")
};
const TCHAR* optionDetails[3] =
{
_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")
};
int optionHeight = SS(100);
int optionGap = SS(22);
int optionTop = contentRect.top + SS(18);
for (int i = 0; i < helpState.optionCount; ++i)
{
RECT optionRect =
{
contentRect.left,
optionTop + i * (optionHeight + optionGap),
contentRect.right,
optionTop + i * (optionHeight + optionGap) + optionHeight
};
bool selected = (i == helpState.selectedIndex);
COLORREF optionFill = selected ? RGB(255, 245, 249) : RGB(255, 252, 250);
COLORREF optionFrame = selected ? accentColor : frameColor;
HPEN optionPen = CreatePen(PS_SOLID, selected ? SS(2) : SS(1), optionFrame);
HBRUSH optionBrush = CreateSolidBrush(optionFill);
oldPen = (HPEN)SelectObject(hdc, optionPen);
oldBrush = (HBRUSH)SelectObject(hdc, optionBrush);
RoundRect(hdc, optionRect.left, optionRect.top, optionRect.right, optionRect.bottom, SS(24), SS(24));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(optionBrush);
DeleteObject(optionPen);
SetTextColor(hdc, selected ? titleColor : textColor);
SelectObject(hdc, sectionFont);
RECT optionTitleRect =
{
optionRect.left + SS(28),
optionRect.top + SS(16),
optionRect.right - SS(28),
optionRect.top + SS(48)
};
DrawText(hdc, optionTitles[i], -1, &optionTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SetTextColor(hdc, RGB(112, 91, 104));
SelectObject(hdc, bodyFont);
RECT optionDetailRect =
{
optionRect.left + SS(28),
optionRect.top + SS(52),
optionRect.right - SS(28),
optionRect.bottom - SS(14)
};
DrawText(hdc, optionDetails[i], -1, &optionDetailRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
}
}
else
{
SetTextColor(hdc, textColor);
SelectObject(hdc, bodyFont);
const TCHAR* pageText = _T("");
UINT pageFlags = DT_LEFT | DT_TOP | DT_WORDBREAK;
if (helpState.currentPage == 1)
{
pageText =
_T("经典模式:只考验堆叠、消行和生存。\r\n\r\n")
_T("Rogue:消行得分和 EXP,升级后选择强化。\r\n\r\n")
_T("战斗越久危险越高,底部封锁区会压缩空间。\r\n\r\n")
_T("死亡后可以按 V 看视频复活一次,经典模式和 Rogue 模式都支持。");
}
else if (helpState.currentPage == 2)
{
pageText =
_T("\u2190 / A\uff1a\u5411\u5de6\u79fb\u52a8\r\n")
_T("\u2192 / D\uff1a\u5411\u53f3\u79fb\u52a8\r\n")
_T("\u2191 / W\uff1a\u65cb\u8f6c\u65b9\u5757\r\n")
_T("\u2193 / S\uff1a\u8f6f\u964d\r\n")
_T("Space\uff1a\u786c\u964d\r\n")
_T("C / Shift\uff1a备用仓\uff08获得后\uff09\r\n")
_T("Z\uff1a黑洞奇点\uff08获得后\uff09\r\n")
_T("X\uff1a清屏炸弹\uff08获得后\uff09\r\n")
_T("V\uff1a空中换形\uff08获得后\uff09\r\n")
_T("P\uff1a\u6682\u505c / \u7ee7\u7eed\r\n")
_T("R\uff1a\u91cd\u5f00\u5f53\u524d\u5bf9\u5c40\r\n")
_T("M\uff1a\u8fd4\u56de\u4e3b\u83dc\u5355\r\n")
_T("死亡后 V:看视频复活一次");
}
else if (helpState.currentPage == 3)
{
const TCHAR* categoryNames[8] =
{
_T("基础"),
_T("保命 / 操作"),
_T("清场"),
_T("特殊方块"),
_T("进阶"),
_T("狂热 / 风险"),
_T("升级系"),
_T("构筑")
};
const TCHAR* categoryTexts[8] =
{
_T("赏金纹章:得分+20%\r\n成长印记:EXP+25%\r\n缓坠羽翼:下落变慢\r\n连击律动:连续消行加分;先见之眼:预览+1"),
_T("最后一搏:濒死清底3行\r\n备用仓:解锁 Hold\r\n完美旋转:卡墙修正\r\n时间缓流:高堆叠减速;空中换形:V 变 I 块"),
_T("卸压清场:清最高行\r\n底线清道夫:更快充能清底\r\n清屏炸弹:X 清底5行,16行充能\r\n黑洞奇点:Z 吞噬最多方块,获得2次"),
_T("爆破核心:更频繁爆破块清 3x3\r\n棱镜激光:更高概率清整列\r\n十字方块:更高概率清行列\r\n彩虹方块:更高概率补齐缺口;方块改造:提高 I 块概率"),
_T("连锁火花:消行追加破坏\r\n连环炸弹:爆破扩大 5x5\r\n雷霆四消:三消/四消额外轰击\r\n雷霆棱镜:三消/四消额外激光"),
_T("狂热节拍:12行进狂热\r\n怒火连段:连击加倍率\r\n无尽狂热:延长狂热\r\n高压悬赏 / 豪赌四消 / 极限玩家:高风险高收益"),
_T("双重抉择:升级多选1个\r\n命运轮盘:6选2但带诅咒\r\n升级冲击波 / 进化冲击:升级清底\r\n成长核心:永久得分 / EXP +15%"),
_T("操控大师:Hold 后减速并预览+1\r\n方块风暴:接下来5个全 I 块\r\n稳定结构:更高概率落地补洞\r\n虚空核心:黑洞和彩虹联动;赌徒契约:强化可能翻倍或落空")
};
int columnGap = SS(18);
int rowGap = SS(14);
int columnWidth = (contentRect.right - contentRect.left - columnGap) / 2;
int rowHeight = (contentRect.bottom - contentRect.top - rowGap * 3) / 4;
for (int i = 0; i < 8; ++i)
{
int column = i % 2;
int row = i / 2;
RECT categoryRect =
{
contentRect.left + column * (columnWidth + columnGap),
contentRect.top + row * (rowHeight + rowGap),
contentRect.left + column * (columnWidth + columnGap) + columnWidth,
contentRect.top + row * (rowHeight + rowGap) + rowHeight
};
HPEN categoryPen = CreatePen(PS_SOLID, 1, RGB(232, 202, 215));
HBRUSH categoryBrush = CreateSolidBrush(RGB(255, 252, 250));
oldPen = (HPEN)SelectObject(hdc, categoryPen);
oldBrush = (HBRUSH)SelectObject(hdc, categoryBrush);
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);
SelectObject(hdc, bodyFont);
RECT categoryTitleRect =
{
categoryRect.left + SS(16),
categoryRect.top + SS(10),
categoryRect.right - SS(16),
categoryRect.top + SS(34)
};
DrawText(hdc, categoryNames[i], -1, &categoryTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SetTextColor(hdc, RGB(86, 66, 80));
SelectObject(hdc, smallFont);
RECT categoryBodyRect =
{
categoryRect.left + SS(16),
categoryRect.top + SS(40),
categoryRect.right - SS(16),
categoryRect.bottom - SS(10)
};
DrawText(hdc, categoryTexts[i], -1, &categoryBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
}
}
if (helpState.currentPage != 3)
{
DrawText(hdc, pageText, -1, &contentRect, pageFlags);
}
}
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT backHintRect =
{
rulesCard.left + SS(36),
rulesCard.bottom - SS(58),
rulesCard.right - SS(36),
rulesCard.bottom - SS(24)
};
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("Esc / Backspace / M \u8fd4\u56de\u5e2e\u52a9");
DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
DrawMusicButton();
SelectObject(hdc, oldFont);
DeleteObject(titleFont);
DeleteObject(sectionFont);
DeleteObject(bodyFont);
DeleteObject(smallFont);
return;
}
DrawPanelCardAlpha(
{ gameRect.left - SS(10), gameRect.top - SS(10), gameRect.right + SS(10), gameRect.bottom + SS(10) },
cardColor,
frameColor,
28,
shellAlpha);
DrawPanelCardAlpha(leftPanelRect, cardColor, frameColor, 30, shellAlpha);
DrawPanelCardAlpha(rightPanelRect, cardColor, frameColor, 30, shellAlpha);
RECT innerRect =
{
gameRect.left + SS(6),
gameRect.top + SS(6),
gameRect.right - SS(6),
gameRect.bottom - SS(6)
};
{
Graphics boardGraphics(hdc);
SolidBrush boardBrush(Color(164, GetRValue(boardColor), GetGValue(boardColor), GetBValue(boardColor)));
SolidBrush innerBrush(Color(176, GetRValue(boardInnerColor), GetGValue(boardInnerColor), GetBValue(boardInnerColor)));
boardGraphics.FillRectangle(
&boardBrush,
static_cast<INT>(gameRect.left),
static_cast<INT>(gameRect.top),
static_cast<INT>(gameRect.right - gameRect.left),
static_cast<INT>(gameRect.bottom - gameRect.top));
boardGraphics.FillRectangle(
&innerBrush,
static_cast<INT>(innerRect.left),
static_cast<INT>(innerRect.top),
static_cast<INT>(innerRect.right - innerRect.left),
static_cast<INT>(innerRect.bottom - innerRect.top));
}
HPEN borderPen = CreatePen(PS_SOLID, SS(2), RGB(132, 108, 146));
oldPen = (HPEN)SelectObject(hdc, borderPen);
oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
RoundRect(hdc, gameRect.left, gameRect.top, gameRect.right, gameRect.bottom, SS(18), SS(18));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(borderPen);
HPEN gridPen = CreatePen(PS_SOLID, 1, lineColor);
oldPen = (HPEN)SelectObject(hdc, gridPen);
for (int i = 1; i < nGameWidth; i++)
{
int x = gameRect.left + i * grid;
MoveToEx(hdc, x, gameRect.top, nullptr);
LineTo(hdc, x, gameRect.bottom);
}
for (int i = 1; i < nGameHeight; i++)
{
int y = gameRect.top + i * grid;
MoveToEx(hdc, gameRect.left, y, nullptr);
LineTo(hdc, gameRect.right, y);
}
SelectObject(hdc, oldPen);
DeleteObject(gridPen);
int lockedRows = GetRogueLockedRows();
if (lockedRows > 0)
{
RECT lockedRect =
{
gameRect.left,
gameRect.top + (nGameHeight - lockedRows) * grid,
gameRect.right,
gameRect.bottom
};
Graphics lockedGraphics(hdc);
SolidBrush lockedBrush(Color(150, 58, 48, 68));
Pen lockedPen(Color(210, 232, 170, 194), 1.0f);
lockedGraphics.FillRectangle(
&lockedBrush,
static_cast<INT>(lockedRect.left),
static_cast<INT>(lockedRect.top),
static_cast<INT>(lockedRect.right - lockedRect.left),
static_cast<INT>(lockedRect.bottom - lockedRect.top));
lockedGraphics.DrawRectangle(
&lockedPen,
static_cast<INT>(lockedRect.left),
static_cast<INT>(lockedRect.top),
static_cast<INT>(lockedRect.right - lockedRect.left),
static_cast<INT>(lockedRect.bottom - lockedRect.top));
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(238, 204, 216));
RECT lockedTextRect =
{
lockedRect.left + SS(12),
lockedRect.top + SS(8),
lockedRect.right - SS(12),
lockedRect.bottom - SS(8)
};
DrawText(hdc, _T("封锁区"), -1, &lockedTextRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
for (int i = 0; i < nGameHeight; i++)
{
for (int j = 0; j < nGameWidth; j++)
{
if (workRegion[i][j] != 0)
{
int cellValue = workRegion[i][j];
bool isRainbowCell = (cellValue == 8);
int colorIndex = isRainbowCell ? 0 : (cellValue - 1);
RECT brickRect =
{
gameRect.left + j * grid + SS(2),
gameRect.top + i * grid + SS(2),
gameRect.left + (j + 1) * grid - SS(2),
gameRect.top + (i + 1) * grid - SS(2)
};
COLORREF fillColor = isRainbowCell ? RGB(255, 214, 120) : BrickColor[colorIndex];
COLORREF borderColor = isRainbowCell ? RGB(255, 248, 191) : RGB(255, 248, 250);
HBRUSH brickBrush = CreateSolidBrush(fillColor);
HPEN brickPen = CreatePen(PS_SOLID, isRainbowCell ? SS(2) : 1, borderColor);
oldPen = (HPEN)SelectObject(hdc, brickPen);
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(10), SS(10));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brickBrush);
DeleteObject(brickPen);
}
}
}
if (targetFlag && !gameOverFlag)
{
HPEN targetPen = CreatePen(PS_DOT, SS(2), RGB(255, 240, 245));
oldPen = (HPEN)SelectObject(hdc, targetPen);
oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (bricks[type][state][i][j] != 0)
{
int drawY = target.y + i;
int drawX = target.x + j;
if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth)
{
RoundRect(
hdc,
gameRect.left + drawX * grid + SS(5),
gameRect.top + drawY * grid + SS(5),
gameRect.left + (drawX + 1) * grid - SS(5),
gameRect.top + (drawY + 1) * grid - SS(5),
SS(8), SS(8));
}
}
}
}
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(targetPen);
}
if (!gameOverFlag)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (bricks[type][state][i][j] != 0)
{
int drawY = point.y + i;
int drawX = point.x + j;
if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth)
{
RECT brickRect =
{
gameRect.left + drawX * grid + SS(2),
gameRect.top + drawY * grid + SS(2),
gameRect.left + (drawX + 1) * grid - SS(2),
gameRect.top + (drawY + 1) * grid - SS(2)
};
HBRUSH brickBrush = CreateSolidBrush(BrickColor[type]);
COLORREF activeOutlineColor = RGB(255, 250, 252);
int activeOutlineWidth = 1;
if (currentPieceIsExplosive)
{
activeOutlineColor = RGB(255, 214, 82);
activeOutlineWidth = SS(3);
}
else if (currentPieceIsLaser)
{
activeOutlineColor = RGB(120, 232, 255);
activeOutlineWidth = SS(3);
}
else if (currentPieceIsCross)
{
activeOutlineColor = RGB(196, 255, 132);
activeOutlineWidth = SS(3);
}
else if (currentPieceIsRainbow)
{
activeOutlineColor = RGB(255, 226, 126);
activeOutlineWidth = SS(3);
}
HPEN brickPen = CreatePen(PS_SOLID, activeOutlineWidth, activeOutlineColor);
oldPen = (HPEN)SelectObject(hdc, brickPen);
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(12), SS(12));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brickBrush);
DeleteObject(brickPen);
}
}
}
}
}
if (clearEffectState.ticks > 0 && clearEffectState.totalTicks > 0)
{
int elapsed = clearEffectState.totalTicks - clearEffectState.ticks;
int alpha = 42 + clearEffectState.ticks * 150 / clearEffectState.totalTicks;
int inset = SS(elapsed * 2);
Graphics flashGraphics(hdc);
for (int i = 0; i < clearEffectState.rowCount; i++)
{
int row = clearEffectState.rows[i];
if (row < 0 || row >= nGameHeight)
{
continue;
}
int top = gameRect.top + row * grid + inset;
int height = grid - inset * 2;
if (height < SS(4))
{
height = SS(4);
}
SolidBrush flashBrush(Color(alpha, 255, 248, 174));
flashGraphics.FillRectangle(
&flashBrush,
static_cast<INT>(gameRect.left + SS(2)),
static_cast<INT>(top),
static_cast<INT>(gameRect.right - gameRect.left - SS(4)),
static_cast<INT>(height));
}
}
for (int i = 0; i < 96; i++)
{
if (particleEffects[i].ticks <= 0 || particleEffects[i].totalTicks <= 0)
{
continue;
}
int elapsed = particleEffects[i].totalTicks - particleEffects[i].ticks;
int particleX = gameRect.left + particleEffects[i].boardX * grid / 100 + SS(particleEffects[i].velocityX * elapsed);
int particleY = gameRect.top + particleEffects[i].boardY * grid / 100 + SS(particleEffects[i].velocityY * elapsed + elapsed * elapsed / 4);
int particleSize = SS(particleEffects[i].size * particleEffects[i].ticks / particleEffects[i].totalTicks);
if (particleSize < SS(2))
{
particleSize = SS(2);
}
RECT particleRect =
{
particleX - particleSize,
particleY - particleSize,
particleX + particleSize,
particleY + particleSize
};
HBRUSH particleBrush = CreateSolidBrush(particleEffects[i].color);
FillRect(hdc, &particleRect, particleBrush);
DeleteObject(particleBrush);
}
for (int i = 0; i < 8; i++)
{
if (floatingTextEffects[i].ticks <= 0 || floatingTextEffects[i].totalTicks <= 0)
{
continue;
}
int elapsed = floatingTextEffects[i].totalTicks - floatingTextEffects[i].ticks;
int textX = gameRect.left + floatingTextEffects[i].boardX * grid / 100;
int textY = gameRect.top + floatingTextEffects[i].boardY * grid / 100 - SS(elapsed * 4);
HFONT oldFloatFont = (HFONT)SelectObject(hdc, sectionFont);
SetTextColor(hdc, floatingTextEffects[i].color);
RECT floatRect =
{
textX - SS(160),
textY - SS(24),
textX + SS(160),
textY + SS(28)
};
DrawText(hdc, floatingTextEffects[i].text, -1, &floatRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, oldFloatFont);
}
HFONT oldFont = (HFONT)SelectObject(hdc, titleFont);
DrawPanelHeader(leftPanelRect, _T("战局信息"), 120);
DrawPanelHeader(rightPanelRect, _T("预览与战术"), 148);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT overviewRect =
{
leftPanelRect.left + SS(20),
leftPanelRect.top + SS(98),
leftPanelRect.right - SS(20),
leftPanelRect.top + SS(252)
};
DrawPanelCardAlpha(overviewRect, RGB(255, 247, 250), RGB(233, 191, 208), 24, panelAlpha);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT overviewTitleRect =
{
overviewRect.left + SS(18),
overviewRect.top + SS(12),
overviewRect.right - SS(18),
overviewRect.top + SS(42)
};
DrawText(hdc, _T("战场状态"), -1, &overviewTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
TCHAR overviewText[160];
int totalLines = (currentMode == MODE_CLASSIC) ? classicStats.totalLinesCleared : rogueStats.totalLinesCleared;
if (currentMode == MODE_ROGUE)
{
_stprintf_s(
overviewText,
_T("得分 %d\r\n模式 Rogue\r\n消行 %d\r\n危险 Lv.%d 封锁 %d"),
tScore,
totalLines,
rogueStats.difficultyLevel,
GetRogueLockedRows());
}
else
{
_stprintf_s(
overviewText,
_T("得分 %d\r\n模式 经典\r\n消行 %d"),
tScore,
totalLines);
}
RECT overviewBodyRect =
{
overviewRect.left + SS(22),
overviewRect.top + SS(62),
overviewRect.right - SS(22),
overviewRect.bottom - SS(22)
};
DrawText(hdc, overviewText, -1, &overviewBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
if (currentMode == MODE_ROGUE)
{
RECT progressRect =
{
leftPanelRect.left + SS(20),
leftPanelRect.top + SS(270),
leftPanelRect.right - SS(20),
leftPanelRect.top + SS(488)
};
DrawPanelCardAlpha(progressRect, RGB(255, 248, 251), RGB(233, 191, 208), 24, panelAlpha);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT progressTitleRect =
{
progressRect.left + SS(18),
progressRect.top + SS(12),
progressRect.right - SS(18),
progressRect.top + SS(42)
};
DrawText(hdc, _T("成长进度"), -1, &progressTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
TCHAR progressText[96];
_stprintf_s(progressText, _T("等级 %d\r\nEXP %d / %d"), rogueStats.level, rogueStats.exp, rogueStats.requiredExp);
RECT progressTextRect =
{
progressRect.left + SS(22),
progressRect.top + SS(56),
progressRect.right - SS(22),
progressRect.top + SS(120)
};
DrawText(hdc, progressText, -1, &progressTextRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
RECT expBarRect =
{
progressRect.left + SS(22),
progressRect.top + SS(122),
progressRect.right - SS(22),
progressRect.top + SS(148)
};
HBRUSH expTrackBrush = CreateSolidBrush(RGB(240, 220, 229));
FillRect(hdc, &expBarRect, expTrackBrush);
DeleteObject(expTrackBrush);
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;
}
if (expFillRect.right > expBarRect.right)
{
expFillRect.right = expBarRect.right;
}
HBRUSH expFillBrush = CreateSolidBrush(accentColor);
FillRect(hdc, &expFillRect, expFillBrush);
DeleteObject(expFillBrush);
const TCHAR* statLabels[2] =
{
_T("\u5f97\u5206\u500d\u7387"),
_T("EXP \u500d\u7387")
};
TCHAR statValues[2][32];
_stprintf_s(statValues[0], _T("%d%%"), rogueStats.scoreMultiplierPercent);
_stprintf_s(statValues[1], _T("%d%%"), rogueStats.expMultiplierPercent);
int statGap = SS(8);
int statWidth = (progressRect.right - progressRect.left - SS(44) - statGap) / 2;
for (int statIndex = 0; statIndex < 2; statIndex++)
{
RECT statRect =
{
progressRect.left + SS(22) + statIndex * (statWidth + statGap),
progressRect.top + SS(154),
progressRect.left + SS(22) + statIndex * (statWidth + statGap) + statWidth,
progressRect.top + SS(204)
};
DrawPanelCardAlpha(statRect, RGB(255, 239, 245), RGB(232, 184, 202), 16, panelNestedAlpha);
RECT statLabelRect =
{
statRect.left + SS(8),
statRect.top + SS(4),
statRect.right - SS(8),
statRect.top + SS(22)
};
RECT statValueRect =
{
statRect.left + SS(8),
statRect.top + SS(21),
statRect.right - SS(8),
statRect.bottom - SS(2)
};
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(132, 102, 118));
DrawText(hdc, statLabels[statIndex], -1, &statLabelRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, titleColor);
DrawText(hdc, statValues[statIndex], -1, &statValueRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
RECT upgradeListRect =
{
leftPanelRect.left + SS(20),
leftPanelRect.top + SS(510),
leftPanelRect.right - SS(20),
leftPanelRect.bottom - SS(20)
};
DrawPanelCardAlpha(upgradeListRect, RGB(255, 248, 251), RGB(233, 191, 208), 24, panelAlpha);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
TextOut(hdc, upgradeListRect.left + SS(18), upgradeListRect.top + SS(16), _T("已掌握强化"), lstrlen(_T("已掌握强化")));
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT upgradeBodyRect =
{
upgradeListRect.left + SS(22),
upgradeListRect.top + SS(56),
upgradeListRect.right - SS(22),
upgradeListRect.bottom - SS(20)
};
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 (rogueStats.pressureReliefLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u51cf\u538b Lv.%d\r\n"), rogueStats.pressureReliefLevel);
}
if (rogueStats.sweeperLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6e05\u626b\u8005 Lv.%d\r\n"), rogueStats.sweeperLevel);
}
if (rogueStats.explosiveLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u7206\u7834\u65b9\u5757 Lv.%d\r\n"), rogueStats.explosiveLevel);
}
if (rogueStats.chainBlastLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8fde\u9501\u7206\u7834 Lv.1\r\n"));
}
if (rogueStats.chainBombLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8fde\u73af\u70b8\u5f39 Lv.1\r\n"));
}
if (rogueStats.laserLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6fc0\u5149\u65b9\u5757 Lv.%d\r\n"), rogueStats.laserLevel);
}
if (rogueStats.thunderTetrisLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u96f7\u9706\u56db\u6d88 Lv.1\r\n"));
}
if (rogueStats.thunderLaserLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u96f7\u9706\u6fc0\u5149 Lv.1\r\n"));
}
if (rogueStats.feverLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u72c2\u70ed\u6a21\u5f0f Lv.1\r\n"));
}
if (rogueStats.rageStackLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u66b4\u8d70\u5806\u53e0 Lv.1\r\n"));
}
if (rogueStats.infiniteFeverLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u65e0\u9650\u72c2\u70ed Lv.1\r\n"));
}
if (rogueStats.screenBombLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6e05\u5c4f\u70b8\u5f39 Lv.1\r\n"));
}
if (rogueStats.terminalClearLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u7ec8\u672b\u6e05\u573a Lv.1\r\n"));
}
if (rogueStats.perfectRotateLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u5b8c\u7f8e\u65cb\u8f6c Lv.1\r\n"));
}
if (rogueStats.timeDilationLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u65f6\u95f4\u7f13\u6d41 Lv.1\r\n"));
}
if (rogueStats.highPressureLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u9ad8\u538b\u5956\u52b1 Lv.1\r\n"));
}
if (rogueStats.tetrisGambleLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8d4c\u547d\u56db\u6d88 Lv.1\r\n"));
}
if (rogueStats.extremePlayerLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6781\u9650\u73a9\u5bb6 Lv.1\r\n"));
}
if (rogueStats.upgradeShockwaveLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u5347\u7ea7\u51b2\u51fb\u6ce2 Lv.1\r\n"));
}
if (rogueStats.evolutionImpactLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8fdb\u5316\u51b2\u51fb Lv.1\r\n"));
}
if (rogueStats.controlMasterLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u64cd\u63a7\u5927\u5e08 Lv.1\r\n"));
}
if (rogueStats.blockStormLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u65b9\u5757\u98ce\u66b4 Lv.1\r\n"));
}
if (rogueStats.crossPieceLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u5341\u5b57\u65b9\u5757 Lv.%d\r\n"), rogueStats.crossPieceLevel);
}
if (rogueStats.blackHoleLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u9ed1\u6d1e Lv.1\r\n"));
}
if (rogueStats.reshapeLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u7a7a\u4e2d\u6362\u5f62 Lv.1\r\n"));
}
if (rogueStats.rainbowPieceLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u5f69\u8679\u65b9\u5757 Lv.1\r\n"));
}
if (rogueStats.voidCoreLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u865a\u7a7a\u6838\u5fc3 Lv.1\r\n"));
}
if (rogueStats.stableStructureLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u7a33\u5b9a\u7ed3\u6784 Lv.%d\r\n"), rogueStats.stableStructureLevel);
}
if (rogueStats.doubleGrowthLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u6210\u957f\u6838\u5fc3 Lv.1\r\n"));
}
if (rogueStats.pieceTuningLevels[0] > 0)
{
_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)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u8d4c\u5f92 Lv.%d\r\n"), rogueStats.gamblerLevel);
}
if (rogueStats.dualChoiceLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u53cc\u91cd\u9009\u62e9 Lv.1\r\n"));
}
if (rogueStats.destinyWheelLevel > 0)
{
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u547d\u8fd0\u8f6e\u76d8 Lv.1\r\n"));
}
if (lstrlen(upgradeSummary) == 0)
{
_stprintf_s(upgradeSummary, _T("尚未掌握强化。\r\n下一次升级将开启构筑。"));
}
DrawText(hdc, upgradeSummary, -1, &upgradeBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
}
else
{
RECT classicInfoRect =
{
leftPanelRect.left + SS(20),
leftPanelRect.top + SS(270),
leftPanelRect.right - SS(20),
leftPanelRect.top + SS(458)
};
DrawPanelCardAlpha(classicInfoRect, RGB(255, 248, 251), RGB(233, 191, 208), 24, panelAlpha);
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT classicBodyRect =
{
classicInfoRect.left + SS(22),
classicInfoRect.top + SS(24),
classicInfoRect.right - SS(22),
classicInfoRect.bottom - SS(24)
};
DrawText(hdc, _T("经典模式:没有成长、EXP 和强化,只保留纯粹的消行挑战。\r\n\r\n在有限空间中保持阵型,尽可能活得更久。"), -1, &classicBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
}
RECT holdNextRect =
{
rightPanelRect.left + SS(20),
rightPanelRect.top + SS(98),
rightPanelRect.right - SS(20),
rightPanelRect.top + SS(326)
};
DrawPanelCardAlpha(holdNextRect, RGB(255, 247, 250), RGB(233, 191, 208), 24, panelAlpha);
RECT holdCard =
{
holdNextRect.left + SS(88),
holdNextRect.top + SS(24),
holdNextRect.left + SS(150),
holdNextRect.top + SS(86)
};
RECT nextSectionRect =
{
holdNextRect.left + SS(18),
holdNextRect.top + SS(146),
holdNextRect.right - SS(18),
holdNextRect.bottom - SS(22)
};
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT holdTitleRect =
{
holdNextRect.left + SS(18),
holdNextRect.top + SS(34),
holdNextRect.left + SS(80),
holdNextRect.top + SS(70)
};
DrawText(hdc, _T("Hold"), -1, &holdTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
RECT nextTitleRect =
{
nextSectionRect.left,
holdNextRect.top + SS(102),
holdNextRect.right - SS(18),
holdNextRect.top + SS(136)
};
DrawText(hdc, _T("\u4e0b\u4e00\u4e2a"), -1, &nextTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
DrawPanelCardAlpha(holdCard, RGB(255, 238, 244), RGB(233, 191, 208), 22, panelNestedAlpha);
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT holdHintRect =
{
holdCard.right + SS(14),
holdCard.top + SS(2),
holdNextRect.right - SS(18),
holdCard.bottom - SS(2)
};
DrawText(
hdc,
currentMode == MODE_ROGUE && rogueStats.holdUnlocked > 0
? (holdUsedThisTurn ? _T("已使用\r\n落地后恢复") : _T("C / Shift\r\n暂存一次"))
: _T("未开启\r\n需备用仓"),
-1,
&holdHintRect,
DT_LEFT | DT_VCENTER | DT_WORDBREAK);
if (currentMode == MODE_ROGUE)
{
if (rogueStats.holdUnlocked == 0)
{
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT holdLockedRect =
{
holdCard.left + SS(10),
holdCard.top + SS(10),
holdCard.right - SS(10),
holdCard.bottom - SS(10)
};
DrawText(hdc, _T("封存"), -1, &holdLockedRect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
else if (holdType >= 0)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (bricks[holdType][0][i][j] != 0)
{
int holdAvailableWidth = holdCard.right - holdCard.left - SS(16);
int holdAvailableHeight = holdCard.bottom - holdCard.top - SS(16);
int holdMiniCell = holdAvailableWidth < holdAvailableHeight ? holdAvailableWidth / 4 : holdAvailableHeight / 4;
if (holdMiniCell < SS(7))
{
holdMiniCell = SS(7);
}
int holdOffsetX = holdCard.left + (holdCard.right - holdCard.left - holdMiniCell * 4) / 2;
int holdOffsetY = holdCard.top + (holdCard.bottom - holdCard.top - holdMiniCell * 4) / 2;
RECT brickRect =
{
holdOffsetX + j * holdMiniCell,
holdOffsetY + i * holdMiniCell,
holdOffsetX + (j + 1) * holdMiniCell - SS(1),
holdOffsetY + (i + 1) * holdMiniCell - SS(1)
};
HBRUSH brickBrush = CreateSolidBrush(BrickColor[holdType]);
HPEN brickPen = CreatePen(PS_SOLID, 1, RGB(255, 248, 250));
oldPen = (HPEN)SelectObject(hdc, brickPen);
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(8), SS(8));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brickBrush);
DeleteObject(brickPen);
}
}
}
}
else
{
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT emptyHoldRect =
{
holdCard.left + SS(10),
holdCard.top + SS(10),
holdCard.right - SS(10),
holdCard.bottom - SS(10)
};
DrawText(hdc, _T("\u7a7a"), -1, &emptyHoldRect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
}
else
{
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT classicHoldRect =
{
holdCard.left + SS(10),
holdCard.top + SS(10),
holdCard.right - SS(10),
holdCard.bottom - SS(10)
};
DrawText(hdc, _T("\u5173"), -1, &classicHoldRect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
int previewCount = 1;
if (currentMode == MODE_ROGUE)
{
previewCount = rogueStats.previewCount;
if (previewCount < 1)
{
previewCount = 1;
}
if (previewCount > 3)
{
previewCount = 3;
}
}
for (int previewIndex = 0; previewIndex < previewCount; previewIndex++)
{
int nextCardGap = SS(8);
int nextCardWidth = (nextSectionRect.right - nextSectionRect.left - nextCardGap * 2) / 3;
RECT nextCard =
{
nextSectionRect.left + previewIndex * (nextCardWidth + nextCardGap),
nextSectionRect.top,
nextSectionRect.left + previewIndex * (nextCardWidth + nextCardGap) + nextCardWidth,
nextSectionRect.bottom
};
DrawPanelCardAlpha(nextCard, RGB(255, 247, 250), RGB(233, 191, 208), 18, panelStrongAlpha);
int previewType = nextTypes[previewIndex];
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (bricks[previewType][0][i][j] != 0)
{
int nextAvailableWidth = nextCard.right - nextCard.left - SS(14);
int nextAvailableHeight = nextCard.bottom - nextCard.top - SS(14);
int nextMiniCell = nextAvailableWidth < nextAvailableHeight ? nextAvailableWidth / 4 : nextAvailableHeight / 4;
if (nextMiniCell < SS(7))
{
nextMiniCell = SS(7);
}
int previewOffsetX = nextCard.left + (nextCard.right - nextCard.left - nextMiniCell * 4) / 2;
int previewOffsetY = nextCard.top + (nextCard.bottom - nextCard.top - nextMiniCell * 4) / 2;
RECT brickRect =
{
previewOffsetX + j * nextMiniCell,
previewOffsetY + i * nextMiniCell,
previewOffsetX + (j + 1) * nextMiniCell - SS(1),
previewOffsetY + (i + 1) * nextMiniCell - SS(1)
};
HBRUSH brickBrush = CreateSolidBrush(BrickColor[previewType]);
HPEN brickPen = CreatePen(PS_SOLID, 1, RGB(255, 248, 250));
oldPen = (HPEN)SelectObject(hdc, brickPen);
oldBrush = (HBRUSH)SelectObject(hdc, brickBrush);
RoundRect(hdc, brickRect.left, brickRect.top, brickRect.right, brickRect.bottom, SS(8), SS(8));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(brickBrush);
DeleteObject(brickPen);
}
}
}
}
RECT controlRect =
{
rightPanelRect.left + SS(20),
rightPanelRect.top + SS(346),
rightPanelRect.right - SS(20),
rightPanelRect.bottom - SS(20)
};
DrawPanelCardAlpha(controlRect, RGB(255, 248, 251), RGB(233, 191, 208), 24, panelAlpha);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
TextOut(hdc, controlRect.left + SS(18), controlRect.top + SS(16), _T("战斗日志"), lstrlen(_T("战斗日志")));
RECT feedbackPanelRect =
{
controlRect.left + SS(18),
controlRect.top + SS(56),
controlRect.right - SS(18),
controlRect.top + SS(172)
};
DrawPanelCardAlpha(feedbackPanelRect, RGB(255, 244, 248), RGB(232, 184, 202), 20, panelNestedAlpha);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, titleColor);
RECT feedbackTitleRect =
{
feedbackPanelRect.left + SS(16),
feedbackPanelRect.top + SS(14),
feedbackPanelRect.right - SS(16),
feedbackPanelRect.top + SS(42)
};
DrawText(
hdc,
feedbackState.visibleTicks > 0 ? feedbackState.title : _T("局势平稳"),
-1,
&feedbackTitleRect,
DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(116, 90, 104));
RECT feedbackBodyRect =
{
feedbackPanelRect.left + SS(16),
feedbackPanelRect.top + SS(46),
feedbackPanelRect.right - SS(16),
feedbackPanelRect.bottom - SS(14)
};
DrawText(
hdc,
feedbackState.visibleTicks > 0
? feedbackState.detail
: _T("连消、主动技能和特殊方块的战斗记录会出现在这里。"),
-1,
&feedbackBodyRect,
DT_LEFT | DT_TOP | DT_WORDBREAK);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, textColor);
RECT tipsTitleRect =
{
controlRect.left + SS(22),
controlRect.top + SS(192),
controlRect.right - SS(22),
controlRect.top + SS(224)
};
DrawText(hdc, _T("操作指令"), -1, &tipsTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT controlBodyRect =
{
controlRect.left + SS(22),
controlRect.top + SS(232),
controlRect.right - SS(22),
controlRect.bottom - SS(22)
};
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);
}
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 菜单"),
-1,
&controlBodyRect,
DT_LEFT | DT_TOP | DT_WORDBREAK);
}
if (suspendFlag || gameOverFlag)
{
RECT overlayRect =
{
gameRect.left + SS(48),
gameRect.top + grid * 7,
gameRect.right - SS(48),
gameRect.top + grid * 11 + SS(18)
};
HBRUSH overlayBrush = CreateSolidBrush(RGB(255, 241, 246));
HPEN overlayPen = CreatePen(PS_SOLID, 2, RGB(232, 170, 194));
oldPen = (HPEN)SelectObject(hdc, overlayPen);
oldBrush = (HBRUSH)SelectObject(hdc, overlayBrush);
RoundRect(hdc, overlayRect.left, overlayRect.top, overlayRect.right, overlayRect.bottom, SS(26), SS(26));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(overlayBrush);
DeleteObject(overlayPen);
RECT titleRect =
{
overlayRect.left + SS(24),
overlayRect.top + SS(18),
overlayRect.right - SS(24),
overlayRect.top + SS(78)
};
RECT tipRect =
{
overlayRect.left + SS(28),
overlayRect.top + SS(86),
overlayRect.right - SS(28),
overlayRect.bottom - SS(20)
};
SetTextColor(hdc, RGB(121, 76, 99));
SelectObject(hdc, titleFont);
if (suspendFlag)
{
DrawText(hdc, _T("时间静止"), -1, &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
DrawText(hdc, _T("按 P 继续战斗"), -1, &tipRect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
else
{
DrawText(hdc, _T("战局崩塌"), -1, &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
DrawText(
hdc,
reviveAvailable
? _T("按 V 看视频复活(仅一次)\r\n按 R 重新挑战 或按 M 返回菜单")
: _T("按 R 重新挑战\r\n或按 M 返回菜单"),
-1,
&tipRect,
DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
}
if (currentScreen == SCREEN_UPGRADE)
{
RECT dimRect =
{
SX(20),
SY(20),
SX(WINDOW_CLIENT_WIDTH - 20),
SY(WINDOW_CLIENT_HEIGHT - 20)
};
Graphics dimGraphics(hdc);
SolidBrush dimBrush(Color(214, 246, 232, 238));
dimGraphics.FillRectangle(
&dimBrush,
static_cast<INT>(dimRect.left),
static_cast<INT>(dimRect.top),
static_cast<INT>(dimRect.right - dimRect.left),
static_cast<INT>(dimRect.bottom - dimRect.top));
RECT overlayRect =
{
SX(60),
SY(80),
SX(WINDOW_CLIENT_WIDTH - 60),
SY(WINDOW_CLIENT_HEIGHT - 80)
};
DrawPanelCardAlpha(overlayRect, RGB(255, 250, 252), RGB(232, 170, 194), 30, 238);
SetTextColor(hdc, titleColor);
SelectObject(hdc, titleFont);
RECT overlayTitleRect =
{
overlayRect.left,
overlayRect.top + SS(24),
overlayRect.right,
overlayRect.top + SS(68)
};
DrawText(hdc, _T("选择强化"), -1, &overlayTitleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, textColor);
RECT overlaySubtitleRect =
{
overlayRect.left,
overlayRect.top + SS(70),
overlayRect.right,
overlayRect.top + SS(106)
};
TCHAR overlaySubtitle[128];
_stprintf_s(
overlaySubtitle,
_T("出现 %d 个强化,还可选择 %d 个"),
upgradeUiState.optionCount,
upgradeUiState.picksRemaining);
DrawText(hdc, overlaySubtitle, -1, &overlaySubtitleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
int gap = SS(18);
int horizontalPadding = SS(36);
int verticalTop = overlayRect.top + SS(138);
int columnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3;
if (columnCount < 1)
{
columnCount = 1;
}
int rowCount = (upgradeUiState.optionCount + columnCount - 1) / columnCount;
if (rowCount < 1)
{
rowCount = 1;
}
int cardWidth = (overlayRect.right - overlayRect.left - horizontalPadding * 2 - gap * (columnCount - 1)) / columnCount;
int availableHeight = overlayRect.bottom - verticalTop - SS(72) - (rowCount - 1) * gap;
int cardHeight = availableHeight / rowCount;
for (int i = 0; i < upgradeUiState.optionCount; i++)
{
bool isSelected = (i == upgradeUiState.selectedIndex);
int column = i % columnCount;
int row = i / columnCount;
int left = overlayRect.left + horizontalPadding + column * (cardWidth + gap);
int top = verticalTop + row * (cardHeight + gap);
int right = left + cardWidth;
RECT cardRect =
{
left,
top,
right,
top + cardHeight
};
COLORREF cardFill = RGB(255, 247, 250);
COLORREF cardBorder = RGB(221, 197, 208);
COLORREF descColor = RGB(108, 86, 99);
COLORREF footerColor = RGB(128, 104, 118);
const TCHAR* synthesisPath = GetUpgradeSynthesisPath(upgradeUiState.options[i].id);
bool hasSynthesisPath = synthesisPath != nullptr && lstrlen(synthesisPath) > 0;
const TCHAR* rarityText = _T("\u7a00\u6709\u5ea6\uff1a\u666e\u901a");
if (upgradeUiState.options[i].cursed)
{
cardFill = isSelected ? RGB(238, 232, 238) : RGB(250, 245, 248);
cardBorder = RGB(38, 34, 42);
descColor = RGB(72, 62, 72);
footerColor = RGB(48, 42, 50);
rarityText = _T("\u7a00\u6709\u5ea6\uff1a\u8bc5\u5492");
}
else if (hasSynthesisPath)
{
cardBorder = RGB(208, 74, 78);
footerColor = RGB(164, 58, 64);
rarityText = _T("\u7a00\u6709\u5ea6\uff1a\u8fdb\u9636");
}
else if (upgradeUiState.options[i].rarity == UPGRADE_RARITY_RARE)
{
cardBorder = RGB(218, 172, 72);
footerColor = RGB(150, 111, 40);
rarityText = _T("\u7a00\u6709\u5ea6\uff1a\u73cd\u7a00");
}
else if (upgradeUiState.options[i].rarity == UPGRADE_RARITY_UNCOMMON)
{
cardBorder = RGB(92, 152, 218);
footerColor = RGB(68, 112, 166);
rarityText = _T("\u7a00\u6709\u5ea6\uff1a\u7f55\u89c1");
}
if (isSelected && !upgradeUiState.options[i].cursed)
{
cardFill = RGB(255, 235, 242);
}
DrawPanelCardAlpha(cardRect, cardFill, cardBorder, 24, isSelected ? 238 : 226);
if (isSelected)
{
HPEN selectedPen = CreatePen(PS_SOLID, SS(3), cardBorder);
oldPen = (HPEN)SelectObject(hdc, selectedPen);
oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
RoundRect(hdc, cardRect.left, cardRect.top, cardRect.right, cardRect.bottom, SS(24), SS(24));
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(selectedPen);
}
TCHAR levelText[32];
_stprintf_s(levelText, _T("Lv.%d"), upgradeUiState.options[i].currentLevel + 1);
RECT levelRect =
{
cardRect.right - SS(88),
cardRect.top + SS(20),
cardRect.right - SS(20),
cardRect.top + SS(48)
};
SelectObject(hdc, smallFont);
SetTextColor(hdc, footerColor);
DrawText(hdc, levelText, -1, &levelRect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, sectionFont);
SetTextColor(hdc, upgradeUiState.options[i].cursed ? RGB(130, 54, 54) : (isSelected ? titleColor : textColor));
RECT nameRect =
{
cardRect.left + SS(20),
cardRect.top + SS(44),
cardRect.right - SS(96),
cardRect.top + SS(98)
};
DrawText(hdc, upgradeUiState.options[i].name, -1, &nameRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
SelectObject(hdc, bodyFont);
SetTextColor(hdc, descColor);
RECT descRect =
{
cardRect.left + SS(20),
cardRect.top + SS(116),
cardRect.right - SS(20),
cardRect.bottom - (hasSynthesisPath ? SS(98) : SS(64))
};
DrawText(hdc, upgradeUiState.options[i].description, -1, &descRect, DT_LEFT | DT_WORDBREAK);
SelectObject(hdc, smallFont);
if (hasSynthesisPath)
{
RECT synthesisRect =
{
cardRect.left + SS(20),
cardRect.bottom - SS(88),
cardRect.right - SS(20),
cardRect.bottom - SS(58)
};
SetTextColor(hdc, upgradeUiState.options[i].cursed ? RGB(142, 78, 78) : RGB(116, 82, 104));
DrawText(hdc, synthesisPath, -1, &synthesisRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
RECT footerRect =
{
cardRect.left + SS(20),
cardRect.bottom - SS(42),
cardRect.right - SS(20),
cardRect.bottom - SS(14)
};
SetTextColor(hdc, footerColor);
DrawText(
hdc,
rarityText,
-1,
&footerRect,
DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
SelectObject(hdc, smallFont);
SetTextColor(hdc, RGB(128, 104, 118));
RECT hintRect =
{
overlayRect.left + SS(30),
overlayRect.bottom - SS(52),
overlayRect.right - SS(30),
overlayRect.bottom - SS(18)
};
DrawText(hdc, _T("W/A/S/D \u6216\u65b9\u5411\u952e\u5207\u6362\uff0cEnter \u6216 Space \u786e\u8ba4"), -1, &hintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
DrawMusicButton();
SelectObject(hdc, oldFont);
DeleteObject(titleFont);
DeleteObject(sectionFont);
DeleteObject(bodyFont);
DeleteObject(smallFont);
}