removed s shortcut for sound fx toggle

This commit is contained in:
2025-12-21 17:22:30 +01:00
parent ab22d4c34f
commit b46af7ab1d
10 changed files with 236 additions and 8 deletions

View File

@ -5,7 +5,7 @@
Fullscreen=1 Fullscreen=1
[Audio] [Audio]
Music=1 Music=0
Sound=1 Sound=1
[Gameplay] [Gameplay]

View File

@ -775,7 +775,8 @@ void TetrisApp::Impl::runLoop()
Settings::instance().setMusicEnabled(true); Settings::instance().setMusicEnabled(true);
} }
} }
if (e.key.scancode == SDL_SCANCODE_S) // K: Toggle sound effects (S is reserved for co-op movement)
if (e.key.scancode == SDL_SCANCODE_K)
{ {
SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled()); SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled());
Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled()); Settings::instance().setSoundEnabled(SoundEffectManager::instance().isEnabled());

View File

@ -932,8 +932,8 @@ void ApplicationManager::setupStateHandlers() {
m_showExitConfirmPopup = true; m_showExitConfirmPopup = true;
return; return;
} }
// S: toggle SFX enable state (music handled globally) // K: toggle SFX enable state (music handled globally)
if (event.key.scancode == SDL_SCANCODE_S) { if (event.key.scancode == SDL_SCANCODE_K) {
SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled()); SoundEffectManager::instance().setEnabled(!SoundEffectManager::instance().isEnabled());
} }
} }

View File

@ -1841,6 +1841,7 @@ void GameRenderer::renderCoopPlayingState(
SDL_Texture* scorePanelTex, SDL_Texture* scorePanelTex,
SDL_Texture* nextPanelTex, SDL_Texture* nextPanelTex,
SDL_Texture* holdPanelTex, SDL_Texture* holdPanelTex,
bool paused,
float logicalW, float logicalW,
float logicalH, float logicalH,
float logicalScale, float logicalScale,
@ -1962,6 +1963,229 @@ void GameRenderer::renderCoopPlayingState(
SDL_RenderLine(renderer, gridX, lineY, gridX + GRID_W, lineY); SDL_RenderLine(renderer, gridX, lineY, gridX + GRID_W, lineY);
} }
// In-grid 3D starfield + ambient sparkles (match classic feel, per-half)
{
static Uint32 s_lastCoopSparkTick = SDL_GetTicks();
static std::mt19937 s_coopSparkRng{ std::random_device{}() };
static std::vector<Sparkle> s_leftSparkles;
static std::vector<Sparkle> s_rightSparkles;
static std::vector<ImpactSpark> s_leftImpactSparks;
static std::vector<ImpactSpark> s_rightImpactSparks;
static float s_leftSparkleSpawnAcc = 0.0f;
static float s_rightSparkleSpawnAcc = 0.0f;
float halfW = GRID_W * 0.5f;
const float leftGridX = gridX;
const float rightGridX = gridX + halfW;
Uint32 sparkNow = nowTicks;
float sparkDeltaMs = static_cast<float>(sparkNow - s_lastCoopSparkTick);
s_lastCoopSparkTick = sparkNow;
if (sparkDeltaMs < 0.0f || sparkDeltaMs > 100.0f) {
sparkDeltaMs = 16.0f;
}
if (!s_starfieldInitialized) {
s_inGridStarfield.init(static_cast<int>(halfW), static_cast<int>(GRID_H), 180);
s_starfieldInitialized = true;
} else {
s_inGridStarfield.resize(static_cast<int>(halfW), static_cast<int>(GRID_H));
}
const float deltaSeconds = std::clamp(sparkDeltaMs / 1000.0f, 0.0f, 0.033f);
s_inGridStarfield.update(deltaSeconds);
struct MagnetInfo { bool active{false}; float x{0.0f}; float y{0.0f}; };
auto computeMagnet = [&](CoopGame::PlayerSide side) -> MagnetInfo {
MagnetInfo info{};
const CoopGame::Piece& activePiece = game->current(side);
const int pieceType = static_cast<int>(activePiece.type);
if (pieceType < 0 || pieceType >= PIECE_COUNT) {
return info;
}
float sumLocalX = 0.0f;
float sumLocalY = 0.0f;
int filledCells = 0;
const int localXOffsetCols = (side == CoopGame::PlayerSide::Right) ? 10 : 0;
for (int cy = 0; cy < 4; ++cy) {
for (int cx = 0; cx < 4; ++cx) {
if (!CoopGame::cellFilled(activePiece, cx, cy)) continue;
sumLocalX += ((activePiece.x - localXOffsetCols) + cx + 0.5f) * finalBlockSize;
sumLocalY += (activePiece.y + cy + 0.5f) * finalBlockSize;
++filledCells;
}
}
if (filledCells <= 0) {
return info;
}
info.active = true;
info.x = std::clamp(sumLocalX / static_cast<float>(filledCells), 0.0f, halfW);
info.y = std::clamp(sumLocalY / static_cast<float>(filledCells), 0.0f, GRID_H);
return info;
};
const MagnetInfo leftMagnet = computeMagnet(CoopGame::PlayerSide::Left);
const MagnetInfo rightMagnet = computeMagnet(CoopGame::PlayerSide::Right);
SDL_BlendMode oldBlend = SDL_BLENDMODE_NONE;
SDL_GetRenderDrawBlendMode(renderer, &oldBlend);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
auto drawStarfieldHalf = [&](float originX, const MagnetInfo& magnet) {
if (magnet.active) {
const float magnetStrength = finalBlockSize * 2.2f;
s_inGridStarfield.setMagnetTarget(magnet.x, magnet.y, magnetStrength);
} else {
s_inGridStarfield.clearMagnetTarget();
}
const float jitterAmp = 1.6f;
const float tms = static_cast<float>(sparkNow) * 0.001f;
const float jitterX = std::sin(tms * 1.7f) * jitterAmp + std::cos(tms * 0.9f) * 0.4f;
const float jitterY = std::sin(tms * 1.1f + 3.7f) * (jitterAmp * 0.6f);
s_inGridStarfield.draw(renderer, originX + jitterX, gridY + jitterY, 0.22f, true);
};
drawStarfieldHalf(leftGridX, leftMagnet);
drawStarfieldHalf(rightGridX, rightMagnet);
auto updateAndDrawSparkleLayer = [&](std::vector<Sparkle>& sparkles,
std::vector<ImpactSpark>& impactSparks,
float& spawnAcc,
const MagnetInfo& magnet,
float originX) {
if (!paused) {
const float spawnInterval = 0.08f;
spawnAcc += deltaSeconds;
while (spawnAcc >= spawnInterval) {
spawnAcc -= spawnInterval;
Sparkle s;
bool spawnNearPiece = magnet.active && (std::uniform_real_distribution<float>(0.0f, 1.0f)(s_coopSparkRng) > 0.35f);
float sx = 0.0f;
float sy = 0.0f;
if (spawnNearPiece) {
float jitterX = std::uniform_real_distribution<float>(-finalBlockSize * 1.2f, finalBlockSize * 1.2f)(s_coopSparkRng);
float jitterY = std::uniform_real_distribution<float>(-finalBlockSize * 1.2f, finalBlockSize * 1.2f)(s_coopSparkRng);
sx = std::clamp(magnet.x + jitterX, -finalBlockSize * 2.0f, halfW + finalBlockSize * 2.0f);
sy = std::clamp(magnet.y + jitterY, -finalBlockSize * 2.0f, GRID_H + finalBlockSize * 2.0f);
} else {
float side = std::uniform_real_distribution<float>(0.0f, 1.0f)(s_coopSparkRng);
const float borderBand = std::max(12.0f, finalBlockSize * 1.0f);
if (side < 0.2f) {
sx = std::uniform_real_distribution<float>(-borderBand, 0.0f)(s_coopSparkRng);
sy = std::uniform_real_distribution<float>(-borderBand, GRID_H + borderBand)(s_coopSparkRng);
} else if (side < 0.4f) {
sx = std::uniform_real_distribution<float>(halfW, halfW + borderBand)(s_coopSparkRng);
sy = std::uniform_real_distribution<float>(-borderBand, GRID_H + borderBand)(s_coopSparkRng);
} else if (side < 0.6f) {
sx = std::uniform_real_distribution<float>(-borderBand, halfW + borderBand)(s_coopSparkRng);
sy = std::uniform_real_distribution<float>(-borderBand, 0.0f)(s_coopSparkRng);
} else if (side < 0.9f) {
sx = std::uniform_real_distribution<float>(0.0f, halfW)(s_coopSparkRng);
sy = std::uniform_real_distribution<float>(0.0f, finalBlockSize * 2.0f)(s_coopSparkRng);
} else {
sx = std::uniform_real_distribution<float>(-borderBand, halfW + borderBand)(s_coopSparkRng);
sy = std::uniform_real_distribution<float>(GRID_H, GRID_H + borderBand)(s_coopSparkRng);
}
}
s.x = sx;
s.y = sy;
float speed = std::uniform_real_distribution<float>(10.0f, 60.0f)(s_coopSparkRng);
float ang = std::uniform_real_distribution<float>(-3.14159f, 3.14159f)(s_coopSparkRng);
s.vx = std::cos(ang) * speed;
s.vy = std::sin(ang) * speed * 0.25f;
s.maxLifeMs = std::uniform_real_distribution<float>(350.0f, 900.0f)(s_coopSparkRng);
s.lifeMs = s.maxLifeMs;
s.size = std::uniform_real_distribution<float>(1.5f, 5.0f)(s_coopSparkRng);
if (std::uniform_real_distribution<float>(0.0f, 1.0f)(s_coopSparkRng) < 0.5f) {
s.color = SDL_Color{255, 230, 180, 255};
} else {
s.color = SDL_Color{180, 220, 255, 255};
}
s.pulse = std::uniform_real_distribution<float>(0.0f, 6.28f)(s_coopSparkRng);
sparkles.push_back(s);
}
}
if (!sparkles.empty()) {
auto it = sparkles.begin();
while (it != sparkles.end()) {
Sparkle& sp = *it;
sp.lifeMs -= sparkDeltaMs;
if (sp.lifeMs <= 0.0f) {
const int burstCount = std::uniform_int_distribution<int>(4, 8)(s_coopSparkRng);
for (int bi = 0; bi < burstCount; ++bi) {
ImpactSpark ps;
ps.x = originX + sp.x + std::uniform_real_distribution<float>(-2.0f, 2.0f)(s_coopSparkRng);
ps.y = gridY + sp.y + std::uniform_real_distribution<float>(-2.0f, 2.0f)(s_coopSparkRng);
float ang = std::uniform_real_distribution<float>(0.0f, 6.2831853f)(s_coopSparkRng);
float speed = std::uniform_real_distribution<float>(10.0f, 120.0f)(s_coopSparkRng);
ps.vx = std::cos(ang) * speed;
ps.vy = std::sin(ang) * speed * 0.8f;
ps.maxLifeMs = std::uniform_real_distribution<float>(220.0f, 500.0f)(s_coopSparkRng);
ps.lifeMs = ps.maxLifeMs;
ps.size = std::max(1.0f, sp.size * 0.5f);
ps.color = sp.color;
impactSparks.push_back(ps);
}
it = sparkles.erase(it);
continue;
}
float lifeRatio = sp.lifeMs / sp.maxLifeMs;
sp.x += sp.vx * deltaSeconds;
sp.y += sp.vy * deltaSeconds;
sp.vy *= 0.995f;
sp.pulse += deltaSeconds * 8.0f;
float pulse = 0.5f + 0.5f * std::sin(sp.pulse);
Uint8 alpha = static_cast<Uint8>(std::clamp(lifeRatio * pulse, 0.0f, 1.0f) * 255.0f);
SDL_SetRenderDrawColor(renderer, sp.color.r, sp.color.g, sp.color.b, alpha);
float half = sp.size * 0.5f;
SDL_FRect fr{ originX + sp.x - half, gridY + sp.y - half, sp.size, sp.size };
SDL_RenderFillRect(renderer, &fr);
++it;
}
}
if (!impactSparks.empty()) {
auto it = impactSparks.begin();
while (it != impactSparks.end()) {
ImpactSpark& spark = *it;
spark.vy += 0.00045f * sparkDeltaMs;
spark.x += spark.vx * sparkDeltaMs;
spark.y += spark.vy * sparkDeltaMs;
spark.lifeMs -= sparkDeltaMs;
if (spark.lifeMs <= 0.0f) {
it = impactSparks.erase(it);
continue;
}
float lifeRatio = spark.lifeMs / spark.maxLifeMs;
Uint8 alpha = static_cast<Uint8>(std::clamp(lifeRatio, 0.0f, 1.0f) * 160.0f);
SDL_SetRenderDrawColor(renderer, spark.color.r, spark.color.g, spark.color.b, alpha);
SDL_FRect sparkRect{
spark.x - spark.size * 0.5f,
spark.y - spark.size * 0.5f,
spark.size,
spark.size * 1.4f
};
SDL_RenderFillRect(renderer, &sparkRect);
++it;
}
}
};
updateAndDrawSparkleLayer(s_leftSparkles, s_leftImpactSparks, s_leftSparkleSpawnAcc, leftMagnet, leftGridX);
updateAndDrawSparkleLayer(s_rightSparkles, s_rightImpactSparks, s_rightSparkleSpawnAcc, rightMagnet, rightGridX);
SDL_SetRenderDrawBlendMode(renderer, oldBlend);
}
// Half-row feedback: lightly tint rows where one side is filled, brighter where both are pending clear // Half-row feedback: lightly tint rows where one side is filled, brighter where both are pending clear
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
const auto& rowStates = game->rowHalfStates(); const auto& rowStates = game->rowHalfStates();

View File

@ -72,6 +72,7 @@ public:
SDL_Texture* scorePanelTex, SDL_Texture* scorePanelTex,
SDL_Texture* nextPanelTex, SDL_Texture* nextPanelTex,
SDL_Texture* holdPanelTex, SDL_Texture* holdPanelTex,
bool paused,
float logicalW, float logicalW,
float logicalH, float logicalH,
float logicalScale, float logicalScale,

View File

@ -232,6 +232,6 @@ void UIRenderer::drawSettingsPopup(SDL_Renderer* renderer, FontAtlas* font, floa
// Instructions // Instructions
font->draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, {200, 200, 220, 255}); font->draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, {200, 200, 220, 255});
font->draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255}); font->draw(renderer, popupX + 20, popupY + 170, "K = TOGGLE SOUND FX", 1.0f, {200, 200, 220, 255});
font->draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, {200, 200, 220, 255}); font->draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, {200, 200, 220, 255});
} }

View File

@ -38,7 +38,7 @@ void Render(SDL_Renderer* renderer, FontAtlas& font, float logicalWidth, float l
{"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"},
{"S", "Toggle sound effects"} {"K", "Toggle sound effects"}
}}; }};
const std::array<ShortcutEntry, 2> menuShortcuts{{ const std::array<ShortcutEntry, 2> menuShortcuts{{

View File

@ -1248,7 +1248,7 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
{"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"},
{"S", "Toggle sound effects"} {"K", "Toggle sound effects"}
}; };
const ShortcutEntry menuShortcuts[] = { const ShortcutEntry menuShortcuts[] = {
{"ARROW KEYS", "Navigate menu buttons"}, {"ARROW KEYS", "Navigate menu buttons"},

View File

@ -322,6 +322,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l
ctx.scorePanelTex, ctx.scorePanelTex,
ctx.nextPanelTex, ctx.nextPanelTex,
ctx.holdPanelTex, ctx.holdPanelTex,
paused,
1200.0f, 1200.0f,
1000.0f, 1000.0f,
logicalScale, logicalScale,
@ -438,6 +439,7 @@ void PlayingState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect l
ctx.scorePanelTex, ctx.scorePanelTex,
ctx.nextPanelTex, ctx.nextPanelTex,
ctx.holdPanelTex, ctx.holdPanelTex,
paused,
1200.0f, 1200.0f,
1000.0f, 1000.0f,
logicalScale, logicalScale,

View File

@ -83,6 +83,6 @@ void menu_drawSettingsPopup(SDL_Renderer* renderer, FontAtlas& font, bool musicE
bool sfxOn = true; bool sfxOn = true;
font.draw(renderer, popupX + 140, popupY + 100, sfxOn ? "ON" : "OFF", 1.5f, sfxOn ? SDL_Color{0,255,0,255} : SDL_Color{255,0,0,255}); font.draw(renderer, popupX + 140, popupY + 100, sfxOn ? "ON" : "OFF", 1.5f, sfxOn ? SDL_Color{0,255,0,255} : SDL_Color{255,0,0,255});
font.draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, SDL_Color{200,200,220,255}); font.draw(renderer, popupX + 20, popupY + 150, "M = TOGGLE MUSIC", 1.0f, SDL_Color{200,200,220,255});
font.draw(renderer, popupX + 20, popupY + 170, "S = TOGGLE SOUND FX", 1.0f, SDL_Color{200,200,220,255}); font.draw(renderer, popupX + 20, popupY + 170, "K = TOGGLE SOUND FX", 1.0f, SDL_Color{200,200,220,255});
font.draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, SDL_Color{200,200,220,255}); font.draw(renderer, popupX + 20, popupY + 190, "ESC = CLOSE", 1.0f, SDL_Color{200,200,220,255});
} }