#include "stdafx.h" #include "TetrisLogicInternal.h" /** * @brief Rogue 创新玩法独立实现文件。 * * 本文件集中放置从基础俄罗斯方块版本扩展出的创新设计,包括强化池、 * 等级成长、特殊方块、主动技能、难度收缩、连锁奖励和升级选择流程。 * 代码保持过程式函数组织,不使用 class、继承或多态。 */ enum UpgradeId { UPGRADE_SCORE_MULTIPLIER = 0, UPGRADE_EXP_MULTIPLIER = 1, UPGRADE_SLOW_FALL = 2, UPGRADE_COMBO_BONUS = 3, UPGRADE_PREVIEW_PLUS_ONE = 4, UPGRADE_LAST_CHANCE = 5, UPGRADE_HOLD_UNLOCK = 6, UPGRADE_PRESSURE_RELIEF = 7, UPGRADE_SWEEPER = 8, UPGRADE_EXPLOSIVE_PIECE = 9, UPGRADE_STABLE_STRUCTURE = 10, UPGRADE_DOUBLE_GROWTH = 11, UPGRADE_PIECE_TUNING = 12, UPGRADE_GAMBLER = 13, UPGRADE_CHAIN_BLAST = 14, UPGRADE_CHAIN_BOMB = 15, UPGRADE_LASER_PIECE = 16, UPGRADE_THUNDER_TETRIS = 17, UPGRADE_THUNDER_LASER = 18, UPGRADE_FEVER_MODE = 19, UPGRADE_RAGE_STACK = 20, UPGRADE_INFINITE_FEVER = 21, UPGRADE_SCREEN_BOMB = 22, UPGRADE_TERMINAL_CLEAR = 23, UPGRADE_DUAL_CHOICE = 24, UPGRADE_DESTINY_WHEEL = 25, UPGRADE_PERFECT_ROTATE = 26, UPGRADE_TIME_DILATION = 27, UPGRADE_HIGH_PRESSURE = 28, UPGRADE_TETRIS_GAMBLE = 29, UPGRADE_EXTREME_PLAYER = 30, UPGRADE_UPGRADE_SHOCKWAVE = 31, UPGRADE_EVOLUTION_IMPACT = 32, UPGRADE_CONTROL_MASTER = 33, UPGRADE_BLOCK_STORM = 34, UPGRADE_CROSS_PIECE = 35, UPGRADE_BLACK_HOLE = 36, UPGRADE_AIR_RESHAPE = 37, UPGRADE_RAINBOW_PIECE = 38, UPGRADE_VOID_CORE = 39 }; static const UpgradeEntry kUpgradePool[] = { { UPGRADE_SCORE_MULTIPLIER, -1, 100, true, _T("赏金纹章"), _T("得分"), _T("所有得分提高 20%,每次选择都会继续叠加。") }, { UPGRADE_COMBO_BONUS, -1, 95, true, _T("连击律动"), _T("节奏"), _T("连续消行会追加连击奖励,节奏越稳收益越高。") }, { UPGRADE_SLOW_FALL, -1, 90, true, _T("缓坠羽翼"), _T("操作"), _T("自然下落延缓 80ms,为摆放和补洞争取更多时间。") }, { UPGRADE_PREVIEW_PLUS_ONE, 3, 85, false, _T("先见之眼"), _T("视野"), _T("下一个方块预览 +1,最多可同时看见 3 个。") }, { UPGRADE_EXP_MULTIPLIER, -1, 100, true, _T("成长印记"), _T("成长"), _T("消行获得的 EXP 提高 25%,更快迎来下一次强化。") }, { UPGRADE_LAST_CHANCE, 1, 72, false, _T("最后一搏"), _T("保命"), _T("首次濒临失败时清除底部 3 行,让本局继续战斗。") }, { UPGRADE_HOLD_UNLOCK, 1, 78, false, _T("备用仓"), _T("特殊"), _T("解锁 Hold。按 C 或 Shift 暂存下落方块,每个方块落地前限用一次。") }, { UPGRADE_PRESSURE_RELIEF, -1, 82, true, _T("卸压清场"), _T("特殊"), _T("立刻清除最高占用行,为棋盘腾出一段喘息空间。") }, { UPGRADE_SWEEPER, -1, 74, true, _T("底线清道夫"), _T("特殊"), _T("消行会为清道夫充能,充满后自动清除底部 1 行。") }, { UPGRADE_EXPLOSIVE_PIECE, -1, 86, true, _T("爆破核心"), _T("特殊"), _T("大幅提高爆破方块出现率。爆破方块落地时清除 3x3 区域。") }, { UPGRADE_CHAIN_BLAST, 1, 92, false, _T("连锁火花"), _T("进阶"), _T("每次消行后,在被清除行附近追加随机破坏。") }, { UPGRADE_CHAIN_BOMB, 1, 110, false, _T("连环炸弹"), _T("进化"), _T("爆破范围扩大为 5x5;若引发消行,再追加一次小爆炸。") }, { UPGRADE_LASER_PIECE, -1, 84, true, _T("棱镜激光"), _T("特殊"), _T("大幅提高激光方块出现率。激光方块落地后清除所在整列。") }, { UPGRADE_THUNDER_TETRIS, 1, 94, false, _T("雷霆四消"), _T("进阶"), _T("完成三消或四消时,额外轰击随机 2 行。") }, { UPGRADE_THUNDER_LASER, 1, 112, false, _T("雷霆棱镜"), _T("进化"), _T("三消或四消时额外发射 2 道激光,随机清除 2 列并获得 EXP。") }, { UPGRADE_FEVER_MODE, 1, 92, false, _T("狂热节拍"), _T("进阶"), _T("累计消行 12 行后进入 12 秒狂热:得分与 EXP 翻倍。") }, { UPGRADE_RAGE_STACK, 1, 84, false, _T("怒火连段"), _T("进阶"), _T("连续消行越多,得分倍率追加越高。") }, { UPGRADE_INFINITE_FEVER, 1, 110, false, _T("无尽狂热"), _T("进化"), _T("狂热期间消行可延长狂热时间;连击越高,倍率越凶。") }, { UPGRADE_SCREEN_BOMB, 1, 90, false, _T("清屏炸弹"), _T("进阶"), _T("立刻获得 1 枚炸弹;之后累计消行 16 行再获得 1 枚。按 X 清底 5 行。") }, { UPGRADE_TERMINAL_CLEAR, 1, 108, false, _T("终末清场"), _T("进化"), _T("最后一搏启动时,自动引爆 1 枚清屏炸弹,并进入 10 秒狂热。") }, { 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_PERFECT_ROTATE, 1, 82, false, _T("完美旋转"), _T("操作"), _T("旋转受阻时,自动尝试左右各偏移 1 格完成修正。") }, { UPGRADE_TIME_DILATION, 1, 80, false, _T("时间缓流"), _T("保命"), _T("棋盘接近顶端时自动减速,为补救留下反应空间。") }, { UPGRADE_HIGH_PRESSURE, 1, 70, false, _T("高压悬赏"), _T("风险"), _T("下落速度提高 15%,但得分与 EXP 额外提高 50%。") }, { UPGRADE_TETRIS_GAMBLE, 1, 78, false, _T("豪赌四消"), _T("风险"), _T("1~3 消收益降低,但四消收益暴增。") }, { UPGRADE_EXTREME_PLAYER, 1, 114, false, _T("极限玩家"), _T("进化"), _T("融合高压悬赏与豪赌四消。速度更快,四消后短暂缓速。") }, { UPGRADE_UPGRADE_SHOCKWAVE, 1, 106, false, _T("升级冲击波"), _T("进阶"), _T("即将升级时自动清除底部 2 行,让强化节奏更猛烈。") }, { UPGRADE_EVOLUTION_IMPACT, 1, 118, false, _T("进化冲击"), _T("进化"), _T("升级时清除底部 3 行,并获得 10 秒双倍 EXP。") }, { UPGRADE_CONTROL_MASTER, 1, 112, false, _T("操控大师"), _T("进化"), _T("使用备用仓后短暂降低下落速度,并额外增加 1 个预览方块。") }, { UPGRADE_BLOCK_STORM, 1, 82, false, _T("方块风暴"), _T("爆发"), _T("接下来 5 个方块全部变为 I 块,快速制造四消机会。") }, { UPGRADE_CROSS_PIECE, -1, 84, true, _T("十字方块"), _T("爆发"), _T("大幅提高十字方块出现率。十字方块落地后清除所在行与所在列。") }, { UPGRADE_BLACK_HOLE, 1, 88, false, _T("黑洞奇点"), _T("特殊"), _T("获得 2 次黑洞。按 Z 吞噬棋盘上数量最多的一种固定方块。") }, { UPGRADE_AIR_RESHAPE, 1, 82, false, _T("空中换形"), _T("操作"), _T("获得 2 次换形。按 V 将正在下落的方块重塑为 I 块。") }, { UPGRADE_RAINBOW_PIECE, 1, 84, false, _T("彩虹方块"), _T("爆发"), _T("更高概率生成彩虹方块。落地后清除自身中心行最多的颜色,并把覆盖行染成场上最多的颜色。") }, { UPGRADE_VOID_CORE, 1, 112, false, _T("虚空核心"), _T("进化"), _T("黑洞后额外生成 1 个彩虹方块;彩虹生效后追加一次小型黑洞。") }, { UPGRADE_STABLE_STRUCTURE, -1, 72, true, _T("稳定结构"), _T("特殊"), _T("落地后有小概率填补邻近空洞,让阵型更加稳固。") }, { UPGRADE_DOUBLE_GROWTH, 1, 84, false, _T("成长核心"), _T("成长"), _T("永久获得 +15% 得分与 +15% EXP;每局只能选择一次。") }, { UPGRADE_PIECE_TUNING, -1, 64, true, _T("方块改造"), _T("特殊"), _T("固定提高 I 方块的生成概率。") }, { UPGRADE_GAMBLER, -1, 64, true, _T("赌徒契约"), _T("特殊"), _T("选择强化时,有概率效果翻倍,也有概率本次落空。") } }; static constexpr int kUpgradePoolSize = sizeof(kUpgradePool) / sizeof(kUpgradePool[0]); static constexpr int kDifficultyStepMs = 30000; static constexpr int kDifficultySpeedStepMs = 18; static constexpr int kMaxRogueLockedRows = 4; static constexpr int kDifficultyLevelsPerLockedRow = 3; static constexpr int kFeverLineThreshold = 12; static constexpr int kFeverDurationTicks = 12; static constexpr int kScreenBombLineThreshold = 16; static constexpr int kExplosiveBaseInterval = 6; static constexpr int kExplosiveMinInterval = 3; static int pendingUpgradeShockwaveRows = 0; static bool pendingEvolutionImpactShockwave = false; static int GetUpgradeCurrentLevel(int upgradeId); static bool IsUpgradePrerequisiteConsumed(int upgradeId); static int GetUpgradeBaseRarity(int upgradeId); static int GetUpgradeDynamicWeight(const UpgradeEntry& entry); static const TCHAR* GetPieceShortName(int pieceType); static bool IsUpgradeSelectable(const UpgradeEntry& entry); static int GetTopOccupiedRow(); static int GetSweeperThreshold(); static bool RollExplosivePiece(); static bool RollLaserPiece(); static bool RollCrossPiece(); static bool RollRainbowPiece(); static int TriggerBlackHole(); static int TriggerChainBlast(int lineAnchor); static int RollNextPieceType(); static int GetRogueScoreByLines(int linesCleared); static int GetRogueExpByLines(int linesCleared); static int ApplyLevelProgress(PlayerStats& stats); static int TriggerUpgradeShockwave(int rowsToClear); static void ResolvePendingUpgradeShockwave(); static void FillUpgradeOptions(); static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount); static void ApplyDestinyCurse(); static void ClearLockedRows(); /** * @brief 限制 Rogue 模式的下一方块预览数量。 */ static int GetNextPreviewLimit() { if (currentMode != MODE_ROGUE) { return 1; } if (rogueStats.previewCount < 1) { return 1; } if (rogueStats.previewCount > 3) { return 3; } return rogueStats.previewCount; } /** * @brief 获取 Rogue 难度系统当前封锁的底部行数。 */ int GetRogueLockedRows() { if (currentMode != MODE_ROGUE) { return 0; } if (rogueStats.lockedRows < 0) { return 0; } return rogueStats.lockedRows > kMaxRogueLockedRows ? kMaxRogueLockedRows : rogueStats.lockedRows; } /** * @brief 计算当前模式下棋盘可操作区域的高度。 */ int GetRoguePlayableHeight() { return nGameHeight - GetRogueLockedRows(); } /** * @brief 清空 Rogue 模式中被难度系统封锁的底部区域。 */ static void ClearLockedRows() { int lockedRows = GetRogueLockedRows(); for (int y = nGameHeight - lockedRows; y < nGameHeight; y++) { if (y < 0 || y >= nGameHeight) { continue; } for (int x = 0; x < nGameWidth; x++) { workRegion[y][x] = 0; } } } /** * @brief 根据经过时间推进 Rogue 难度、封锁行数和下落速度。 */ void AdvanceRogueDifficulty(int elapsedMs) { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag || elapsedMs <= 0) { return; } rogueStats.difficultyElapsedMs += elapsedMs; bool difficultyChanged = false; while (rogueStats.difficultyElapsedMs >= kDifficultyStepMs) { rogueStats.difficultyElapsedMs -= kDifficultyStepMs; rogueStats.difficultyLevel++; difficultyChanged = true; } if (!difficultyChanged) { return; } int nextLockedRows = rogueStats.difficultyLevel / kDifficultyLevelsPerLockedRow; if (nextLockedRows > kMaxRogueLockedRows) { nextLockedRows = kMaxRogueLockedRows; } bool lockedRowsChanged = nextLockedRows > rogueStats.lockedRows; if (lockedRowsChanged) { rogueStats.lockedRows = nextLockedRows; ClearLockedRows(); ComputeTarget(); } currentFallInterval = GetRogueFallInterval(); TCHAR difficultyDetail[128]; _stprintf_s( difficultyDetail, _T("危险等级 Lv.%d,上方压力增强,底部封锁 %d/%d 行。"), rogueStats.difficultyLevel, GetRogueLockedRows(), kMaxRogueLockedRows); SetFeedbackMessage(lockedRowsChanged ? _T("战场收缩") : _T("压力升高"), difficultyDetail, 12); } /** * @brief 读取指定强化在当前 Rogue 属性中的等级或拥有状态。 */ static int GetUpgradeCurrentLevel(int upgradeId) { switch (upgradeId) { case UPGRADE_SCORE_MULTIPLIER: return rogueStats.scoreUpgradeLevel; case UPGRADE_EXP_MULTIPLIER: return rogueStats.expUpgradeLevel; case UPGRADE_SLOW_FALL: return rogueStats.slowFallStacks; case UPGRADE_COMBO_BONUS: return rogueStats.comboBonusStacks; case UPGRADE_PREVIEW_PLUS_ONE: return rogueStats.previewUpgradeLevel; case UPGRADE_LAST_CHANCE: return rogueStats.lastChanceUpgradeLevel; case UPGRADE_HOLD_UNLOCK: return rogueStats.holdUnlocked; case UPGRADE_PRESSURE_RELIEF: return rogueStats.pressureReliefLevel; case UPGRADE_SWEEPER: return rogueStats.sweeperLevel; case UPGRADE_EXPLOSIVE_PIECE: return rogueStats.explosiveLevel; case UPGRADE_CHAIN_BLAST: return rogueStats.chainBlastLevel; case UPGRADE_CHAIN_BOMB: return rogueStats.chainBombLevel; case UPGRADE_LASER_PIECE: return rogueStats.laserLevel; case UPGRADE_THUNDER_TETRIS: return rogueStats.thunderTetrisLevel; case UPGRADE_THUNDER_LASER: return rogueStats.thunderLaserLevel; case UPGRADE_FEVER_MODE: return rogueStats.feverLevel; case UPGRADE_RAGE_STACK: return rogueStats.rageStackLevel; case UPGRADE_INFINITE_FEVER: return rogueStats.infiniteFeverLevel; case UPGRADE_SCREEN_BOMB: return rogueStats.screenBombLevel; case UPGRADE_TERMINAL_CLEAR: return rogueStats.terminalClearLevel; case UPGRADE_DUAL_CHOICE: return rogueStats.dualChoiceLevel; case UPGRADE_DESTINY_WHEEL: return rogueStats.destinyWheelLevel; case UPGRADE_PERFECT_ROTATE: return rogueStats.perfectRotateLevel; case UPGRADE_TIME_DILATION: return rogueStats.timeDilationLevel; case UPGRADE_HIGH_PRESSURE: return rogueStats.highPressureLevel; case UPGRADE_TETRIS_GAMBLE: return rogueStats.tetrisGambleLevel; case UPGRADE_EXTREME_PLAYER: return rogueStats.extremePlayerLevel; case UPGRADE_UPGRADE_SHOCKWAVE: return rogueStats.upgradeShockwaveLevel; case UPGRADE_EVOLUTION_IMPACT: return rogueStats.evolutionImpactLevel; case UPGRADE_CONTROL_MASTER: return rogueStats.controlMasterLevel; case UPGRADE_BLOCK_STORM: return rogueStats.blockStormLevel; case UPGRADE_CROSS_PIECE: return rogueStats.crossPieceLevel; case UPGRADE_BLACK_HOLE: return rogueStats.blackHoleLevel; case UPGRADE_AIR_RESHAPE: return rogueStats.reshapeLevel; case UPGRADE_RAINBOW_PIECE: return rogueStats.rainbowPieceLevel; case UPGRADE_VOID_CORE: return rogueStats.voidCoreLevel; case UPGRADE_STABLE_STRUCTURE: return rogueStats.stableStructureLevel; case UPGRADE_DOUBLE_GROWTH: return rogueStats.doubleGrowthLevel; case UPGRADE_GAMBLER: return rogueStats.gamblerLevel; default: return 0; } } /** * @brief 返回进化类强化在界面中展示的合成路线说明。 */ const TCHAR* GetUpgradeSynthesisPath(int upgradeId) { switch (upgradeId) { case UPGRADE_CHAIN_BOMB: return _T("\u5408\u6210\uff1a\u7206\u7834\u65b9\u5757 -> \u8fde\u73af\u70b8\u5f39"); case UPGRADE_THUNDER_LASER: return _T("\u5408\u6210\uff1a\u96f7\u9706\u56db\u6d88 + \u6fc0\u5149\u65b9\u5757"); case UPGRADE_INFINITE_FEVER: return _T("\u5408\u6210\uff1a\u72c2\u70ed\u6a21\u5f0f + \u66b4\u8d70\u5806\u53e0"); case UPGRADE_TERMINAL_CLEAR: return _T("\u5408\u6210\uff1a\u6700\u540e\u4e00\u640f + \u6e05\u5c4f\u70b8\u5f39 + \u72c2\u70ed"); case UPGRADE_DESTINY_WHEEL: return _T("\u5408\u6210\uff1a\u53cc\u91cd\u9009\u62e9 -> \u547d\u8fd0\u8f6e\u76d8"); case UPGRADE_CONTROL_MASTER: return _T("\u5408\u6210\uff1aHold \u89e3\u9501 + \u989d\u5916\u9884\u89c8"); case UPGRADE_EXTREME_PLAYER: return _T("\u5408\u6210\uff1a\u9ad8\u538b\u5956\u52b1 + \u8d4c\u547d\u56db\u6d88"); case UPGRADE_EVOLUTION_IMPACT: return _T("\u5408\u6210\uff1a\u5347\u7ea7\u51b2\u51fb\u6ce2 + \u6210\u957f\u6838\u5fc3"); case UPGRADE_VOID_CORE: return _T("\u5408\u6210\uff1a\u9ed1\u6d1e + \u5f69\u8679\u65b9\u5757"); default: return _T(""); } } /** * @brief 获取指定强化的基础稀有度分类。 */ static int GetUpgradeBaseRarity(int upgradeId) { switch (upgradeId) { case UPGRADE_SCORE_MULTIPLIER: case UPGRADE_EXP_MULTIPLIER: case UPGRADE_SLOW_FALL: case UPGRADE_COMBO_BONUS: case UPGRADE_PRESSURE_RELIEF: case UPGRADE_SWEEPER: case UPGRADE_PIECE_TUNING: return UPGRADE_RARITY_COMMON; case UPGRADE_PREVIEW_PLUS_ONE: case UPGRADE_LAST_CHANCE: case UPGRADE_HOLD_UNLOCK: case UPGRADE_EXPLOSIVE_PIECE: case UPGRADE_CHAIN_BLAST: case UPGRADE_LASER_PIECE: case UPGRADE_THUNDER_TETRIS: case UPGRADE_FEVER_MODE: case UPGRADE_RAGE_STACK: case UPGRADE_SCREEN_BOMB: case UPGRADE_PERFECT_ROTATE: case UPGRADE_TIME_DILATION: case UPGRADE_HIGH_PRESSURE: case UPGRADE_TETRIS_GAMBLE: case UPGRADE_BLOCK_STORM: case UPGRADE_CROSS_PIECE: case UPGRADE_BLACK_HOLE: case UPGRADE_AIR_RESHAPE: case UPGRADE_STABLE_STRUCTURE: case UPGRADE_DOUBLE_GROWTH: return UPGRADE_RARITY_UNCOMMON; case UPGRADE_RAINBOW_PIECE: case UPGRADE_GAMBLER: return UPGRADE_RARITY_RARE; default: return UPGRADE_RARITY_RARE; } } /** * @brief 判断指定强化是否已被更高阶合成强化替代。 */ static bool IsUpgradePrerequisiteConsumed(int upgradeId) { switch (upgradeId) { case UPGRADE_EXPLOSIVE_PIECE: return rogueStats.chainBombLevel > 0; case UPGRADE_LASER_PIECE: case UPGRADE_THUNDER_TETRIS: return rogueStats.thunderLaserLevel > 0; case UPGRADE_FEVER_MODE: return rogueStats.infiniteFeverLevel > 0 || rogueStats.terminalClearLevel > 0; case UPGRADE_RAGE_STACK: return rogueStats.infiniteFeverLevel > 0; case UPGRADE_LAST_CHANCE: case UPGRADE_SCREEN_BOMB: return rogueStats.terminalClearLevel > 0; case UPGRADE_DUAL_CHOICE: return rogueStats.destinyWheelLevel > 0; case UPGRADE_HOLD_UNLOCK: case UPGRADE_PREVIEW_PLUS_ONE: return rogueStats.controlMasterLevel > 0; case UPGRADE_HIGH_PRESSURE: case UPGRADE_TETRIS_GAMBLE: return rogueStats.extremePlayerLevel > 0; case UPGRADE_UPGRADE_SHOCKWAVE: case UPGRADE_DOUBLE_GROWTH: return rogueStats.evolutionImpactLevel > 0; case UPGRADE_BLACK_HOLE: case UPGRADE_RAINBOW_PIECE: return rogueStats.voidCoreLevel > 0; default: return false; } } /** * @brief 根据当前局势动态计算强化选项的抽取权重。 */ static int GetUpgradeDynamicWeight(const UpgradeEntry& entry) { int weight = entry.baseWeight; if (entry.id == UPGRADE_PRESSURE_RELIEF) { int topRow = GetTopOccupiedRow(); if (topRow >= 0) { int occupiedHeight = GetRoguePlayableHeight() - topRow; if (occupiedHeight >= 14) { weight += 50; } else if (occupiedHeight <= 7) { weight -= 25; } } } if (entry.id == UPGRADE_LAST_CHANCE && rogueStats.lastChanceCount > 0) { weight -= 40; } if (entry.id == UPGRADE_SLOW_FALL && rogueStats.slowFallStacks >= 4) { weight -= 20; } if (entry.id == UPGRADE_SCREEN_BOMB && rogueStats.screenBombCount > 0) { weight += 20; } if (entry.id == UPGRADE_BLACK_HOLE && rogueStats.blackHoleCharges > 0) { weight += 20; } if (entry.id == UPGRADE_AIR_RESHAPE && rogueStats.reshapeCharges > 0) { weight += 20; } int rarity = GetUpgradeBaseRarity(entry.id); if (rarity == UPGRADE_RARITY_UNCOMMON) { weight = weight * 65 / 100; } else if (rarity == UPGRADE_RARITY_RARE) { weight = weight * 40 / 100; } return weight < 1 ? 1 : weight; } /** * @brief 把方块类型编号转换为界面展示用的短名称。 */ static const TCHAR* GetPieceShortName(int pieceType) { static const TCHAR* kNames[7] = { _T("I"), _T("T"), _T("L"), _T("J"), _T("O"), _T("S"), _T("Z") }; if (pieceType < 0 || pieceType >= 7) { return _T("?"); } return kNames[pieceType]; } /** * @brief 判断强化是否满足当前等级、前置和互斥条件。 */ static bool IsUpgradeSelectable(const UpgradeEntry& entry) { if (IsUpgradePrerequisiteConsumed(entry.id)) { return false; } if (entry.id == UPGRADE_CHAIN_BOMB) { return rogueStats.explosiveLevel > 0 && rogueStats.chainBombLevel == 0; } if (entry.id == UPGRADE_THUNDER_LASER) { return rogueStats.thunderTetrisLevel > 0 && rogueStats.laserLevel > 0 && rogueStats.thunderLaserLevel == 0; } if (entry.id == UPGRADE_INFINITE_FEVER) { return rogueStats.feverLevel > 0 && rogueStats.rageStackLevel > 0 && rogueStats.infiniteFeverLevel == 0; } if (entry.id == UPGRADE_TERMINAL_CLEAR) { return rogueStats.lastChanceUpgradeLevel > 0 && rogueStats.screenBombLevel > 0 && rogueStats.feverLevel > 0 && rogueStats.terminalClearLevel == 0; } if (entry.id == UPGRADE_DESTINY_WHEEL) { return rogueStats.dualChoiceLevel > 0 && rogueStats.destinyWheelLevel == 0; } if (entry.id == UPGRADE_CONTROL_MASTER) { return rogueStats.holdUnlocked > 0 && rogueStats.previewUpgradeLevel > 0 && rogueStats.controlMasterLevel == 0; } if (entry.id == UPGRADE_EXTREME_PLAYER) { return rogueStats.highPressureLevel > 0 && rogueStats.tetrisGambleLevel > 0 && rogueStats.extremePlayerLevel == 0; } if (entry.id == UPGRADE_VOID_CORE) { return rogueStats.blackHoleLevel > 0 && rogueStats.rainbowPieceLevel > 0 && rogueStats.voidCoreLevel == 0; } if (entry.id == UPGRADE_EVOLUTION_IMPACT) { return rogueStats.upgradeShockwaveLevel > 0 && rogueStats.doubleGrowthLevel > 0 && rogueStats.evolutionImpactLevel == 0; } if (entry.repeatable) { return true; } if (entry.maxLevel <= 0) { return GetUpgradeCurrentLevel(entry.id) == 0; } return GetUpgradeCurrentLevel(entry.id) < entry.maxLevel; } /** * @brief 查找棋盘中最高的已占用行,用于评估局势压力。 */ static int GetTopOccupiedRow() { for (int i = 0; i < GetRoguePlayableHeight(); i++) { for (int j = 0; j < nGameWidth; j++) { if (workRegion[i][j] != 0) { return i; } } } return -1; } /** * @brief 计算底线清道夫触发一次自动清底需要的消行充能。 */ static int GetSweeperThreshold() { int reduction = (rogueStats.sweeperLevel - 1); int threshold = 5 - reduction; return threshold < 2 ? 2 : threshold; } /** * @brief 根据爆破强化等级随机判定当前方块是否获得爆破特性。 */ static bool RollExplosivePiece() { if (currentMode != MODE_ROGUE || rogueStats.explosiveLevel <= 0) { return false; } int explosiveInterval = kExplosiveBaseInterval - (rogueStats.explosiveLevel - 1); if (explosiveInterval < kExplosiveMinInterval) { explosiveInterval = kExplosiveMinInterval; } if (rogueStats.feverTicks > 0 && explosiveInterval > kExplosiveMinInterval) { explosiveInterval--; } if (rogueStats.explosivePieceCounter < explosiveInterval) { rogueStats.explosivePieceCounter++; } if (rogueStats.explosivePieceCounter < explosiveInterval) { return false; } rogueStats.explosivePieceCounter = 0; return true; } /** * @brief 根据激光强化等级随机判定当前方块是否获得激光特性。 */ static bool RollLaserPiece() { if (currentMode != MODE_ROGUE || rogueStats.laserLevel <= 0) { return false; } int chancePercent = 16 + (rogueStats.laserLevel - 1) * 10; if (chancePercent > 55) { chancePercent = 55; } if (rogueStats.feverTicks > 0) { chancePercent += 15; if (chancePercent > 70) { chancePercent = 70; } } return (rand() % 100) < chancePercent; } /** * @brief 根据十字强化等级随机判定当前方块是否获得十字清除特性。 */ static bool RollCrossPiece() { if (currentMode != MODE_ROGUE || rogueStats.crossPieceLevel <= 0) { return false; } int chancePercent = 14 + (rogueStats.crossPieceLevel - 1) * 8; if (chancePercent > 45) { chancePercent = 45; } if (rogueStats.feverTicks > 0) { chancePercent += 15; if (chancePercent > 60) { chancePercent = 60; } } return (rand() % 100) < chancePercent; } /** * @brief 根据彩虹强化等级随机判定当前方块是否获得行清除与染色特性。 */ static bool RollRainbowPiece() { if (currentMode != MODE_ROGUE || rogueStats.rainbowPieceLevel <= 0) { return false; } int chancePercent = 18; if (rogueStats.feverTicks > 0) { chancePercent += 17; } return (rand() % 100) < chancePercent; } /** * @brief 判断棋盘格是否属于彩虹方块固定后的特殊格。 */ bool IsRainbowBoardCell(int cellValue) { return cellValue == 8; } /** * @brief 触发小型黑洞,随机吞噬限定数量的固定方块。 */ int TriggerMiniBlackHole(int maxCellsToClear) { int blockCounts[8] = { 0 }; for (int y = 0; y < GetRoguePlayableHeight(); y++) { for (int x = 0; x < nGameWidth; x++) { int cell = workRegion[y][x]; if (cell >= 1 && cell <= 7) { blockCounts[cell]++; } } } int targetBlock = 0; int maxCount = 0; for (int block = 1; block <= 7; block++) { if (blockCounts[block] > maxCount) { maxCount = blockCounts[block]; targetBlock = block; } } if (targetBlock == 0 || maxCellsToClear <= 0) { return 0; } int clearedCellCount = 0; Point clearedCells[16] = {}; for (int y = GetRoguePlayableHeight() - 1; y >= 0 && clearedCellCount < maxCellsToClear; y--) { for (int x = 0; x < nGameWidth && clearedCellCount < maxCellsToClear; x++) { if (workRegion[y][x] == targetBlock) { if (clearedCellCount < 16) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = y; } workRegion[y][x] = 0; clearedCellCount++; } } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount < 16 ? clearedCellCount : 16, RGB(24, 20, 28), true); return clearedCellCount; } /** * @brief 为当前活动方块刷新爆破、激光、十字和彩虹等特殊标记。 */ void RollCurrentPieceSpecialFlags(bool allowRandomSpecials) { if (!allowRandomSpecials) { currentPieceIsExplosive = false; currentPieceIsLaser = false; currentPieceIsCross = false; currentPieceIsRainbow = false; return; } if (currentMode == MODE_ROGUE && rogueStats.pendingRainbowPieceCount > 0) { rogueStats.pendingRainbowPieceCount--; currentPieceIsExplosive = false; currentPieceIsLaser = false; currentPieceIsCross = false; currentPieceIsRainbow = true; return; } currentPieceIsExplosive = RollExplosivePiece(); currentPieceIsLaser = !currentPieceIsExplosive && RollLaserPiece(); currentPieceIsCross = !currentPieceIsExplosive && !currentPieceIsLaser && RollCrossPiece(); currentPieceIsRainbow = !currentPieceIsExplosive && !currentPieceIsLaser && !currentPieceIsCross && RollRainbowPiece(); } /** * @brief 以指定棋盘格为中心清除爆破范围内的固定方块。 */ int ClearExplosiveAreaAt(int centerY, int centerX) { int radius = (currentMode == MODE_ROGUE && rogueStats.chainBombLevel > 0) ? 2 : 1; int clearedCellCount = 0; Point clearedCells[25] = {}; for (int y = centerY - radius; y <= centerY + radius; y++) { for (int x = centerX - radius; x <= centerX + radius; x++) { if (y >= 0 && y < GetRoguePlayableHeight() && x >= 0 && x < nGameWidth) { if (workRegion[y][x] != 0) { if (clearedCellCount < 25) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = y; } clearedCellCount++; workRegion[y][x] = 0; } } } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount < 25 ? clearedCellCount : 25, RGB(255, 116, 78), true); return clearedCellCount; } /** * @brief 清除指定列中的所有固定方块并返回清除数量。 */ int ClearColumnAt(int column) { return ClearColumnAtWithColor(column, RGB(120, 232, 255)); } /** * @brief 使用指定颜色特效清除指定列并返回清除数量。 */ int ClearColumnAtWithColor(int column, COLORREF flashColor) { int clearedCellCount = 0; Point clearedCells[20] = {}; if (column < 0 || column >= nGameWidth) { return 0; } for (int y = 0; y < GetRoguePlayableHeight(); y++) { if (workRegion[y][column] != 0) { if (clearedCellCount < 20) { clearedCells[clearedCellCount].x = column; clearedCells[clearedCellCount].y = y; } workRegion[y][column] = 0; clearedCellCount++; } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount, flashColor, false); return clearedCellCount; } /** * @brief 清除指定行中的所有固定方块并返回清除数量。 */ int ClearRowAt(int row) { int clearedCellCount = 0; Point clearedCells[10] = {}; if (row < 0 || row >= GetRoguePlayableHeight()) { return 0; } for (int x = 0; x < nGameWidth; x++) { if (workRegion[row][x] != 0) { if (clearedCellCount < 10) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = row; } workRegion[row][x] = 0; clearedCellCount++; } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount, RGB(196, 255, 132), false); return clearedCellCount; } /** * @brief 触发彩虹方块行清除与覆盖行染色效果。 */ int TriggerRainbowColorShift(int anchorRow, int minRow, int maxRow, int& recoloredCount) { recoloredCount = 0; int clearedCellCount = 0; Point clearedCells[10] = {}; Point recoloredCells[40] = {}; if (anchorRow < 0) { anchorRow = 0; } if (anchorRow >= GetRoguePlayableHeight()) { anchorRow = GetRoguePlayableHeight() - 1; } if (minRow < 0) { minRow = 0; } if (maxRow >= GetRoguePlayableHeight()) { maxRow = GetRoguePlayableHeight() - 1; } int rowColorCounts[8] = {}; for (int x = 0; x < nGameWidth; x++) { int cell = workRegion[anchorRow][x]; if (cell >= 1 && cell <= 7) { rowColorCounts[cell]++; } } int rowTargetColor = 0; int rowTargetCount = 0; for (int cell = 1; cell <= 7; cell++) { if (rowColorCounts[cell] > rowTargetCount) { rowTargetColor = cell; rowTargetCount = rowColorCounts[cell]; } } if (rowTargetColor > 0) { for (int x = 0; x < nGameWidth; x++) { if (workRegion[anchorRow][x] == rowTargetColor) { if (clearedCellCount < 10) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = anchorRow; } workRegion[anchorRow][x] = 0; clearedCellCount++; } } } int boardColorCounts[8] = {}; for (int y = 0; y < GetRoguePlayableHeight(); y++) { for (int x = 0; x < nGameWidth; x++) { int cell = workRegion[y][x]; if (cell >= 1 && cell <= 7) { boardColorCounts[cell]++; } } } int boardTargetColor = 0; int boardTargetCount = 0; for (int cell = 1; cell <= 7; cell++) { if (boardColorCounts[cell] > boardTargetCount) { boardTargetColor = cell; boardTargetCount = boardColorCounts[cell]; } } if (boardTargetColor == 0) { boardTargetColor = rowTargetColor > 0 ? rowTargetColor : 1; } for (int y = minRow; y <= maxRow; y++) { for (int x = 0; x < nGameWidth; x++) { if (workRegion[y][x] != 0 && workRegion[y][x] != boardTargetColor) { workRegion[y][x] = boardTargetColor; if (recoloredCount < 40) { recoloredCells[recoloredCount].x = x; recoloredCells[recoloredCount].y = y; } recoloredCount++; } } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount, RGB(255, 255, 255), true); TriggerColoredCellClearEffect(recoloredCells, recoloredCount < 40 ? recoloredCount : 40, RGB(218, 178, 255), false); return clearedCellCount; } /** * @brief 触发黑洞,吞噬棋盘中数量最多的一类固定方块。 */ static int TriggerBlackHole() { int blockCounts[8] = { 0 }; for (int y = 0; y < GetRoguePlayableHeight(); y++) { for (int x = 0; x < nGameWidth; x++) { int cell = workRegion[y][x]; if (cell >= 1 && cell <= 7) { blockCounts[cell]++; } } } int targetBlock = 0; int maxCount = 0; for (int block = 1; block <= 7; block++) { if (blockCounts[block] > maxCount) { maxCount = blockCounts[block]; targetBlock = block; } } if (targetBlock == 0) { return 0; } int clearedCellCount = 0; Point clearedCells[200] = {}; for (int y = 0; y < GetRoguePlayableHeight(); y++) { for (int x = 0; x < nGameWidth; x++) { if (workRegion[y][x] == targetBlock) { if (clearedCellCount < 200) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = y; } workRegion[y][x] = 0; clearedCellCount++; } } } TriggerColoredCellClearEffect(clearedCells, clearedCellCount < 200 ? clearedCellCount : 200, RGB(24, 20, 28), true); return clearedCellCount; } /** * @brief 引爆清屏炸弹,清除当前可玩区域底部多行方块。 */ int TriggerScreenBomb() { int clearedCellCount = 0; Point clearedCells[50] = {}; for (int i = 0; i < 5; i++) { int row = GetRoguePlayableHeight() - 1 - i; if (row < 0) { break; } for (int x = 0; x < nGameWidth; x++) { if (workRegion[row][x] != 0) { if (clearedCellCount < 50) { clearedCells[clearedCellCount].x = x; clearedCells[clearedCellCount].y = row; } clearedCellCount++; } } DeleteOneLine(row); } TriggerCellClearEffect(clearedCells, clearedCellCount < 50 ? clearedCellCount : 50, true); return clearedCellCount; } /** * @brief 围绕消行位置触发连锁火花,随机破坏附近固定方块。 */ static int TriggerChainBlast(int lineAnchor) { if (currentMode != MODE_ROGUE || rogueStats.chainBlastLevel <= 0) { return 0; } int clearedCellCount = 0; Point clearedCells[16] = {}; int blastCount = 2 + rogueStats.chainBlastLevel; for (int blastIndex = 0; blastIndex < blastCount; blastIndex++) { int randomY = lineAnchor + (rand() % 3) - 1; int randomX = rand() % nGameWidth; if (randomY < 0) { randomY = 0; } if (randomY >= GetRoguePlayableHeight()) { randomY = GetRoguePlayableHeight() - 1; } if (workRegion[randomY][randomX] != 0) { if (clearedCellCount < 16) { clearedCells[clearedCellCount].x = randomX; clearedCells[clearedCellCount].y = randomY; } workRegion[randomY][randomX] = 0; clearedCellCount++; } } TriggerCellClearEffect(clearedCells, clearedCellCount < 16 ? clearedCellCount : 16, false); return clearedCellCount; } /** * @brief 根据 Rogue 强化和权重系统随机生成下一个方块类型。 */ static int RollNextPieceType() { if (currentMode == MODE_ROGUE && rogueStats.blockStormPiecesRemaining > 0) { rogueStats.blockStormPiecesRemaining--; return 0; } int weights[7] = { 100, 100, 100, 100, 100, 100, 100 }; if (currentMode == MODE_ROGUE) { weights[0] += rogueStats.pieceTuningLevels[0] * 55; if (weights[0] > 430) { weights[0] = 430; } } int totalWeight = 0; for (int i = 0; i < 7; i++) { totalWeight += weights[i]; } int roll = rand() % totalWeight; for (int i = 0; i < 7; i++) { if (roll < weights[i]) { return i; } roll -= weights[i]; } return rand() % 7; } /** * @brief 尝试用稳定结构强化填补局部空洞并返回填补数量。 */ int TryStabilizeBoard() { if (currentMode != MODE_ROGUE || rogueStats.stableStructureLevel <= 0) { return 0; } int triggerChance = 25 + (rogueStats.stableStructureLevel - 1) * 12; if (triggerChance > 70) { triggerChance = 70; } if ((rand() % 100) >= triggerChance) { return 0; } for (int y = GetRoguePlayableHeight() - 2; y >= 1; y--) { for (int x = 1; x < nGameWidth - 1; x++) { if (workRegion[y][x] != 0) { continue; } bool hasSupportBelow = workRegion[y + 1][x] != 0; bool hasLeftNeighbor = workRegion[y][x - 1] != 0; bool hasRightNeighbor = workRegion[y][x + 1] != 0; if (hasSupportBelow && hasLeftNeighbor && hasRightNeighbor) { int fillValue = workRegion[y + 1][x]; if (fillValue == 0) { fillValue = hasLeftNeighbor ? workRegion[y][x - 1] : workRegion[y][x + 1]; } workRegion[y][x] = fillValue; return 1; } } } return 0; } /** * @brief 重置下一方块队列并按当前预览上限填充。 */ void ResetNextQueue() { for (int i = 0; i < 3; i++) { nextTypes[i] = RollNextPieceType(); } } /** * @brief 取出队首方块并补充新的下一方块。 */ int ConsumeNextType() { int nextType = nextTypes[0]; nextTypes[0] = nextTypes[1]; nextTypes[1] = nextTypes[2]; nextTypes[2] = RollNextPieceType(); return nextType; } /** * @brief 按 Rogue 规则计算指定消行数对应的基础得分。 */ static int GetRogueScoreByLines(int linesCleared) { switch (linesCleared) { case 1: return 100; case 2: return 300; case 3: return 500; case 4: return 800; default: return 0; } } /** * @brief 按 Rogue 规则计算指定消行数对应的基础经验。 */ static int GetRogueExpByLines(int linesCleared) { switch (linesCleared) { case 1: return 12; case 2: return 32; case 3: return 58; case 4: return 100; default: return 0; } } /** * @brief 结算经验条并返回本次连续升级次数。 */ static int ApplyLevelProgress(PlayerStats& stats) { int levelUps = 0; while (stats.requiredExp > 0 && stats.exp >= stats.requiredExp) { stats.exp -= stats.requiredExp; stats.level++; stats.requiredExp = 6 + stats.level * 5; levelUps++; } return levelUps; } /** * @brief 触发升级冲击波,清除底部指定数量的行。 */ static int TriggerUpgradeShockwave(int rowsToClear) { int clearedRows = 0; for (int i = 0; i < rowsToClear; i++) { DeleteOneLine(GetRoguePlayableHeight() - 1); clearedRows++; } return clearedRows; } /** * @brief 按权重和当前局势生成升级菜单中的候选强化。 */ static void FillUpgradeOptions() { int selectableIndexes[kUpgradePoolSize] = { 0 }; int selectableWeights[kUpgradePoolSize] = { 0 }; int selectableCount = 0; for (int i = 0; i < kUpgradePoolSize; i++) { if (IsUpgradeSelectable(kUpgradePool[i])) { selectableIndexes[selectableCount] = i; selectableWeights[selectableCount] = GetUpgradeDynamicWeight(kUpgradePool[i]); selectableCount++; } } int optionLimit = (rogueStats.destinyWheelLevel > 0) ? 6 : 3; int optionCount = selectableCount < optionLimit ? selectableCount : optionLimit; upgradeUiState.optionCount = optionCount; upgradeUiState.selectedIndex = 0; upgradeUiState.picksRemaining = (rogueStats.dualChoiceLevel > 0 || rogueStats.destinyWheelLevel > 0) ? 2 : 1; upgradeUiState.markedCount = 0; for (int i = 0; i < 6; i++) { upgradeUiState.marked[i] = false; } for (int i = 0; i < optionCount; i++) { int totalWeight = 0; for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++) { totalWeight += selectableWeights[weightIndex]; } int pickSlot = 0; if (totalWeight > 0) { int roll = rand() % totalWeight; int accumulatedWeight = 0; for (int weightIndex = 0; weightIndex < selectableCount; weightIndex++) { accumulatedWeight += selectableWeights[weightIndex]; if (roll < accumulatedWeight) { pickSlot = weightIndex; break; } } } int pickedIndex = selectableIndexes[pickSlot]; const UpgradeEntry& pickedEntry = kUpgradePool[pickedIndex]; upgradeUiState.options[i].id = pickedEntry.id; upgradeUiState.options[i].currentLevel = GetUpgradeCurrentLevel(pickedEntry.id); upgradeUiState.options[i].targetPieceType = -1; upgradeUiState.options[i].rarity = GetUpgradeBaseRarity(pickedEntry.id); upgradeUiState.options[i].cursed = false; upgradeUiState.options[i].name = pickedEntry.name; upgradeUiState.options[i].category = pickedEntry.category; upgradeUiState.options[i].description = pickedEntry.description; if (pickedEntry.id == UPGRADE_PIECE_TUNING) { int targetPieceType = 0; upgradeUiState.options[i].targetPieceType = targetPieceType; upgradeUiState.options[i].currentLevel = rogueStats.pieceTuningLevels[0]; upgradeUiState.options[i].name = _T("方块改造"); static TCHAR tuningDescriptions[6][64]; _stprintf_s( tuningDescriptions[i], _T("%s 块的生成概率提高。"), GetPieceShortName(0)); upgradeUiState.options[i].description = tuningDescriptions[i]; } selectableIndexes[pickSlot] = selectableIndexes[selectableCount - 1]; selectableWeights[pickSlot] = selectableWeights[selectableCount - 1]; selectableCount--; } if (rogueStats.destinyWheelLevel > 0 && optionCount > 0) { int cursedIndex = rand() % optionCount; upgradeUiState.options[cursedIndex].cursed = true; } } /** * @brief 综合难度、强化和临时状态计算 Rogue 模式下落间隔。 */ int GetRogueFallInterval() { int baseInterval = 500 + rogueStats.slowFallStacks * 80; baseInterval -= rogueStats.difficultyLevel * kDifficultySpeedStepMs; if (rogueStats.highPressureLevel > 0) { baseInterval = baseInterval * 85 / 100; } if (rogueStats.extremePlayerLevel > 0) { baseInterval = baseInterval * 90 / 100; } if (rogueStats.extremeDangerLevel > 0) { baseInterval = baseInterval * (100 - rogueStats.extremeDangerLevel * 8) / 100; } if (currentMode == MODE_ROGUE && rogueStats.feverTicks > 0) { baseInterval += 120; } if (rogueStats.timeDilationTicks > 0) { baseInterval = baseInterval * 130 / 100; } if (rogueStats.extremeSlowTicks > 0) { baseInterval += 180; } if (rogueStats.holdSlowTicks > 0) { baseInterval += 140; } if (baseInterval < 120) { baseInterval = 120; } return baseInterval; } /** * @brief 根据强化编号把对应效果写入 Rogue 属性。 */ static void ApplyUpgradeById(int upgradeId, int targetPieceType, int applyCount) { if (applyCount <= 0) { return; } switch (upgradeId) { case UPGRADE_SCORE_MULTIPLIER: 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; currentFallInterval = GetRogueFallInterval(); break; case UPGRADE_PREVIEW_PLUS_ONE: for (int i = 0; i < applyCount; i++) { if (rogueStats.previewCount < 3) { rogueStats.previewCount++; } } rogueStats.previewUpgradeLevel = rogueStats.previewCount - 1; break; case UPGRADE_LAST_CHANCE: rogueStats.lastChanceCount += applyCount; rogueStats.lastChanceUpgradeLevel += applyCount; break; case UPGRADE_HOLD_UNLOCK: rogueStats.holdUnlocked = 1; 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; break; case UPGRADE_EXPLOSIVE_PIECE: rogueStats.explosiveLevel += applyCount; break; case UPGRADE_CHAIN_BLAST: rogueStats.chainBlastLevel = 1; break; case UPGRADE_CHAIN_BOMB: rogueStats.chainBombLevel = 1; break; case UPGRADE_LASER_PIECE: rogueStats.laserLevel += applyCount; 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 = 1; 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: rogueStats.blockStormLevel = 1; rogueStats.blockStormPiecesRemaining = 5; nextTypes[0] = 0; nextTypes[1] = 0; nextTypes[2] = 0; break; case UPGRADE_CROSS_PIECE: rogueStats.crossPieceLevel += applyCount; break; case UPGRADE_BLACK_HOLE: rogueStats.blackHoleLevel = 1; rogueStats.blackHoleCharges += 2 * applyCount; break; case UPGRADE_AIR_RESHAPE: rogueStats.reshapeLevel = 1; 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; break; default: break; } } /** * @brief 为技能清除的格子结算得分和经验奖励。 */ void AwardRogueSkillClearRewards(int clearedCells, int& scoreGain, int& expGain, bool allowLevelProgress) { scoreGain = 0; expGain = 0; if (currentMode != MODE_ROGUE || clearedCells <= 0) { return; } scoreGain = clearedCells * rogueStats.scoreMultiplierPercent / 100; expGain = clearedCells * rogueStats.expMultiplierPercent / 100; if (rogueStats.doubleGrowthLevel > 0) { int growthMultiplierPercent = 100 + rogueStats.doubleGrowthLevel * 15; scoreGain = scoreGain * growthMultiplierPercent / 100; expGain = expGain * growthMultiplierPercent / 100; } if (rogueStats.feverTicks > 0) { scoreGain *= 2; expGain *= 2; } if (scoreGain < clearedCells) { scoreGain = clearedCells; } if (expGain < clearedCells) { expGain = clearedCells; } rogueStats.score += scoreGain; rogueStats.exp += expGain; tScore = rogueStats.score; if (rogueStats.screenBombLevel > 0) { rogueStats.screenBombCharge += clearedCells; while (rogueStats.screenBombCharge >= kScreenBombLineThreshold) { rogueStats.screenBombCharge -= kScreenBombLineThreshold; rogueStats.screenBombCount++; } } if (allowLevelProgress) { int levelUps = ApplyLevelProgress(rogueStats); if (levelUps > 0) { upgradeUiState.pendingCount += levelUps; OpenUpgradeMenu(); } } } /** * @brief 检查 Rogue 经验是否升级,并在需要时打开强化选择界面。 */ void CheckRogueLevelProgress() { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING) { return; } int levelUps = ApplyLevelProgress(rogueStats); if (levelUps <= 0) { return; } upgradeUiState.pendingCount += levelUps; int shockwaveRows = 0; if (rogueStats.evolutionImpactLevel > 0) { shockwaveRows = 3; rogueStats.feverTicks = kFeverDurationTicks; currentFallInterval = GetRogueFallInterval(); } else if (rogueStats.upgradeShockwaveLevel > 0) { shockwaveRows = 2; } if (shockwaveRows > 0) { pendingUpgradeShockwaveRows = shockwaveRows; pendingEvolutionImpactShockwave = rogueStats.evolutionImpactLevel > 0; } TCHAR feedbackTitle[64]; TCHAR feedbackDetail[128]; _stprintf_s(feedbackTitle, _T("灵感涌现 x%d"), levelUps); _stprintf_s( feedbackDetail, _T("等级 Lv.%d EXP %d/%d 选择新的强化"), rogueStats.level, rogueStats.exp, rogueStats.requiredExp); SetFeedbackMessage(feedbackTitle, feedbackDetail, 10); OpenUpgradeMenu(); } /** * @brief 对棋盘固定方块应用重力,使悬空方块自然下落。 */ void ApplyBoardGravity() { int playableHeight = GetRoguePlayableHeight(); for (int x = 0; x < nGameWidth; x++) { int writeY = playableHeight - 1; for (int y = playableHeight - 1; y >= 0; y--) { if (workRegion[y][x] != 0) { int cell = workRegion[y][x]; workRegion[y][x] = 0; workRegion[writeY][x] = cell; writeY--; } } for (int y = writeY; y >= 0; y--) { workRegion[y][x] = 0; } } } /** * @brief 结算一次标准消行带来的 Rogue 得分、经验、连击和派生效果。 */ void ApplyLineClearResult(int linesCleared) { if (linesCleared <= 0) { if (currentMode == MODE_ROGUE) { rogueStats.comboChain = 0; } return; } if (currentMode == MODE_CLASSIC) { classicStats.totalLinesCleared += linesCleared; classicStats.score += linesCleared * 100; tScore = classicStats.score; return; } int scoreGain = GetRogueScoreByLines(linesCleared); scoreGain = scoreGain * rogueStats.scoreMultiplierPercent / 100; int expGain = GetRogueExpByLines(linesCleared); expGain = expGain * rogueStats.expMultiplierPercent / 100; if (rogueStats.tetrisGambleLevel > 0) { if (linesCleared == 4) { scoreGain *= 4; } else { scoreGain = scoreGain * 50 / 100; } } if (rogueStats.doubleGrowthLevel > 0) { int growthMultiplierPercent = 100 + rogueStats.doubleGrowthLevel * 15; scoreGain = scoreGain * growthMultiplierPercent / 100; expGain = expGain * growthMultiplierPercent / 100; } if (rogueStats.feverTicks > 0) { scoreGain *= 2; expGain *= 2; } int gamblerBonusPercent = 0; if (rogueStats.gamblerLevel > 0) { int variance = 20 + (rogueStats.gamblerLevel - 1) * 10; if (variance > 50) { variance = 50; } gamblerBonusPercent = (rand() % (variance * 2 + 1)) - variance; scoreGain = scoreGain * (100 + gamblerBonusPercent) / 100; expGain = expGain * (100 + gamblerBonusPercent) / 100; } rogueStats.comboChain++; if (rogueStats.comboBonusStacks > 0 && rogueStats.comboChain > 1) { scoreGain += (rogueStats.comboChain - 1) * rogueStats.comboBonusStacks * 50; } if (rogueStats.rageStackLevel > 0 && rogueStats.comboChain > 1) { int rageBonusPercent = (rogueStats.comboChain - 1) * 15; if (rogueStats.infiniteFeverLevel > 0 && rogueStats.feverTicks > 0) { rageBonusPercent += (rogueStats.comboChain - 1) * 10; } scoreGain += scoreGain * rageBonusPercent / 100; } rogueStats.totalLinesCleared += linesCleared; rogueStats.score += scoreGain; rogueStats.exp += expGain; if (rogueStats.feverLevel > 0) { rogueStats.feverLineCharge += linesCleared; while (rogueStats.feverLineCharge >= kFeverLineThreshold) { rogueStats.feverLineCharge -= kFeverLineThreshold; rogueStats.feverTicks = kFeverDurationTicks; currentFallInterval = GetRogueFallInterval(); SetFeedbackMessage(_T("\u8fdb\u5165\u72c2\u70ed\u6a21\u5f0f"), _T("\u672a\u6765 12 \u79d2\u5f97\u5206 / EXP x2\uff0c\u4e0b\u843d\u66f4\u6162\uff0c\u7279\u6b8a\u65b9\u5757\u66f4\u6613\u51fa\u73b0\u3002"), 12); } } if (rogueStats.infiniteFeverLevel > 0 && rogueStats.feverTicks > 0) { rogueStats.feverTicks += 1; if (linesCleared == 4) { rogueStats.feverTicks += 3; } if (rogueStats.feverTicks > 24) { rogueStats.feverTicks = 24; } currentFallInterval = GetRogueFallInterval(); } if (rogueStats.extremePlayerLevel > 0 && linesCleared == 4) { rogueStats.extremeSlowTicks = 5; rogueStats.extremeDangerTicks = 30; currentFallInterval = GetRogueFallInterval(); SetFeedbackMessage(_T("\u6781\u9650\u73a9\u5bb6\u89e6\u53d1"), _T("\u56db\u6d88\u6210\u529f\uff0c\u63a5\u4e0b\u6765 5 \u79d2\u77ed\u6682\u7f13\u901f\u8865\u4f4d\u3002"), 12); } if (rogueStats.screenBombLevel > 0) { rogueStats.screenBombCharge += linesCleared; while (rogueStats.screenBombCharge >= kScreenBombLineThreshold) { rogueStats.screenBombCharge -= kScreenBombLineThreshold; rogueStats.screenBombCount++; SetFeedbackMessage(_T("\u6e05\u5c4f\u70b8\u5f39\u5c31\u7eea"), _T("\u5df2\u83b7\u5f97 1 \u6b21\u6e05\u9664\u5e95\u90e8 5 \u884c\u7684\u673a\u4f1a\u3002"), 12); } } if (rogueStats.chainBlastLevel > 0) { int chainBlastCells = 0; for (int i = 0; i < linesCleared; i++) { chainBlastCells += TriggerChainBlast(GetRoguePlayableHeight() - 1 - i); } if (chainBlastCells > 0) { int chainBlastScore = 0; int chainBlastExp = 0; AwardRogueSkillClearRewards(chainBlastCells, chainBlastScore, chainBlastExp, false); ApplyBoardGravity(); TCHAR blastDetail[128]; _stprintf_s( blastDetail, _T("余波炸裂,清除 %d 格 +%d 分 +%d EXP"), chainBlastCells, chainBlastScore, chainBlastExp); SetFeedbackMessage(_T("连锁火花"), blastDetail, 12); } } if (linesCleared >= 3 && rogueStats.thunderTetrisLevel > 0) { int thunderRowsCleared = 0; Point thunderCells[20] = {}; for (int i = 0; i < 2; i++) { int randomRow = rand() % GetRoguePlayableHeight(); for (int x = 0; x < nGameWidth; x++) { if (workRegion[randomRow][x] != 0) { if (thunderRowsCleared < 20) { thunderCells[thunderRowsCleared].x = x; thunderCells[thunderRowsCleared].y = randomRow; } workRegion[randomRow][x] = 0; thunderRowsCleared++; } } } if (thunderRowsCleared > 0) { TriggerCellClearEffect(thunderCells, thunderRowsCleared < 20 ? thunderRowsCleared : 20, true); int thunderScore = 0; int thunderExp = 0; AwardRogueSkillClearRewards(thunderRowsCleared, thunderScore, thunderExp, false); ApplyBoardGravity(); SetFeedbackMessage(_T("雷霆四消"), _T("雷击落下,额外清理了 2 行范围内的方块。"), 12); } } if (linesCleared >= 3 && rogueStats.thunderLaserLevel > 0) { int laserCellsCleared = 0; for (int i = 0; i < 2; i++) { laserCellsCleared += ClearColumnAt(rand() % nGameWidth); } if (laserCellsCleared > 0) { int laserScore = 0; int laserExp = 0; AwardRogueSkillClearRewards(laserCellsCleared, laserScore, laserExp, false); ApplyBoardGravity(); TCHAR thunderLaserDetail[128]; _stprintf_s( thunderLaserDetail, _T("雷光穿透,清除 %d 格 +%d 分 +%d EXP"), laserCellsCleared, laserScore, laserExp); SetFeedbackMessage(_T("雷霆棱镜"), thunderLaserDetail, 12); } } if (rogueStats.sweeperLevel > 0) { rogueStats.sweeperCharge += linesCleared; int sweeperThreshold = GetSweeperThreshold(); int clearedBySweeper = 0; while (rogueStats.sweeperCharge >= sweeperThreshold) { rogueStats.sweeperCharge -= sweeperThreshold; DeleteOneLine(GetRoguePlayableHeight() - 1); clearedBySweeper++; } if (clearedBySweeper > 0) { TCHAR sweeperDetail[128]; _stprintf_s( sweeperDetail, _T("清扫底部 %d 行 充能 %d/%d"), clearedBySweeper, rogueStats.sweeperCharge, sweeperThreshold); SetFeedbackMessage(_T("底线清道夫"), sweeperDetail, 12); } } int levelUps = ApplyLevelProgress(rogueStats); upgradeUiState.pendingCount += levelUps; tScore = rogueStats.score; if (levelUps > 0) { int shockwaveRows = 0; if (rogueStats.evolutionImpactLevel > 0) { shockwaveRows = 3; rogueStats.feverTicks = kFeverDurationTicks; currentFallInterval = GetRogueFallInterval(); } else if (rogueStats.upgradeShockwaveLevel > 0) { shockwaveRows = 2; } if (shockwaveRows > 0) { pendingUpgradeShockwaveRows = shockwaveRows; pendingEvolutionImpactShockwave = rogueStats.evolutionImpactLevel > 0; } TCHAR feedbackTitle[64]; TCHAR feedbackDetail[128]; _stprintf_s(feedbackTitle, _T("灵感涌现 x%d"), levelUps); _stprintf_s( feedbackDetail, _T("等级 Lv.%d EXP %d/%d 选择新的强化"), rogueStats.level, rogueStats.exp, rogueStats.requiredExp); SetFeedbackMessage(feedbackTitle, feedbackDetail, 10); OpenUpgradeMenu(); } currentFallInterval = GetRogueFallInterval(); } /** * @brief 应用命运轮盘的诅咒,提高下一次升级所需经验。 */ static void ApplyDestinyCurse() { rogueStats.requiredExp = rogueStats.requiredExp * 125 / 100; if (rogueStats.requiredExp < 10) { rogueStats.requiredExp = 10; } } /** * @brief 在升级选择结束后处理延迟触发的升级冲击波。 */ static void ResolvePendingUpgradeShockwave() { if (pendingUpgradeShockwaveRows <= 0) { return; } int shockwaveRows = pendingUpgradeShockwaveRows; bool evolutionImpact = pendingEvolutionImpactShockwave; pendingUpgradeShockwaveRows = 0; pendingEvolutionImpactShockwave = false; int clearedRows = TriggerUpgradeShockwave(shockwaveRows); int effectRows[4] = {}; int effectRowCount = clearedRows; if (effectRowCount > 4) { effectRowCount = 4; } for (int i = 0; i < effectRowCount; i++) { effectRows[i] = GetRoguePlayableHeight() - 1 - i; } TriggerLineClearEffect(effectRows, effectRowCount, clearedRows); int scoreGain = 0; int expGain = 0; AwardRogueSkillClearRewards(clearedRows * nGameWidth, scoreGain, expGain, true); TCHAR shockwaveDetail[128]; if (evolutionImpact) { _stprintf_s( shockwaveDetail, _T("进化能量爆发,清除底部 %d 行 +%d 分 +%d EXP。"), clearedRows, scoreGain, expGain); SetFeedbackMessage(_T("进化冲击"), shockwaveDetail, 14); } else { _stprintf_s(shockwaveDetail, _T("灵感涌现后,冲击波清除底部 %d 行 +%d 分 +%d EXP。"), clearedRows, scoreGain, expGain); SetFeedbackMessage(_T("升级冲击波"), shockwaveDetail, 12); } } /** * @brief 重置 Rogue 模式中等待播放的特殊视觉事件。 */ void ResetPendingRogueVisualEvents() { pendingUpgradeShockwaveRows = 0; pendingEvolutionImpactShockwave = false; } /** * @brief 在 Rogue 模式中打开升级强化选择界面。 */ void OpenUpgradeMenu() { if (currentMode != MODE_ROGUE || upgradeUiState.pendingCount <= 0) { return; } FillUpgradeOptions(); currentScreen = SCREEN_UPGRADE; } /** * @brief 确认升级菜单中的选择并应用对应强化效果。 */ void ConfirmUpgradeSelection() { if (currentScreen != SCREEN_UPGRADE || upgradeUiState.optionCount <= 0) { return; } if (upgradeUiState.picksRemaining > 1) { if (upgradeUiState.markedCount != upgradeUiState.picksRemaining) { SetFeedbackMessage(_T("选择未完成"), _T("请先标记足够的强化,再按 Enter 确认。"), 10); return; } TCHAR feedbackTitle[64] = _T("获得强化"); TCHAR feedbackDetail[128] = _T(""); int appliedSelections = 0; for (int optionIndex = 0; optionIndex < upgradeUiState.optionCount; optionIndex++) { if (!upgradeUiState.marked[optionIndex]) { continue; } UpgradeOption selectedOption = upgradeUiState.options[optionIndex]; int applyCount = 1; TCHAR gamblerSuffix[64] = _T(""); if (currentMode == MODE_ROGUE && rogueStats.gamblerLevel > 0) { int gamblerChance = 20 + (rogueStats.gamblerLevel - 1) * 5; if (gamblerChance > 40) { gamblerChance = 40; } int roll = rand() % 100; if (roll < gamblerChance) { applyCount = 2; _stprintf_s(gamblerSuffix, _T("(翻倍)")); } else if (roll >= 100 - gamblerChance) { applyCount = 0; _stprintf_s(gamblerSuffix, _T("(落空)")); } } 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); 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(); return; } UpgradeOption selectedOption = upgradeUiState.options[upgradeUiState.selectedIndex]; int applyCount = 1; TCHAR gamblerSuffix[64] = _T(""); if (currentMode == MODE_ROGUE && rogueStats.gamblerLevel > 0) { int gamblerChance = 20 + (rogueStats.gamblerLevel - 1) * 5; if (gamblerChance > 40) { gamblerChance = 40; } int roll = rand() % 100; if (roll < gamblerChance) { applyCount = 2; _stprintf_s(gamblerSuffix, _T(" 赌徒命中:效果翻倍")); } else if (roll >= 100 - gamblerChance) { applyCount = 0; _stprintf_s(gamblerSuffix, _T(" 赌徒失手:本次落空")); } } ApplyUpgradeById(selectedOption.id, selectedOption.targetPieceType, applyCount); upgradeUiState.totalChosenCount++; TCHAR feedbackTitle[64]; TCHAR feedbackDetail[128]; _stprintf_s(feedbackTitle, _T("获得强化:%s"), selectedOption.name); if (selectedOption.id == UPGRADE_PIECE_TUNING && selectedOption.targetPieceType >= 0) { _stprintf_s( feedbackDetail, _T("%s 块的生成概率提高%s"), GetPieceShortName(0), gamblerSuffix); } else { _stprintf_s(feedbackDetail, _T("%s%s"), selectedOption.description, gamblerSuffix); } if (selectedOption.cursed) { ApplyDestinyCurse(); _stprintf_s( feedbackDetail + lstrlen(feedbackDetail), 128 - lstrlen(feedbackDetail), _T(" 诅咒缠身:下一次升级所需 EXP 提高 25%。")); } SetFeedbackMessage(feedbackTitle, feedbackDetail, 12); if (upgradeUiState.picksRemaining > 0) { upgradeUiState.picksRemaining--; } for (int i = upgradeUiState.selectedIndex; i + 1 < upgradeUiState.optionCount; i++) { upgradeUiState.options[i] = upgradeUiState.options[i + 1]; } if (upgradeUiState.optionCount > 0) { upgradeUiState.optionCount--; } if (upgradeUiState.optionCount > 0 && upgradeUiState.selectedIndex >= upgradeUiState.optionCount) { upgradeUiState.selectedIndex = upgradeUiState.optionCount - 1; } if (upgradeUiState.picksRemaining > 0 && upgradeUiState.optionCount > 0) { currentScreen = SCREEN_UPGRADE; return; } 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(); } /** * @brief 执行 Hold 操作,在备用仓与当前方块之间交换。 */ void HoldCurrentPiece() { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return; } if (rogueStats.holdUnlocked == 0) { SetFeedbackMessage(_T("备用仓未开启"), _T("获得备用仓强化后,才能用 C 或 Shift 暂存方块。"), 10); return; } if (holdUsedThisTurn) { SetFeedbackMessage(_T("备用仓冷却中"), _T("每个方块落地前只能暂存一次。"), 10); return; } int previousHoldType = holdType; holdType = type; state = 0; holdUsedThisTurn = true; if (previousHoldType < 0) { type = ConsumeNextType(); nType = nextTypes[0]; RollCurrentPieceSpecialFlags(true); } else { type = previousHoldType; RollCurrentPieceSpecialFlags(false); } point = GetSpawnPoint(type); target = point; if (currentMode == MODE_ROGUE && rogueStats.controlMasterLevel > 0) { rogueStats.holdSlowTicks = 4; currentFallInterval = GetRogueFallInterval(); } if (!IsPiecePlacementValid(type, state, point)) { gameOverFlag = true; SetFeedbackMessage(_T("交换失败"), _T("换出的方块无处落位,战局崩塌。"), 12); return; } ComputeTarget(); if (previousHoldType < 0) { SetFeedbackMessage(_T("已存入备用仓"), _T("当前方块已暂存,新的方块进入战场。"), 10); } else { SetFeedbackMessage(_T("备用仓交换"), _T("已换出备用方块。"), 10); } } /** * @brief 响应玩家主动使用清屏炸弹的操作。 */ void UseScreenBomb() { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return; } if (rogueStats.screenBombLevel <= 0) { SetFeedbackMessage(_T("清屏炸弹未获得"), _T("掌握清屏炸弹后,才能按 X 主动引爆。"), 10); return; } if (rogueStats.screenBombCount <= 0) { SetFeedbackMessage(_T("清屏炸弹未就绪"), _T("继续消行充能,充满后即可使用。"), 10); return; } rogueStats.screenBombCount--; int clearedCells = TriggerScreenBomb(); ApplyBoardGravity(); int scoreGain = 0; int expGain = 0; AwardRogueSkillClearRewards(clearedCells, scoreGain, expGain, true); TCHAR detail[128]; _stprintf_s( detail, _T("炸开底部 5 行,清除 %d 格 +%d 分 +%d EXP"), clearedCells, scoreGain, expGain); SetFeedbackMessage(_T("清屏炸弹引爆"), detail, 12); currentFallInterval = GetRogueFallInterval(); ComputeTarget(); } /** * @brief 响应玩家主动释放黑洞的操作。 */ void UseBlackHole() { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return; } if (rogueStats.blackHoleLevel <= 0) { SetFeedbackMessage(_T("黑洞尚未觉醒"), _T("获得黑洞奇点后,才能按 Z 释放。"), 10); return; } if (rogueStats.blackHoleCharges <= 0) { SetFeedbackMessage(_T("黑洞能量不足"), _T("没有可释放的黑洞次数。"), 10); return; } int clearedCells = TriggerBlackHole(); if (clearedCells <= 0) { SetFeedbackMessage(_T("黑洞无处落点"), _T("棋盘上没有可吞噬的固定方块。"), 10); return; } rogueStats.blackHoleCharges--; ApplyBoardGravity(); int scoreGain = 0; int expGain = 0; AwardRogueSkillClearRewards(clearedCells, scoreGain, expGain, true); if (rogueStats.voidCoreLevel > 0) { rogueStats.pendingRainbowPieceCount++; } TCHAR detail[128]; if (rogueStats.voidCoreLevel > 0) { _stprintf_s( detail, _T("吞噬最多的一种方块,清除 %d 格 +%d 分 +%d EXP 并召来 1 个彩虹方块"), clearedCells, scoreGain, expGain); } else { _stprintf_s( detail, _T("吞噬最多的一种方块,清除 %d 格 +%d 分 +%d EXP"), clearedCells, scoreGain, expGain); } SetFeedbackMessage(_T("黑洞展开"), detail, 12); currentFallInterval = GetRogueFallInterval(); ComputeTarget(); } /** * @brief 响应玩家主动使用空中换形,将当前方块重塑为 I 块。 */ void UseAirReshape() { if (currentMode != MODE_ROGUE || currentScreen != SCREEN_PLAYING || suspendFlag || gameOverFlag) { return; } if (rogueStats.reshapeLevel <= 0) { SetFeedbackMessage(_T("换形术未掌握"), _T("获得空中换形后,才能按 V 重塑方块。"), 10); return; } if (rogueStats.reshapeCharges <= 0) { SetFeedbackMessage(_T("换形能量不足"), _T("没有可使用的换形次数。"), 10); return; } const int targetType = 0; const int candidateStates[2] = { 0, 1 }; const int candidateOffsets[5] = { 0, -1, 1, -2, 2 }; Point originalPoint = point; int originalType = type; int originalState = state; bool transformed = false; for (int stateIndex = 0; stateIndex < 2 && !transformed; stateIndex++) { int nextState = candidateStates[stateIndex]; for (int offsetIndex = 0; offsetIndex < 5; offsetIndex++) { Point candidatePoint = originalPoint; candidatePoint.x += candidateOffsets[offsetIndex]; if (IsPiecePlacementValid(targetType, nextState, candidatePoint)) { type = targetType; state = nextState; point = candidatePoint; transformed = true; break; } } } if (!transformed) { type = originalType; state = originalState; point = originalPoint; SetFeedbackMessage(_T("换形失败"), _T("空间不足,无法重塑为 I 块。"), 10); return; } rogueStats.reshapeCharges--; TCHAR detail[128]; _stprintf_s(detail, _T("方块已重塑为 I 块,剩余 %d 次。"), rogueStats.reshapeCharges); SetFeedbackMessage(_T("空中换形"), detail, 12); ComputeTarget(); }