Compare commits
4 Commits
1c000c3c21
...
be95bd25e1
| Author | SHA1 | Date | |
|---|---|---|---|
| be95bd25e1 | |||
| b98d2c9d59 | |||
| a331162349 | |||
| 58ab400949 |
@@ -1,5 +1,6 @@
|
|||||||
# Build outputs
|
# Build outputs
|
||||||
/.vscode-build/
|
/.vscode-build/
|
||||||
|
/.worktrees/
|
||||||
/build/
|
/build/
|
||||||
/bin/
|
/bin/
|
||||||
/obj/
|
/obj/
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 572 KiB After Width: | Height: | Size: 722 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 722 KiB After Width: | Height: | Size: 84 KiB |
+135
-55
@@ -382,40 +382,42 @@ void DropDown()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 将当前活动方块固定到工作区,并生成下一个活动方块。
|
* @brief 收集当前方块将要固定到棋盘上的格子,并标记是否越过顶部。
|
||||||
*
|
* @param overflowTop 返回是否有方块格位于可视区域顶部之外。
|
||||||
* 遍历当前方块 4x4 形状矩阵,把其中所有非空单元写入工作区数组,
|
* @param fixedCells 返回普通落地格,用于后续特殊效果定位。
|
||||||
* 表示该方块已经落地并转为固定状态。
|
* @param fixedCellCount 返回普通落地格数量。
|
||||||
* 如果固定时仍有任意非空单元位于可视区域顶部之外,则判定游戏结束。
|
* @param explosiveCells 返回爆破方块落地格。
|
||||||
* 此时当前方块在可视区域内的部分仍会保留在工作区中。
|
* @param explosiveCellCount 返回爆破方块落地格数量。
|
||||||
* 若未超出顶部,再将“下一方块”切换为新的当前方块,重置旋转状态,
|
|
||||||
* 并把新方块生成到工作区上方的初始位置,同时刷新预测落点。
|
|
||||||
*/
|
*/
|
||||||
void Fixing()
|
static void CollectAndWriteFixedCells(
|
||||||
|
bool& overflowTop,
|
||||||
|
Point fixedCells[],
|
||||||
|
int& fixedCellCount,
|
||||||
|
Point explosiveCells[],
|
||||||
|
int& explosiveCellCount)
|
||||||
{
|
{
|
||||||
bool overflowTop = false;
|
overflowTop = false;
|
||||||
Point fixedCells[4] = {};
|
fixedCellCount = 0;
|
||||||
int fixedCellCount = 0;
|
explosiveCellCount = 0;
|
||||||
Point explosiveCells[4] = {};
|
|
||||||
int explosiveCellCount = 0;
|
|
||||||
pendingChainBombFollowup = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < 4; j++)
|
for (int j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
if (bricks[type][state][i][j] != 0)
|
if (bricks[type][state][i][j] == 0)
|
||||||
{
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int fixY = point.y + i;
|
int fixY = point.y + i;
|
||||||
int fixX = point.x + j;
|
int fixX = point.x + j;
|
||||||
|
|
||||||
// 只要当前方块任意非空单元仍超出顶部,就标记为结束
|
// 顶部溢出只记录状态,真正的复活或结束逻辑在后续统一处理。
|
||||||
if (fixY < 0)
|
if (fixY < 0)
|
||||||
{
|
{
|
||||||
overflowTop = true;
|
overflowTop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将当前方块在可视区域内的部分写入工作区
|
|
||||||
if (fixY >= 0 && fixY < GetRoguePlayableHeight() && fixX >= 0 && fixX < nGameWidth)
|
if (fixY >= 0 && fixY < GetRoguePlayableHeight() && fixX >= 0 && fixX < nGameWidth)
|
||||||
{
|
{
|
||||||
workRegion[fixY][fixX] = currentPieceIsRainbow ? 8 : bricks[type][state][i][j];
|
workRegion[fixY][fixX] = currentPieceIsRainbow ? 8 : bricks[type][state][i][j];
|
||||||
@@ -434,12 +436,20 @@ void Fixing()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理方块固定时的顶部溢出、终末清场和最后一搏。
|
||||||
|
* @param overflowTop 是否出现顶部溢出。
|
||||||
|
* @return 溢出已被处理且游戏可以继续时返回 true;需要结束游戏时返回 false。
|
||||||
|
*/
|
||||||
|
static bool ResolveFixingOverflow(bool overflowTop)
|
||||||
|
{
|
||||||
|
if (!overflowTop)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyRainbowLandingEffect(overflowTop, fixedCells, fixedCellCount);
|
|
||||||
|
|
||||||
if (overflowTop)
|
|
||||||
{
|
|
||||||
if (currentMode == MODE_ROGUE && rogueStats.terminalClearLevel > 0 && rogueStats.lastChanceCount > 0 && rogueStats.screenBombCount > 0)
|
if (currentMode == MODE_ROGUE && rogueStats.terminalClearLevel > 0 && rogueStats.lastChanceCount > 0 && rogueStats.screenBombCount > 0)
|
||||||
{
|
{
|
||||||
rogueStats.lastChanceCount--;
|
rogueStats.lastChanceCount--;
|
||||||
@@ -455,8 +465,10 @@ void Fixing()
|
|||||||
_T("终末清场启动,清除 %d 格,并进入 10 秒狂热。"),
|
_T("终末清场启动,清除 %d 格,并进入 10 秒狂热。"),
|
||||||
clearedByTerminal);
|
clearedByTerminal);
|
||||||
SetFeedbackMessage(_T("终末清场"), terminalDetail, 14);
|
SetFeedbackMessage(_T("终末清场"), terminalDetail, 14);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (currentMode == MODE_ROGUE && rogueStats.lastChanceCount > 0)
|
|
||||||
|
if (currentMode == MODE_ROGUE && rogueStats.lastChanceCount > 0)
|
||||||
{
|
{
|
||||||
rogueStats.lastChanceCount--;
|
rogueStats.lastChanceCount--;
|
||||||
|
|
||||||
@@ -469,13 +481,56 @@ void Fixing()
|
|||||||
_T("最后一搏"),
|
_T("最后一搏"),
|
||||||
_T("底部 3 行被清除,战局得以延续。"),
|
_T("底部 3 行被清除,战局得以延续。"),
|
||||||
14);
|
14);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
gameOverFlag = true;
|
gameOverFlag = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 生成下一枚活动方块,并刷新 Hold、特殊方块和预测落点状态。
|
||||||
|
*/
|
||||||
|
static void SpawnNextFallingPiece()
|
||||||
|
{
|
||||||
|
// 消耗预览队列后重置本回合状态,确保 Hold 和特殊标记只影响新方块。
|
||||||
|
type = ConsumeNextType();
|
||||||
|
nType = nextTypes[0];
|
||||||
|
state = 0;
|
||||||
|
holdUsedThisTurn = false;
|
||||||
|
RollCurrentPieceSpecialFlags(true);
|
||||||
|
point = GetSpawnPoint(type);
|
||||||
|
target = point;
|
||||||
|
ComputeTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将当前活动方块固定到工作区,并生成下一个活动方块。
|
||||||
|
*
|
||||||
|
* 遍历当前方块 4x4 形状矩阵,把其中所有非空单元写入工作区数组,
|
||||||
|
* 表示该方块已经落地并转为固定状态。
|
||||||
|
* 如果固定时仍有任意非空单元位于可视区域顶部之外,则判定游戏结束。
|
||||||
|
* 此时当前方块在可视区域内的部分仍会保留在工作区中。
|
||||||
|
* 若未超出顶部,再将“下一方块”切换为新的当前方块,重置旋转状态,
|
||||||
|
* 并把新方块生成到工作区上方的初始位置,同时刷新预测落点。
|
||||||
|
*/
|
||||||
|
void Fixing()
|
||||||
|
{
|
||||||
|
bool overflowTop = false;
|
||||||
|
Point fixedCells[4] = {};
|
||||||
|
int fixedCellCount = 0;
|
||||||
|
Point explosiveCells[4] = {};
|
||||||
|
int explosiveCellCount = 0;
|
||||||
|
pendingChainBombFollowup = false;
|
||||||
|
|
||||||
|
CollectAndWriteFixedCells(overflowTop, fixedCells, fixedCellCount, explosiveCells, explosiveCellCount);
|
||||||
|
|
||||||
|
ApplyRainbowLandingEffect(overflowTop, fixedCells, fixedCellCount);
|
||||||
|
|
||||||
|
if (!ResolveFixingOverflow(overflowTop))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ApplySpecialLandingEffects(fixedCells, fixedCellCount, explosiveCells, explosiveCellCount);
|
ApplySpecialLandingEffects(fixedCells, fixedCellCount, explosiveCells, explosiveCellCount);
|
||||||
|
|
||||||
@@ -484,15 +539,7 @@ void Fixing()
|
|||||||
currentFallInterval = GetRogueFallInterval();
|
currentFallInterval = GetRogueFallInterval();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成下一个活动方块
|
SpawnNextFallingPiece();
|
||||||
type = ConsumeNextType();
|
|
||||||
nType = nextTypes[0];
|
|
||||||
state = 0;
|
|
||||||
holdUsedThisTurn = false;
|
|
||||||
RollCurrentPieceSpecialFlags(true);
|
|
||||||
point = GetSpawnPoint(type);
|
|
||||||
target = point;
|
|
||||||
ComputeTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -521,20 +568,15 @@ void DeleteOneLine(int number)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查并删除所有已满的行,同时更新当前得分。
|
* @brief 从底向上扫描满行并删除,记录本次消除的原始行号。
|
||||||
*
|
* @param clearedRows 返回最多 8 个被消除行号,用于播放消行动画。
|
||||||
* 该函数会从底部向上遍历工作区,判断每一行是否被完全填满。
|
* @param clearedRowCount 返回记录的行号数量。
|
||||||
* 如果某一行全部非 0,则调用 DeleteOneLine 删除该行,
|
* @return 本次标准消行数量。
|
||||||
* 并将该行上方的内容整体下移。为了避免连续满行被漏检,
|
|
||||||
* 删除后会继续检查当前行号。每成功消除 1 行,当前得分增加 100 分。
|
|
||||||
*
|
|
||||||
* @return 本次实际消除的行数。
|
|
||||||
*/
|
*/
|
||||||
int DeleteLines()
|
static int ScanAndDeleteFullLines(int clearedRows[], int& clearedRowCount)
|
||||||
{
|
{
|
||||||
int clearedLines = 0;
|
int clearedLines = 0;
|
||||||
int clearedRows[8] = {};
|
clearedRowCount = 0;
|
||||||
int clearedRowCount = 0;
|
|
||||||
|
|
||||||
int playableHeight = GetRoguePlayableHeight();
|
int playableHeight = GetRoguePlayableHeight();
|
||||||
for (int i = playableHeight - 1; i >= 0; i--)
|
for (int i = playableHeight - 1; i >= 0; i--)
|
||||||
@@ -564,8 +606,17 @@ int DeleteLines()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 消行数量先进入玩法结算,再根据是否正在升级决定动画立即播放还是暂存。
|
return clearedLines;
|
||||||
ApplyLineClearResult(clearedLines);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 根据当前界面状态立即播放或暂存消行动画。
|
||||||
|
* @param clearedRows 已消除行号数组。
|
||||||
|
* @param clearedRowCount 行号数量。
|
||||||
|
* @param clearedLines 本次消行数量。
|
||||||
|
*/
|
||||||
|
static void DispatchLineClearEffect(const int clearedRows[], int clearedRowCount, int clearedLines)
|
||||||
|
{
|
||||||
if (currentScreen == SCREEN_UPGRADE)
|
if (currentScreen == SCREEN_UPGRADE)
|
||||||
{
|
{
|
||||||
QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
QueueLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
@@ -574,10 +625,20 @@ int DeleteLines()
|
|||||||
{
|
{
|
||||||
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
TriggerLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 连环炸弹的追加爆破只在爆破方块导致后续消行时触发一次。
|
/**
|
||||||
if (pendingChainBombFollowup && clearedLines > 0)
|
* @brief 处理连环炸弹因消行触发的一次追加 3x3 爆破。
|
||||||
|
* @param clearedLines 本次标准消行数量。
|
||||||
|
*/
|
||||||
|
static void ResolveChainBombFollowup(int clearedLines)
|
||||||
|
{
|
||||||
|
if (!pendingChainBombFollowup || clearedLines <= 0)
|
||||||
{
|
{
|
||||||
|
pendingChainBombFollowup = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pendingChainBombFollowup = false;
|
pendingChainBombFollowup = false;
|
||||||
|
|
||||||
int followupCleared = 0;
|
int followupCleared = 0;
|
||||||
@@ -618,11 +679,30 @@ int DeleteLines()
|
|||||||
followupExp);
|
followupExp);
|
||||||
SetFeedbackMessage(_T("连环炸弹"), followupDetail, 12);
|
SetFeedbackMessage(_T("连环炸弹"), followupDetail, 12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
/**
|
||||||
pendingChainBombFollowup = false;
|
* @brief 检查并删除所有已满的行,同时更新当前得分。
|
||||||
}
|
*
|
||||||
|
* 该函数会从底部向上遍历工作区,判断每一行是否被完全填满。
|
||||||
|
* 如果某一行全部非 0,则调用 DeleteOneLine 删除该行,
|
||||||
|
* 并将该行上方的内容整体下移。为了避免连续满行被漏检,
|
||||||
|
* 删除后会继续检查当前行号。每成功消除 1 行,当前得分增加 100 分。
|
||||||
|
*
|
||||||
|
* @return 本次实际消除的行数。
|
||||||
|
*/
|
||||||
|
int DeleteLines()
|
||||||
|
{
|
||||||
|
int clearedRows[8] = {};
|
||||||
|
int clearedRowCount = 0;
|
||||||
|
int clearedLines = ScanAndDeleteFullLines(clearedRows, clearedRowCount);
|
||||||
|
|
||||||
|
// 消行数量先进入玩法结算,再根据是否正在升级决定动画立即播放还是暂存。
|
||||||
|
ApplyLineClearResult(clearedLines);
|
||||||
|
DispatchLineClearEffect(clearedRows, clearedRowCount, clearedLines);
|
||||||
|
|
||||||
|
// 连环炸弹的追加爆破只在爆破方块导致后续消行时触发一次。
|
||||||
|
ResolveChainBombFollowup(clearedLines);
|
||||||
|
|
||||||
return clearedLines;
|
return clearedLines;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -646,7 +646,7 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
{
|
{
|
||||||
_T("赏金纹章:所有得分收益提高 20%,可重复叠加,是最直接的分数成长。\r\n成长印记:所有 EXP 收益提高 25%,可重复叠加,用来更快进入后续构筑。\r\n缓坠羽翼:降低自然下落速度,最多叠加 4 次。\r\n连击律动:连续消行时追加得分和 EXP,断连后重新累计。\r\n先见之眼:额外显示 1 个后续方块;第三个预览由操控大师解锁。"),
|
_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("最后一搏:首次濒死时自动清理底部 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("卸压清场:获得时立刻清除最高的一条占用行,直接降低顶部压力。\r\n底线清道夫:通过消行充能自动清底;每级降低需求,最多 4 级。\r\n清屏炸弹:一次性解锁,之后通过消行充能;满 16 行获得 1 枚,按 X 清底 5 行。\r\n黑洞奇点:可重复补充次数,按 Z 吞噬当前场上数量最多的一种颜色方块。"),
|
||||||
_T("爆破核心:一次性解锁橙红边框方块,落地后以落点为中心清除 3x3 区域。\r\n棱镜激光:一次性解锁青色边框方块,按落地中心列清除整列固定方块。\r\n十字方块:一次性解锁绿色边框方块,按落地中心同时清除一行一列。\r\n彩虹方块:一次性解锁紫色边框方块,清中心行主色并把覆盖行染成场上主色。\r\n方块改造:提高指定方块出现概率,目前主要强化 I 块生成。"),
|
_T("爆破核心:一次性解锁橙红边框方块,落地后以落点为中心清除 3x3 区域。\r\n棱镜激光:一次性解锁青色边框方块,按落地中心列清除整列固定方块。\r\n十字方块:一次性解锁绿色边框方块,按落地中心同时清除一行一列。\r\n彩虹方块:一次性解锁紫色边框方块,清中心行主色并把覆盖行染成场上主色。\r\n方块改造:提高指定方块出现概率,目前主要强化 I 块生成。"),
|
||||||
_T("连锁火花:完成消行后追加随机破坏,适合配合稳定堆叠扩大收益。\r\n连环炸弹:强化爆破核心,将爆破范围从 3x3 扩大为 5x5。\r\n雷霆四消:三消或四消后追加雷击,额外清除局部方块。\r\n雷霆棱镜:三消或四消后追加激光清列,强化四消后的清场能力。"),
|
_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("狂热节拍:累计清除 12 行进入狂热,狂热期间收益更高。\r\n怒火连段:连击越高倍率越高,适合连续小消和稳定节奏。\r\n无尽狂热:狂热期间继续消行会延长狂热时间。\r\n高压悬赏:游戏速度更快,但高压下收益也更高。\r\n豪赌四消:四消收益更高,但非四消表现更不稳定。\r\n极限玩家:危险高度下获得更高收益,同时承担更高操作压力。\r\n赌徒契约:后续强化有概率翻倍或落空,每级提高概率,最多 4 级。"),
|
||||||
@@ -844,20 +844,22 @@ void TDrawScreen(HDC hdc, HWND hWnd)
|
|||||||
else if (helpState.currentPage == 4)
|
else if (helpState.currentPage == 4)
|
||||||
{
|
{
|
||||||
const int creditAnimationTotalTicks = 60;
|
const int creditAnimationTotalTicks = 60;
|
||||||
constexpr int creditPageCount = 4;
|
constexpr int creditPageCount = 5;
|
||||||
const TCHAR* creditNames[creditPageCount] =
|
const TCHAR* creditNames[creditPageCount] =
|
||||||
{
|
{
|
||||||
_T("qls"),
|
_T("qls"),
|
||||||
_T("wyk"),
|
_T("wyk"),
|
||||||
_T("juju"),
|
_T("swj"),
|
||||||
_T("qhy")
|
_T("qhy"),
|
||||||
|
_T("syc")
|
||||||
};
|
};
|
||||||
const TCHAR* creditTexts[creditPageCount] =
|
const TCHAR* creditTexts[creditPageCount] =
|
||||||
{
|
{
|
||||||
_T("\u611f\u8c22\u6fc0\u60c5\u6295\u8eab\u4e8e\u6d4b\u8bd5\u4e4b\u4e2d\u7684Lisa"),
|
_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\u70ed\u5ff1coding\u7684\u5c0f\u86cb\u7cd5"),
|
||||||
_T("\u611f\u8c22\u8bfe\u524d\u95f2\u91cc\u5077\u5fd9\u7684juju"),
|
_T("\u611f\u8c22\u8bfe\u524d\u95f2\u91cc\u5077\u5fd9\u7684juju"),
|
||||||
_T("\u611f\u8c22qhy\u7684\u5929\u624d\u6784\u60f3")
|
_T("\u611f\u8c22qhy\u7684\u5929\u624d\u6784\u60f3"),
|
||||||
|
_T("\u611f\u8c22syc\u63fd\u4e0b\u6240\u6709\u6742\u6d3b")
|
||||||
};
|
};
|
||||||
|
|
||||||
int currentCredit = creditPageIndex;
|
int currentCredit = creditPageIndex;
|
||||||
|
|||||||
+290
-163
@@ -417,6 +417,88 @@ static bool HandleMenuKey(HWND hWnd, WPARAM key)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在帮助首页选项之间循环移动。
|
||||||
|
* @param direction 负数向前,正数向后。
|
||||||
|
*/
|
||||||
|
static void MoveHelpHomeSelection(int direction)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex += direction;
|
||||||
|
if (helpState.selectedIndex < 0)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex = helpState.optionCount - 1;
|
||||||
|
}
|
||||||
|
if (helpState.selectedIndex >= helpState.optionCount)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在技能演示列表中移动选中项,并同步滚动偏移。
|
||||||
|
* @param direction 负数向上,正数向下。
|
||||||
|
*/
|
||||||
|
static void MoveSkillDemoSelection(int direction)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex += direction;
|
||||||
|
if (helpState.selectedIndex < 0)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex = GetRogueSkillDemoCount() - 1;
|
||||||
|
}
|
||||||
|
if (helpState.selectedIndex >= GetRogueSkillDemoCount())
|
||||||
|
{
|
||||||
|
helpState.selectedIndex = 0;
|
||||||
|
helpScrollOffset = 0;
|
||||||
|
}
|
||||||
|
else if (direction < 0 && helpState.selectedIndex * 68 < helpScrollOffset)
|
||||||
|
{
|
||||||
|
helpScrollOffset = helpState.selectedIndex * 68;
|
||||||
|
}
|
||||||
|
else if (direction > 0 && helpState.selectedIndex * 68 > helpScrollOffset + 360)
|
||||||
|
{
|
||||||
|
helpScrollOffset = helpState.selectedIndex * 68 - 360;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 打开帮助首页当前选中的子页面。
|
||||||
|
*/
|
||||||
|
static void ActivateHelpSelection()
|
||||||
|
{
|
||||||
|
if (helpState.selectedIndex == 3)
|
||||||
|
{
|
||||||
|
helpState.currentPage = 5;
|
||||||
|
helpState.selectedIndex = 0;
|
||||||
|
helpScrollOffset = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
helpState.currentPage = helpState.selectedIndex + 1;
|
||||||
|
helpScrollOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从帮助子页返回帮助首页或主菜单。
|
||||||
|
*/
|
||||||
|
static void LeaveRulesPage()
|
||||||
|
{
|
||||||
|
int previousPage = helpState.currentPage;
|
||||||
|
if (helpState.currentPage == 0)
|
||||||
|
{
|
||||||
|
ReturnToMainMenu();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
helpState.currentPage = 0;
|
||||||
|
if (previousPage == 4 || previousPage == 5)
|
||||||
|
{
|
||||||
|
helpState.selectedIndex = 3;
|
||||||
|
}
|
||||||
|
helpScrollOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 处理帮助和致谢页键盘导航。
|
* @brief 处理帮助和致谢页键盘导航。
|
||||||
* @param hWnd 当前窗口句柄。
|
* @param hWnd 当前窗口句柄。
|
||||||
@@ -438,11 +520,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
case 'A':
|
case 'A':
|
||||||
if (helpState.currentPage == 0)
|
if (helpState.currentPage == 0)
|
||||||
{
|
{
|
||||||
helpState.selectedIndex--;
|
MoveHelpHomeSelection(-1);
|
||||||
if (helpState.selectedIndex < 0)
|
|
||||||
{
|
|
||||||
helpState.selectedIndex = helpState.optionCount - 1;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
else if (helpState.currentPage == 4)
|
else if (helpState.currentPage == 4)
|
||||||
@@ -452,15 +530,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
}
|
}
|
||||||
else if (helpState.currentPage == 5)
|
else if (helpState.currentPage == 5)
|
||||||
{
|
{
|
||||||
helpState.selectedIndex--;
|
MoveSkillDemoSelection(-1);
|
||||||
if (helpState.selectedIndex < 0)
|
|
||||||
{
|
|
||||||
helpState.selectedIndex = GetRogueSkillDemoCount() - 1;
|
|
||||||
}
|
|
||||||
if (helpState.selectedIndex * 68 < helpScrollOffset)
|
|
||||||
{
|
|
||||||
helpScrollOffset = helpState.selectedIndex * 68;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -470,11 +540,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
case 'D':
|
case 'D':
|
||||||
if (helpState.currentPage == 0)
|
if (helpState.currentPage == 0)
|
||||||
{
|
{
|
||||||
helpState.selectedIndex++;
|
MoveHelpHomeSelection(1);
|
||||||
if (helpState.selectedIndex >= helpState.optionCount)
|
|
||||||
{
|
|
||||||
helpState.selectedIndex = 0;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
else if (helpState.currentPage == 4)
|
else if (helpState.currentPage == 4)
|
||||||
@@ -484,16 +550,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
}
|
}
|
||||||
else if (helpState.currentPage == 5)
|
else if (helpState.currentPage == 5)
|
||||||
{
|
{
|
||||||
helpState.selectedIndex++;
|
MoveSkillDemoSelection(1);
|
||||||
if (helpState.selectedIndex >= GetRogueSkillDemoCount())
|
|
||||||
{
|
|
||||||
helpState.selectedIndex = 0;
|
|
||||||
helpScrollOffset = 0;
|
|
||||||
}
|
|
||||||
else if (helpState.selectedIndex * 68 > helpScrollOffset + 360)
|
|
||||||
{
|
|
||||||
helpScrollOffset = helpState.selectedIndex * 68 - 360;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -501,17 +558,7 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
case VK_SPACE:
|
case VK_SPACE:
|
||||||
if (helpState.currentPage == 0)
|
if (helpState.currentPage == 0)
|
||||||
{
|
{
|
||||||
if (helpState.selectedIndex == 3)
|
ActivateHelpSelection();
|
||||||
{
|
|
||||||
helpState.currentPage = 5;
|
|
||||||
helpState.selectedIndex = 0;
|
|
||||||
helpScrollOffset = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
helpState.currentPage = helpState.selectedIndex + 1;
|
|
||||||
helpScrollOffset = 0;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
else if (helpState.currentPage == 5)
|
else if (helpState.currentPage == 5)
|
||||||
@@ -524,24 +571,9 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
case VK_ESCAPE:
|
case VK_ESCAPE:
|
||||||
case VK_BACK:
|
case VK_BACK:
|
||||||
case 'M':
|
case 'M':
|
||||||
{
|
LeaveRulesPage();
|
||||||
int previousPage = helpState.currentPage;
|
|
||||||
if (helpState.currentPage == 0)
|
|
||||||
{
|
|
||||||
ReturnToMainMenu();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
helpState.currentPage = 0;
|
|
||||||
if (previousPage == 4 || previousPage == 5)
|
|
||||||
{
|
|
||||||
helpState.selectedIndex = 3;
|
|
||||||
}
|
|
||||||
helpScrollOffset = 0;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -549,6 +581,92 @@ static bool HandleRulesKey(HWND hWnd, WPARAM key)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算升级卡片网格列数。
|
||||||
|
* @return 当前升级界面使用的列数,至少为 1。
|
||||||
|
*/
|
||||||
|
static int GetUpgradeColumnCount()
|
||||||
|
{
|
||||||
|
int upgradeColumnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3;
|
||||||
|
if (upgradeColumnCount < 1)
|
||||||
|
{
|
||||||
|
upgradeColumnCount = 1;
|
||||||
|
}
|
||||||
|
return upgradeColumnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在升级卡片网格中横向移动选中项。
|
||||||
|
* @param direction 负数向左,正数向右。
|
||||||
|
*/
|
||||||
|
static void MoveUpgradeSelectionHorizontal(int direction)
|
||||||
|
{
|
||||||
|
if (upgradeUiState.optionCount <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int upgradeColumnCount = GetUpgradeColumnCount();
|
||||||
|
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
|
||||||
|
int rowEnd = rowStart + upgradeColumnCount - 1;
|
||||||
|
if (rowEnd >= upgradeUiState.optionCount)
|
||||||
|
{
|
||||||
|
rowEnd = upgradeUiState.optionCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction < 0)
|
||||||
|
{
|
||||||
|
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex > rowStart) ? upgradeUiState.selectedIndex - 1 : rowEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex < rowEnd) ? upgradeUiState.selectedIndex + 1 : rowStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在升级卡片网格中纵向移动选中项。
|
||||||
|
* @param direction 负数向上,正数向下。
|
||||||
|
*/
|
||||||
|
static void MoveUpgradeSelectionVertical(int direction)
|
||||||
|
{
|
||||||
|
int upgradeColumnCount = GetUpgradeColumnCount();
|
||||||
|
if (direction < 0 && upgradeUiState.selectedIndex >= upgradeColumnCount)
|
||||||
|
{
|
||||||
|
upgradeUiState.selectedIndex -= upgradeColumnCount;
|
||||||
|
}
|
||||||
|
else if (direction > 0 && upgradeUiState.selectedIndex + upgradeColumnCount < upgradeUiState.optionCount)
|
||||||
|
{
|
||||||
|
upgradeUiState.selectedIndex += upgradeColumnCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 切换多选升级中的当前卡片标记状态。
|
||||||
|
*/
|
||||||
|
static void ToggleUpgradeMarkedSelection()
|
||||||
|
{
|
||||||
|
if (upgradeUiState.picksRemaining <= 1 || upgradeUiState.optionCount <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool currentlyMarked = upgradeUiState.marked[upgradeUiState.selectedIndex];
|
||||||
|
if (currentlyMarked)
|
||||||
|
{
|
||||||
|
upgradeUiState.marked[upgradeUiState.selectedIndex] = false;
|
||||||
|
if (upgradeUiState.markedCount > 0)
|
||||||
|
{
|
||||||
|
upgradeUiState.markedCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (upgradeUiState.markedCount < upgradeUiState.picksRemaining)
|
||||||
|
{
|
||||||
|
upgradeUiState.marked[upgradeUiState.selectedIndex] = true;
|
||||||
|
upgradeUiState.markedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 处理升级选择界面键盘导航。
|
* @brief 处理升级选择界面键盘导航。
|
||||||
* @param hWnd 当前窗口句柄。
|
* @param hWnd 当前窗口句柄。
|
||||||
@@ -562,64 +680,26 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int upgradeColumnCount = upgradeUiState.optionCount <= 3 ? upgradeUiState.optionCount : 3;
|
|
||||||
if (upgradeColumnCount < 1)
|
|
||||||
{
|
|
||||||
upgradeColumnCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case VK_LEFT:
|
case VK_LEFT:
|
||||||
case 'A':
|
case 'A':
|
||||||
if (upgradeUiState.optionCount > 1)
|
MoveUpgradeSelectionHorizontal(-1);
|
||||||
{
|
|
||||||
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
|
|
||||||
if (upgradeUiState.selectedIndex > rowStart)
|
|
||||||
{
|
|
||||||
upgradeUiState.selectedIndex--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int rowEnd = rowStart + upgradeColumnCount - 1;
|
|
||||||
if (rowEnd >= upgradeUiState.optionCount)
|
|
||||||
{
|
|
||||||
rowEnd = upgradeUiState.optionCount - 1;
|
|
||||||
}
|
|
||||||
upgradeUiState.selectedIndex = rowEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case VK_RIGHT:
|
case VK_RIGHT:
|
||||||
case 'D':
|
case 'D':
|
||||||
if (upgradeUiState.optionCount > 1)
|
MoveUpgradeSelectionHorizontal(1);
|
||||||
{
|
|
||||||
int rowStart = upgradeUiState.selectedIndex - (upgradeUiState.selectedIndex % upgradeColumnCount);
|
|
||||||
int rowEnd = rowStart + upgradeColumnCount - 1;
|
|
||||||
if (rowEnd >= upgradeUiState.optionCount)
|
|
||||||
{
|
|
||||||
rowEnd = upgradeUiState.optionCount - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
upgradeUiState.selectedIndex = (upgradeUiState.selectedIndex < rowEnd) ? upgradeUiState.selectedIndex + 1 : rowStart;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case VK_UP:
|
case VK_UP:
|
||||||
case 'W':
|
case 'W':
|
||||||
if (upgradeUiState.selectedIndex >= upgradeColumnCount)
|
MoveUpgradeSelectionVertical(-1);
|
||||||
{
|
|
||||||
upgradeUiState.selectedIndex -= upgradeColumnCount;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case VK_DOWN:
|
case VK_DOWN:
|
||||||
case 'S':
|
case 'S':
|
||||||
if (upgradeUiState.selectedIndex + upgradeColumnCount < upgradeUiState.optionCount)
|
MoveUpgradeSelectionVertical(1);
|
||||||
{
|
|
||||||
upgradeUiState.selectedIndex += upgradeColumnCount;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
break;
|
break;
|
||||||
case VK_RETURN:
|
case VK_RETURN:
|
||||||
@@ -630,20 +710,7 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
|
|||||||
case VK_SPACE:
|
case VK_SPACE:
|
||||||
if (upgradeUiState.picksRemaining > 1 && upgradeUiState.optionCount > 0)
|
if (upgradeUiState.picksRemaining > 1 && upgradeUiState.optionCount > 0)
|
||||||
{
|
{
|
||||||
bool currentlyMarked = upgradeUiState.marked[upgradeUiState.selectedIndex];
|
ToggleUpgradeMarkedSelection();
|
||||||
if (currentlyMarked)
|
|
||||||
{
|
|
||||||
upgradeUiState.marked[upgradeUiState.selectedIndex] = false;
|
|
||||||
if (upgradeUiState.markedCount > 0)
|
|
||||||
{
|
|
||||||
upgradeUiState.markedCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (upgradeUiState.markedCount < upgradeUiState.picksRemaining)
|
|
||||||
{
|
|
||||||
upgradeUiState.marked[upgradeUiState.selectedIndex] = true;
|
|
||||||
upgradeUiState.markedCount++;
|
|
||||||
}
|
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -665,40 +732,54 @@ static bool HandleUpgradeKey(HWND hWnd, WPARAM key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 处理游戏过程中的按键。
|
* @brief 处理 Rogue 技能演示模式的专用按键。
|
||||||
* @param hWnd 当前窗口句柄。
|
* @param hWnd 当前窗口句柄。
|
||||||
* @param key 按键虚拟键码。
|
* @param key 按键虚拟键码。
|
||||||
|
* @return 已处理返回 true。
|
||||||
*/
|
*/
|
||||||
static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
static bool HandleDemoPlayingKey(HWND hWnd, WPARAM key)
|
||||||
{
|
{
|
||||||
if (IsRogueSkillDemoMode())
|
if (!IsRogueSkillDemoMode())
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (key == 'N')
|
if (key == 'N')
|
||||||
{
|
{
|
||||||
AdvanceRogueSkillDemo();
|
AdvanceRogueSkillDemo();
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == 'R')
|
if (key == 'R')
|
||||||
{
|
{
|
||||||
RestartCurrentRogueSkillDemo();
|
RestartCurrentRogueSkillDemo();
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == VK_ESCAPE || key == VK_BACK || key == 'M')
|
if (key == VK_ESCAPE || key == VK_BACK || key == 'M')
|
||||||
{
|
{
|
||||||
OpenSkillDemoScreen();
|
OpenSkillDemoScreen();
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理正常战局控制键,如菜单、重开、暂停、目标提示和复活。
|
||||||
|
* @param hWnd 当前窗口句柄。
|
||||||
|
* @param key 按键虚拟键码。
|
||||||
|
* @return 已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool HandleBattleControlKey(HWND hWnd, WPARAM key)
|
||||||
|
{
|
||||||
if (!IsRogueSkillDemoMode() && key == 'M')
|
if (!IsRogueSkillDemoMode() && key == 'M')
|
||||||
{
|
{
|
||||||
ReturnToMainMenu();
|
ReturnToMainMenu();
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsRogueSkillDemoMode() && key == 'R')
|
if (!IsRogueSkillDemoMode() && key == 'R')
|
||||||
@@ -706,21 +787,21 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
|||||||
StartGameWithMode(currentMode);
|
StartGameWithMode(currentMode);
|
||||||
ResetGameTimer(hWnd);
|
ResetGameTimer(hWnd);
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsRogueSkillDemoMode() && key == 'P')
|
if (!IsRogueSkillDemoMode() && key == 'P')
|
||||||
{
|
{
|
||||||
suspendFlag = !suspendFlag;
|
suspendFlag = !suspendFlag;
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == 'G')
|
if (key == 'G')
|
||||||
{
|
{
|
||||||
targetFlag = !targetFlag;
|
targetFlag = !targetFlag;
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameOverFlag && reviveAvailable && key == 'V')
|
if (gameOverFlag && reviveAvailable && key == 'V')
|
||||||
@@ -735,23 +816,70 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
|||||||
SetFeedbackMessage(_T("视频播放失败"), _T("无法打开复活视频,复活机会未消耗。"), 14);
|
SetFeedbackMessage(_T("视频播放失败"), _T("无法打开复活视频,复活机会未消耗。"), 14);
|
||||||
}
|
}
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameOverFlag || suspendFlag)
|
return false;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理 Rogue 侧栏滚动和主动技能按键。
|
||||||
|
* @param hWnd 当前窗口句柄。
|
||||||
|
* @param key 按键虚拟键码。
|
||||||
|
* @return 已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool HandleRogueSkillKey(HWND hWnd, WPARAM key)
|
||||||
|
{
|
||||||
if (currentMode == MODE_ROGUE && (key == 'J' || key == 'K'))
|
if (currentMode == MODE_ROGUE && (key == 'J' || key == 'K'))
|
||||||
{
|
{
|
||||||
int direction = (key == 'J') ? 1 : -1;
|
int direction = (key == 'J') ? 1 : -1;
|
||||||
AdjustScrollOffset(upgradeListScrollOffset, direction * GetScrollStep(hWnd, 52));
|
AdjustScrollOffset(upgradeListScrollOffset, direction * GetScrollStep(hWnd, 52));
|
||||||
InvalidateRect(hWnd, nullptr, FALSE);
|
InvalidateRect(hWnd, nullptr, FALSE);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正常游玩按键先改变方块或触发技能,再统一刷新预测落点和界面。
|
switch (key)
|
||||||
|
{
|
||||||
|
case 'C':
|
||||||
|
case VK_SHIFT:
|
||||||
|
case VK_LSHIFT:
|
||||||
|
case VK_RSHIFT:
|
||||||
|
HoldCurrentPiece();
|
||||||
|
return true;
|
||||||
|
case 'Z':
|
||||||
|
UseBlackHole();
|
||||||
|
return true;
|
||||||
|
case 'X':
|
||||||
|
UseScreenBomb();
|
||||||
|
return true;
|
||||||
|
case 'V':
|
||||||
|
UseAirReshape();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 固定当前方块后执行消行和 Rogue 升级检查。
|
||||||
|
*/
|
||||||
|
static void FixPieceAndResolveLines()
|
||||||
|
{
|
||||||
|
Fixing();
|
||||||
|
if (!gameOverFlag)
|
||||||
|
{
|
||||||
|
DeleteLines();
|
||||||
|
CheckRogueLevelProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理移动、旋转、软降和硬降等方块操作键。
|
||||||
|
* @param key 按键虚拟键码。
|
||||||
|
* @return 已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool HandlePieceMovementKey(WPARAM key)
|
||||||
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case VK_LEFT:
|
case VK_LEFT:
|
||||||
@@ -760,14 +888,14 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
|||||||
{
|
{
|
||||||
MoveLeft();
|
MoveLeft();
|
||||||
}
|
}
|
||||||
break;
|
return true;
|
||||||
case VK_RIGHT:
|
case VK_RIGHT:
|
||||||
case 'D':
|
case 'D':
|
||||||
if (CanMoveRight())
|
if (CanMoveRight())
|
||||||
{
|
{
|
||||||
MoveRight();
|
MoveRight();
|
||||||
}
|
}
|
||||||
break;
|
return true;
|
||||||
case VK_DOWN:
|
case VK_DOWN:
|
||||||
case 'S':
|
case 'S':
|
||||||
if (CanMoveDown())
|
if (CanMoveDown())
|
||||||
@@ -776,44 +904,43 @@ static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Fixing();
|
FixPieceAndResolveLines();
|
||||||
if (!gameOverFlag)
|
|
||||||
{
|
|
||||||
DeleteLines();
|
|
||||||
CheckRogueLevelProgress();
|
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
break;
|
|
||||||
case VK_UP:
|
case VK_UP:
|
||||||
case 'W':
|
case 'W':
|
||||||
Rotate();
|
Rotate();
|
||||||
break;
|
return true;
|
||||||
case VK_SPACE:
|
case VK_SPACE:
|
||||||
DropDown();
|
DropDown();
|
||||||
Fixing();
|
FixPieceAndResolveLines();
|
||||||
if (!gameOverFlag)
|
return true;
|
||||||
{
|
|
||||||
DeleteLines();
|
|
||||||
CheckRogueLevelProgress();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
case VK_SHIFT:
|
|
||||||
case VK_LSHIFT:
|
|
||||||
case VK_RSHIFT:
|
|
||||||
HoldCurrentPiece();
|
|
||||||
break;
|
|
||||||
case 'Z':
|
|
||||||
UseBlackHole();
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
UseScreenBomb();
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
UseAirReshape();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 处理游戏过程中的按键。
|
||||||
|
* @param hWnd 当前窗口句柄。
|
||||||
|
* @param key 按键虚拟键码。
|
||||||
|
*/
|
||||||
|
static void HandlePlayingKey(HWND hWnd, WPARAM key)
|
||||||
|
{
|
||||||
|
if (HandleDemoPlayingKey(hWnd, key) || HandleBattleControlKey(hWnd, key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameOverFlag || suspendFlag)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常游玩按键先改变方块或触发技能,再统一刷新预测落点和界面。
|
||||||
|
if (!HandlePieceMovementKey(key))
|
||||||
|
{
|
||||||
|
HandleRogueSkillKey(hWnd, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gameOverFlag)
|
if (!gameOverFlag)
|
||||||
|
|||||||
@@ -726,7 +726,7 @@ void OpenCreditScreen()
|
|||||||
*/
|
*/
|
||||||
void ChangeCreditPage(int direction)
|
void ChangeCreditPage(int direction)
|
||||||
{
|
{
|
||||||
constexpr int creditPageCount = 4;
|
constexpr int creditPageCount = 5;
|
||||||
if (direction == 0)
|
if (direction == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ Bitmap* LoadBackgroundImage()
|
|||||||
*/
|
*/
|
||||||
Bitmap* LoadCreditImage(int index)
|
Bitmap* LoadCreditImage(int index)
|
||||||
{
|
{
|
||||||
constexpr int creditPageCount = 4;
|
constexpr int creditPageCount = 5;
|
||||||
static Bitmap* creditImages[creditPageCount] = {};
|
static Bitmap* creditImages[creditPageCount] = {};
|
||||||
static bool attempted[creditPageCount] = {};
|
static bool attempted[creditPageCount] = {};
|
||||||
|
|
||||||
@@ -121,7 +121,8 @@ Bitmap* LoadCreditImage(int index)
|
|||||||
L"assets\\images\\qls.jpg",
|
L"assets\\images\\qls.jpg",
|
||||||
L"assets\\images\\wyk.jpg",
|
L"assets\\images\\wyk.jpg",
|
||||||
L"assets\\images\\swj.jpg",
|
L"assets\\images\\swj.jpg",
|
||||||
L"assets\\images\\qhy.jpg"
|
L"assets\\images\\qhy.jpg",
|
||||||
|
L"assets\\images\\syc.jpg"
|
||||||
};
|
};
|
||||||
const std::wstring creditExtraCandidates[] =
|
const std::wstring creditExtraCandidates[] =
|
||||||
{
|
{
|
||||||
|
|||||||
+429
-321
@@ -78,7 +78,7 @@ static const UpgradeEntry kUpgradePool[] =
|
|||||||
{ UPGRADE_FEVER_MODE, 1, 92, false, _T("狂热节拍"), _T("进阶"), _T("累计消行 12 行后进入 12 秒狂热:得分与 EXP 翻倍。") },
|
{ UPGRADE_FEVER_MODE, 1, 92, false, _T("狂热节拍"), _T("进阶"), _T("累计消行 12 行后进入 12 秒狂热:得分与 EXP 翻倍。") },
|
||||||
{ UPGRADE_RAGE_STACK, 1, 84, false, _T("怒火连段"), _T("进阶"), _T("连续消行越多,得分倍率追加越高。") },
|
{ UPGRADE_RAGE_STACK, 1, 84, false, _T("怒火连段"), _T("进阶"), _T("连续消行越多,得分倍率追加越高。") },
|
||||||
{ UPGRADE_INFINITE_FEVER, 1, 110, false, _T("无尽狂热"), _T("进化"), _T("狂热期间消行可延长狂热时间;连击越高,倍率越凶。") },
|
{ UPGRADE_INFINITE_FEVER, 1, 110, false, _T("无尽狂热"), _T("进化"), _T("狂热期间消行可延长狂热时间;连击越高,倍率越凶。") },
|
||||||
{ UPGRADE_SCREEN_BOMB, -1, 90, true, _T("清屏炸弹"), _T("进阶"), _T("立刻获得 1 枚炸弹;可重复选择补充数量,之后累计消行 16 行再获得 1 枚。按 X 清底 5 行。") },
|
{ UPGRADE_SCREEN_BOMB, 1, 90, false, _T("清屏炸弹"), _T("进阶"), _T("解锁清屏炸弹;之后累计消行 16 行获得 1 枚。按 X 清底 5 行。") },
|
||||||
{ UPGRADE_TERMINAL_CLEAR, 1, 108, false, _T("终末清场"), _T("进化"), _T("最后一搏启动时,自动引爆 1 枚清屏炸弹,并进入 10 秒狂热。") },
|
{ UPGRADE_TERMINAL_CLEAR, 1, 108, false, _T("终末清场"), _T("进化"), _T("最后一搏启动时,自动引爆 1 枚清屏炸弹,并进入 10 秒狂热。") },
|
||||||
{ UPGRADE_DUAL_CHOICE, 1, 68, false, _T("双重抉择"), _T("进阶"), _T("每次升级可额外选择 1 个强化,但下一次升级所需 EXP +30%。") },
|
{ UPGRADE_DUAL_CHOICE, 1, 68, false, _T("双重抉择"), _T("进阶"), _T("每次升级可额外选择 1 个强化,但下一次升级所需 EXP +30%。") },
|
||||||
{ UPGRADE_DESTINY_WHEEL, 1, 104, false, _T("命运轮盘"), _T("进化"), _T("升级时出现 6 个选项,可选择 2 个;其中 1 个会携带诅咒。") },
|
{ UPGRADE_DESTINY_WHEEL, 1, 104, false, _T("命运轮盘"), _T("进化"), _T("升级时出现 6 个选项,可选择 2 个;其中 1 个会携带诅咒。") },
|
||||||
@@ -581,11 +581,6 @@ static int GetUpgradeDynamicWeight(const UpgradeEntry& entry)
|
|||||||
weight -= 20;
|
weight -= 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.id == UPGRADE_SCREEN_BOMB && rogueStats.screenBombCount > 0)
|
|
||||||
{
|
|
||||||
weight += 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.id == UPGRADE_BLACK_HOLE && rogueStats.blackHoleCharges > 0)
|
if (entry.id == UPGRADE_BLACK_HOLE && rogueStats.blackHoleCharges > 0)
|
||||||
{
|
{
|
||||||
weight += 20;
|
weight += 20;
|
||||||
@@ -1555,18 +1550,15 @@ static int TriggerUpgradeShockwave(int rowsToClear)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按权重和当前局势生成升级菜单中的候选强化。
|
* @brief 收集当前可进入升级池的强化下标和动态权重。
|
||||||
*
|
* @param selectableIndexes 返回候选在强化池中的下标。
|
||||||
* 该函数只负责生成 UI 选项,不应用强化效果。候选池会先过滤前置、
|
* @param selectableWeights 返回候选动态权重。
|
||||||
* 等级上限和互斥条件,再按动态权重不放回抽取,保证同一轮不会重复出现。
|
* @return 候选数量。
|
||||||
*/
|
*/
|
||||||
static void FillUpgradeOptions()
|
static int CollectSelectableUpgrades(int selectableIndexes[], int selectableWeights[])
|
||||||
{
|
{
|
||||||
int selectableIndexes[kUpgradePoolSize] = { 0 };
|
|
||||||
int selectableWeights[kUpgradePoolSize] = { 0 };
|
|
||||||
int selectableCount = 0;
|
int selectableCount = 0;
|
||||||
|
|
||||||
// 第一段:筛出当前真正可选的强化,并记录它们的动态权重。
|
|
||||||
for (int i = 0; i < kUpgradePoolSize; i++)
|
for (int i = 0; i < kUpgradePoolSize; i++)
|
||||||
{
|
{
|
||||||
if (IsUpgradeSelectable(kUpgradePool[i]))
|
if (IsUpgradeSelectable(kUpgradePool[i]))
|
||||||
@@ -1577,7 +1569,16 @@ static void FillUpgradeOptions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命运轮盘把候选扩到 6 个;双重抉择和命运轮盘都会允许本轮选 2 个。
|
return selectableCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化本轮升级界面的数量、选中项和多选状态。
|
||||||
|
* @param selectableCount 当前可选候选数量。
|
||||||
|
* @return 本轮实际展示的选项数量。
|
||||||
|
*/
|
||||||
|
static int PrepareUpgradeOptionState(int selectableCount)
|
||||||
|
{
|
||||||
int optionLimit = (rogueStats.destinyWheelLevel > 0) ? 6 : 3;
|
int optionLimit = (rogueStats.destinyWheelLevel > 0) ? 6 : 3;
|
||||||
int optionCount = selectableCount < optionLimit ? selectableCount : optionLimit;
|
int optionCount = selectableCount < optionLimit ? selectableCount : optionLimit;
|
||||||
upgradeUiState.optionCount = optionCount;
|
upgradeUiState.optionCount = optionCount;
|
||||||
@@ -1589,9 +1590,17 @@ static void FillUpgradeOptions()
|
|||||||
upgradeUiState.marked[i] = false;
|
upgradeUiState.marked[i] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第二段:按权重不放回抽取候选,抽中后用末尾元素覆盖当前槽位。
|
return optionCount;
|
||||||
for (int i = 0; i < optionCount; i++)
|
}
|
||||||
{
|
|
||||||
|
/**
|
||||||
|
* @brief 按权重从候选数组中抽取一个槽位。
|
||||||
|
* @param selectableWeights 候选权重数组。
|
||||||
|
* @param selectableCount 候选数量。
|
||||||
|
* @return 被抽中的候选槽位。
|
||||||
|
*/
|
||||||
|
static int PickUpgradeSlotByWeight(const int selectableWeights[], int selectableCount)
|
||||||
|
{
|
||||||
int totalWeight = 0;
|
int totalWeight = 0;
|
||||||
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
|
for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++)
|
||||||
{
|
{
|
||||||
@@ -1615,45 +1624,80 @@ static void FillUpgradeOptions()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pickedIndex = selectableIndexes[pickSlot];
|
return pickSlot;
|
||||||
const UpgradeEntry& pickedEntry = kUpgradePool[pickedIndex];
|
}
|
||||||
|
|
||||||
upgradeUiState.options[i].id = pickedEntry.id;
|
/**
|
||||||
upgradeUiState.options[i].currentLevel = GetUpgradeCurrentLevel(pickedEntry.id);
|
* @brief 将抽中的强化池条目写入升级 UI 选项。
|
||||||
upgradeUiState.options[i].targetPieceType = -1;
|
* @param optionIndex UI 选项下标。
|
||||||
upgradeUiState.options[i].rarity = GetUpgradeBaseRarity(pickedEntry.id);
|
* @param pickedEntry 被抽中的强化池条目。
|
||||||
upgradeUiState.options[i].cursed = false;
|
*/
|
||||||
upgradeUiState.options[i].name = pickedEntry.name;
|
static void FillUpgradeOptionFromEntry(int optionIndex, const UpgradeEntry& pickedEntry)
|
||||||
upgradeUiState.options[i].category = pickedEntry.category;
|
{
|
||||||
upgradeUiState.options[i].description = pickedEntry.description;
|
upgradeUiState.options[optionIndex].id = pickedEntry.id;
|
||||||
|
upgradeUiState.options[optionIndex].currentLevel = GetUpgradeCurrentLevel(pickedEntry.id);
|
||||||
|
upgradeUiState.options[optionIndex].targetPieceType = -1;
|
||||||
|
upgradeUiState.options[optionIndex].rarity = GetUpgradeBaseRarity(pickedEntry.id);
|
||||||
|
upgradeUiState.options[optionIndex].cursed = false;
|
||||||
|
upgradeUiState.options[optionIndex].name = pickedEntry.name;
|
||||||
|
upgradeUiState.options[optionIndex].category = pickedEntry.category;
|
||||||
|
upgradeUiState.options[optionIndex].description = pickedEntry.description;
|
||||||
|
|
||||||
// 方块改造目前固定展示 I 块概率提升,说明文字需要运行时拼接。
|
|
||||||
if (pickedEntry.id == UPGRADE_PIECE_TUNING)
|
if (pickedEntry.id == UPGRADE_PIECE_TUNING)
|
||||||
{
|
{
|
||||||
int targetPieceType = 0;
|
int targetPieceType = 0;
|
||||||
upgradeUiState.options[i].targetPieceType = targetPieceType;
|
upgradeUiState.options[optionIndex].targetPieceType = targetPieceType;
|
||||||
upgradeUiState.options[i].currentLevel = rogueStats.pieceTuningLevels[0];
|
upgradeUiState.options[optionIndex].currentLevel = rogueStats.pieceTuningLevels[0];
|
||||||
upgradeUiState.options[i].name = _T("方块改造");
|
upgradeUiState.options[optionIndex].name = _T("方块改造");
|
||||||
|
|
||||||
static TCHAR tuningDescriptions[6][64];
|
static TCHAR tuningDescriptions[6][64];
|
||||||
_stprintf_s(
|
_stprintf_s(
|
||||||
tuningDescriptions[i],
|
tuningDescriptions[optionIndex],
|
||||||
_T("%s 块的生成概率提高。"),
|
_T("%s 块的生成概率提高。"),
|
||||||
GetPieceShortName(0));
|
GetPieceShortName(0));
|
||||||
upgradeUiState.options[i].description = tuningDescriptions[i];
|
upgradeUiState.options[optionIndex].description = tuningDescriptions[optionIndex];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 命运轮盘生效时,为本轮候选随机附加一个诅咒标记。
|
||||||
|
* @param optionCount 本轮展示选项数量。
|
||||||
|
*/
|
||||||
|
static void MarkDestinyCursedOption(int optionCount)
|
||||||
|
{
|
||||||
|
if (rogueStats.destinyWheelLevel > 0 && optionCount > 0)
|
||||||
|
{
|
||||||
|
int cursedIndex = rand() % optionCount;
|
||||||
|
upgradeUiState.options[cursedIndex].cursed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按权重和当前局势生成升级菜单中的候选强化。
|
||||||
|
*
|
||||||
|
* 该函数只负责生成 UI 选项,不应用强化效果。候选池会先过滤前置、
|
||||||
|
* 等级上限和互斥条件,再按动态权重不放回抽取,保证同一轮不会重复出现。
|
||||||
|
*/
|
||||||
|
static void FillUpgradeOptions()
|
||||||
|
{
|
||||||
|
int selectableIndexes[kUpgradePoolSize] = { 0 };
|
||||||
|
int selectableWeights[kUpgradePoolSize] = { 0 };
|
||||||
|
int selectableCount = CollectSelectableUpgrades(selectableIndexes, selectableWeights);
|
||||||
|
int optionCount = PrepareUpgradeOptionState(selectableCount);
|
||||||
|
|
||||||
|
// 第二段:按权重不放回抽取候选,抽中后用末尾元素覆盖当前槽位。
|
||||||
|
for (int i = 0; i < optionCount; i++)
|
||||||
|
{
|
||||||
|
int pickSlot = PickUpgradeSlotByWeight(selectableWeights, selectableCount);
|
||||||
|
int pickedIndex = selectableIndexes[pickSlot];
|
||||||
|
FillUpgradeOptionFromEntry(i, kUpgradePool[pickedIndex]);
|
||||||
|
|
||||||
selectableIndexes[pickSlot] = selectableIndexes[selectableCount - 1];
|
selectableIndexes[pickSlot] = selectableIndexes[selectableCount - 1];
|
||||||
selectableWeights[pickSlot] = selectableWeights[selectableCount - 1];
|
selectableWeights[pickSlot] = selectableWeights[selectableCount - 1];
|
||||||
selectableCount--;
|
selectableCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命运轮盘在候选中随机附加一个诅咒,确认时才提高下一次升级需求。
|
MarkDestinyCursedOption(optionCount);
|
||||||
if (rogueStats.destinyWheelLevel > 0 && optionCount > 0)
|
|
||||||
{
|
|
||||||
int cursedIndex = rand() % optionCount;
|
|
||||||
upgradeUiState.options[cursedIndex].cursed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1708,6 +1752,240 @@ int GetRogueFallInterval()
|
|||||||
return baseInterval;
|
return baseInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用成长、保命和基础操作类强化。
|
||||||
|
* @param upgradeId 强化编号。
|
||||||
|
* @param applyCount 本次应用次数。
|
||||||
|
* @return 当前强化已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool ApplyGrowthOrOperationUpgrade(int upgradeId, int applyCount)
|
||||||
|
{
|
||||||
|
switch (upgradeId)
|
||||||
|
{
|
||||||
|
case UPGRADE_SCORE_MULTIPLIER:
|
||||||
|
rogueStats.scoreMultiplierPercent += 20 * applyCount;
|
||||||
|
rogueStats.scoreUpgradeLevel += applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_COMBO_BONUS:
|
||||||
|
rogueStats.comboBonusStacks += applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_EXP_MULTIPLIER:
|
||||||
|
rogueStats.expMultiplierPercent += 25 * applyCount;
|
||||||
|
rogueStats.expUpgradeLevel += applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_SLOW_FALL:
|
||||||
|
rogueStats.slowFallStacks += applyCount;
|
||||||
|
if (rogueStats.slowFallStacks > 4)
|
||||||
|
{
|
||||||
|
rogueStats.slowFallStacks = 4;
|
||||||
|
}
|
||||||
|
currentFallInterval = GetRogueFallInterval();
|
||||||
|
return true;
|
||||||
|
case UPGRADE_PREVIEW_PLUS_ONE:
|
||||||
|
if (rogueStats.previewCount < 2)
|
||||||
|
{
|
||||||
|
rogueStats.previewCount = 2;
|
||||||
|
}
|
||||||
|
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_LAST_CHANCE:
|
||||||
|
rogueStats.lastChanceCount += applyCount;
|
||||||
|
rogueStats.lastChanceUpgradeLevel += applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_HOLD_UNLOCK:
|
||||||
|
rogueStats.holdUnlocked = 1;
|
||||||
|
holdUsedThisTurn = false;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_PERFECT_ROTATE:
|
||||||
|
rogueStats.perfectRotateLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_TIME_DILATION:
|
||||||
|
rogueStats.timeDilationLevel = 1;
|
||||||
|
currentFallInterval = GetRogueFallInterval();
|
||||||
|
return true;
|
||||||
|
case UPGRADE_CONTROL_MASTER:
|
||||||
|
rogueStats.controlMasterLevel = 1;
|
||||||
|
if (rogueStats.previewCount < 3)
|
||||||
|
{
|
||||||
|
rogueStats.previewCount++;
|
||||||
|
}
|
||||||
|
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_DOUBLE_GROWTH:
|
||||||
|
rogueStats.doubleGrowthLevel = 1;
|
||||||
|
rogueStats.scoreMultiplierPercent += 15;
|
||||||
|
rogueStats.expMultiplierPercent += 15;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用特殊方块和落地派生类强化。
|
||||||
|
* @param upgradeId 强化编号。
|
||||||
|
* @param applyCount 本次应用次数。
|
||||||
|
* @return 当前强化已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool ApplySpecialPieceUpgrade(int upgradeId, int applyCount)
|
||||||
|
{
|
||||||
|
switch (upgradeId)
|
||||||
|
{
|
||||||
|
case UPGRADE_PRESSURE_RELIEF:
|
||||||
|
rogueStats.pressureReliefLevel += applyCount;
|
||||||
|
for (int i = 0; i < applyCount; i++)
|
||||||
|
{
|
||||||
|
int topOccupiedRow = GetTopOccupiedRow();
|
||||||
|
if (topOccupiedRow >= 0)
|
||||||
|
{
|
||||||
|
DeleteOneLine(topOccupiedRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case UPGRADE_SWEEPER:
|
||||||
|
rogueStats.sweeperLevel += applyCount;
|
||||||
|
if (rogueStats.sweeperLevel > 4)
|
||||||
|
{
|
||||||
|
rogueStats.sweeperLevel = 4;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case UPGRADE_EXPLOSIVE_PIECE:
|
||||||
|
rogueStats.explosiveLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_CHAIN_BLAST:
|
||||||
|
rogueStats.chainBlastLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_CHAIN_BOMB:
|
||||||
|
rogueStats.chainBombLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_LASER_PIECE:
|
||||||
|
rogueStats.laserLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_THUNDER_TETRIS:
|
||||||
|
rogueStats.thunderTetrisLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_THUNDER_LASER:
|
||||||
|
rogueStats.thunderLaserLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_CROSS_PIECE:
|
||||||
|
rogueStats.crossPieceLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_RAINBOW_PIECE:
|
||||||
|
rogueStats.rainbowPieceLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_STABLE_STRUCTURE:
|
||||||
|
rogueStats.stableStructureLevel += applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_PIECE_TUNING:
|
||||||
|
rogueStats.pieceTuningLevels[0] += applyCount;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用主动技能、爆发和资源次数类强化。
|
||||||
|
* @param upgradeId 强化编号。
|
||||||
|
* @param applyCount 本次应用次数。
|
||||||
|
* @return 当前强化已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool ApplyActiveSkillUpgrade(int upgradeId, int applyCount)
|
||||||
|
{
|
||||||
|
switch (upgradeId)
|
||||||
|
{
|
||||||
|
case UPGRADE_FEVER_MODE:
|
||||||
|
rogueStats.feverLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_RAGE_STACK:
|
||||||
|
rogueStats.rageStackLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_INFINITE_FEVER:
|
||||||
|
rogueStats.infiniteFeverLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_SCREEN_BOMB:
|
||||||
|
rogueStats.screenBombLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_TERMINAL_CLEAR:
|
||||||
|
rogueStats.terminalClearLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_BLOCK_STORM:
|
||||||
|
rogueStats.blockStormLevel = 1;
|
||||||
|
rogueStats.blockStormPiecesRemaining = 5;
|
||||||
|
nextTypes[0] = 0;
|
||||||
|
nextTypes[1] = 0;
|
||||||
|
nextTypes[2] = 0;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_BLACK_HOLE:
|
||||||
|
rogueStats.blackHoleLevel = 1;
|
||||||
|
rogueStats.blackHoleCharges += 2 * applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_AIR_RESHAPE:
|
||||||
|
rogueStats.reshapeLevel += applyCount;
|
||||||
|
rogueStats.reshapeCharges += 2 * applyCount;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_VOID_CORE:
|
||||||
|
rogueStats.voidCoreLevel = 1;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用风险、升级规则和进化类强化。
|
||||||
|
* @param upgradeId 强化编号。
|
||||||
|
* @param applyCount 本次应用次数。
|
||||||
|
* @return 当前强化已处理返回 true。
|
||||||
|
*/
|
||||||
|
static bool ApplyRiskOrEvolutionUpgrade(int upgradeId, int applyCount)
|
||||||
|
{
|
||||||
|
switch (upgradeId)
|
||||||
|
{
|
||||||
|
case UPGRADE_DUAL_CHOICE:
|
||||||
|
rogueStats.dualChoiceLevel = 1;
|
||||||
|
rogueStats.requiredExp = rogueStats.requiredExp * 130 / 100;
|
||||||
|
if (rogueStats.requiredExp < 10)
|
||||||
|
{
|
||||||
|
rogueStats.requiredExp = 10;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case UPGRADE_DESTINY_WHEEL:
|
||||||
|
rogueStats.destinyWheelLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_HIGH_PRESSURE:
|
||||||
|
rogueStats.highPressureLevel = 1;
|
||||||
|
rogueStats.scoreMultiplierPercent += 50;
|
||||||
|
rogueStats.expMultiplierPercent += 50;
|
||||||
|
currentFallInterval = GetRogueFallInterval();
|
||||||
|
return true;
|
||||||
|
case UPGRADE_TETRIS_GAMBLE:
|
||||||
|
rogueStats.tetrisGambleLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_EXTREME_PLAYER:
|
||||||
|
rogueStats.extremePlayerLevel = 1;
|
||||||
|
rogueStats.extremeDangerTicks = 30;
|
||||||
|
rogueStats.extremeDangerLevel = 0;
|
||||||
|
currentFallInterval = GetRogueFallInterval();
|
||||||
|
return true;
|
||||||
|
case UPGRADE_UPGRADE_SHOCKWAVE:
|
||||||
|
rogueStats.upgradeShockwaveLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_EVOLUTION_IMPACT:
|
||||||
|
rogueStats.evolutionImpactLevel = 1;
|
||||||
|
return true;
|
||||||
|
case UPGRADE_GAMBLER:
|
||||||
|
rogueStats.gamblerLevel += applyCount;
|
||||||
|
if (rogueStats.gamblerLevel > 4)
|
||||||
|
{
|
||||||
|
rogueStats.gamblerLevel = 4;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 根据强化编号把对应效果写入 Rogue 属性。
|
* @brief 根据强化编号把对应效果写入 Rogue 属性。
|
||||||
*
|
*
|
||||||
@@ -1727,191 +2005,10 @@ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础成长类强化直接叠加倍率或层数。
|
ApplyGrowthOrOperationUpgrade(upgradeId, applyCount) ||
|
||||||
switch (upgradeId)
|
ApplySpecialPieceUpgrade(upgradeId, applyCount) ||
|
||||||
{
|
ApplyActiveSkillUpgrade(upgradeId, applyCount) ||
|
||||||
case UPGRADE_SCORE_MULTIPLIER:
|
ApplyRiskOrEvolutionUpgrade(upgradeId, applyCount);
|
||||||
rogueStats.scoreMultiplierPercent += 20 * applyCount;
|
|
||||||
rogueStats.scoreUpgradeLevel += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_COMBO_BONUS:
|
|
||||||
rogueStats.comboBonusStacks += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_EXP_MULTIPLIER:
|
|
||||||
rogueStats.expMultiplierPercent += 25 * applyCount;
|
|
||||||
rogueStats.expUpgradeLevel += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_SLOW_FALL:
|
|
||||||
rogueStats.slowFallStacks += applyCount;
|
|
||||||
if (rogueStats.slowFallStacks > 4)
|
|
||||||
{
|
|
||||||
rogueStats.slowFallStacks = 4;
|
|
||||||
}
|
|
||||||
currentFallInterval = GetRogueFallInterval();
|
|
||||||
break;
|
|
||||||
case UPGRADE_PREVIEW_PLUS_ONE:
|
|
||||||
if (rogueStats.previewCount < 2)
|
|
||||||
{
|
|
||||||
rogueStats.previewCount = 2;
|
|
||||||
}
|
|
||||||
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_LAST_CHANCE:
|
|
||||||
rogueStats.lastChanceCount += applyCount;
|
|
||||||
rogueStats.lastChanceUpgradeLevel += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_HOLD_UNLOCK:
|
|
||||||
rogueStats.holdUnlocked = 1;
|
|
||||||
holdUsedThisTurn = false;
|
|
||||||
break;
|
|
||||||
case UPGRADE_PRESSURE_RELIEF:
|
|
||||||
{
|
|
||||||
// 卸压清场立即从最高占用行开始删除,直接改善当前局面。
|
|
||||||
rogueStats.pressureReliefLevel += applyCount;
|
|
||||||
for (int i = 0; i < applyCount; i++)
|
|
||||||
{
|
|
||||||
int topOccupiedRow = GetTopOccupiedRow();
|
|
||||||
if (topOccupiedRow >= 0)
|
|
||||||
{
|
|
||||||
DeleteOneLine(topOccupiedRow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPGRADE_SWEEPER:
|
|
||||||
rogueStats.sweeperLevel += applyCount;
|
|
||||||
if (rogueStats.sweeperLevel > 4)
|
|
||||||
{
|
|
||||||
rogueStats.sweeperLevel = 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case UPGRADE_EXPLOSIVE_PIECE:
|
|
||||||
rogueStats.explosiveLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_CHAIN_BLAST:
|
|
||||||
rogueStats.chainBlastLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_CHAIN_BOMB:
|
|
||||||
rogueStats.chainBombLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_LASER_PIECE:
|
|
||||||
rogueStats.laserLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_THUNDER_TETRIS:
|
|
||||||
rogueStats.thunderTetrisLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_THUNDER_LASER:
|
|
||||||
rogueStats.thunderLaserLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_FEVER_MODE:
|
|
||||||
rogueStats.feverLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_RAGE_STACK:
|
|
||||||
rogueStats.rageStackLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_INFINITE_FEVER:
|
|
||||||
rogueStats.infiniteFeverLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_SCREEN_BOMB:
|
|
||||||
rogueStats.screenBombLevel += applyCount;
|
|
||||||
rogueStats.screenBombCount += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_TERMINAL_CLEAR:
|
|
||||||
rogueStats.terminalClearLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_DUAL_CHOICE:
|
|
||||||
rogueStats.dualChoiceLevel = 1;
|
|
||||||
rogueStats.requiredExp = rogueStats.requiredExp * 130 / 100;
|
|
||||||
if (rogueStats.requiredExp < 10)
|
|
||||||
{
|
|
||||||
rogueStats.requiredExp = 10;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case UPGRADE_DESTINY_WHEEL:
|
|
||||||
rogueStats.destinyWheelLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_PERFECT_ROTATE:
|
|
||||||
rogueStats.perfectRotateLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_TIME_DILATION:
|
|
||||||
rogueStats.timeDilationLevel = 1;
|
|
||||||
currentFallInterval = GetRogueFallInterval();
|
|
||||||
break;
|
|
||||||
case UPGRADE_HIGH_PRESSURE:
|
|
||||||
rogueStats.highPressureLevel = 1;
|
|
||||||
rogueStats.scoreMultiplierPercent += 50;
|
|
||||||
rogueStats.expMultiplierPercent += 50;
|
|
||||||
currentFallInterval = GetRogueFallInterval();
|
|
||||||
break;
|
|
||||||
case UPGRADE_TETRIS_GAMBLE:
|
|
||||||
rogueStats.tetrisGambleLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_EXTREME_PLAYER:
|
|
||||||
rogueStats.extremePlayerLevel = 1;
|
|
||||||
rogueStats.extremeDangerTicks = 30;
|
|
||||||
rogueStats.extremeDangerLevel = 0;
|
|
||||||
currentFallInterval = GetRogueFallInterval();
|
|
||||||
break;
|
|
||||||
case UPGRADE_UPGRADE_SHOCKWAVE:
|
|
||||||
rogueStats.upgradeShockwaveLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_EVOLUTION_IMPACT:
|
|
||||||
rogueStats.evolutionImpactLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_CONTROL_MASTER:
|
|
||||||
rogueStats.controlMasterLevel = 1;
|
|
||||||
if (rogueStats.previewCount < 3)
|
|
||||||
{
|
|
||||||
rogueStats.previewCount++;
|
|
||||||
}
|
|
||||||
rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_BLOCK_STORM:
|
|
||||||
// 方块风暴会同时改写预览队列,确保玩家马上看到连续 I 块。
|
|
||||||
rogueStats.blockStormLevel = 1;
|
|
||||||
rogueStats.blockStormPiecesRemaining = 5;
|
|
||||||
nextTypes[0] = 0;
|
|
||||||
nextTypes[1] = 0;
|
|
||||||
nextTypes[2] = 0;
|
|
||||||
break;
|
|
||||||
case UPGRADE_CROSS_PIECE:
|
|
||||||
rogueStats.crossPieceLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_BLACK_HOLE:
|
|
||||||
rogueStats.blackHoleLevel = 1;
|
|
||||||
rogueStats.blackHoleCharges += 2 * applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_AIR_RESHAPE:
|
|
||||||
rogueStats.reshapeLevel += applyCount;
|
|
||||||
rogueStats.reshapeCharges += 2 * applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_RAINBOW_PIECE:
|
|
||||||
rogueStats.rainbowPieceLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_VOID_CORE:
|
|
||||||
rogueStats.voidCoreLevel = 1;
|
|
||||||
break;
|
|
||||||
case UPGRADE_STABLE_STRUCTURE:
|
|
||||||
rogueStats.stableStructureLevel += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_DOUBLE_GROWTH:
|
|
||||||
rogueStats.doubleGrowthLevel = 1;
|
|
||||||
rogueStats.scoreMultiplierPercent += 15;
|
|
||||||
rogueStats.expMultiplierPercent += 15;
|
|
||||||
break;
|
|
||||||
case UPGRADE_PIECE_TUNING:
|
|
||||||
rogueStats.pieceTuningLevels[0] += applyCount;
|
|
||||||
break;
|
|
||||||
case UPGRADE_GAMBLER:
|
|
||||||
rogueStats.gamblerLevel += applyCount;
|
|
||||||
if (rogueStats.gamblerLevel > 4)
|
|
||||||
{
|
|
||||||
rogueStats.gamblerLevel = 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1959,16 +2056,6 @@ void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain,
|
|||||||
rogueStats.exp += expGain;
|
rogueStats.exp += expGain;
|
||||||
tScore = rogueStats.score;
|
tScore = rogueStats.score;
|
||||||
|
|
||||||
if (rogueStats.screenBombLevel > 0)
|
|
||||||
{
|
|
||||||
rogueStats.screenBombCharge += clearedCells;
|
|
||||||
while (rogueStats.screenBombCharge >= kScreenBombLineThreshold)
|
|
||||||
{
|
|
||||||
rogueStats.screenBombCharge -= kScreenBombLineThreshold;
|
|
||||||
rogueStats.screenBombCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowLevelProgress && !rogueDemoMode)
|
if (allowLevelProgress && !rogueDemoMode)
|
||||||
{
|
{
|
||||||
int levelUps = ApplyLevelProgress(rogueStats);
|
int levelUps = ApplyLevelProgress(rogueStats);
|
||||||
@@ -2473,6 +2560,109 @@ void OpenUpgradeMenu()
|
|||||||
currentScreen = SCREEN_UPGRADE;
|
currentScreen = SCREEN_UPGRADE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结束一轮升级选择,如果还有待处理升级则刷新下一轮选项。
|
||||||
|
* @return 仍停留在升级界面返回 true;已回到游戏返回 false。
|
||||||
|
*/
|
||||||
|
static bool FinishUpgradeSelectionRound()
|
||||||
|
{
|
||||||
|
if (upgradeUiState.pendingCount > 0)
|
||||||
|
{
|
||||||
|
upgradeUiState.pendingCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upgradeUiState.pendingCount > 0)
|
||||||
|
{
|
||||||
|
FillUpgradeOptions();
|
||||||
|
currentScreen = SCREEN_UPGRADE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
upgradeUiState.optionCount = 0;
|
||||||
|
upgradeUiState.picksRemaining = 0;
|
||||||
|
upgradeUiState.markedCount = 0;
|
||||||
|
|
||||||
|
currentScreen = SCREEN_PLAYING;
|
||||||
|
ResolvePendingUpgradeShockwave();
|
||||||
|
PlayPendingLineClearEffect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 构造单选强化的反馈正文。
|
||||||
|
* @param selectedOption 被选择的强化选项。
|
||||||
|
* @param gamblerSuffix 赌徒契约附加说明。
|
||||||
|
* @param feedbackDetail 返回反馈正文。
|
||||||
|
* @param feedbackDetailCapacity 反馈正文缓冲区长度。
|
||||||
|
*/
|
||||||
|
static void BuildSingleUpgradeFeedback(const UpgradeOption& selectedOption, const TCHAR* gamblerSuffix, TCHAR feedbackDetail[], int feedbackDetailCapacity)
|
||||||
|
{
|
||||||
|
if (selectedOption.id == UPGRADE_PIECE_TUNING && selectedOption.targetPieceType >= 0)
|
||||||
|
{
|
||||||
|
_stprintf_s(
|
||||||
|
feedbackDetail,
|
||||||
|
feedbackDetailCapacity,
|
||||||
|
_T("%s 块的生成概率提高%s"),
|
||||||
|
GetPieceShortName(0),
|
||||||
|
gamblerSuffix);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stprintf_s(feedbackDetail, feedbackDetailCapacity, _T("%s%s"), selectedOption.description, gamblerSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedOption.cursed)
|
||||||
|
{
|
||||||
|
_stprintf_s(
|
||||||
|
feedbackDetail + lstrlen(feedbackDetail),
|
||||||
|
feedbackDetailCapacity - lstrlen(feedbackDetail),
|
||||||
|
_T(" 诅咒缠身:下一次升级所需 EXP 提高 25%。"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用多选升级中一个已标记的选项并拼接反馈。
|
||||||
|
* @param selectedOption 被应用的选项。
|
||||||
|
* @param appliedSelections 已应用选项数量。
|
||||||
|
* @param feedbackTitle 返回反馈标题。
|
||||||
|
* @param feedbackDetail 返回反馈正文。
|
||||||
|
*/
|
||||||
|
static void ApplyMarkedUpgradeOption(const UpgradeOption& selectedOption, int& appliedSelections, TCHAR feedbackTitle[], TCHAR feedbackDetail[])
|
||||||
|
{
|
||||||
|
TCHAR gamblerSuffix[64] = _T("");
|
||||||
|
int applyCount = RollGamblerApplyCount(gamblerSuffix, 64, true);
|
||||||
|
|
||||||
|
ApplyUpgradeById(selectedOption.id, selectedOption.targetPieceType, applyCount);
|
||||||
|
upgradeUiState.totalChosenCount++;
|
||||||
|
|
||||||
|
if (selectedOption.cursed)
|
||||||
|
{
|
||||||
|
ApplyDestinyCurse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appliedSelections == 0)
|
||||||
|
{
|
||||||
|
_stprintf_s(feedbackTitle, 64, _T("获得强化:%s"), selectedOption.name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stprintf_s(feedbackTitle, 64, _T("获得强化 x%d"), appliedSelections + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lstrlen(feedbackDetail) > 0)
|
||||||
|
{
|
||||||
|
_stprintf_s(feedbackDetail + lstrlen(feedbackDetail), 128 - lstrlen(feedbackDetail), _T(";"));
|
||||||
|
}
|
||||||
|
_stprintf_s(
|
||||||
|
feedbackDetail + lstrlen(feedbackDetail),
|
||||||
|
128 - lstrlen(feedbackDetail),
|
||||||
|
_T("%s%s%s"),
|
||||||
|
selectedOption.name,
|
||||||
|
gamblerSuffix,
|
||||||
|
selectedOption.cursed ? _T(" [诅咒]") : _T(""));
|
||||||
|
appliedSelections++;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 确认升级菜单中的选择并应用对应强化效果。
|
* @brief 确认升级菜单中的选择并应用对应强化效果。
|
||||||
*
|
*
|
||||||
@@ -2507,59 +2697,11 @@ void ConfirmUpgradeSelection()
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpgradeOption selectedOption = upgradeUiState.options[optionIndex];
|
UpgradeOption selectedOption = upgradeUiState.options[optionIndex];
|
||||||
TCHAR gamblerSuffix[64] = _T("");
|
ApplyMarkedUpgradeOption(selectedOption, appliedSelections, feedbackTitle, feedbackDetail);
|
||||||
int applyCount = RollGamblerApplyCount(gamblerSuffix, 64, true);
|
|
||||||
|
|
||||||
ApplyUpgradeById(selectedOption.id, selectedOption.targetPieceType, applyCount);
|
|
||||||
upgradeUiState.totalChosenCount++;
|
|
||||||
|
|
||||||
if (selectedOption.cursed)
|
|
||||||
{
|
|
||||||
ApplyDestinyCurse();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appliedSelections == 0)
|
|
||||||
{
|
|
||||||
_stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_stprintf_s(feedbackTitle, _T("获得强化 x%d"), appliedSelections + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lstrlen(feedbackDetail) > 0)
|
|
||||||
{
|
|
||||||
_stprintf_s(feedbackDetail + lstrlen(feedbackDetail), 128 - lstrlen(feedbackDetail), _T(";"));
|
|
||||||
}
|
|
||||||
_stprintf_s(
|
|
||||||
feedbackDetail + lstrlen(feedbackDetail),
|
|
||||||
128 - lstrlen(feedbackDetail),
|
|
||||||
_T("%s%s%s"),
|
|
||||||
selectedOption.name,
|
|
||||||
gamblerSuffix,
|
|
||||||
selectedOption.cursed ? _T(" [诅咒]") : _T(""));
|
|
||||||
appliedSelections++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
|
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
|
||||||
if (upgradeUiState.pendingCount > 0)
|
FinishUpgradeSelectionRound();
|
||||||
{
|
|
||||||
upgradeUiState.pendingCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upgradeUiState.pendingCount > 0)
|
|
||||||
{
|
|
||||||
FillUpgradeOptions();
|
|
||||||
currentScreen = SCREEN_UPGRADE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
upgradeUiState.optionCount = 0;
|
|
||||||
upgradeUiState.picksRemaining = 0;
|
|
||||||
upgradeUiState.markedCount = 0;
|
|
||||||
currentScreen = SCREEN_PLAYING;
|
|
||||||
ResolvePendingUpgradeShockwave();
|
|
||||||
PlayPendingLineClearEffect();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2573,26 +2715,11 @@ void ConfirmUpgradeSelection()
|
|||||||
TCHAR feedbackTitle[64];
|
TCHAR feedbackTitle[64];
|
||||||
TCHAR feedbackDetail[128];
|
TCHAR feedbackDetail[128];
|
||||||
_stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name);
|
_stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name);
|
||||||
if (selectedOption.id == UPGRADE_PIECE_TUNING && selectedOption.targetPieceType >= 0)
|
BuildSingleUpgradeFeedback(selectedOption, gamblerSuffix, feedbackDetail, 128);
|
||||||
{
|
|
||||||
_stprintf_s(
|
|
||||||
feedbackDetail,
|
|
||||||
_T("%s 块的生成概率提高%s"),
|
|
||||||
GetPieceShortName(0),
|
|
||||||
gamblerSuffix);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_stprintf_s(feedbackDetail, _T("%s%s"), selectedOption.description, gamblerSuffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedOption.cursed)
|
if (selectedOption.cursed)
|
||||||
{
|
{
|
||||||
ApplyDestinyCurse();
|
ApplyDestinyCurse();
|
||||||
_stprintf_s(
|
|
||||||
feedbackDetail + lstrlen(feedbackDetail),
|
|
||||||
128 - lstrlen(feedbackDetail),
|
|
||||||
_T(" 诅咒缠身:下一次升级所需 EXP 提高 25%。"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
|
SetFeedbackMessage(feedbackTitle, feedbackDetail, 12);
|
||||||
@@ -2622,26 +2749,7 @@ void ConfirmUpgradeSelection()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连续升级时重新生成下一轮强化;全部完成后回到游戏并播放延迟效果。
|
FinishUpgradeSelectionRound();
|
||||||
if (upgradeUiState.pendingCount > 0)
|
|
||||||
{
|
|
||||||
upgradeUiState.pendingCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upgradeUiState.pendingCount > 0)
|
|
||||||
{
|
|
||||||
FillUpgradeOptions();
|
|
||||||
currentScreen = SCREEN_UPGRADE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
upgradeUiState.optionCount = 0;
|
|
||||||
upgradeUiState.picksRemaining = 0;
|
|
||||||
upgradeUiState.markedCount = 0;
|
|
||||||
|
|
||||||
currentScreen = SCREEN_PLAYING;
|
|
||||||
ResolvePendingUpgradeShockwave();
|
|
||||||
PlayPendingLineClearEffect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user