2843 lines
114 KiB
C++
2843 lines
114 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 HBRUSH GetCachedParticleBrush(COLORREF color)
|
|
{
|
|
static COLORREF cachedColors[16] = {};
|
|
static HBRUSH cachedBrushes[16] = {};
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (cachedBrushes[i] != nullptr && cachedColors[i] == color)
|
|
{
|
|
return cachedBrushes[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
if (cachedBrushes[i] == nullptr)
|
|
{
|
|
cachedColors[i] = color;
|
|
cachedBrushes[i] = CreateSolidBrush(color);
|
|
return cachedBrushes[i];
|
|
}
|
|
}
|
|
|
|
return CreateSolidBrush(color);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @brief 按序号加载致谢页图片资源。
|
|
*/
|
|
static Bitmap* LoadCreditImage(int index)
|
|
{
|
|
constexpr int creditPageCount = 4;
|
|
static ULONG_PTR gdiplusToken = 0;
|
|
static Bitmap* creditImages[creditPageCount] = {};
|
|
static bool attempted[creditPageCount] = {};
|
|
|
|
if (index < 0 || index >= creditPageCount)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (!attempted[index])
|
|
{
|
|
attempted[index] = true;
|
|
|
|
GdiplusStartupInput startupInput;
|
|
if (GdiplusStartup(&gdiplusToken, &startupInput, nullptr) == Ok)
|
|
{
|
|
const wchar_t* imageNames[creditPageCount] =
|
|
{
|
|
L"assets\\images\\qls.jpg",
|
|
L"assets\\images\\wyk.jpg",
|
|
L"assets\\images\\swj.jpg",
|
|
L"assets\\images\\qhy.jpg"
|
|
};
|
|
const std::wstring creditExtraCandidates[] =
|
|
{
|
|
BuildAssetPath(imageNames[index]),
|
|
BuildWorkingDirAssetPath(imageNames[index]),
|
|
BuildAssetPath(L"assets\\images\\qhy.png"),
|
|
BuildWorkingDirAssetPath(L"assets\\images\\qhy.png"),
|
|
BuildAssetPath(L"assets\\images\\qhy.jpeg"),
|
|
BuildWorkingDirAssetPath(L"assets\\images\\qhy.jpeg"),
|
|
BuildAssetPath(L"assets\\images\\qhy.bmp"),
|
|
BuildWorkingDirAssetPath(L"assets\\images\\qhy.bmp")
|
|
};
|
|
int candidateCount = (index == 3) ? 8 : 2;
|
|
|
|
for (int i = 0; i < candidateCount; i++)
|
|
{
|
|
const std::wstring& candidate = creditExtraCandidates[i];
|
|
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)
|
|
{
|
|
creditImages[index] = loadedImage;
|
|
break;
|
|
}
|
|
|
|
delete loadedImage;
|
|
}
|
|
}
|
|
}
|
|
|
|
return creditImages[index];
|
|
}
|
|
|
|
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);
|
|
};
|
|
|
|
auto DrawBackButton = [&]()
|
|
{
|
|
RECT backButtonRect =
|
|
{
|
|
SX(6),
|
|
SY(6),
|
|
SX(34),
|
|
SY(34)
|
|
};
|
|
HBRUSH backBrush = CreateSolidBrush(RGB(255, 242, 247));
|
|
HPEN backFramePen = CreatePen(PS_SOLID, 1, RGB(222, 130, 166));
|
|
HBRUSH oldBackBrush = (HBRUSH)SelectObject(hdc, backBrush);
|
|
HPEN oldBackFramePen = (HPEN)SelectObject(hdc, backFramePen);
|
|
RoundRect(hdc, backButtonRect.left, backButtonRect.top, backButtonRect.right, backButtonRect.bottom, SS(12), SS(12));
|
|
SelectObject(hdc, oldBackBrush);
|
|
SelectObject(hdc, oldBackFramePen);
|
|
DeleteObject(backFramePen);
|
|
DeleteObject(backBrush);
|
|
|
|
HPEN backPen = CreatePen(PS_SOLID, SS(3), RGB(128, 70, 100));
|
|
HPEN oldBackPen = (HPEN)SelectObject(hdc, backPen);
|
|
MoveToEx(hdc, backButtonRect.left + SS(20), backButtonRect.top + SS(8), nullptr);
|
|
LineTo(hdc, backButtonRect.left + SS(10), backButtonRect.top + SS(14));
|
|
LineTo(hdc, backButtonRect.left + SS(20), backButtonRect.top + SS(20));
|
|
MoveToEx(hdc, backButtonRect.left + SS(10), backButtonRect.top + SS(14), nullptr);
|
|
LineTo(hdc, backButtonRect.right - SS(8), backButtonRect.top + SS(14));
|
|
SelectObject(hdc, oldBackPen);
|
|
DeleteObject(backPen);
|
|
};
|
|
|
|
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[4] =
|
|
{
|
|
_T("\u7ecf\u5178\u6a21\u5f0f"),
|
|
_T("Rogue \u6a21\u5f0f"),
|
|
_T("\u5e2e\u52a9"),
|
|
_T("\u81f4\u8c22")
|
|
};
|
|
|
|
const TCHAR* modeDescriptions[4] =
|
|
{
|
|
_T("纯粹方块挑战,专注消行、堆叠与生存。"),
|
|
_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");
|
|
}
|
|
else if (helpState.currentPage == 4)
|
|
{
|
|
helpTitle = _T("\u81f4\u8c22");
|
|
}
|
|
else if (helpState.currentPage == 5)
|
|
{
|
|
helpTitle = _T("\u6280\u80fd\u6f14\u793a");
|
|
}
|
|
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[4] =
|
|
{
|
|
_T("\u6e38\u620f\u4ecb\u7ecd"),
|
|
_T("\u64cd\u4f5c\u8bf4\u660e"),
|
|
_T("\u5f3a\u5316\u56fe\u9274"),
|
|
_T("\u6280\u80fd\u6f14\u793a")
|
|
};
|
|
const TCHAR* optionDetails[4] =
|
|
{
|
|
_T("\u7ecf\u5178\u6a21\u5f0f\u3001Rogue \u6a21\u5f0f\u548c\u590d\u6d3b\u89c4\u5219\u6982\u89c8\u3002"),
|
|
_T("\u79fb\u52a8\u3001\u65cb\u8f6c\u3001\u786c\u964d\u3001Hold \u4e0e\u6280\u80fd\u5feb\u6377\u952e\u3002"),
|
|
_T("\u67e5\u770b Rogue \u6a21\u5f0f\u5168\u90e8\u5f3a\u5316\u7684\u7b80\u8981\u6548\u679c\u3002"),
|
|
_T("\u8fdb\u5165\u81ea\u52a8\u8f6e\u64ad\u6f14\u793a\uff0c\u4f9d\u6b21\u5c55\u793a Rogue \u4e3b\u8981\u6280\u80fd\u548c\u68cb\u76d8\u6548\u679c\u3002")
|
|
};
|
|
|
|
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("战斗越久危险越高,底部封锁区会压缩空间。\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缓坠羽翼:降低自然下落速度,最多叠加 4 次。\r\n连击律动:连续消行时追加得分和 EXP,断连后重新累计。\r\n先见之眼:额外显示 1 个后续方块;第三个预览由操控大师解锁。"),
|
|
_T("最后一搏:首次濒死时自动清理底部 3 行,并保留本局继续机会。\r\n备用仓:解锁 C / Shift,将当前方块放入 Hold 仓或取出备用方块。\r\n完美旋转:旋转被阻挡时尝试左右修正,提高贴墙和缝隙旋转成功率。\r\n时间缓流:堆叠过高时自动减速,给危险局面留出处理时间。\r\n空中换形:可重复补充次数,按 V 将当前下落方块变成 I 块。"),
|
|
_T("卸压清场:获得时立刻清除最高的一条占用行,直接降低顶部压力。\r\n底线清道夫:通过消行充能自动清底;每级降低需求,最多 4 级。\r\n清屏炸弹:可重复补充数量,按 X 主动清理底部 5 行,消行也会继续充能。\r\n黑洞奇点:可重复补充次数,按 Z 吞噬当前场上数量最多的一种颜色方块。"),
|
|
_T("爆破核心:一次性解锁橙红边框方块,落地后以落点为中心清除 3x3 区域。\r\n棱镜激光:一次性解锁青色边框方块,按落地中心列清除整列固定方块。\r\n十字方块:一次性解锁绿色边框方块,按落地中心同时清除一行一列。\r\n彩虹方块:一次性解锁紫色边框方块,清中心行主色并把覆盖行染成场上主色。\r\n方块改造:提高指定方块出现概率,目前主要强化 I 块生成。"),
|
|
_T("连锁火花:完成消行后追加随机破坏,适合配合稳定堆叠扩大收益。\r\n连环炸弹:强化爆破核心,将爆破范围从 3x3 扩大为 5x5。\r\n雷霆四消:三消或四消后追加雷击,额外清除局部方块。\r\n雷霆棱镜:三消或四消后追加激光清列,强化四消后的清场能力。"),
|
|
_T("狂热节拍:累计清除 12 行进入狂热,狂热期间收益更高。\r\n怒火连段:连击越高倍率越高,适合连续小消和稳定节奏。\r\n无尽狂热:狂热期间继续消行会延长狂热时间。\r\n高压悬赏:游戏速度更快,但高压下收益也更高。\r\n豪赌四消:四消收益更高,但非四消表现更不稳定。\r\n极限玩家:危险高度下获得更高收益,同时承担更高操作压力。\r\n赌徒契约:后续强化有概率翻倍或落空,每级提高概率,最多 4 级。"),
|
|
_T("双重抉择:升级时可额外选择 1 个强化,加快构筑成型。\r\n命运轮盘:升级变为 6 选 2,但选项中可能混入诅咒。\r\n升级冲击波:每次升级后清理底部 2 行。\r\n进化冲击:升级后清理底部 3 行,是升级冲击波的进阶路线。\r\n成长核心:永久提高得分和 EXP 收益 15%。"),
|
|
_T("操控大师:使用 Hold 后短暂减速,并额外增加预览能力。\r\n方块风暴:接下来 5 个方块都变为 I 块,适合冲四消或紧急清场。\r\n稳定结构:落地后概率填补局部小洞,让场地更平整。\r\n虚空核心:黑洞生效后生成彩虹方块,彩虹生效时会追加小型黑洞。")
|
|
};
|
|
|
|
int sectionGap = SS(18);
|
|
int titleHeight = SS(34);
|
|
int bodyPadding = SS(16);
|
|
int contentHeight = contentRect.bottom - contentRect.top;
|
|
int virtualHeight = 0;
|
|
SelectObject(hdc, bodyFont);
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
RECT calculateBodyRect =
|
|
{
|
|
contentRect.left + bodyPadding,
|
|
0,
|
|
contentRect.right - bodyPadding,
|
|
0
|
|
};
|
|
DrawText(hdc, categoryTexts[i], -1, &calculateBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
|
|
virtualHeight += titleHeight + (calculateBodyRect.bottom - calculateBodyRect.top) + sectionGap;
|
|
}
|
|
int maxHelpScroll = virtualHeight - contentHeight;
|
|
if (maxHelpScroll < 0)
|
|
{
|
|
maxHelpScroll = 0;
|
|
}
|
|
if (helpScrollOffset > maxHelpScroll)
|
|
{
|
|
helpScrollOffset = maxHelpScroll;
|
|
}
|
|
|
|
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
|
|
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
|
|
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
|
|
SelectClipRgn(hdc, contentClipRegion);
|
|
|
|
int sectionTop = contentRect.top - helpScrollOffset;
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
RECT bodyCalculateRect =
|
|
{
|
|
contentRect.left + bodyPadding,
|
|
0,
|
|
contentRect.right - bodyPadding,
|
|
0
|
|
};
|
|
SelectObject(hdc, bodyFont);
|
|
DrawText(hdc, categoryTexts[i], -1, &bodyCalculateRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
|
|
int bodyHeight = bodyCalculateRect.bottom - bodyCalculateRect.top;
|
|
RECT categoryRect =
|
|
{
|
|
contentRect.left,
|
|
sectionTop,
|
|
contentRect.right,
|
|
sectionTop + titleHeight + bodyHeight + SS(12)
|
|
};
|
|
|
|
if (categoryRect.bottom < contentRect.top || categoryRect.top > contentRect.bottom)
|
|
{
|
|
sectionTop += titleHeight + bodyHeight + sectionGap;
|
|
continue;
|
|
}
|
|
|
|
SetTextColor(hdc, titleColor);
|
|
SelectObject(hdc, sectionFont);
|
|
RECT categoryTitleRect =
|
|
{
|
|
categoryRect.left,
|
|
categoryRect.top,
|
|
categoryRect.right,
|
|
categoryRect.top + titleHeight
|
|
};
|
|
DrawText(hdc, categoryNames[i], -1, &categoryTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
HPEN categoryLinePen = CreatePen(PS_SOLID, 1, RGB(232, 202, 215));
|
|
oldPen = (HPEN)SelectObject(hdc, categoryLinePen);
|
|
MoveToEx(hdc, categoryRect.left, categoryTitleRect.bottom, nullptr);
|
|
LineTo(hdc, categoryRect.right, categoryTitleRect.bottom);
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(categoryLinePen);
|
|
|
|
SetTextColor(hdc, RGB(86, 66, 80));
|
|
SelectObject(hdc, bodyFont);
|
|
RECT categoryBodyRect =
|
|
{
|
|
categoryRect.left + bodyPadding,
|
|
categoryTitleRect.bottom + SS(10),
|
|
categoryRect.right - bodyPadding,
|
|
categoryTitleRect.bottom + SS(10) + bodyHeight
|
|
};
|
|
DrawText(hdc, categoryTexts[i], -1, &categoryBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
|
|
sectionTop += titleHeight + bodyHeight + sectionGap;
|
|
}
|
|
|
|
if (hasOldClipRegion == 1)
|
|
{
|
|
SelectClipRgn(hdc, oldClipRegion);
|
|
}
|
|
else
|
|
{
|
|
SelectClipRgn(hdc, nullptr);
|
|
}
|
|
DeleteObject(contentClipRegion);
|
|
DeleteObject(oldClipRegion);
|
|
}
|
|
else if (helpState.currentPage == 5)
|
|
{
|
|
int demoCount = GetRogueSkillDemoCount();
|
|
int itemHeight = SS(58);
|
|
int itemGap = SS(10);
|
|
int contentHeight = contentRect.bottom - contentRect.top;
|
|
int virtualHeight = SS(8) + demoCount * (itemHeight + itemGap);
|
|
int maxHelpScroll = virtualHeight - contentHeight;
|
|
if (maxHelpScroll < 0)
|
|
{
|
|
maxHelpScroll = 0;
|
|
}
|
|
if (helpScrollOffset > maxHelpScroll)
|
|
{
|
|
helpScrollOffset = maxHelpScroll;
|
|
}
|
|
|
|
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
|
|
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
|
|
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
|
|
SelectClipRgn(hdc, contentClipRegion);
|
|
|
|
int itemTop = contentRect.top + SS(8) - helpScrollOffset;
|
|
for (int i = 0; i < demoCount; i++)
|
|
{
|
|
RECT itemRect =
|
|
{
|
|
contentRect.left,
|
|
itemTop + i * (itemHeight + itemGap),
|
|
contentRect.right,
|
|
itemTop + i * (itemHeight + itemGap) + itemHeight
|
|
};
|
|
|
|
if (itemRect.bottom < contentRect.top || itemRect.top > contentRect.bottom)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool selected = (i == helpState.selectedIndex);
|
|
COLORREF itemFill = selected ? RGB(255, 245, 249) : RGB(255, 252, 250);
|
|
COLORREF itemFrame = selected ? accentColor : frameColor;
|
|
HPEN itemPen = CreatePen(PS_SOLID, selected ? SS(2) : SS(1), itemFrame);
|
|
HBRUSH itemBrush = CreateSolidBrush(itemFill);
|
|
oldPen = (HPEN)SelectObject(hdc, itemPen);
|
|
oldBrush = (HBRUSH)SelectObject(hdc, itemBrush);
|
|
RoundRect(hdc, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom, SS(18), SS(18));
|
|
SelectObject(hdc, oldBrush);
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(itemBrush);
|
|
DeleteObject(itemPen);
|
|
|
|
SetTextColor(hdc, selected ? titleColor : textColor);
|
|
SelectObject(hdc, sectionFont);
|
|
RECT nameRect =
|
|
{
|
|
itemRect.left + SS(22),
|
|
itemRect.top + SS(8),
|
|
itemRect.left + SS(210),
|
|
itemRect.bottom - SS(8)
|
|
};
|
|
DrawText(hdc, GetRogueSkillDemoName(i), -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SetTextColor(hdc, RGB(112, 91, 104));
|
|
SelectObject(hdc, bodyFont);
|
|
RECT detailRect =
|
|
{
|
|
itemRect.left + SS(220),
|
|
itemRect.top + SS(8),
|
|
itemRect.right - SS(22),
|
|
itemRect.bottom - SS(8)
|
|
};
|
|
DrawText(hdc, GetRogueSkillDemoDetail(i), -1, &detailRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
}
|
|
|
|
if (hasOldClipRegion == 1)
|
|
{
|
|
SelectClipRgn(hdc, oldClipRegion);
|
|
}
|
|
else
|
|
{
|
|
SelectClipRgn(hdc, nullptr);
|
|
}
|
|
DeleteObject(contentClipRegion);
|
|
DeleteObject(oldClipRegion);
|
|
}
|
|
else if (helpState.currentPage == 4)
|
|
{
|
|
const int creditAnimationTotalTicks = 60;
|
|
constexpr int creditPageCount = 4;
|
|
const TCHAR* creditNames[creditPageCount] =
|
|
{
|
|
_T("qls"),
|
|
_T("wyk"),
|
|
_T("juju"),
|
|
_T("qhy")
|
|
};
|
|
const TCHAR* creditTexts[creditPageCount] =
|
|
{
|
|
_T("\u611f\u8c22\u6fc0\u60c5\u6295\u8eab\u4e8e\u6d4b\u8bd5\u4e4b\u4e2d\u7684Lisa"),
|
|
_T("\u611f\u8c22\u70ed\u5ff1coding\u7684\u5c0f\u86cb\u7cd5"),
|
|
_T("\u611f\u8c22\u8bfe\u524d\u95f2\u91cc\u5077\u5fd9\u7684juju"),
|
|
_T("\u611f\u8c22qhy\u7684\u5929\u624d\u6784\u60f3")
|
|
};
|
|
|
|
int currentCredit = creditPageIndex;
|
|
if (currentCredit < 0)
|
|
{
|
|
currentCredit = 0;
|
|
}
|
|
if (currentCredit >= creditPageCount)
|
|
{
|
|
currentCredit = creditPageCount - 1;
|
|
}
|
|
|
|
int previousCredit = currentCredit - creditAnimationDirection;
|
|
if (previousCredit < 0)
|
|
{
|
|
previousCredit = creditPageCount - 1;
|
|
}
|
|
if (previousCredit >= creditPageCount)
|
|
{
|
|
previousCredit = 0;
|
|
}
|
|
|
|
RECT imageArea =
|
|
{
|
|
contentRect.left + SS(92),
|
|
contentRect.top + SS(10),
|
|
contentRect.right - SS(92),
|
|
contentRect.bottom - SS(112)
|
|
};
|
|
RECT textArea =
|
|
{
|
|
contentRect.left + SS(72),
|
|
contentRect.bottom - SS(94),
|
|
contentRect.right - SS(72),
|
|
contentRect.bottom - SS(18)
|
|
};
|
|
|
|
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
|
|
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
|
|
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
|
|
SelectClipRgn(hdc, contentClipRegion);
|
|
|
|
int slideDistance = contentRect.right - contentRect.left;
|
|
int currentOffset = 0;
|
|
int previousOffset = 0;
|
|
if (creditAnimationTicks > 0 && creditAnimationDirection != 0)
|
|
{
|
|
currentOffset = creditAnimationDirection * slideDistance * creditAnimationTicks / creditAnimationTotalTicks;
|
|
previousOffset = currentOffset - creditAnimationDirection * slideDistance;
|
|
}
|
|
|
|
Bitmap* preloadedCreditImages[creditPageCount] = {};
|
|
for (int i = 0; i < creditPageCount; i++)
|
|
{
|
|
preloadedCreditImages[i] = LoadCreditImage(i);
|
|
}
|
|
Graphics creditGraphics(hdc);
|
|
creditGraphics.SetInterpolationMode(InterpolationModeHighQualityBilinear);
|
|
|
|
auto DrawCreditCard = [&](int cardIndex, int offset)
|
|
{
|
|
RECT shiftedImageArea = imageArea;
|
|
RECT shiftedTextArea = textArea;
|
|
OffsetRect(&shiftedImageArea, offset, 0);
|
|
OffsetRect(&shiftedTextArea, offset, 0);
|
|
|
|
Bitmap* creditImage = nullptr;
|
|
if (cardIndex >= 0 && cardIndex < creditPageCount)
|
|
{
|
|
creditImage = preloadedCreditImages[cardIndex];
|
|
}
|
|
if (creditImage != nullptr)
|
|
{
|
|
int imageWidth = static_cast<int>(creditImage->GetWidth());
|
|
int imageHeight = static_cast<int>(creditImage->GetHeight());
|
|
int areaWidth = shiftedImageArea.right - shiftedImageArea.left;
|
|
int areaHeight = shiftedImageArea.bottom - shiftedImageArea.top;
|
|
int drawWidth = areaWidth;
|
|
int drawHeight = imageHeight * drawWidth / imageWidth;
|
|
if (drawHeight > areaHeight)
|
|
{
|
|
drawHeight = areaHeight;
|
|
drawWidth = imageWidth * drawHeight / imageHeight;
|
|
}
|
|
|
|
int drawLeft = shiftedImageArea.left + (areaWidth - drawWidth) / 2;
|
|
int drawTop = shiftedImageArea.top + (areaHeight - drawHeight) / 2;
|
|
creditGraphics.DrawImage(creditImage, Rect(drawLeft, drawTop, drawWidth, drawHeight));
|
|
}
|
|
else
|
|
{
|
|
HBRUSH missingBrush = CreateSolidBrush(RGB(255, 245, 249));
|
|
HPEN missingPen = CreatePen(PS_DASH, SS(2), frameColor);
|
|
oldBrush = (HBRUSH)SelectObject(hdc, missingBrush);
|
|
oldPen = (HPEN)SelectObject(hdc, missingPen);
|
|
RoundRect(hdc, shiftedImageArea.left, shiftedImageArea.top, shiftedImageArea.right, shiftedImageArea.bottom, SS(24), SS(24));
|
|
SelectObject(hdc, oldBrush);
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(missingPen);
|
|
DeleteObject(missingBrush);
|
|
|
|
SetTextColor(hdc, RGB(128, 104, 118));
|
|
SelectObject(hdc, bodyFont);
|
|
DrawText(hdc, _T("\u56fe\u7247\u8d44\u6e90\u672a\u627e\u5230"), -1, &shiftedImageArea, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
|
|
if (creditAnimationTicks <= 0)
|
|
{
|
|
SetTextColor(hdc, titleColor);
|
|
SelectObject(hdc, sectionFont);
|
|
RECT nameRect =
|
|
{
|
|
shiftedTextArea.left,
|
|
shiftedTextArea.top,
|
|
shiftedTextArea.right,
|
|
shiftedTextArea.top + SS(30)
|
|
};
|
|
DrawText(hdc, creditNames[cardIndex], -1, &nameRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SetTextColor(hdc, textColor);
|
|
SelectObject(hdc, bodyFont);
|
|
RECT detailRect =
|
|
{
|
|
shiftedTextArea.left,
|
|
shiftedTextArea.top + SS(34),
|
|
shiftedTextArea.right,
|
|
shiftedTextArea.bottom
|
|
};
|
|
DrawText(hdc, creditTexts[cardIndex], -1, &detailRect, DT_CENTER | DT_TOP | DT_WORDBREAK);
|
|
}
|
|
};
|
|
|
|
if (creditAnimationTicks > 0 && creditAnimationDirection != 0)
|
|
{
|
|
DrawCreditCard(previousCredit, previousOffset);
|
|
}
|
|
DrawCreditCard(currentCredit, currentOffset);
|
|
|
|
if (hasOldClipRegion == 1)
|
|
{
|
|
SelectClipRgn(hdc, oldClipRegion);
|
|
}
|
|
else
|
|
{
|
|
SelectClipRgn(hdc, nullptr);
|
|
}
|
|
DeleteObject(contentClipRegion);
|
|
DeleteObject(oldClipRegion);
|
|
|
|
RECT leftArrow =
|
|
{
|
|
rulesCard.left + SS(52),
|
|
(rulesCard.top + rulesCard.bottom) / 2 - SS(27),
|
|
rulesCard.left + SS(106),
|
|
(rulesCard.top + rulesCard.bottom) / 2 + SS(27)
|
|
};
|
|
RECT rightArrow =
|
|
{
|
|
rulesCard.right - SS(106),
|
|
(rulesCard.top + rulesCard.bottom) / 2 - SS(27),
|
|
rulesCard.right - SS(52),
|
|
(rulesCard.top + rulesCard.bottom) / 2 + SS(27)
|
|
};
|
|
|
|
HBRUSH arrowBrush = CreateSolidBrush(RGB(255, 245, 249));
|
|
HPEN arrowPen = CreatePen(PS_SOLID, SS(2), accentColor);
|
|
oldBrush = (HBRUSH)SelectObject(hdc, arrowBrush);
|
|
oldPen = (HPEN)SelectObject(hdc, arrowPen);
|
|
RoundRect(hdc, leftArrow.left, leftArrow.top, leftArrow.right, leftArrow.bottom, SS(18), SS(18));
|
|
RoundRect(hdc, rightArrow.left, rightArrow.top, rightArrow.right, rightArrow.bottom, SS(18), SS(18));
|
|
SelectObject(hdc, oldBrush);
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(arrowPen);
|
|
DeleteObject(arrowBrush);
|
|
|
|
HPEN chevronPen = CreatePen(PS_SOLID, SS(4), titleColor);
|
|
oldPen = (HPEN)SelectObject(hdc, chevronPen);
|
|
MoveToEx(hdc, leftArrow.left + SS(32), leftArrow.top + SS(16), nullptr);
|
|
LineTo(hdc, leftArrow.left + SS(22), leftArrow.top + SS(27));
|
|
LineTo(hdc, leftArrow.left + SS(32), leftArrow.bottom - SS(16));
|
|
MoveToEx(hdc, rightArrow.right - SS(32), rightArrow.top + SS(16), nullptr);
|
|
LineTo(hdc, rightArrow.right - SS(22), rightArrow.top + SS(27));
|
|
LineTo(hdc, rightArrow.right - SS(32), rightArrow.bottom - SS(16));
|
|
SelectObject(hdc, oldPen);
|
|
DeleteObject(chevronPen);
|
|
}
|
|
if (helpState.currentPage != 3 && helpState.currentPage != 4 && helpState.currentPage != 5)
|
|
{
|
|
RECT calculateRect = { contentRect.left, contentRect.top, contentRect.right, contentRect.top };
|
|
DrawText(hdc, pageText, -1, &calculateRect, pageFlags | DT_CALCRECT);
|
|
int maxHelpScroll = (calculateRect.bottom - calculateRect.top) - (contentRect.bottom - contentRect.top);
|
|
if (maxHelpScroll < 0)
|
|
{
|
|
maxHelpScroll = 0;
|
|
}
|
|
if (helpScrollOffset > maxHelpScroll)
|
|
{
|
|
helpScrollOffset = maxHelpScroll;
|
|
}
|
|
|
|
HRGN oldClipRegion = CreateRectRgn(0, 0, 0, 0);
|
|
int hasOldClipRegion = GetClipRgn(hdc, oldClipRegion);
|
|
HRGN contentClipRegion = CreateRectRgn(contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
|
|
SelectClipRgn(hdc, contentClipRegion);
|
|
|
|
RECT scrolledContentRect = contentRect;
|
|
scrolledContentRect.top -= helpScrollOffset;
|
|
scrolledContentRect.bottom += maxHelpScroll;
|
|
DrawText(hdc, pageText, -1, &scrolledContentRect, pageFlags);
|
|
|
|
if (hasOldClipRegion == 1)
|
|
{
|
|
SelectClipRgn(hdc, oldClipRegion);
|
|
}
|
|
else
|
|
{
|
|
SelectClipRgn(hdc, nullptr);
|
|
}
|
|
DeleteObject(contentClipRegion);
|
|
DeleteObject(oldClipRegion);
|
|
}
|
|
}
|
|
|
|
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")
|
|
: (helpState.currentPage == 4
|
|
? _T("\u5de6\u53f3\u65b9\u5411\u952e / A D \u5207\u6362\uff0cEsc / Backspace / M \u8fd4\u56de\u4e3b\u83dc\u5355")
|
|
: (helpState.currentPage == 5
|
|
? _T("\u70b9\u51fb\u6280\u80fd\u540d\u79f0\u6216 Enter \u6f14\u793a\uff0c\u6eda\u8f6e\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9")
|
|
: _T("\u9f20\u6807\u6eda\u8f6e\u4e0a\u4e0b\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9")));
|
|
DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SelectClipRgn(hdc, nullptr);
|
|
DrawBackButton();
|
|
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(186, 154, 255) : BrickColor[colorIndex];
|
|
COLORREF borderColor = isRainbowCell ? RGB(250, 238, 255) : 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, 116, 78);
|
|
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(186, 126, 255);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 80; i++)
|
|
{
|
|
if (gravityFallEffects[i].ticks <= 0 || gravityFallEffects[i].totalTicks <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (gravityFallEffects[i].x < 0 || gravityFallEffects[i].x >= nGameWidth ||
|
|
gravityFallEffects[i].fromY < 0 || gravityFallEffects[i].fromY >= nGameHeight ||
|
|
gravityFallEffects[i].toY < 0 || gravityFallEffects[i].toY >= nGameHeight)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int elapsed = gravityFallEffects[i].totalTicks - gravityFallEffects[i].ticks;
|
|
int travel = gravityFallEffects[i].toY - gravityFallEffects[i].fromY;
|
|
int offsetY = travel * grid * elapsed / gravityFallEffects[i].totalTicks;
|
|
int drawX = gameRect.left + gravityFallEffects[i].x * grid;
|
|
int fromPixelY = gameRect.top + gravityFallEffects[i].fromY * grid;
|
|
int toPixelY = gameRect.top + gravityFallEffects[i].toY * grid;
|
|
int currentPixelY = fromPixelY + offsetY;
|
|
int colorIndex = gravityFallEffects[i].cellValue - 1;
|
|
if (colorIndex < 0 || colorIndex >= 7)
|
|
{
|
|
colorIndex = (gravityFallEffects[i].x + gravityFallEffects[i].toY) % 7;
|
|
}
|
|
COLORREF fallColor = BrickColor[colorIndex];
|
|
|
|
int alpha = 56 + gravityFallEffects[i].ticks * 150 / gravityFallEffects[i].totalTicks;
|
|
int trailTop = fromPixelY + grid / 2;
|
|
int trailBottom = currentPixelY + grid / 2;
|
|
if (trailBottom < trailTop + SS(8))
|
|
{
|
|
trailBottom = trailTop + SS(8);
|
|
}
|
|
if (trailBottom > toPixelY + grid / 2)
|
|
{
|
|
trailBottom = toPixelY + grid / 2;
|
|
}
|
|
|
|
Graphics fallGraphics(hdc);
|
|
fallGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
|
|
SolidBrush trailBrush(Color(alpha / 2, GetRValue(fallColor), GetGValue(fallColor), GetBValue(fallColor)));
|
|
fallGraphics.FillRectangle(
|
|
&trailBrush,
|
|
static_cast<INT>(drawX + SS(12)),
|
|
static_cast<INT>(trailTop),
|
|
static_cast<INT>(grid - SS(24)),
|
|
static_cast<INT>(trailBottom - trailTop));
|
|
|
|
SolidBrush ghostBrush(Color(alpha, GetRValue(fallColor), GetGValue(fallColor), GetBValue(fallColor)));
|
|
Pen ghostPen(Color(230, 255, 255, 255), static_cast<REAL>(SS(2)));
|
|
fallGraphics.FillRectangle(
|
|
&ghostBrush,
|
|
static_cast<INT>(drawX + SS(3)),
|
|
static_cast<INT>(currentPixelY + SS(3)),
|
|
static_cast<INT>(grid - SS(6)),
|
|
static_cast<INT>(grid - SS(6)));
|
|
fallGraphics.DrawRectangle(
|
|
&ghostPen,
|
|
static_cast<INT>(drawX + SS(3)),
|
|
static_cast<INT>(currentPixelY + SS(3)),
|
|
static_cast<INT>(grid - SS(6)),
|
|
static_cast<INT>(grid - SS(6)));
|
|
}
|
|
|
|
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 < 64; i++)
|
|
{
|
|
if (cellFlashEffects[i].ticks <= 0 || cellFlashEffects[i].totalTicks <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (cellFlashEffects[i].x < 0 || cellFlashEffects[i].x >= nGameWidth ||
|
|
cellFlashEffects[i].y < 0 || cellFlashEffects[i].y >= nGameHeight)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int alpha = 48 + cellFlashEffects[i].ticks * 168 / cellFlashEffects[i].totalTicks;
|
|
int pulseInset = SS(2 + (cellFlashEffects[i].totalTicks - cellFlashEffects[i].ticks) % 3);
|
|
RECT flashRect =
|
|
{
|
|
gameRect.left + cellFlashEffects[i].x * grid + pulseInset,
|
|
gameRect.top + cellFlashEffects[i].y * grid + pulseInset,
|
|
gameRect.left + (cellFlashEffects[i].x + 1) * grid - pulseInset,
|
|
gameRect.top + (cellFlashEffects[i].y + 1) * grid - pulseInset
|
|
};
|
|
|
|
Graphics cellGraphics(hdc);
|
|
cellGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
|
|
SolidBrush cellBrush(Color(alpha, GetRValue(cellFlashEffects[i].color), GetGValue(cellFlashEffects[i].color), GetBValue(cellFlashEffects[i].color)));
|
|
Pen cellPen(Color(230, GetRValue(cellFlashEffects[i].color), GetGValue(cellFlashEffects[i].color), GetBValue(cellFlashEffects[i].color)), static_cast<REAL>(SS(2)));
|
|
cellGraphics.FillRectangle(
|
|
&cellBrush,
|
|
static_cast<INT>(flashRect.left),
|
|
static_cast<INT>(flashRect.top),
|
|
static_cast<INT>(flashRect.right - flashRect.left),
|
|
static_cast<INT>(flashRect.bottom - flashRect.top));
|
|
cellGraphics.DrawRectangle(
|
|
&cellPen,
|
|
static_cast<INT>(flashRect.left),
|
|
static_cast<INT>(flashRect.top),
|
|
static_cast<INT>(flashRect.right - flashRect.left),
|
|
static_cast<INT>(flashRect.bottom - flashRect.top));
|
|
}
|
|
|
|
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 = particleEffects[i].size * particleEffects[i].ticks / particleEffects[i].totalTicks;
|
|
if (particleSize < 2)
|
|
{
|
|
particleSize = 2;
|
|
}
|
|
if (particleSize > 6)
|
|
{
|
|
particleSize = 6;
|
|
}
|
|
|
|
RECT particleRect =
|
|
{
|
|
particleX - particleSize,
|
|
particleY - particleSize,
|
|
particleX + particleSize,
|
|
particleY + particleSize
|
|
};
|
|
|
|
HBRUSH particleBrush = GetCachedParticleBrush(particleEffects[i].color);
|
|
FillRect(hdc, &particleRect, 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)
|
|
{
|
|
int progressTop = IsRogueSkillDemoMode() ? 350 : 270;
|
|
int progressBottom = IsRogueSkillDemoMode() ? 568 : 488;
|
|
int upgradeTop = IsRogueSkillDemoMode() ? 590 : 510;
|
|
|
|
if (IsRogueSkillDemoMode())
|
|
{
|
|
RECT demoSkillRect =
|
|
{
|
|
leftPanelRect.left + SS(20),
|
|
leftPanelRect.top + SS(270),
|
|
leftPanelRect.right - SS(20),
|
|
leftPanelRect.top + SS(332)
|
|
};
|
|
DrawPanelCardAlpha(demoSkillRect, RGB(255, 244, 248), RGB(232, 184, 202), 20, panelNestedAlpha);
|
|
|
|
SelectObject(hdc, smallFont);
|
|
SetTextColor(hdc, RGB(132, 102, 118));
|
|
RECT demoSkillLabelRect =
|
|
{
|
|
demoSkillRect.left + SS(18),
|
|
demoSkillRect.top + SS(8),
|
|
demoSkillRect.right - SS(18),
|
|
demoSkillRect.top + SS(26)
|
|
};
|
|
DrawText(hdc, _T("\u5f53\u524d\u6280\u80fd"), -1, &demoSkillLabelRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SelectObject(hdc, sectionFont);
|
|
SetTextColor(hdc, titleColor);
|
|
RECT demoSkillNameRect =
|
|
{
|
|
demoSkillRect.left + SS(18),
|
|
demoSkillRect.top + SS(26),
|
|
demoSkillRect.right - SS(18),
|
|
demoSkillRect.bottom - SS(8)
|
|
};
|
|
DrawText(hdc, GetCurrentRogueSkillDemoName(), -1, &demoSkillNameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
}
|
|
|
|
RECT progressRect =
|
|
{
|
|
leftPanelRect.left + SS(20),
|
|
leftPanelRect.top + SS(progressTop),
|
|
leftPanelRect.right - SS(20),
|
|
leftPanelRect.top + SS(progressBottom)
|
|
};
|
|
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(upgradeTop),
|
|
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(148, 118, 132));
|
|
RECT upgradeScrollHintRect =
|
|
{
|
|
upgradeListRect.left + SS(116),
|
|
upgradeListRect.top + SS(17),
|
|
upgradeListRect.right - SS(18),
|
|
upgradeListRect.top + SS(42)
|
|
};
|
|
DrawText(hdc, _T("J/K / 滚轮"), -1, &upgradeScrollHintRect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SelectObject(hdc, smallFont);
|
|
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.%d \u5269\u4f59 %d \u679a\r\n"), rogueStats.screenBombLevel, rogueStats.screenBombCount);
|
|
}
|
|
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.%d \u5269\u4f59 %d \u6b21\r\n"), rogueStats.blackHoleLevel, rogueStats.blackHoleCharges);
|
|
}
|
|
if (rogueStats.reshapeLevel > 0)
|
|
{
|
|
_stprintf_s(upgradeSummary + lstrlen(upgradeSummary), 512 - lstrlen(upgradeSummary), _T("\u7a7a\u4e2d\u6362\u5f62 Lv.%d \u5269\u4f59 %d \u6b21\r\n"), rogueStats.reshapeLevel, rogueStats.reshapeCharges);
|
|
}
|
|
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下一次升级将开启构筑。"));
|
|
}
|
|
|
|
RECT upgradeCalculateRect =
|
|
{
|
|
upgradeBodyRect.left,
|
|
upgradeBodyRect.top,
|
|
upgradeBodyRect.right,
|
|
upgradeBodyRect.top
|
|
};
|
|
DrawText(hdc, upgradeSummary, -1, &upgradeCalculateRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT);
|
|
int maxUpgradeScroll = (upgradeCalculateRect.bottom - upgradeCalculateRect.top) - (upgradeBodyRect.bottom - upgradeBodyRect.top);
|
|
if (maxUpgradeScroll < 0)
|
|
{
|
|
maxUpgradeScroll = 0;
|
|
}
|
|
if (upgradeListScrollOffset > maxUpgradeScroll)
|
|
{
|
|
upgradeListScrollOffset = maxUpgradeScroll;
|
|
}
|
|
|
|
HRGN oldUpgradeClipRegion = CreateRectRgn(0, 0, 0, 0);
|
|
int hasOldUpgradeClipRegion = GetClipRgn(hdc, oldUpgradeClipRegion);
|
|
HRGN upgradeClipRegion = CreateRectRgn(upgradeBodyRect.left, upgradeBodyRect.top, upgradeBodyRect.right, upgradeBodyRect.bottom);
|
|
SelectClipRgn(hdc, upgradeClipRegion);
|
|
|
|
RECT scrolledUpgradeBodyRect = upgradeBodyRect;
|
|
scrolledUpgradeBodyRect.top -= upgradeListScrollOffset;
|
|
scrolledUpgradeBodyRect.bottom += maxUpgradeScroll;
|
|
DrawText(hdc, upgradeSummary, -1, &scrolledUpgradeBodyRect, DT_LEFT | DT_TOP | DT_WORDBREAK);
|
|
|
|
if (hasOldUpgradeClipRegion == 1)
|
|
{
|
|
SelectClipRgn(hdc, oldUpgradeClipRegion);
|
|
}
|
|
else
|
|
{
|
|
SelectClipRgn(hdc, nullptr);
|
|
}
|
|
DeleteObject(upgradeClipRegion);
|
|
DeleteObject(oldUpgradeClipRegion);
|
|
SelectObject(hdc, sectionFont);
|
|
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(28),
|
|
gameRect.top + grid * 6 + SS(10),
|
|
gameRect.right - SS(28),
|
|
gameRect.top + grid * 10 + SS(30)
|
|
};
|
|
|
|
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);
|
|
int buttonGap = SS(18);
|
|
int buttonWidth = (overlayRect.right - overlayRect.left - SS(68) - buttonGap) / 2;
|
|
RECT continueRect =
|
|
{
|
|
overlayRect.left + SS(34),
|
|
overlayRect.top + SS(94),
|
|
overlayRect.left + SS(34) + buttonWidth,
|
|
overlayRect.top + SS(138)
|
|
};
|
|
RECT menuRect =
|
|
{
|
|
continueRect.right + buttonGap,
|
|
continueRect.top,
|
|
overlayRect.right - SS(34),
|
|
continueRect.bottom
|
|
};
|
|
DrawPanelCardAlpha(continueRect, RGB(255, 232, 240), RGB(222, 130, 166), 18, 224);
|
|
DrawPanelCardAlpha(menuRect, RGB(255, 247, 250), RGB(210, 182, 194), 18, 210);
|
|
SelectObject(hdc, bodyFont);
|
|
DrawText(hdc, _T("继续 (P)"), -1, &continueRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
DrawText(hdc, _T("主菜单 (M)"), -1, &menuRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
else
|
|
{
|
|
DrawText(hdc, _T("战局崩塌"), -1, &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
if (reviveAvailable)
|
|
{
|
|
int buttonGap = SS(8);
|
|
int buttonWidth = (overlayRect.right - overlayRect.left - SS(28) - buttonGap * 2) / 3;
|
|
RECT reviveRect =
|
|
{
|
|
overlayRect.left + SS(14),
|
|
overlayRect.top + SS(94),
|
|
overlayRect.left + SS(14) + buttonWidth,
|
|
overlayRect.top + SS(138)
|
|
};
|
|
RECT restartRect =
|
|
{
|
|
reviveRect.right + buttonGap,
|
|
reviveRect.top,
|
|
reviveRect.right + buttonGap + buttonWidth,
|
|
reviveRect.bottom
|
|
};
|
|
RECT menuRect =
|
|
{
|
|
restartRect.right + buttonGap,
|
|
reviveRect.top,
|
|
overlayRect.right - SS(14),
|
|
reviveRect.bottom
|
|
};
|
|
DrawPanelCardAlpha(reviveRect, RGB(255, 232, 240), RGB(222, 130, 166), 18, 224);
|
|
DrawPanelCardAlpha(restartRect, RGB(255, 247, 250), RGB(210, 182, 194), 18, 210);
|
|
DrawPanelCardAlpha(menuRect, RGB(255, 247, 250), RGB(210, 182, 194), 18, 210);
|
|
SelectObject(hdc, bodyFont);
|
|
DrawText(hdc, _T("复活 (V)"), -1, &reviveRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
DrawText(hdc, _T("重开 (R)"), -1, &restartRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
DrawText(hdc, _T("主菜单 (M)"), -1, &menuRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
else
|
|
{
|
|
int buttonGap = SS(18);
|
|
int buttonWidth = (overlayRect.right - overlayRect.left - SS(68) - buttonGap) / 2;
|
|
RECT restartRect =
|
|
{
|
|
overlayRect.left + SS(34),
|
|
overlayRect.top + SS(94),
|
|
overlayRect.left + SS(34) + buttonWidth,
|
|
overlayRect.top + SS(138)
|
|
};
|
|
RECT menuRect =
|
|
{
|
|
restartRect.right + buttonGap,
|
|
restartRect.top,
|
|
overlayRect.right - SS(34),
|
|
restartRect.bottom
|
|
};
|
|
DrawPanelCardAlpha(restartRect, RGB(255, 232, 240), RGB(222, 130, 166), 18, 224);
|
|
DrawPanelCardAlpha(menuRect, RGB(255, 247, 250), RGB(210, 182, 194), 18, 210);
|
|
SelectObject(hdc, bodyFont);
|
|
DrawText(hdc, _T("重开 (R)"), -1, &restartRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
DrawText(hdc, _T("主菜单 (M)"), -1, &menuRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
}
|
|
}
|
|
|
|
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];
|
|
if (upgradeUiState.picksRemaining > 1)
|
|
{
|
|
_stprintf_s(
|
|
overlaySubtitle,
|
|
_T("出现 %d 个强化,请标记 %d 个(已选 %d 个)"),
|
|
upgradeUiState.optionCount,
|
|
upgradeUiState.picksRemaining,
|
|
upgradeUiState.markedCount);
|
|
}
|
|
else
|
|
{
|
|
_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);
|
|
bool isMarked = upgradeUiState.marked[i];
|
|
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("\u8bc5\u5492\uff1a\u4e0b\u6b21\u5347\u7ea7\u6240\u9700 EXP +25%");
|
|
}
|
|
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 (isMarked && !upgradeUiState.options[i].cursed)
|
|
{
|
|
cardFill = RGB(255, 226, 236);
|
|
}
|
|
|
|
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);
|
|
if (isMarked)
|
|
{
|
|
DrawText(hdc, _T("[已选]"), -1, &levelRect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
else
|
|
{
|
|
DrawText(hdc, levelText, -1, &levelRect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
|
|
if (hasSynthesisPath)
|
|
{
|
|
SelectObject(hdc, smallFont);
|
|
SetTextColor(hdc, upgradeUiState.options[i].cursed ? RGB(142, 78, 78) : RGB(116, 82, 104));
|
|
RECT synthesisRect =
|
|
{
|
|
cardRect.left + SS(20),
|
|
cardRect.top + SS(18),
|
|
cardRect.right - SS(116),
|
|
cardRect.top + SS(40)
|
|
};
|
|
DrawText(hdc, synthesisPath, -1, &synthesisRect, DT_LEFT | 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 + (hasSynthesisPath ? SS(44) : SS(38)),
|
|
cardRect.right - SS(20),
|
|
cardRect.top + (hasSynthesisPath ? SS(76) : SS(70))
|
|
};
|
|
DrawText(hdc, upgradeUiState.options[i].name, -1, &nameRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
SelectObject(hdc, bodyFont);
|
|
SetTextColor(hdc, descColor);
|
|
RECT descRect =
|
|
{
|
|
cardRect.left + SS(20),
|
|
cardRect.top + (hasSynthesisPath ? SS(84) : SS(78)),
|
|
cardRect.right - SS(20),
|
|
cardRect.bottom - SS(48)
|
|
};
|
|
DrawText(hdc, upgradeUiState.options[i].description, -1, &descRect, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_END_ELLIPSIS);
|
|
|
|
SelectObject(hdc, smallFont);
|
|
RECT footerRect =
|
|
{
|
|
cardRect.left + SS(20),
|
|
cardRect.bottom - SS(34),
|
|
cardRect.right - SS(20),
|
|
cardRect.bottom - SS(10)
|
|
};
|
|
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,
|
|
upgradeUiState.picksRemaining > 1
|
|
? _T("W/A/S/D 或方向键切换,Space 标记,Enter 确认已选强化")
|
|
: _T("W/A/S/D 或方向键切换,Enter 或 Space 确认"),
|
|
-1,
|
|
&hintRect,
|
|
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
}
|
|
|
|
if (currentScreen != SCREEN_MENU)
|
|
{
|
|
DrawBackButton();
|
|
}
|
|
DrawMusicButton();
|
|
|
|
SelectObject(hdc, oldFont);
|
|
DeleteObject(titleFont);
|
|
DeleteObject(sectionFont);
|
|
DeleteObject(bodyFont);
|
|
DeleteObject(smallFont);
|
|
}
|