Added new level sound
This commit is contained in:
@ -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();
|
||||
|
||||
Reference in New Issue
Block a user