127 lines
3.9 KiB
C++
127 lines
3.9 KiB
C++
// LineEffect.h - Line clearing visual and audio effects
|
|
#pragma once
|
|
#include <SDL3/SDL.h>
|
|
#include <vector>
|
|
#include <random>
|
|
#include <array>
|
|
|
|
#include "../core/Game.h"
|
|
|
|
class LineEffect {
|
|
public:
|
|
struct Particle {
|
|
enum class Style { Shard, Ember };
|
|
float x, y;
|
|
float vx, vy;
|
|
float size;
|
|
float alpha;
|
|
float life;
|
|
float lifeSpan;
|
|
int blockType;
|
|
SDL_Color color;
|
|
Style style;
|
|
|
|
Particle(float px, float py, Style particleStyle, SDL_Color tint);
|
|
void update(float dt);
|
|
void render(SDL_Renderer* renderer, SDL_Texture* blocksTex);
|
|
bool isAlive() const { return life < lifeSpan && alpha > 0.01f; }
|
|
};
|
|
|
|
struct Spark {
|
|
float x, y;
|
|
float vx, vy;
|
|
float life;
|
|
float maxLife;
|
|
float thickness;
|
|
SDL_Color color;
|
|
|
|
Spark(float px, float py, SDL_Color tint);
|
|
void update(float dt);
|
|
void render(SDL_Renderer* renderer) const;
|
|
bool isAlive() const { return life < maxLife; }
|
|
};
|
|
|
|
struct GlowPulse {
|
|
float x, y;
|
|
float baseRadius;
|
|
float maxRadius;
|
|
float life;
|
|
float maxLife;
|
|
SDL_Color color;
|
|
|
|
GlowPulse(float px, float py, float baseR, float maxR, SDL_Color tint);
|
|
void update(float dt);
|
|
void render(SDL_Renderer* renderer) const;
|
|
bool isAlive() const { return life < maxLife; }
|
|
};
|
|
|
|
enum class AnimationState {
|
|
IDLE,
|
|
FLASH_WHITE,
|
|
EXPLODE_BLOCKS,
|
|
BLOCKS_DROP
|
|
};
|
|
|
|
LineEffect();
|
|
~LineEffect();
|
|
|
|
bool init(SDL_Renderer* renderer);
|
|
void shutdown();
|
|
|
|
// Start line clear effect for the specified rows
|
|
void startLineClear(const std::vector<int>& rows, int gridX, int gridY, int blockSize, int gridCols = Game::COLS, int gapPx = 0, int gapAfterCol = 0);
|
|
|
|
// Update and render the effect
|
|
bool update(float deltaTime); // Returns true if effect is complete
|
|
void render(SDL_Renderer* renderer, SDL_Texture* blocksTex, int gridX, int gridY, int blockSize, int gapPx = 0, int gapAfterCol = 0);
|
|
float getRowDropOffset(int row) const;
|
|
|
|
// Audio
|
|
void playLineClearSound(int lineCount);
|
|
|
|
bool isActive() const { return state != AnimationState::IDLE; }
|
|
|
|
private:
|
|
SDL_Renderer* renderer{nullptr};
|
|
AnimationState state{AnimationState::IDLE};
|
|
float timer{0.0f};
|
|
std::vector<int> clearingRows;
|
|
std::vector<Particle> particles;
|
|
std::vector<Spark> sparks;
|
|
std::vector<GlowPulse> glowPulses;
|
|
std::mt19937 rng{std::random_device{}()};
|
|
|
|
// Audio resources
|
|
SDL_AudioStream* audioStream{nullptr};
|
|
std::vector<int16_t> lineClearSample;
|
|
std::vector<int16_t> tetrisSample;
|
|
|
|
// Animation timing - Flash then immediate explosion effect
|
|
static constexpr float FLASH_DURATION = 0.18f; // Slightly longer flash for anticipation
|
|
static constexpr float EXPLODE_DURATION = 0.9f; // Extended fireworks time
|
|
static constexpr float DROP_DURATION = 0.35f; // Allow lingering sparks before collapse
|
|
|
|
void createParticles(int row, int gridX, int gridY, int blockSize);
|
|
void spawnShardBurst(float x, float y, SDL_Color tint);
|
|
void spawnSparkBurst(float x, float y, SDL_Color tint);
|
|
void spawnGlowPulse(float x, float y, float blockSize, SDL_Color tint);
|
|
void updateParticles(float dt);
|
|
void updateSparks(float dt);
|
|
void updateGlowPulses(float dt);
|
|
void renderFlash(int gridX, int gridY, int blockSize);
|
|
void renderExplosion(SDL_Texture* blocksTex);
|
|
void renderGlowPulses();
|
|
void renderSparks();
|
|
void renderParticleGlows();
|
|
bool loadAudioSample(const std::string& path, std::vector<int16_t>& sample);
|
|
void initAudio();
|
|
SDL_Color pickFireColor() const;
|
|
|
|
std::array<float, Game::ROWS> rowDropTargets{};
|
|
float dropProgress = 0.0f;
|
|
int dropBlockSize = 0;
|
|
int effectGridCols = Game::COLS;
|
|
int effectGapPx = 0;
|
|
int effectGapAfterCol = 0;
|
|
};
|