#include "stdafx.h" #include "Tetris.h" #include #include #include #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(rect.left); INT top = static_cast(rect.top); INT width = static_cast(rect.right - rect.left); INT height = static_cast(rect.bottom - rect.top); INT corner = static_cast(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(gameRect.left), static_cast(gameRect.top), static_cast(gameRect.right - gameRect.left), static_cast(gameRect.bottom - gameRect.top)); boardGraphics.FillRectangle( &innerBrush, static_cast(innerRect.left), static_cast(innerRect.top), static_cast(innerRect.right - innerRect.left), static_cast(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(lockedRect.left), static_cast(lockedRect.top), static_cast(lockedRect.right - lockedRect.left), static_cast(lockedRect.bottom - lockedRect.top)); lockedGraphics.DrawRectangle( &lockedPen, static_cast(lockedRect.left), static_cast(lockedRect.top), static_cast(lockedRect.right - lockedRect.left), static_cast(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(gameRect.left + SS(2)), static_cast(top), static_cast(gameRect.right - gameRect.left - SS(4)), static_cast(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(dimRect.left), static_cast(dimRect.top), static_cast(dimRect.right - dimRect.left), static_cast(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); }