761 lines
20 KiB
C++
761 lines
20 KiB
C++
#include "stdafx.h"
|
|
/**
|
|
* @file TetrisGameExtensions.cpp
|
|
* @brief 实现玩家统计、视觉特效、模式切换、复活和帮助/致谢页面状态管理。
|
|
*/
|
|
|
|
#include "TetrisLogicInternal.h"
|
|
|
|
int pendingLineClearEffectTicks = 0;
|
|
int pendingLineClearEffectRows[8] = {};
|
|
int pendingLineClearEffectRowCount = 0;
|
|
int pendingLineClearEffectLineCount = 0;
|
|
|
|
/**
|
|
* @brief 重置经典或 Rogue 模式使用的玩家统计数据。
|
|
* @param stats 需要重置的统计结构。
|
|
* @param useRogueRules 是否按 Rogue 模式设置初始经验需求。
|
|
*/
|
|
void ResetPlayerStats(PlayerStats& stats, bool useRogueRules)
|
|
{
|
|
stats.score = 0;
|
|
stats.level = 1;
|
|
stats.exp = 0;
|
|
stats.requiredExp = useRogueRules ? 10 : 0;
|
|
stats.totalLinesCleared = 0;
|
|
stats.scoreMultiplierPercent = 100;
|
|
stats.expMultiplierPercent = 100;
|
|
stats.slowFallStacks = 0;
|
|
stats.comboBonusStacks = 0;
|
|
stats.comboChain = 0;
|
|
stats.previewCount = 1;
|
|
stats.lastChanceCount = 0;
|
|
stats.scoreUpgradeLevel = 0;
|
|
stats.expUpgradeLevel = 0;
|
|
stats.previewUpgradeLevel = 0;
|
|
stats.lastChanceUpgradeLevel = 0;
|
|
stats.holdUnlocked = 0;
|
|
stats.pressureReliefLevel = 0;
|
|
stats.sweeperLevel = 0;
|
|
stats.sweeperCharge = 0;
|
|
stats.explosiveLevel = 0;
|
|
stats.explosivePieceCounter = 0;
|
|
stats.chainBlastLevel = 0;
|
|
stats.chainBombLevel = 0;
|
|
stats.laserLevel = 0;
|
|
stats.thunderTetrisLevel = 0;
|
|
stats.thunderLaserLevel = 0;
|
|
stats.feverLevel = 0;
|
|
stats.rageStackLevel = 0;
|
|
stats.infiniteFeverLevel = 0;
|
|
stats.feverLineCharge = 0;
|
|
stats.feverTicks = 0;
|
|
stats.screenBombLevel = 0;
|
|
stats.screenBombCharge = 0;
|
|
stats.screenBombCount = 0;
|
|
stats.terminalClearLevel = 0;
|
|
stats.dualChoiceLevel = 0;
|
|
stats.destinyWheelLevel = 0;
|
|
stats.perfectRotateLevel = 0;
|
|
stats.timeDilationLevel = 0;
|
|
stats.timeDilationTicks = 0;
|
|
stats.highPressureLevel = 0;
|
|
stats.tetrisGambleLevel = 0;
|
|
stats.extremePlayerLevel = 0;
|
|
stats.extremeSlowTicks = 0;
|
|
stats.extremeDangerTicks = 30;
|
|
stats.extremeDangerLevel = 0;
|
|
stats.upgradeShockwaveLevel = 0;
|
|
stats.evolutionImpactLevel = 0;
|
|
stats.controlMasterLevel = 0;
|
|
stats.holdSlowTicks = 0;
|
|
stats.blockStormLevel = 0;
|
|
stats.blockStormPiecesRemaining = 0;
|
|
stats.blackHoleLevel = 0;
|
|
stats.blackHoleCharges = 0;
|
|
stats.reshapeLevel = 0;
|
|
stats.reshapeCharges = 0;
|
|
stats.rainbowPieceLevel = 0;
|
|
stats.voidCoreLevel = 0;
|
|
stats.pendingRainbowPieceCount = 0;
|
|
stats.stableStructureLevel = 0;
|
|
stats.doubleGrowthLevel = 0;
|
|
stats.gamblerLevel = 0;
|
|
stats.difficultyElapsedMs = 0;
|
|
stats.difficultyLevel = 0;
|
|
stats.lockedRows = 0;
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
stats.pieceTuningLevels[i] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 设置界面右侧显示的即时反馈标题、内容和持续时间。
|
|
* @param title 反馈标题。
|
|
* @param detail 反馈详情。
|
|
* @param ticks 显示持续的游戏计时次数。
|
|
*/
|
|
void SetFeedbackMessage(const TCHAR* title, const TCHAR* detail, int ticks)
|
|
{
|
|
feedbackState.visibleTicks = ticks;
|
|
lstrcpyn(feedbackState.title, title, sizeof(feedbackState.title) / sizeof(TCHAR));
|
|
lstrcpyn(feedbackState.detail, detail, sizeof(feedbackState.detail) / sizeof(TCHAR));
|
|
}
|
|
|
|
/**
|
|
* @brief 清空所有消行、浮动文字和粒子视觉效果。
|
|
*/
|
|
void ResetVisualEffects()
|
|
{
|
|
clearEffectState.ticks = 0;
|
|
clearEffectState.totalTicks = 0;
|
|
clearEffectState.rowCount = 0;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
floatingTextEffects[i].ticks = 0;
|
|
}
|
|
|
|
for (int i = 0; i < 96; i++)
|
|
{
|
|
particleEffects[i].ticks = 0;
|
|
}
|
|
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
cellFlashEffects[i].ticks = 0;
|
|
}
|
|
|
|
for (int i = 0; i < 80; i++)
|
|
{
|
|
gravityFallEffects[i].ticks = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 推进视觉效果计时,并返回是否仍有动画需要刷新。
|
|
* @return 仍有动画需要刷新返回 true,否则返回 false。
|
|
*/
|
|
bool TickVisualEffects()
|
|
{
|
|
bool active = false;
|
|
|
|
if (clearEffectState.ticks > 0)
|
|
{
|
|
clearEffectState.ticks--;
|
|
active = true;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (floatingTextEffects[i].ticks > 0)
|
|
{
|
|
floatingTextEffects[i].ticks--;
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 96; i++)
|
|
{
|
|
if (particleEffects[i].ticks > 0)
|
|
{
|
|
particleEffects[i].ticks--;
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if (cellFlashEffects[i].ticks > 0)
|
|
{
|
|
cellFlashEffects[i].ticks--;
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 80; i++)
|
|
{
|
|
if (gravityFallEffects[i].ticks > 0)
|
|
{
|
|
gravityFallEffects[i].ticks--;
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
return active;
|
|
}
|
|
|
|
/**
|
|
* @brief 推进致谢页左右切换动画,并返回是否需要刷新界面。
|
|
* @return 需要刷新界面返回 true,否则返回 false。
|
|
*/
|
|
bool TickCreditAnimation()
|
|
{
|
|
if (creditAnimationTicks > 0)
|
|
{
|
|
creditAnimationTicks--;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief 添加一段棋盘坐标系中的浮动文字效果。
|
|
* @param boardX 棋盘内部横坐标,使用 100 为一格的坐标系。
|
|
* @param boardY 棋盘内部纵坐标,使用 100 为一格的坐标系。
|
|
* @param text 浮动文字内容。
|
|
* @param color 文字颜色。
|
|
*/
|
|
static void AddFloatingText(int boardX, int boardY, const TCHAR* text, COLORREF color)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (floatingTextEffects[i].ticks <= 0)
|
|
{
|
|
floatingTextEffects[i].ticks = 22;
|
|
floatingTextEffects[i].totalTicks = 22;
|
|
floatingTextEffects[i].boardX = boardX;
|
|
floatingTextEffects[i].boardY = boardY;
|
|
floatingTextEffects[i].color = color;
|
|
lstrcpyn(floatingTextEffects[i].text, text, sizeof(floatingTextEffects[i].text) / sizeof(TCHAR));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 添加一个棋盘坐标系中的粒子效果。
|
|
* @param boardX 粒子起始横坐标。
|
|
* @param boardY 粒子起始纵坐标。
|
|
* @param velocityX 横向速度。
|
|
* @param velocityY 纵向速度。
|
|
* @param size 粒子尺寸。
|
|
* @param color 粒子颜色。
|
|
*/
|
|
static void AddParticle(int boardX, int boardY, int velocityX, int velocityY, int size, COLORREF color)
|
|
{
|
|
for (int i = 0; i < 96; i++)
|
|
{
|
|
if (particleEffects[i].ticks <= 0)
|
|
{
|
|
particleEffects[i].ticks = 12 + rand() % 7;
|
|
particleEffects[i].totalTicks = particleEffects[i].ticks;
|
|
particleEffects[i].boardX = boardX;
|
|
particleEffects[i].boardY = boardY;
|
|
particleEffects[i].velocityX = velocityX;
|
|
particleEffects[i].velocityY = velocityY;
|
|
particleEffects[i].size = size;
|
|
particleEffects[i].color = color;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 在指定棋盘坐标周围生成一组爆裂粒子。
|
|
* @param boardX 爆裂中心横坐标。
|
|
* @param boardY 爆裂中心纵坐标。
|
|
* @param baseColor 主粒子颜色。
|
|
* @param strongBurst 是否使用更强的粒子数量和速度。
|
|
*/
|
|
static void AddBurstParticles(int boardX, int boardY, COLORREF baseColor, bool strongBurst)
|
|
{
|
|
int burstCount = strongBurst ? 4 : 2;
|
|
for (int i = 0; i < burstCount; i++)
|
|
{
|
|
int angleSeed = rand() % 8;
|
|
int speed = strongBurst ? (9 + rand() % 9) : (6 + rand() % 7);
|
|
int velocityX = 0;
|
|
int velocityY = 0;
|
|
|
|
switch (angleSeed)
|
|
{
|
|
case 0:
|
|
velocityX = speed;
|
|
velocityY = -rand() % 4;
|
|
break;
|
|
case 1:
|
|
velocityX = -speed;
|
|
velocityY = -rand() % 4;
|
|
break;
|
|
case 2:
|
|
velocityX = (rand() % 5) - 2;
|
|
velocityY = -speed;
|
|
break;
|
|
case 3:
|
|
velocityX = (rand() % 5) - 2;
|
|
velocityY = speed / 2;
|
|
break;
|
|
case 4:
|
|
velocityX = speed;
|
|
velocityY = -speed;
|
|
break;
|
|
case 5:
|
|
velocityX = -speed;
|
|
velocityY = -speed;
|
|
break;
|
|
case 6:
|
|
velocityX = speed;
|
|
velocityY = speed / 3;
|
|
break;
|
|
default:
|
|
velocityX = -speed;
|
|
velocityY = speed / 3;
|
|
break;
|
|
}
|
|
|
|
velocityX += (rand() % 7) - 3;
|
|
velocityY += (rand() % 7) - 3;
|
|
|
|
COLORREF color = (i % 3 == 0) ? RGB(255, 248, 220) : baseColor;
|
|
AddParticle(
|
|
boardX + (rand() % 31) - 15,
|
|
boardY + (rand() % 31) - 15,
|
|
velocityX,
|
|
velocityY,
|
|
strongBurst ? (4 + rand() % 5) : (3 + rand() % 4),
|
|
color);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 添加一个被清除格子的短时高亮效果。
|
|
* @param x 棋盘列号。
|
|
* @param y 棋盘行号。
|
|
* @param color 高亮颜色。
|
|
* @param strongFlash 是否使用更长的强高亮。
|
|
*/
|
|
static void AddCellFlash(int x, int y, COLORREF color, bool strongFlash)
|
|
{
|
|
for (int i = 0; i < 64; i++)
|
|
{
|
|
if (cellFlashEffects[i].ticks <= 0)
|
|
{
|
|
cellFlashEffects[i].ticks = strongFlash ? 18 : 14;
|
|
cellFlashEffects[i].totalTicks = cellFlashEffects[i].ticks;
|
|
cellFlashEffects[i].x = x;
|
|
cellFlashEffects[i].y = y;
|
|
cellFlashEffects[i].color = color;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 暂存消行动画,等待升级选择结束后再播放。
|
|
* @param rows 被消除的行号数组。
|
|
* @param rowCount 行号数量。
|
|
* @param linesCleared 实际消除行数。
|
|
*/
|
|
void QueueLineClearEffect(const int* rows, int rowCount, int linesCleared)
|
|
{
|
|
if (rows == nullptr || rowCount <= 0 || linesCleared <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (rowCount > 8)
|
|
{
|
|
rowCount = 8;
|
|
}
|
|
|
|
pendingLineClearEffectTicks = 1;
|
|
pendingLineClearEffectRowCount = rowCount;
|
|
pendingLineClearEffectLineCount = linesCleared;
|
|
for (int i = 0; i < rowCount; i++)
|
|
{
|
|
pendingLineClearEffectRows[i] = rows[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 播放之前暂存的消行动画。
|
|
*/
|
|
void PlayPendingLineClearEffect()
|
|
{
|
|
if (pendingLineClearEffectTicks <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pendingLineClearEffectTicks = 0;
|
|
TriggerLineClearEffect(
|
|
pendingLineClearEffectRows,
|
|
pendingLineClearEffectRowCount,
|
|
pendingLineClearEffectLineCount);
|
|
pendingLineClearEffectRowCount = 0;
|
|
pendingLineClearEffectLineCount = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 触发标准消行动画和浮动文字。
|
|
* @param rows 被消除的行号数组。
|
|
* @param rowCount 行号数量。
|
|
* @param linesCleared 实际消除行数。
|
|
*/
|
|
void TriggerLineClearEffect(const int* rows, int rowCount, int linesCleared)
|
|
{
|
|
if (rows == nullptr || rowCount <= 0 || linesCleared <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (rowCount > 8)
|
|
{
|
|
rowCount = 8;
|
|
}
|
|
|
|
clearEffectState.ticks = 16;
|
|
clearEffectState.totalTicks = 16;
|
|
clearEffectState.rowCount = rowCount;
|
|
|
|
int rowSum = 0;
|
|
for (int i = 0; i < rowCount; i++)
|
|
{
|
|
clearEffectState.rows[i] = rows[i];
|
|
rowSum += rows[i];
|
|
for (int x = 0; x < nGameWidth; x++)
|
|
{
|
|
COLORREF particleColor = BrickColor[(x + rows[i]) % 7];
|
|
int centerX = x * 100 + 50;
|
|
int centerY = rows[i] * 100 + 50;
|
|
AddBurstParticles(centerX, centerY, particleColor, linesCleared >= 4);
|
|
|
|
if (linesCleared >= 4)
|
|
{
|
|
AddParticle(
|
|
centerX,
|
|
centerY,
|
|
((x < nGameWidth / 2) ? -1 : 1) * (16 + rand() % 12),
|
|
-16 - rand() % 10,
|
|
4 + rand() % 3,
|
|
RGB(255, 238, 120));
|
|
}
|
|
}
|
|
}
|
|
|
|
TCHAR text[64];
|
|
if (linesCleared >= 4)
|
|
{
|
|
_stprintf_s(text, _T("TETRIS"));
|
|
}
|
|
else
|
|
{
|
|
_stprintf_s(text, _T("%d LINE%s"), linesCleared, linesCleared > 1 ? _T("S") : _T(""));
|
|
}
|
|
AddFloatingText(nGameWidth * 50, (rowSum * 100 / rowCount) - 20, text, linesCleared >= 4 ? RGB(255, 232, 120) : RGB(255, 250, 252));
|
|
}
|
|
|
|
/**
|
|
* @brief 为指定棋盘格集合触发清除粒子效果。
|
|
* @param cells 被清除格子数组。
|
|
* @param cellCount 格子数量。
|
|
* @param strongBurst 是否使用更强的粒子爆裂效果。
|
|
*/
|
|
void TriggerCellClearEffect(const Point* cells, int cellCount, bool strongBurst)
|
|
{
|
|
TriggerColoredCellClearEffect(cells, cellCount, RGB(255, 238, 120), strongBurst);
|
|
}
|
|
|
|
/**
|
|
* @brief 为指定棋盘格集合触发带颜色区分的清除高亮和粒子效果。
|
|
* @param cells 被清除格子数组。
|
|
* @param cellCount 格子数量。
|
|
* @param flashColor 高亮颜色。
|
|
* @param strongBurst 是否使用更强的粒子爆裂效果。
|
|
*/
|
|
void TriggerColoredCellClearEffect(const Point* cells, int cellCount, COLORREF flashColor, bool strongBurst)
|
|
{
|
|
if (cells == nullptr || cellCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < cellCount; i++)
|
|
{
|
|
if (cells[i].x < 0 || cells[i].x >= nGameWidth || cells[i].y < 0 || cells[i].y >= nGameHeight)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
COLORREF particleColor = BrickColor[(cells[i].x + cells[i].y) % 7];
|
|
AddCellFlash(cells[i].x, cells[i].y, flashColor, strongBurst);
|
|
AddBurstParticles(cells[i].x * 100 + 50, cells[i].y * 100 + 50, particleColor, strongBurst);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 为一个受重力下落的固定方块记录纵向残影和落点粒子。
|
|
* @param x 棋盘列号。
|
|
* @param fromY 起始行号。
|
|
* @param toY 目标行号。
|
|
* @param cellValue 方块格子值。
|
|
*/
|
|
void TriggerGravityFallEffect(int x, int fromY, int toY, int cellValue)
|
|
{
|
|
if (x < 0 || x >= nGameWidth || fromY < 0 || fromY >= nGameHeight ||
|
|
toY < 0 || toY >= nGameHeight || toY <= fromY || cellValue == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int effectIndex = -1;
|
|
for (int i = 0; i < 80; i++)
|
|
{
|
|
if (gravityFallEffects[i].ticks <= 0)
|
|
{
|
|
effectIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (effectIndex < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int totalTicks = 12 + (toY - fromY) * 2;
|
|
if (totalTicks > 26)
|
|
{
|
|
totalTicks = 26;
|
|
}
|
|
|
|
gravityFallEffects[effectIndex].ticks = totalTicks;
|
|
gravityFallEffects[effectIndex].totalTicks = totalTicks;
|
|
gravityFallEffects[effectIndex].x = x;
|
|
gravityFallEffects[effectIndex].fromY = fromY;
|
|
gravityFallEffects[effectIndex].toY = toY;
|
|
gravityFallEffects[effectIndex].cellValue = cellValue;
|
|
|
|
COLORREF particleColor = BrickColor[(cellValue - 1) % 7];
|
|
AddParticle(x * 100 + 50, toY * 100 + 18, -2 - rand() % 5, -12 - rand() % 7, 4, particleColor);
|
|
AddParticle(x * 100 + 50, toY * 100 + 18, 2 + rand() % 5, -12 - rand() % 7, 4, particleColor);
|
|
AddCellFlash(x, toY, RGB(210, 245, 255), true);
|
|
}
|
|
|
|
/**
|
|
* @brief 判断指定方块、旋转状态和位置是否可以合法放置。
|
|
* @param pieceType 方块类型编号。
|
|
* @param pieceState 方块旋转状态。
|
|
* @param position 待检测的左上角坐标。
|
|
* @return 可以放置返回 true,否则返回 false。
|
|
*/
|
|
bool IsPiecePlacementValid(int pieceType, int pieceState, Point position)
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
if (bricks[pieceType][pieceState][i][j] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int checkY = position.y + i;
|
|
int checkX = position.x + j;
|
|
|
|
if (checkX < 0 || checkX >= nGameWidth || checkY >= GetRoguePlayableHeight())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (checkY >= 0 && workRegion[checkY][checkX] != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief 尝试把旋转后的方块横向偏移指定格数后放置。
|
|
* @param nextState 旋转后的状态编号。
|
|
* @param offsetX 横向试探偏移。
|
|
* @return 偏移后可以放置返回 true,否则返回 false。
|
|
*/
|
|
bool TryRotateWithOffset(int nextState, int offsetX)
|
|
{
|
|
Point rotatedPoint = point;
|
|
rotatedPoint.x += offsetX;
|
|
return IsPiecePlacementValid(type, nextState, rotatedPoint);
|
|
}
|
|
|
|
/**
|
|
* @brief 视频复活后清理顶部空间并恢复一局游戏。
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @brief 按指定模式开始新游戏。
|
|
* @param mode 游戏模式,取值来自 GameMode。
|
|
*/
|
|
void StartGameWithMode(int mode)
|
|
{
|
|
rogueDemoMode = false;
|
|
currentMode = mode;
|
|
currentScreen = SCREEN_PLAYING;
|
|
upgradeListScrollOffset = 0;
|
|
Restart();
|
|
currentFallInterval = (currentMode == MODE_ROGUE) ? GetRogueFallInterval() : 500;
|
|
tScore = (currentMode == MODE_CLASSIC) ? classicStats.score : rogueStats.score;
|
|
}
|
|
|
|
/**
|
|
* @brief 返回主菜单并清理游戏中的临时界面状态。
|
|
*/
|
|
void ReturnToMainMenu()
|
|
{
|
|
rogueDemoMode = false;
|
|
currentScreen = SCREEN_MENU;
|
|
suspendFlag = false;
|
|
gameOverFlag = false;
|
|
ResetVisualEffects();
|
|
ResetPendingRogueVisualEvents();
|
|
helpScrollOffset = 0;
|
|
creditPageIndex = 0;
|
|
creditAnimationTicks = 0;
|
|
creditAnimationDirection = 0;
|
|
upgradeListScrollOffset = 0;
|
|
pendingLineClearEffectTicks = 0;
|
|
pendingLineClearEffectRowCount = 0;
|
|
pendingLineClearEffectLineCount = 0;
|
|
menuState.optionCount = 4;
|
|
ResetUpgradeUiState();
|
|
|
|
if (menuState.selectedIndex < 0 || menuState.selectedIndex >= menuState.optionCount)
|
|
{
|
|
menuState.selectedIndex = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 打开规则说明界面并重置说明页状态。
|
|
*/
|
|
void OpenRulesScreen()
|
|
{
|
|
rogueDemoMode = false;
|
|
currentScreen = SCREEN_RULES;
|
|
suspendFlag = false;
|
|
helpState.selectedIndex = 0;
|
|
helpState.optionCount = 4;
|
|
helpState.currentPage = 0;
|
|
helpScrollOffset = 0;
|
|
creditPageIndex = 0;
|
|
creditAnimationTicks = 0;
|
|
creditAnimationDirection = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 打开 Rogue 技能演示选择页并重置帮助页状态。
|
|
*/
|
|
void OpenSkillDemoScreen()
|
|
{
|
|
rogueDemoMode = false;
|
|
currentScreen = SCREEN_RULES;
|
|
suspendFlag = false;
|
|
helpState.selectedIndex = 0;
|
|
helpState.optionCount = 4;
|
|
helpState.currentPage = 5;
|
|
helpScrollOffset = 0;
|
|
creditPageIndex = 0;
|
|
creditAnimationTicks = 0;
|
|
creditAnimationDirection = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 打开致谢界面并重置致谢页切换状态。
|
|
*/
|
|
void OpenCreditScreen()
|
|
{
|
|
rogueDemoMode = false;
|
|
currentScreen = SCREEN_RULES;
|
|
suspendFlag = false;
|
|
helpState.selectedIndex = 0;
|
|
helpState.optionCount = 4;
|
|
helpState.currentPage = 4;
|
|
helpScrollOffset = 0;
|
|
creditPageIndex = 0;
|
|
creditAnimationTicks = 0;
|
|
creditAnimationDirection = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 切换致谢页图片,并启动左右滑动动画。
|
|
* @param direction 小于 0 向前切换,大于 0 向后切换。
|
|
*/
|
|
void ChangeCreditPage(int direction)
|
|
{
|
|
constexpr int creditPageCount = 5;
|
|
if (direction == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int oldPageIndex = creditPageIndex;
|
|
if (direction > 0)
|
|
{
|
|
creditPageIndex++;
|
|
creditAnimationDirection = 1;
|
|
}
|
|
else
|
|
{
|
|
creditPageIndex--;
|
|
creditAnimationDirection = -1;
|
|
}
|
|
|
|
if (creditPageIndex < 0)
|
|
{
|
|
creditPageIndex = creditPageCount - 1;
|
|
}
|
|
if (creditPageIndex >= creditPageCount)
|
|
{
|
|
creditPageIndex = 0;
|
|
}
|
|
|
|
if (creditPageIndex != oldPageIndex)
|
|
{
|
|
creditAnimationTicks = 60;
|
|
}
|
|
}
|