添加看视频复活

This commit is contained in:
2026-04-26 14:19:27 +08:00
parent aa3d4d3945
commit 96ab175877
8 changed files with 158 additions and 801 deletions
+3
View File
@@ -202,6 +202,7 @@ extern bool gameOverFlag;
extern bool suspendFlag;
extern bool targetFlag;
extern bool bgmEnabled;
extern bool reviveAvailable;
extern int workRegion[20][10];
extern Point point;
extern Point target;
@@ -241,6 +242,8 @@ void ComputeTarget();
void Restart();
void StartGameWithMode(int mode);
void ReturnToMainMenu();
void ReviveAfterVideo();
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks);
void OpenRulesScreen();
void OpenUpgradeMenu();
void ConfirmUpgradeSelection();
+106
View File
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "Tetris.h"
#include <string>
#include <shellapi.h>
#define MAX_LOADSTRING 100
#define GAME_TIMER_ID 1
@@ -16,11 +17,17 @@ bool bgmEnabled = true;
static bool bgmPlaying = false;
static bool bgmUsingMci = false;
static constexpr const wchar_t* kBgmAlias = L"TereisBgm";
static constexpr const wchar_t* kReviveVideoAlias = L"TereisReviveVideo";
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
static std::wstring BuildAssetPath(const wchar_t* relativePath);
static std::wstring BuildWorkingDirAssetPath(const wchar_t* relativePath);
static bool FileExists(const std::wstring& path);
static void StopBackgroundMusic();
static void StartBackgroundMusic();
static void ResetGameTimer(HWND hWnd)
{
@@ -28,6 +35,90 @@ static void ResetGameTimer(HWND hWnd)
SetTimer(hWnd, GAME_TIMER_ID, currentFallInterval > 0 ? currentFallInterval : GAME_TIMER_INTERVAL, nullptr);
}
static bool PlayReviveVideo(HWND hWnd)
{
std::wstring videoPath = BuildAssetPath(L"assets\\video\\video.avi");
if (!FileExists(videoPath))
{
videoPath = BuildWorkingDirAssetPath(L"assets\\video\\video.avi");
}
if (!FileExists(videoPath))
{
videoPath = BuildAssetPath(L"assets\\video\\video.mp4");
}
if (!FileExists(videoPath))
{
videoPath = BuildWorkingDirAssetPath(L"assets\\video\\video.mp4");
}
if (!FileExists(videoPath))
{
return false;
}
bool shouldResumeBgm = bgmEnabled;
if (bgmPlaying)
{
StopBackgroundMusic();
}
auto tryPlayWithMci = [&](bool forceMpegVideo) -> bool
{
mciSendStringW((std::wstring(L"close ") + kReviveVideoAlias).c_str(), nullptr, 0, nullptr);
std::wstring openCommand = L"open \"" + videoPath + L"\" ";
if (forceMpegVideo)
{
openCommand += L"type mpegvideo ";
}
openCommand += L"alias ";
openCommand += kReviveVideoAlias;
if (mciSendStringW(openCommand.c_str(), nullptr, 0, hWnd) != 0)
{
return false;
}
std::wstring playCommand = std::wstring(L"play ") + kReviveVideoAlias + L" fullscreen wait";
MCIERROR playResult = mciSendStringW(playCommand.c_str(), nullptr, 0, hWnd);
mciSendStringW((std::wstring(L"close ") + kReviveVideoAlias).c_str(), nullptr, 0, nullptr);
return playResult == 0;
};
bool played = tryPlayWithMci(true);
if (!played)
{
played = tryPlayWithMci(false);
}
if (!played)
{
SHELLEXECUTEINFOW shellInfo = {};
shellInfo.cbSize = sizeof(shellInfo);
shellInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shellInfo.hwnd = hWnd;
shellInfo.lpVerb = L"open";
shellInfo.lpFile = videoPath.c_str();
shellInfo.nShow = SW_SHOWNORMAL;
if (ShellExecuteExW(&shellInfo))
{
if (shellInfo.hProcess != nullptr)
{
WaitForSingleObject(shellInfo.hProcess, INFINITE);
CloseHandle(shellInfo.hProcess);
}
played = true;
}
}
if (shouldResumeBgm)
{
StartBackgroundMusic();
}
return played;
}
static std::wstring BuildAssetPath(const wchar_t* relativePath)
{
wchar_t modulePath[MAX_PATH] = {};
@@ -729,6 +820,21 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break;
}
if (gameOverFlag && reviveAvailable && wParam == 'V')
{
if (PlayReviveVideo(hWnd))
{
ReviveAfterVideo();
ResetGameTimer(hWnd);
}
else
{
SetFeedbackMessage(_T("视频播放失败"), _T("无法打开复活视频,复活机会未消耗。"), 14);
}
InvalidateRect(hWnd, nullptr, FALSE);
break;
}
if (gameOverFlag || suspendFlag)
{
break;
+41
View File
@@ -9,6 +9,7 @@ int tScore = 0;
bool gameOverFlag = false;
bool suspendFlag = false;
bool targetFlag = false;
bool reviveAvailable = false;
int workRegion[20][10] = { 0 };
Point point = { 0, 0 };
Point target = { 0, 0 };
@@ -1160,6 +1161,7 @@ void Restart()
gameOverFlag = false;
suspendFlag = false;
targetFlag = true;
reviveAvailable = true;
currentFallInterval = 500;
ResetPlayerStats(classicStats, false);
@@ -1194,6 +1196,45 @@ void Restart()
ComputeTarget();
}
void ReviveAfterVideo()
{
if (!gameOverFlag || !reviveAvailable)
{
return;
}
reviveAvailable = false;
gameOverFlag = false;
suspendFlag = false;
currentScreen = SCREEN_PLAYING;
int playableHeight = GetRoguePlayableHeight();
int rowsToClear = playableHeight / 3;
if (rowsToClear < 5)
{
rowsToClear = 5;
}
for (int y = 0; y < rowsToClear && y < playableHeight; y++)
{
for (int x = 0; x < nGameWidth; x++)
{
workRegion[y][x] = 0;
}
}
type = ConsumeNextType();
nType = nextTypes[0];
state = 0;
holdUsedThisTurn = false;
RollCurrentPieceSpecialFlags(true);
point = GetSpawnPoint(type);
target = point;
ComputeTarget();
SetFeedbackMessage(_T("复活成功"), _T("已清理顶部空间,本局复活机会已用完。"), 14);
}
void StartGameWithMode(int mode)
{
currentMode = mode;
+8 -1
View File
@@ -1663,7 +1663,14 @@ void TDrawScreen(HDC hdc, HWND hWnd)
{
DrawText(hdc, _T("战局崩塌"), -1, &titleRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, bodyFont);
DrawText(hdc, _T("按 R 重新挑战\r\n或按 M 返回菜单"), -1, &tipRect, DT_CENTER | DT_VCENTER | DT_WORDBREAK);
DrawText(
hdc,
reviveAvailable
? _T("按 V 看视频复活(仅一次)\r\n按 R 重新挑战 或按 M 返回菜单")
: _T("按 R 重新挑战\r\n或按 M 返回菜单"),
-1,
&tipRect,
DT_CENTER | DT_VCENTER | DT_WORDBREAK);
}
}