Added new level sound

This commit is contained in:
2025-11-30 10:37:35 +01:00
parent 69b2521695
commit 5fd77fdaf0
7 changed files with 219 additions and 5 deletions

View File

@ -6,8 +6,23 @@
#include <array>
#include <cmath>
#include <cstdio>
#include <random>
#include <vector>
#include "../../core/Settings.h"
namespace {
struct ImpactSpark {
float x = 0.0f;
float y = 0.0f;
float vx = 0.0f;
float vy = 0.0f;
float lifeMs = 0.0f;
float maxLifeMs = 0.0f;
float size = 0.0f;
SDL_Color color{255, 255, 255, 255};
};
}
// Color constants (copied from main.cpp)
static const SDL_Color COLORS[] = {
{0, 0, 0, 255}, // 0: BLACK (empty)
@ -126,6 +141,18 @@ void GameRenderer::renderPlayingState(
float winH
) {
if (!game || !pixelFont) return;
static std::vector<ImpactSpark> s_impactSparks;
static uint32_t s_lastImpactFxId = 0;
static Uint32 s_lastImpactTick = SDL_GetTicks();
static std::mt19937 s_impactRng{ std::random_device{}() };
Uint32 nowTicks = SDL_GetTicks();
float sparkDeltaMs = static_cast<float>(nowTicks - s_lastImpactTick);
s_lastImpactTick = nowTicks;
if (sparkDeltaMs < 0.0f || sparkDeltaMs > 100.0f) {
sparkDeltaMs = 16.0f;
}
// Calculate actual content area (centered within the window)
float contentScale = logicalScale;
@ -223,19 +250,132 @@ void GameRenderer::renderPlayingState(
drawRectWithOffset(nextX - 3 - contentOffsetX, nextY - 3 - contentOffsetY, nextW + 6, nextH + 6, {100, 120, 200, 255});
drawRectWithOffset(nextX - contentOffsetX, nextY - contentOffsetY, nextW, nextH, {30, 35, 50, 255});
// Precompute row drop offsets (line collapse effect)
std::array<float, Game::ROWS> rowDropOffsets{};
for (int y = 0; y < Game::ROWS; ++y) {
rowDropOffsets[y] = (lineEffect ? lineEffect->getRowDropOffset(y) : 0.0f);
}
// Draw the game board
const auto &board = game->boardRef();
float impactStrength = 0.0f;
float impactEased = 0.0f;
std::array<uint8_t, Game::COLS * Game::ROWS> impactMask{};
std::array<float, Game::COLS * Game::ROWS> impactWeight{};
if (game->hasHardDropShake()) {
impactStrength = static_cast<float>(game->hardDropShakeStrength());
impactStrength = std::clamp(impactStrength, 0.0f, 1.0f);
impactEased = impactStrength * impactStrength;
const auto& impactCells = game->getHardDropCells();
for (const auto& cell : impactCells) {
if (cell.x < 0 || cell.x >= Game::COLS || cell.y < 0 || cell.y >= Game::ROWS) {
continue;
}
int idx = cell.y * Game::COLS + cell.x;
impactMask[idx] = 1;
impactWeight[idx] = 1.0f;
int depth = 0;
for (int ny = cell.y + 1; ny < Game::ROWS && depth < 4; ++ny) {
if (board[ny * Game::COLS + cell.x] == 0) {
break;
}
++depth;
int nidx = ny * Game::COLS + cell.x;
impactMask[nidx] = 1;
float weight = std::max(0.15f, 1.0f - depth * 0.35f);
impactWeight[nidx] = std::max(impactWeight[nidx], weight);
}
}
}
bool shouldSpawnCrackles = game->hasHardDropShake() && !game->getHardDropCells().empty() && game->getHardDropFxId() != s_lastImpactFxId;
if (shouldSpawnCrackles) {
s_lastImpactFxId = game->getHardDropFxId();
std::uniform_real_distribution<float> jitter(-finalBlockSize * 0.2f, finalBlockSize * 0.2f);
std::uniform_real_distribution<float> velX(-0.04f, 0.04f);
std::uniform_real_distribution<float> velY(0.035f, 0.07f);
std::uniform_real_distribution<float> lifespan(210.0f, 320.0f);
std::uniform_real_distribution<float> sizeDist(finalBlockSize * 0.08f, finalBlockSize * 0.14f);
const auto& impactCells = game->getHardDropCells();
for (const auto& cell : impactCells) {
if (cell.x < 0 || cell.x >= Game::COLS || cell.y < 0 || cell.y >= Game::ROWS) {
continue;
}
int idx = cell.y * Game::COLS + cell.x;
int v = (cell.y >= 0) ? board[idx] : 0;
SDL_Color baseColor = (v > 0 && v < PIECE_COUNT + 1) ? COLORS[v] : SDL_Color{255, 220, 180, 255};
float cellX = gridX + (cell.x + 0.5f) * finalBlockSize;
float cellY = gridY + (cell.y + 0.85f) * finalBlockSize + rowDropOffsets[cell.y];
for (int i = 0; i < 4; ++i) {
ImpactSpark spark;
spark.x = cellX + jitter(s_impactRng);
spark.y = cellY + jitter(s_impactRng) * 0.25f;
spark.vx = velX(s_impactRng);
spark.vy = velY(s_impactRng);
spark.lifeMs = lifespan(s_impactRng);
spark.maxLifeMs = spark.lifeMs;
spark.size = sizeDist(s_impactRng);
spark.color = SDL_Color{
static_cast<Uint8>(std::min(255, baseColor.r + 30)),
static_cast<Uint8>(std::min(255, baseColor.g + 30)),
static_cast<Uint8>(std::min(255, baseColor.b + 30)),
255
};
s_impactSparks.push_back(spark);
}
}
}
for (int y = 0; y < Game::ROWS; ++y) {
float dropOffset = (lineEffect ? lineEffect->getRowDropOffset(y) : 0.0f);
float dropOffset = rowDropOffsets[y];
for (int x = 0; x < Game::COLS; ++x) {
int v = board[y * Game::COLS + x];
if (v > 0) {
float bx = gridX + x * finalBlockSize;
float by = gridY + y * finalBlockSize + dropOffset;
const int cellIdx = y * Game::COLS + x;
float weight = impactWeight[cellIdx];
if (impactStrength > 0.0f && weight > 0.0f && impactMask[cellIdx]) {
float cellSeed = static_cast<float>((x * 37 + y * 61) % 113);
float t = static_cast<float>(nowTicks % 10000) * 0.018f + cellSeed;
float amplitude = 6.0f * impactEased * weight;
float freq = 2.0f + weight * 1.3f;
bx += amplitude * std::sin(t * freq);
by += amplitude * 0.75f * std::cos(t * (freq + 1.1f));
}
drawBlockTexture(renderer, blocksTex, bx, by, finalBlockSize, v - 1);
}
}
}
if (!s_impactSparks.empty()) {
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
auto it = s_impactSparks.begin();
while (it != s_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 = s_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;
}
}
bool allowActivePieceRender = true;
const bool smoothScrollEnabled = Settings::instance().isSmoothScrollEnabled();