添加bgm
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -171,6 +171,7 @@ extern int tScore;
|
|||||||
extern bool gameOverFlag;
|
extern bool gameOverFlag;
|
||||||
extern bool suspendFlag;
|
extern bool suspendFlag;
|
||||||
extern bool targetFlag;
|
extern bool targetFlag;
|
||||||
|
extern bool bgmEnabled;
|
||||||
extern int workRegion[20][10];
|
extern int workRegion[20][10];
|
||||||
extern Point point;
|
extern Point point;
|
||||||
extern Point target;
|
extern Point target;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "Tetris.h"
|
#include "Tetris.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#define MAX_LOADSTRING 100
|
#define MAX_LOADSTRING 100
|
||||||
#define GAME_TIMER_ID 1
|
#define GAME_TIMER_ID 1
|
||||||
@@ -8,6 +9,11 @@
|
|||||||
HINSTANCE hInst;
|
HINSTANCE hInst;
|
||||||
TCHAR szTitle[MAX_LOADSTRING];
|
TCHAR szTitle[MAX_LOADSTRING];
|
||||||
TCHAR szWindowClass[MAX_LOADSTRING];
|
TCHAR szWindowClass[MAX_LOADSTRING];
|
||||||
|
bool bgmEnabled = true;
|
||||||
|
|
||||||
|
static bool bgmPlaying = false;
|
||||||
|
static bool bgmUsingMci = false;
|
||||||
|
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
|
||||||
|
|
||||||
ATOM MyRegisterClass(HINSTANCE hInstance);
|
ATOM MyRegisterClass(HINSTANCE hInstance);
|
||||||
BOOL InitInstance(HINSTANCE, int);
|
BOOL InitInstance(HINSTANCE, int);
|
||||||
@@ -20,6 +26,229 @@ static void ResetGameTimer(HWND hWnd)
|
|||||||
SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr);
|
SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 bool FileExists(const std::wstring& path)
|
||||||
|
{
|
||||||
|
DWORD attributes = GetFileAttributesW(path.c_str());
|
||||||
|
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RECT GetMusicButtonRect(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 offsetX = (clientWidth - layoutWidth) / 2;
|
||||||
|
int offsetY = 0;
|
||||||
|
int size = MulDiv(28, scale, 1000);
|
||||||
|
if (size < 22)
|
||||||
|
{
|
||||||
|
size = 22;
|
||||||
|
}
|
||||||
|
int marginRight = MulDiv(12, scale, 1000);
|
||||||
|
if (marginRight < 6)
|
||||||
|
{
|
||||||
|
marginRight = 6;
|
||||||
|
}
|
||||||
|
int marginBottom = MulDiv(12, scale, 1000);
|
||||||
|
if (marginBottom < 6)
|
||||||
|
{
|
||||||
|
marginBottom = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT buttonRect =
|
||||||
|
{
|
||||||
|
offsetX + layoutWidth - marginRight - size,
|
||||||
|
offsetY + MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000) - marginBottom - size,
|
||||||
|
offsetX + layoutWidth - marginRight,
|
||||||
|
offsetY + MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000) - marginBottom
|
||||||
|
};
|
||||||
|
return buttonRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsPointInRect(const RECT& rect, int x, int y)
|
||||||
|
{
|
||||||
|
return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryPlayMciLoop(const std::wstring& path, bool forceMpegVideo)
|
||||||
|
{
|
||||||
|
if (!FileExists(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
||||||
|
|
||||||
|
std::wstring openCommand = L"open \"" + path + L"\" ";
|
||||||
|
if (forceMpegVideo)
|
||||||
|
{
|
||||||
|
openCommand += L"type mpegvideo ";
|
||||||
|
}
|
||||||
|
openCommand += L"alias ";
|
||||||
|
openCommand += kBgmAlias;
|
||||||
|
|
||||||
|
if (mciSendStringW(openCommand.c_str(), nullptr, 0, nullptr) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring playCommand = std::wstring(L"play ") + kBgmAlias + L" repeat";
|
||||||
|
if (mciSendStringW(playCommand.c_str(), nullptr, 0, nullptr) != 0)
|
||||||
|
{
|
||||||
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bgmPlaying = true;
|
||||||
|
bgmUsingMci = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StopBackgroundMusic()
|
||||||
|
{
|
||||||
|
if (bgmUsingMci)
|
||||||
|
{
|
||||||
|
mciSendStringW((std::wstring(L"stop ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
||||||
|
mciSendStringW((std::wstring(L"close ") + kBgmAlias).c_str(), nullptr, 0, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlaySoundW(nullptr, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bgmPlaying = false;
|
||||||
|
bgmUsingMci = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartBackgroundMusic()
|
||||||
|
{
|
||||||
|
if (!bgmEnabled || bgmPlaying)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* bgmWavRelativePath = L"assets\\audio\\bgm.wav";
|
||||||
|
const std::wstring bgmWavCandidates[] =
|
||||||
|
{
|
||||||
|
BuildAssetPath(bgmWavRelativePath),
|
||||||
|
BuildWorkingDirAssetPath(bgmWavRelativePath)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::wstring& candidate : bgmWavCandidates)
|
||||||
|
{
|
||||||
|
if (FileExists(candidate) &&
|
||||||
|
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
|
||||||
|
{
|
||||||
|
bgmPlaying = true;
|
||||||
|
bgmUsingMci = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* oggRelativePath = L"assets\\audio\\bgm.ogg";
|
||||||
|
const std::wstring oggCandidates[] =
|
||||||
|
{
|
||||||
|
BuildAssetPath(oggRelativePath),
|
||||||
|
BuildWorkingDirAssetPath(oggRelativePath)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::wstring& candidate : oggCandidates)
|
||||||
|
{
|
||||||
|
if (TryPlayMciLoop(candidate, false) || TryPlayMciLoop(candidate, true))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* fallbackWavRelativePath = L"assets\\audio\\background.wav";
|
||||||
|
const std::wstring fallbackWavCandidates[] =
|
||||||
|
{
|
||||||
|
BuildAssetPath(fallbackWavRelativePath),
|
||||||
|
BuildWorkingDirAssetPath(fallbackWavRelativePath)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const std::wstring& candidate : fallbackWavCandidates)
|
||||||
|
{
|
||||||
|
if (FileExists(candidate) &&
|
||||||
|
PlaySoundW(candidate.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_LOOP))
|
||||||
|
{
|
||||||
|
bgmPlaying = true;
|
||||||
|
bgmUsingMci = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bgmEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ToggleBackgroundMusic(HWND hWnd)
|
||||||
|
{
|
||||||
|
bgmEnabled = !bgmEnabled;
|
||||||
|
if (bgmEnabled)
|
||||||
|
{
|
||||||
|
StartBackgroundMusic();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StopBackgroundMusic();
|
||||||
|
}
|
||||||
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
int APIENTRY _tWinMain(HINSTANCE hInstance,
|
int APIENTRY _tWinMain(HINSTANCE hInstance,
|
||||||
HINSTANCE hPrevInstance,
|
HINSTANCE hPrevInstance,
|
||||||
LPTSTR lpCmdLine,
|
LPTSTR lpCmdLine,
|
||||||
@@ -124,6 +353,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
srand((unsigned int)time(nullptr));
|
srand((unsigned int)time(nullptr));
|
||||||
ReturnToMainMenu();
|
ReturnToMainMenu();
|
||||||
|
StartBackgroundMusic();
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
@@ -295,6 +525,19 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
{
|
||||||
|
int mouseX = static_cast<short>(LOWORD(lParam));
|
||||||
|
int mouseY = static_cast<short>(HIWORD(lParam));
|
||||||
|
RECT musicButtonRect = GetMusicButtonRect(hWnd);
|
||||||
|
if (IsPointInRect(musicButtonRect, mouseX, mouseY))
|
||||||
|
{
|
||||||
|
ToggleBackgroundMusic(hWnd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
if (currentScreen == SCREEN_MENU)
|
if (currentScreen == SCREEN_MENU)
|
||||||
{
|
{
|
||||||
@@ -580,6 +823,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
break;
|
break;
|
||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
KillTimer(hWnd, GAME_TIMER_ID);
|
KillTimer(hWnd, GAME_TIMER_ID);
|
||||||
|
StopBackgroundMusic();
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -302,6 +302,52 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
DeleteObject(localAccentPen);
|
DeleteObject(localAccentPen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto DrawMusicButton = [&]()
|
||||||
|
{
|
||||||
|
RECT musicButtonRect =
|
||||||
|
{
|
||||||
|
SX(WINDOW_CLIENT_WIDTH - 40),
|
||||||
|
SY(WINDOW_CLIENT_HEIGHT - 40),
|
||||||
|
SX(WINDOW_CLIENT_WIDTH - 12),
|
||||||
|
SY(WINDOW_CLIENT_HEIGHT - 12)
|
||||||
|
};
|
||||||
|
DrawPanelCardAlpha(
|
||||||
|
musicButtonRect,
|
||||||
|
bgmEnabled ? RGB(255, 238, 246) : RGB(244, 236, 240),
|
||||||
|
bgmEnabled ? RGB(222, 130, 166) : RGB(170, 148, 158),
|
||||||
|
12,
|
||||||
|
bgmEnabled ? 218 : 190);
|
||||||
|
|
||||||
|
HPEN musicPen = CreatePen(PS_SOLID, SS(3), bgmEnabled ? RGB(128, 70, 100) : RGB(128, 112, 120));
|
||||||
|
HPEN oldMusicPen = (HPEN)SelectObject(hdc, musicPen);
|
||||||
|
HBRUSH oldMusicBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
|
||||||
|
|
||||||
|
int noteStemX = musicButtonRect.left + SS(17);
|
||||||
|
int noteTop = musicButtonRect.top + SS(7);
|
||||||
|
int noteBottom = musicButtonRect.bottom - SS(9);
|
||||||
|
MoveToEx(hdc, noteStemX, noteTop, nullptr);
|
||||||
|
LineTo(hdc, noteStemX, noteBottom);
|
||||||
|
MoveToEx(hdc, noteStemX, noteTop, nullptr);
|
||||||
|
LineTo(hdc, musicButtonRect.right - SS(7), noteTop + SS(4));
|
||||||
|
LineTo(hdc, musicButtonRect.right - SS(7), noteTop + SS(10));
|
||||||
|
Ellipse(
|
||||||
|
hdc,
|
||||||
|
musicButtonRect.left + SS(7),
|
||||||
|
musicButtonRect.bottom - SS(13),
|
||||||
|
musicButtonRect.left + SS(19),
|
||||||
|
musicButtonRect.bottom - SS(4));
|
||||||
|
|
||||||
|
if (!bgmEnabled)
|
||||||
|
{
|
||||||
|
MoveToEx(hdc, musicButtonRect.left + SS(7), musicButtonRect.bottom - SS(7), nullptr);
|
||||||
|
LineTo(hdc, musicButtonRect.right - SS(6), musicButtonRect.top + SS(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectObject(hdc, oldMusicBrush);
|
||||||
|
SelectObject(hdc, oldMusicPen);
|
||||||
|
DeleteObject(musicPen);
|
||||||
|
};
|
||||||
|
|
||||||
if (currentScreen == SCREEN_MENU)
|
if (currentScreen == SCREEN_MENU)
|
||||||
{
|
{
|
||||||
RECT menuCard =
|
RECT menuCard =
|
||||||
@@ -418,6 +464,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
SetTextColor(hdc, RGB(128, 104, 118));
|
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);
|
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);
|
SelectObject(hdc, oldFont);
|
||||||
DeleteObject(titleFont);
|
DeleteObject(titleFont);
|
||||||
DeleteObject(sectionFont);
|
DeleteObject(sectionFont);
|
||||||
@@ -557,6 +604,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
};
|
};
|
||||||
DrawText(hdc, _T("Esc / Backspace / M \u8fd4\u56de\u4e3b\u83dc\u5355"), -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
DrawText(hdc, _T("Esc / Backspace / M \u8fd4\u56de\u4e3b\u83dc\u5355"), -1, &backHintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
|
DrawMusicButton();
|
||||||
SelectObject(hdc, oldFont);
|
SelectObject(hdc, oldFont);
|
||||||
DeleteObject(titleFont);
|
DeleteObject(titleFont);
|
||||||
DeleteObject(sectionFont);
|
DeleteObject(sectionFont);
|
||||||
@@ -1765,6 +1813,8 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
DrawText(hdc, _T("W/A/S/D \u6216\u65b9\u5411\u952e\u5207\u6362\uff0cEnter \u6216 Space \u786e\u8ba4"), -1, &hintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
DrawText(hdc, _T("W/A/S/D \u6216\u65b9\u5411\u952e\u5207\u6362\uff0cEnter \u6216 Space \u786e\u8ba4"), -1, &hintRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawMusicButton();
|
||||||
|
|
||||||
SelectObject(hdc, oldFont);
|
SelectObject(hdc, oldFont);
|
||||||
DeleteObject(titleFont);
|
DeleteObject(titleFont);
|
||||||
DeleteObject(sectionFont);
|
DeleteObject(sectionFont);
|
||||||
|
|||||||
Reference in New Issue
Block a user