// LineEffect.h - Line clearing visual and audio effects #pragma once #include #include #include #include #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& 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 clearingRows; std::vector particles; std::vector sparks; std::vector glowPulses; std::mt19937 rng{std::random_device{}()}; // Audio resources SDL_AudioStream* audioStream{nullptr}; std::vector lineClearSample; std::vector 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& sample); void initAudio(); SDL_Color pickFireColor() const; std::array rowDropTargets{}; float dropProgress = 0.0f; int dropBlockSize = 0; int effectGridCols = Game::COLS; int effectGapPx = 0; int effectGapAfterCol = 0; };