添加致谢页
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## 项目名称
|
||||||
|
使用大模型辅助开发俄罗斯方块程序
|
||||||
|
|
||||||
|
## 基本开发要求
|
||||||
|
|
||||||
|
### 编程语言
|
||||||
|
- 使用 C++。
|
||||||
|
- 仅使用课程已学基础语法:数组、循环、分支、函数、结构体等。
|
||||||
|
- 不使用 `class`、继承、多态等面向对象特性。
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
- 源码主要位于 `src` 目录。
|
||||||
|
- 不要随意改动已有全局变量、函数声明和文件结构。
|
||||||
|
- 如需新增创新功能,可以新增 `.cpp` 文件。
|
||||||
|
|
||||||
|
## 构建方式
|
||||||
|
|
||||||
|
优先使用项目根目录下的构建脚本:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build-mingw.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
## 代码质量要求
|
||||||
|
|
||||||
|
1. 每次只实现一个明确功能。
|
||||||
|
2. 每个函数必须有功能描述注释。
|
||||||
|
3. 变量命名保持和原框架一致。
|
||||||
|
4. 不随意改动已有全局变量和函数声明。
|
||||||
|
5. 生成代码后必须人工审查。
|
||||||
|
6. 每个阶段完成后必须编译运行。
|
||||||
|
7. 出现 bug 时,应记录问题、原因和修复过程。
|
||||||
|
|
||||||
|
## 开发注意事项
|
||||||
|
|
||||||
|
1. 每次补全前后都要保存版本,便于报告展示。
|
||||||
|
2. 现场汇报时,所有组员都可能被提问,不能只有一人理解代码。
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
@@ -227,6 +227,9 @@ extern Point target;
|
|||||||
extern MenuState menuState;
|
extern MenuState menuState;
|
||||||
extern HelpState helpState;
|
extern HelpState helpState;
|
||||||
extern int helpScrollOffset;
|
extern int helpScrollOffset;
|
||||||
|
extern int creditPageIndex;
|
||||||
|
extern int creditAnimationTicks;
|
||||||
|
extern int creditAnimationDirection;
|
||||||
extern int upgradeListScrollOffset;
|
extern int upgradeListScrollOffset;
|
||||||
extern PlayerStats classicStats;
|
extern PlayerStats classicStats;
|
||||||
extern PlayerStats rogueStats;
|
extern PlayerStats rogueStats;
|
||||||
@@ -267,6 +270,8 @@ void ReturnToMainMenu();
|
|||||||
void ReviveAfterVideo();
|
void ReviveAfterVideo();
|
||||||
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks);
|
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks);
|
||||||
void OpenRulesScreen();
|
void OpenRulesScreen();
|
||||||
|
void OpenCreditScreen();
|
||||||
|
void ChangeCreditPage(int direction);
|
||||||
void OpenUpgradeMenu();
|
void OpenUpgradeMenu();
|
||||||
void ConfirmUpgradeSelection();
|
void ConfirmUpgradeSelection();
|
||||||
void ResetUpgradeUiState();
|
void ResetUpgradeUiState();
|
||||||
@@ -277,6 +282,7 @@ void UseAirReshape();
|
|||||||
void ResetPendingRogueVisualEvents();
|
void ResetPendingRogueVisualEvents();
|
||||||
void ResetVisualEffects();
|
void ResetVisualEffects();
|
||||||
bool TickVisualEffects();
|
bool TickVisualEffects();
|
||||||
|
bool TickCreditAnimation();
|
||||||
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared);
|
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared);
|
||||||
void PlayPendingLineClearEffect();
|
void PlayPendingLineClearEffect();
|
||||||
void TriggerCellClearEffect(const Point* cells, int cellCount, bool strongBurst);
|
void TriggerCellClearEffect(const Point* cells, int cellCount, bool strongBurst);
|
||||||
|
|||||||
+115
-2
@@ -6,8 +6,11 @@
|
|||||||
#define MAX_LOADSTRING 100
|
#define MAX_LOADSTRING 100
|
||||||
#define GAME_TIMER_ID 1
|
#define GAME_TIMER_ID 1
|
||||||
#define EFFECT_TIMER_ID 2
|
#define EFFECT_TIMER_ID 2
|
||||||
|
#define CREDIT_TIMER_ID 3
|
||||||
|
#define WM_CREDIT_TICK (WM_APP + 1)
|
||||||
#define GAME_TIMER_INTERVAL 500
|
#define GAME_TIMER_INTERVAL 500
|
||||||
#define EFFECT_TIMER_INTERVAL 16
|
#define EFFECT_TIMER_INTERVAL 16
|
||||||
|
#define CREDIT_TIMER_INTERVAL 5
|
||||||
|
|
||||||
HINSTANCE hInst;
|
HINSTANCE hInst;
|
||||||
TCHAR szTitle[MAX_LOADSTRING];
|
TCHAR szTitle[MAX_LOADSTRING];
|
||||||
@@ -16,6 +19,7 @@ bool bgmEnabled = true;
|
|||||||
|
|
||||||
static bool bgmPlaying = false;
|
static bool bgmPlaying = false;
|
||||||
static bool bgmUsingMci = false;
|
static bool bgmUsingMci = false;
|
||||||
|
static MMRESULT creditTimerHandle = 0;
|
||||||
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
|
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
|
||||||
static constexpr const wchar_t* kReviveVideoAlias = L"TereisReviveVideo";
|
static constexpr const wchar_t* kReviveVideoAlias = L"TereisReviveVideo";
|
||||||
|
|
||||||
@@ -29,6 +33,18 @@ static bool FileExists(const std::wstring& path);
|
|||||||
static void StopBackgroundMusic();
|
static void StopBackgroundMusic();
|
||||||
static void StartBackgroundMusic();
|
static void StartBackgroundMusic();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 多媒体定时器回调,用于高频率请求致谢页动画刷新。
|
||||||
|
*/
|
||||||
|
static void CALLBACK CreditTimerCallback(UINT, UINT, DWORD_PTR userData, DWORD_PTR, DWORD_PTR)
|
||||||
|
{
|
||||||
|
HWND hWnd = reinterpret_cast<HWND>(userData);
|
||||||
|
if (hWnd != nullptr)
|
||||||
|
{
|
||||||
|
PostMessage(hWnd, WM_CREDIT_TICK, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将指定滚动偏移按步长调整,并限制在非负范围内。
|
* @brief 将指定滚动偏移按步长调整,并限制在非负范围内。
|
||||||
*/
|
*/
|
||||||
@@ -195,6 +211,29 @@ static RECT GetHelpBackHintRect(HWND hWnd)
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取致谢页左右切换按钮的绘制和点击区域。
|
||||||
|
*/
|
||||||
|
static RECT GetCreditArrowRect(HWND hWnd, int direction)
|
||||||
|
{
|
||||||
|
LayoutMetrics metrics = GetLayoutMetrics(hWnd);
|
||||||
|
RECT rulesCard = GetRulesCardRect(hWnd);
|
||||||
|
int size = ScaleValue(metrics, 54);
|
||||||
|
int centerY = (rulesCard.top + rulesCard.bottom) / 2;
|
||||||
|
int left = direction < 0
|
||||||
|
? rulesCard.left + ScaleValue(metrics, 52)
|
||||||
|
: rulesCard.right - ScaleValue(metrics, 52) - size;
|
||||||
|
|
||||||
|
RECT rect =
|
||||||
|
{
|
||||||
|
left,
|
||||||
|
centerY - size / 2,
|
||||||
|
left + size,
|
||||||
|
centerY + size / 2
|
||||||
|
};
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
static RECT GetUpgradeOverlayRect(HWND hWnd)
|
static RECT GetUpgradeOverlayRect(HWND hWnd)
|
||||||
{
|
{
|
||||||
LayoutMetrics metrics = GetLayoutMetrics(hWnd);
|
LayoutMetrics metrics = GetLayoutMetrics(hWnd);
|
||||||
@@ -681,11 +720,22 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
|
timeBeginPeriod(1);
|
||||||
srand((unsigned int)time(nullptr));
|
srand((unsigned int)time(nullptr));
|
||||||
ReturnToMainMenu();
|
ReturnToMainMenu();
|
||||||
StartBackgroundMusic();
|
StartBackgroundMusic();
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
SetTimer(hWnd, EFFECT_TIMER_ID, EFFECT_TIMER_INTERVAL, nullptr);
|
SetTimer(hWnd, EFFECT_TIMER_ID, EFFECT_TIMER_INTERVAL, nullptr);
|
||||||
|
creditTimerHandle = timeSetEvent(
|
||||||
|
CREDIT_TIMER_INTERVAL,
|
||||||
|
1,
|
||||||
|
CreditTimerCallback,
|
||||||
|
reinterpret_cast<DWORD_PTR>(hWnd),
|
||||||
|
TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
|
||||||
|
if (creditTimerHandle == 0)
|
||||||
|
{
|
||||||
|
SetTimer(hWnd, CREDIT_TIMER_ID, CREDIT_TIMER_INTERVAL, nullptr);
|
||||||
|
}
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
@@ -705,6 +755,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case WM_CREDIT_TICK:
|
||||||
|
if (currentScreen == SCREEN_RULES && helpState.currentPage == 4 && TickCreditAnimation())
|
||||||
|
{
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
if (wParam == EFFECT_TIMER_ID)
|
if (wParam == EFFECT_TIMER_ID)
|
||||||
{
|
{
|
||||||
@@ -714,6 +770,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (wParam == CREDIT_TIMER_ID && creditTimerHandle == 0)
|
||||||
|
{
|
||||||
|
if (currentScreen == SCREEN_RULES && helpState.currentPage == 4 && TickCreditAnimation())
|
||||||
|
{
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (wParam == GAME_TIMER_ID)
|
if (wParam == GAME_TIMER_ID)
|
||||||
{
|
{
|
||||||
@@ -902,10 +966,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
{
|
{
|
||||||
StartGameWithMode(MODE_ROGUE);
|
StartGameWithMode(MODE_ROGUE);
|
||||||
}
|
}
|
||||||
else
|
else if (i == 2)
|
||||||
{
|
{
|
||||||
OpenRulesScreen();
|
OpenRulesScreen();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OpenCreditScreen();
|
||||||
|
}
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
@@ -935,9 +1003,26 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (IsPointInRect(GetHelpBackHintRect(hWnd), mouseX, mouseY))
|
else if (IsPointInRect(GetHelpBackHintRect(hWnd), mouseX, mouseY))
|
||||||
|
{
|
||||||
|
if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
ReturnToMainMenu();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
helpState.currentPage = 0;
|
helpState.currentPage = 0;
|
||||||
helpScrollOffset = 0;
|
helpScrollOffset = 0;
|
||||||
|
}
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
else if (helpState.currentPage == 4 && IsPointInRect(GetCreditArrowRect(hWnd, -1), mouseX, mouseY))
|
||||||
|
{
|
||||||
|
ChangeCreditPage(-1);
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
else if (helpState.currentPage == 4 && IsPointInRect(GetCreditArrowRect(hWnd, 1), mouseX, mouseY))
|
||||||
|
{
|
||||||
|
ChangeCreditPage(1);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1108,10 +1193,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
{
|
{
|
||||||
StartGameWithMode(MODE_ROGUE);
|
StartGameWithMode(MODE_ROGUE);
|
||||||
}
|
}
|
||||||
else
|
else if (menuState.selectedIndex == 2)
|
||||||
{
|
{
|
||||||
OpenRulesScreen();
|
OpenRulesScreen();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OpenCreditScreen();
|
||||||
|
}
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
@@ -1141,6 +1230,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
|
else if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
ChangeCreditPage(-1);
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VK_DOWN:
|
case VK_DOWN:
|
||||||
case VK_RIGHT:
|
case VK_RIGHT:
|
||||||
@@ -1155,6 +1249,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
}
|
}
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
|
else if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
ChangeCreditPage(1);
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VK_RETURN:
|
case VK_RETURN:
|
||||||
case VK_SPACE:
|
case VK_SPACE:
|
||||||
@@ -1172,6 +1271,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
{
|
{
|
||||||
ReturnToMainMenu();
|
ReturnToMainMenu();
|
||||||
}
|
}
|
||||||
|
else if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
ReturnToMainMenu();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
helpState.currentPage = 0;
|
helpState.currentPage = 0;
|
||||||
@@ -1454,7 +1557,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
KillTimer(hWnd, GAME_TIMER_ID);
|
KillTimer(hWnd, GAME_TIMER_ID);
|
||||||
KillTimer(hWnd, EFFECT_TIMER_ID);
|
KillTimer(hWnd, EFFECT_TIMER_ID);
|
||||||
|
if (creditTimerHandle != 0)
|
||||||
|
{
|
||||||
|
timeKillEvent(creditTimerHandle);
|
||||||
|
creditTimerHandle = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
KillTimer(hWnd, CREDIT_TIMER_ID);
|
||||||
|
}
|
||||||
StopBackgroundMusic();
|
StopBackgroundMusic();
|
||||||
|
timeEndPeriod(1);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ bool reviveAvailable = false;
|
|||||||
int workRegion[20][10] = { 0 };
|
int workRegion[20][10] = { 0 };
|
||||||
Point point = { 0, 0 };
|
Point point = { 0, 0 };
|
||||||
Point target = { 0, 0 };
|
Point target = { 0, 0 };
|
||||||
MenuState menuState = { 0, 2 };
|
MenuState menuState = { 0, 4 };
|
||||||
HelpState helpState = { 0, 3, 0 };
|
HelpState helpState = { 0, 3, 0 };
|
||||||
int helpScrollOffset = 0;
|
int helpScrollOffset = 0;
|
||||||
|
int creditPageIndex = 0;
|
||||||
|
int creditAnimationTicks = 0;
|
||||||
|
int creditAnimationDirection = 0;
|
||||||
int upgradeListScrollOffset = 0;
|
int upgradeListScrollOffset = 0;
|
||||||
PlayerStats classicStats = { 0, 1, 0, 0, 0 };
|
PlayerStats classicStats = { 0, 1, 0, 0, 0 };
|
||||||
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
|
PlayerStats rogueStats = { 0, 1, 0, 30, 0, 100, 100, 0 };
|
||||||
|
|||||||
@@ -161,6 +161,20 @@ bool TickVisualEffects()
|
|||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 推进致谢页左右切换动画,并返回是否需要刷新界面。
|
||||||
|
*/
|
||||||
|
bool TickCreditAnimation()
|
||||||
|
{
|
||||||
|
if (creditAnimationTicks > 0)
|
||||||
|
{
|
||||||
|
creditAnimationTicks--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 添加一段棋盘坐标系中的浮动文字效果。
|
* @brief 添加一段棋盘坐标系中的浮动文字效果。
|
||||||
*/
|
*/
|
||||||
@@ -523,11 +537,14 @@ void ReturnToMainMenu()
|
|||||||
ResetVisualEffects();
|
ResetVisualEffects();
|
||||||
ResetPendingRogueVisualEvents();
|
ResetPendingRogueVisualEvents();
|
||||||
helpScrollOffset = 0;
|
helpScrollOffset = 0;
|
||||||
|
creditPageIndex = 0;
|
||||||
|
creditAnimationTicks = 0;
|
||||||
|
creditAnimationDirection = 0;
|
||||||
upgradeListScrollOffset = 0;
|
upgradeListScrollOffset = 0;
|
||||||
pendingLineClearEffectTicks = 0;
|
pendingLineClearEffectTicks = 0;
|
||||||
pendingLineClearEffectRowCount = 0;
|
pendingLineClearEffectRowCount = 0;
|
||||||
pendingLineClearEffectLineCount = 0;
|
pendingLineClearEffectLineCount = 0;
|
||||||
menuState.optionCount = 3;
|
menuState.optionCount = 4;
|
||||||
ResetUpgradeUiState();
|
ResetUpgradeUiState();
|
||||||
|
|
||||||
if (menuState.selectedIndex < 0 || menuState.selectedIndex >= menuState.optionCount)
|
if (menuState.selectedIndex < 0 || menuState.selectedIndex >= menuState.optionCount)
|
||||||
@@ -547,4 +564,60 @@ void OpenRulesScreen()
|
|||||||
helpState.optionCount = 3;
|
helpState.optionCount = 3;
|
||||||
helpState.currentPage = 0;
|
helpState.currentPage = 0;
|
||||||
helpScrollOffset = 0;
|
helpScrollOffset = 0;
|
||||||
|
creditPageIndex = 0;
|
||||||
|
creditAnimationTicks = 0;
|
||||||
|
creditAnimationDirection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 打开致谢界面并重置致谢页切换状态。
|
||||||
|
*/
|
||||||
|
void OpenCreditScreen()
|
||||||
|
{
|
||||||
|
currentScreen = SCREEN_RULES;
|
||||||
|
suspendFlag = false;
|
||||||
|
helpState.selectedIndex = 0;
|
||||||
|
helpState.optionCount = 3;
|
||||||
|
helpState.currentPage = 4;
|
||||||
|
helpScrollOffset = 0;
|
||||||
|
creditPageIndex = 0;
|
||||||
|
creditAnimationTicks = 0;
|
||||||
|
creditAnimationDirection = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 切换致谢页图片,并启动左右滑动动画。
|
||||||
|
*/
|
||||||
|
void ChangeCreditPage(int direction)
|
||||||
|
{
|
||||||
|
if (direction == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldPageIndex = creditPageIndex;
|
||||||
|
if (direction > 0)
|
||||||
|
{
|
||||||
|
creditPageIndex++;
|
||||||
|
creditAnimationDirection = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
creditPageIndex--;
|
||||||
|
creditAnimationDirection = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creditPageIndex < 0)
|
||||||
|
{
|
||||||
|
creditPageIndex = 1;
|
||||||
|
}
|
||||||
|
if (creditPageIndex > 1)
|
||||||
|
{
|
||||||
|
creditPageIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creditPageIndex != oldPageIndex)
|
||||||
|
{
|
||||||
|
creditAnimationTicks = 60;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+275
-12
@@ -126,6 +126,66 @@ static Bitmap* LoadBackgroundImage()
|
|||||||
return backgroundImage;
|
return backgroundImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按序号加载致谢页图片资源。
|
||||||
|
*/
|
||||||
|
static Bitmap* LoadCreditImage(int index)
|
||||||
|
{
|
||||||
|
static ULONG_PTR gdiplusToken = 0;
|
||||||
|
static Bitmap* creditImages[2] = {};
|
||||||
|
static bool attempted[2] = {};
|
||||||
|
|
||||||
|
if (index < 0 || index >= 2)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attempted[index])
|
||||||
|
{
|
||||||
|
attempted[index] = true;
|
||||||
|
|
||||||
|
GdiplusStartupInput startupInput;
|
||||||
|
if (GdiplusStartup(&gdiplusToken, &startupInput, nullptr) == Ok)
|
||||||
|
{
|
||||||
|
const wchar_t* imageNames[2] =
|
||||||
|
{
|
||||||
|
L"assets\\images\\qls.jpg",
|
||||||
|
L"assets\\images\\wyk.jpg"
|
||||||
|
};
|
||||||
|
const std::wstring candidates[] =
|
||||||
|
{
|
||||||
|
BuildAssetPath(imageNames[index]),
|
||||||
|
BuildWorkingDirAssetPath(imageNames[index])
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
creditImages[index] = loadedImage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete loadedImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return creditImages[index];
|
||||||
|
}
|
||||||
|
|
||||||
void TDrawScreen(HDC hdc, HWND hWnd)
|
void TDrawScreen(HDC hdc, HWND hWnd)
|
||||||
{
|
{
|
||||||
RECT clientRect;
|
RECT clientRect;
|
||||||
@@ -383,12 +443,15 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
SX(34),
|
SX(34),
|
||||||
SY(34)
|
SY(34)
|
||||||
};
|
};
|
||||||
DrawPanelCardAlpha(
|
HBRUSH backBrush = CreateSolidBrush(RGB(255, 242, 247));
|
||||||
backButtonRect,
|
HPEN backFramePen = CreatePen(PS_SOLID, 1, RGB(222, 130, 166));
|
||||||
RGB(255, 242, 247),
|
HBRUSH oldBackBrush = (HBRUSH)SelectObject(hdc, backBrush);
|
||||||
RGB(222, 130, 166),
|
HPEN oldBackFramePen = (HPEN)SelectObject(hdc, backFramePen);
|
||||||
12,
|
RoundRect(hdc, backButtonRect.left, backButtonRect.top, backButtonRect.right, backButtonRect.bottom, SS(12), SS(12));
|
||||||
214);
|
SelectObject(hdc, oldBackBrush);
|
||||||
|
SelectObject(hdc, oldBackFramePen);
|
||||||
|
DeleteObject(backFramePen);
|
||||||
|
DeleteObject(backBrush);
|
||||||
|
|
||||||
HPEN backPen = CreatePen(PS_SOLID, SS(3), RGB(128, 70, 100));
|
HPEN backPen = CreatePen(PS_SOLID, SS(3), RGB(128, 70, 100));
|
||||||
HPEN oldBackPen = (HPEN)SelectObject(hdc, backPen);
|
HPEN oldBackPen = (HPEN)SelectObject(hdc, backPen);
|
||||||
@@ -436,18 +499,20 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
SetTextColor(hdc, textColor);
|
SetTextColor(hdc, textColor);
|
||||||
DrawText(hdc, _T("选择你的战局"), -1, &subtitleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
DrawText(hdc, _T("选择你的战局"), -1, &subtitleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
const TCHAR* modeNames[3] =
|
const TCHAR* modeNames[4] =
|
||||||
{
|
{
|
||||||
_T("\u7ecf\u5178\u6a21\u5f0f"),
|
_T("\u7ecf\u5178\u6a21\u5f0f"),
|
||||||
_T("Rogue \u6a21\u5f0f"),
|
_T("Rogue \u6a21\u5f0f"),
|
||||||
_T("\u5e2e\u52a9")
|
_T("\u5e2e\u52a9"),
|
||||||
|
_T("\u81f4\u8c22")
|
||||||
};
|
};
|
||||||
|
|
||||||
const TCHAR* modeDescriptions[3] =
|
const TCHAR* modeDescriptions[4] =
|
||||||
{
|
{
|
||||||
_T("纯粹方块挑战,专注消行、堆叠与生存。"),
|
_T("纯粹方块挑战,专注消行、堆叠与生存。"),
|
||||||
_T("在不断升级的棋盘中收集强化,构筑本局专属流派。"),
|
_T("在不断升级的棋盘中收集强化,构筑本局专属流派。"),
|
||||||
_T("查看操作、模式规则与全部强化效果。")
|
_T("查看操作、模式规则与全部强化效果。"),
|
||||||
|
_T("感谢程序测试者与代码贡献者。")
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < menuState.optionCount; i++)
|
for (int i = 0; i < menuState.optionCount; i++)
|
||||||
@@ -568,6 +633,10 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
{
|
{
|
||||||
helpTitle = _T("\u5f3a\u5316\u56fe\u9274");
|
helpTitle = _T("\u5f3a\u5316\u56fe\u9274");
|
||||||
}
|
}
|
||||||
|
else if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
helpTitle = _T("\u81f4\u8c22");
|
||||||
|
}
|
||||||
DrawText(hdc, helpTitle, -1, &rulesTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
DrawText(hdc, helpTitle, -1, &rulesTitleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
HPEN rulesAccentPen = CreatePen(PS_SOLID, SS(3), accentColor);
|
HPEN rulesAccentPen = CreatePen(PS_SOLID, SS(3), accentColor);
|
||||||
@@ -807,7 +876,198 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
DeleteObject(contentClipRegion);
|
DeleteObject(contentClipRegion);
|
||||||
DeleteObject(oldClipRegion);
|
DeleteObject(oldClipRegion);
|
||||||
}
|
}
|
||||||
if (helpState.currentPage != 3)
|
else if (helpState.currentPage == 4)
|
||||||
|
{
|
||||||
|
const int creditAnimationTotalTicks = 60;
|
||||||
|
const TCHAR* creditNames[2] =
|
||||||
|
{
|
||||||
|
_T("qls"),
|
||||||
|
_T("wyk")
|
||||||
|
};
|
||||||
|
const TCHAR* creditTexts[2] =
|
||||||
|
{
|
||||||
|
_T("\u611f\u8c22\u6fc0\u60c5\u6295\u8eab\u4e8e\u6d4b\u8bd5\u4e4b\u4e2d\u7684Lisa"),
|
||||||
|
_T("\u611f\u8c22\u70ed\u5ff1coding\u7684\u5c0f\u86cb\u7cd5")
|
||||||
|
};
|
||||||
|
|
||||||
|
int currentCredit = creditPageIndex;
|
||||||
|
if (currentCredit < 0)
|
||||||
|
{
|
||||||
|
currentCredit = 0;
|
||||||
|
}
|
||||||
|
if (currentCredit > 1)
|
||||||
|
{
|
||||||
|
currentCredit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int previousCredit = currentCredit - creditAnimationDirection;
|
||||||
|
if (previousCredit < 0)
|
||||||
|
{
|
||||||
|
previousCredit = 1;
|
||||||
|
}
|
||||||
|
if (previousCredit > 1)
|
||||||
|
{
|
||||||
|
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* preloadedCreditImageA = LoadCreditImage(0);
|
||||||
|
Bitmap* preloadedCreditImageB = LoadCreditImage(1);
|
||||||
|
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 = (cardIndex == 0) ? preloadedCreditImageA : preloadedCreditImageB;
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
RECT calculateRect = { contentRect.left, contentRect.top, contentRect.right, contentRect.top };
|
RECT calculateRect = { contentRect.left, contentRect.top, contentRect.right, contentRect.top };
|
||||||
DrawText(hdc, pageText, -1, &calculateRect, pageFlags | DT_CALCRECT);
|
DrawText(hdc, pageText, -1, &calculateRect, pageFlags | DT_CALCRECT);
|
||||||
@@ -855,9 +1115,12 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
};
|
};
|
||||||
const TCHAR* helpHint = helpState.currentPage == 0
|
const TCHAR* helpHint = helpState.currentPage == 0
|
||||||
? _T("\u65b9\u5411\u952e / WASD \u5207\u6362\uff0cEnter / Space \u786e\u8ba4\uff0cEsc / M \u8fd4\u56de\u4e3b\u83dc\u5355")
|
? _T("\u65b9\u5411\u952e / WASD \u5207\u6362\uff0cEnter / Space \u786e\u8ba4\uff0cEsc / M \u8fd4\u56de\u4e3b\u83dc\u5355")
|
||||||
: _T("\u9f20\u6807\u6eda\u8f6e\u4e0a\u4e0b\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9");
|
: (helpState.currentPage == 4
|
||||||
|
? _T("\u5de6\u53f3\u65b9\u5411\u952e / A D \u5207\u6362\uff0cEsc / Backspace / M \u8fd4\u56de\u4e3b\u83dc\u5355")
|
||||||
|
: _T("\u9f20\u6807\u6eda\u8f6e\u4e0a\u4e0b\u7ffb\u52a8\uff0cEsc / Backspace / M \u8fd4\u56de\u5e2e\u52a9"));
|
||||||
DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
DrawText(hdc, helpHint, -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
|
SelectClipRgn(hdc, nullptr);
|
||||||
DrawBackButton();
|
DrawBackButton();
|
||||||
DrawMusicButton();
|
DrawMusicButton();
|
||||||
SelectObject(hdc, oldFont);
|
SelectObject(hdc, oldFont);
|
||||||
|
|||||||
Reference in New Issue
Block a user