Hold block added
This commit is contained in:
@ -362,7 +362,7 @@ bool ApplicationManager::initializeManagers() {
|
|||||||
consume = true;
|
consume = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!consume && sc == SDL_SCANCODE_H) {
|
if (!consume && sc == SDL_SCANCODE_F1) {
|
||||||
AppState currentState = m_stateManager ? m_stateManager->getState() : AppState::Loading;
|
AppState currentState = m_stateManager ? m_stateManager->getState() : AppState::Loading;
|
||||||
if (currentState != AppState::Loading) {
|
if (currentState != AppState::Loading) {
|
||||||
m_showHelpOverlay = !m_showHelpOverlay;
|
m_showHelpOverlay = !m_showHelpOverlay;
|
||||||
|
|||||||
@ -1357,6 +1357,11 @@ void GameRenderer::renderPlayingState(
|
|||||||
statLines.push_back({dropStr, 370.0f, 0.7f, dropColor});
|
statLines.push_back({dropStr, 370.0f, 0.7f, dropColor});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool scorePanelMetricsValid = false;
|
||||||
|
float scorePanelTop = 0.0f;
|
||||||
|
float scorePanelLeftX = 0.0f;
|
||||||
|
float scorePanelWidth = 0.0f;
|
||||||
|
|
||||||
if (!statLines.empty()) {
|
if (!statLines.empty()) {
|
||||||
float statsContentTop = std::numeric_limits<float>::max();
|
float statsContentTop = std::numeric_limits<float>::max();
|
||||||
float statsContentBottom = std::numeric_limits<float>::lowest();
|
float statsContentBottom = std::numeric_limits<float>::lowest();
|
||||||
@ -1383,6 +1388,11 @@ void GameRenderer::renderPlayingState(
|
|||||||
SDL_SetRenderDrawColor(renderer, 12, 18, 32, 205);
|
SDL_SetRenderDrawColor(renderer, 12, 18, 32, 205);
|
||||||
SDL_RenderFillRect(renderer, &statsBg);
|
SDL_RenderFillRect(renderer, &statsBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scorePanelMetricsValid = true;
|
||||||
|
scorePanelTop = statsPanelTop;
|
||||||
|
scorePanelLeftX = statsPanelLeft;
|
||||||
|
scorePanelWidth = statsPanelWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& line : statLines) {
|
for (const auto& line : statLines) {
|
||||||
@ -1393,10 +1403,30 @@ void GameRenderer::renderPlayingState(
|
|||||||
pixelFont->draw(renderer, logicalW - 260, 10, gravityHud, 0.9f, {200, 200, 220, 255});
|
pixelFont->draw(renderer, logicalW - 260, 10, gravityHud, 0.9f, {200, 200, 220, 255});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold piece (if implemented)
|
// Hold piece (right side, above score dashboard)
|
||||||
if (game->held().type < PIECE_COUNT) {
|
if (game->held().type < PIECE_COUNT) {
|
||||||
pixelFont->draw(renderer, statsX + 10, statsY + statsH - 80, "HOLD", 1.0f, {255, 220, 0, 255});
|
float holdLabelX = statsTextX;
|
||||||
drawSmallPiece(renderer, blocksTex, static_cast<PieceType>(game->held().type), statsX + 60, statsY + statsH - 80, finalBlockSize * 0.6f);
|
float holdY = statsY + statsH - 80.0f;
|
||||||
|
if (scorePanelMetricsValid) {
|
||||||
|
const float holdGap = 18.0f;
|
||||||
|
const float holdBlockH = (finalBlockSize * 0.6f) * 4.0f;
|
||||||
|
holdY = scorePanelTop - holdBlockH - holdGap;
|
||||||
|
holdLabelX = statsTextX;
|
||||||
|
// Ensure HOLD block doesn't drift too far left if the score panel gets narrow.
|
||||||
|
holdLabelX = std::max(holdLabelX, scorePanelLeftX + 14.0f);
|
||||||
|
// If the score panel is extremely narrow, keep within its bounds.
|
||||||
|
holdLabelX = std::min(holdLabelX, scorePanelLeftX + std::max(0.0f, scorePanelWidth - 90.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelFont->draw(renderer, holdLabelX, holdY, "HOLD", 1.0f, {255, 220, 0, 255});
|
||||||
|
drawSmallPiece(
|
||||||
|
renderer,
|
||||||
|
blocksTex,
|
||||||
|
static_cast<PieceType>(game->held().type),
|
||||||
|
holdLabelX + 50.0f,
|
||||||
|
holdY + 2.0f,
|
||||||
|
finalBlockSize * 0.6f
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause overlay logic moved to renderPauseOverlay
|
// Pause overlay logic moved to renderPauseOverlay
|
||||||
|
|||||||
@ -34,7 +34,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l
|
|||||||
if (!renderer) return;
|
if (!renderer) return;
|
||||||
|
|
||||||
const std::array<ShortcutEntry, 5> generalShortcuts{{
|
const std::array<ShortcutEntry, 5> generalShortcuts{{
|
||||||
{"H", "Toggle this help overlay"},
|
{"F1", "Toggle this help overlay"},
|
||||||
{"ESC", "Back / cancel current popup"},
|
{"ESC", "Back / cancel current popup"},
|
||||||
{"F11 or ALT+ENTER", "Toggle fullscreen"},
|
{"F11 or ALT+ENTER", "Toggle fullscreen"},
|
||||||
{"M", "Mute or unmute music"},
|
{"M", "Mute or unmute music"},
|
||||||
@ -46,11 +46,12 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l
|
|||||||
{"ENTER / SPACE", "Activate highlighted action"}
|
{"ENTER / SPACE", "Activate highlighted action"}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const std::array<ShortcutEntry, 7> gameplayShortcuts{{
|
const std::array<ShortcutEntry, 8> gameplayShortcuts{{
|
||||||
{"LEFT / RIGHT", "Move active piece"},
|
{"LEFT / RIGHT", "Move active piece"},
|
||||||
{"DOWN", "Soft drop (faster fall)"},
|
{"DOWN", "Soft drop (faster fall)"},
|
||||||
{"SPACE", "Hard drop / instant lock"},
|
{"SPACE", "Hard drop / instant lock"},
|
||||||
{"UP", "Rotate clockwise"},
|
{"UP", "Rotate clockwise"},
|
||||||
|
{"H", "Hold / swap current piece"},
|
||||||
{"X", "Toggle rotation direction used by UP"},
|
{"X", "Toggle rotation direction used by UP"},
|
||||||
{"P", "Pause or resume"},
|
{"P", "Pause or resume"},
|
||||||
{"ESC", "Open exit confirmation"}
|
{"ESC", "Open exit confirmation"}
|
||||||
@ -134,7 +135,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l
|
|||||||
SDL_SetRenderDrawColor(renderer, 90, 110, 170, 255);
|
SDL_SetRenderDrawColor(renderer, 90, 110, 170, 255);
|
||||||
SDL_RenderRect(renderer, &footerRect);
|
SDL_RenderRect(renderer, &footerRect);
|
||||||
|
|
||||||
const char* closeLabel = "PRESS H OR ESC TO CLOSE";
|
const char* closeLabel = "PRESS F1 OR ESC TO CLOSE";
|
||||||
float closeScale = fitScale(font, closeLabel, 1.0f, footerRect.w - footerPadding * 2.0f);
|
float closeScale = fitScale(font, closeLabel, 1.0f, footerRect.w - footerPadding * 2.0f);
|
||||||
int closeW = 0, closeH = 0;
|
int closeW = 0, closeH = 0;
|
||||||
font.measure(closeLabel, closeScale, closeW, closeH);
|
font.measure(closeLabel, closeScale, closeW, closeH);
|
||||||
|
|||||||
@ -848,8 +848,8 @@ int main(int, char **)
|
|||||||
SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled());
|
SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled());
|
||||||
Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled());
|
Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled());
|
||||||
}
|
}
|
||||||
// Disable H-help shortcut on the main menu; keep it elsewhere
|
// Help overlay toggle: F1 (keep it disabled on Loading/Menu)
|
||||||
if (e.key.scancode == SDL_SCANCODE_H && state != AppState::Loading && state != AppState::Menu)
|
if (e.key.scancode == SDL_SCANCODE_F1 && state != AppState::Loading && state != AppState::Menu)
|
||||||
{
|
{
|
||||||
showHelpOverlay = !showHelpOverlay;
|
showHelpOverlay = !showHelpOverlay;
|
||||||
if (state == AppState::Playing) {
|
if (state == AppState::Playing) {
|
||||||
|
|||||||
@ -1134,7 +1134,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
|||||||
// Shortcut entries (copied from HelpOverlay)
|
// Shortcut entries (copied from HelpOverlay)
|
||||||
struct ShortcutEntry { const char* combo; const char* description; };
|
struct ShortcutEntry { const char* combo; const char* description; };
|
||||||
const ShortcutEntry generalShortcuts[] = {
|
const ShortcutEntry generalShortcuts[] = {
|
||||||
{"H", "Toggle this help overlay"},
|
{"F1", "Toggle this help overlay"},
|
||||||
{"ESC", "Back / cancel current popup"},
|
{"ESC", "Back / cancel current popup"},
|
||||||
{"F11 or ALT+ENTER", "Toggle fullscreen"},
|
{"F11 or ALT+ENTER", "Toggle fullscreen"},
|
||||||
{"M", "Mute or unmute music"},
|
{"M", "Mute or unmute music"},
|
||||||
@ -1149,6 +1149,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
|||||||
{"DOWN", "Soft drop (faster fall)"},
|
{"DOWN", "Soft drop (faster fall)"},
|
||||||
{"SPACE", "Hard drop / instant lock"},
|
{"SPACE", "Hard drop / instant lock"},
|
||||||
{"UP", "Rotate clockwise"},
|
{"UP", "Rotate clockwise"},
|
||||||
|
{"H", "Hold / swap current piece"},
|
||||||
{"X", "Toggle rotation direction used by UP"},
|
{"X", "Toggle rotation direction used by UP"},
|
||||||
{"P", "Pause or resume"},
|
{"P", "Pause or resume"},
|
||||||
{"ESC", "Open exit confirmation"}
|
{"ESC", "Open exit confirmation"}
|
||||||
|
|||||||
@ -118,6 +118,12 @@ void PlayingState::handleEvent(const SDL_Event& e) {
|
|||||||
|
|
||||||
// Tetris controls (only when not paused)
|
// Tetris controls (only when not paused)
|
||||||
if (!ctx.game->isPaused()) {
|
if (!ctx.game->isPaused()) {
|
||||||
|
// Hold / swap current piece (H)
|
||||||
|
if (e.key.scancode == SDL_SCANCODE_H) {
|
||||||
|
ctx.game->holdCurrent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Rotation (still event-based for precise timing)
|
// Rotation (still event-based for precise timing)
|
||||||
if (e.key.scancode == SDL_SCANCODE_UP) {
|
if (e.key.scancode == SDL_SCANCODE_UP) {
|
||||||
// Use user setting to determine whether UP rotates clockwise
|
// Use user setting to determine whether UP rotates clockwise
|
||||||
|
|||||||
Reference in New Issue
Block a user