#include "app/Fireworks.h" #include #include #include #include #include namespace { struct BlockParticle { float x{}, y{}, vx{}, vy{}, size{}, alpha{}, decay{}, wobblePhase{}, wobbleSpeed{}, coreHeat{}; BlockParticle(float sx, float sy) : x(sx), y(sy) { const float spreadDeg = 35.0f; const float angleDeg = -90.0f + spreadDeg * ((rand() % 200) / 100.0f - 1.0f); const float angleRad = angleDeg * 3.1415926f / 180.0f; float speed = 1.3f + (rand() % 220) / 80.0f; vx = std::cos(angleRad) * speed * 0.55f; vy = std::sin(angleRad) * speed; size = 6.0f + (rand() % 40) / 10.0f; alpha = 1.0f; decay = 0.0095f + (rand() % 180) / 12000.0f; wobblePhase = (rand() % 628) / 100.0f; wobbleSpeed = 0.08f + (rand() % 60) / 600.0f; coreHeat = 0.65f + (rand() % 35) / 100.0f; } bool update() { vx *= 0.992f; vy = vy * 0.985f - 0.015f; x += vx; y += vy; wobblePhase += wobbleSpeed; x += std::sin(wobblePhase) * 0.12f; alpha -= decay; size = std::max(1.8f, size - 0.03f); coreHeat = std::max(0.0f, coreHeat - decay * 0.6f); return alpha > 0.03f; } }; struct TetrisFirework { std::vector particles; TetrisFirework(float x, float y) { int particleCount = 30 + rand() % 25; particles.reserve(particleCount); for (int i=0;iupdate()) it = particles.erase(it); else ++it; } return !particles.empty(); } }; static std::vector fireworks; static double logoAnimCounter = 0.0; static int hoveredButton = -1; static SDL_Color blendFireColor(float heat, float alphaScale, Uint8 minG, Uint8 minB) { heat = std::clamp(heat, 0.0f, 1.0f); Uint8 r = 255; Uint8 g = static_cast(std::clamp(120.0f + heat * (255.0f - 120.0f), float(minG), 255.0f)); Uint8 b = static_cast(std::clamp(40.0f + (1.0f - heat) * 60.0f, float(minB), 255.0f)); Uint8 a = static_cast(std::clamp(alphaScale * 255.0f, 0.0f, 255.0f)); return SDL_Color{r,g,b,a}; } } // namespace namespace AppFireworks { void update(double frameMs) { if (fireworks.size() < 5 && (rand() % 100) < 2) { float x = 1200.0f * 0.55f + float(rand() % int(1200.0f * 0.35f)); float y = 1000.0f * 0.80f + float(rand() % int(1000.0f * 0.15f)); fireworks.emplace_back(x,y); } for (auto it = fireworks.begin(); it != fireworks.end();) { if (!it->update()) it = fireworks.erase(it); else ++it; } } void draw(SDL_Renderer* renderer, SDL_Texture*) { if (!renderer) return; SDL_BlendMode previousBlend = SDL_BLENDMODE_NONE; SDL_GetRenderDrawBlendMode(renderer, &previousBlend); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD); static constexpr int quadIdx[6] = {0,1,2,2,1,3}; auto makeV = [](float px, float py, SDL_Color c){ SDL_Vertex v{}; v.position.x = px; v.position.y = py; v.color = SDL_FColor{ c.r/255.0f, c.g/255.0f, c.b/255.0f, c.a/255.0f }; return v; }; for (auto& f : fireworks) { for (auto& p : f.particles) { const float heat = std::clamp(p.alpha * 1.25f + p.coreHeat * 0.5f, 0.0f, 1.0f); SDL_Color glow = blendFireColor(0.45f + heat * 0.55f, p.alpha * 0.55f, 100, 40); SDL_Color tailBase = blendFireColor(heat * 0.75f, p.alpha * 0.5f, 70, 25); SDL_Color tailTip = blendFireColor(heat * 0.35f, p.alpha * 0.2f, 40, 15); SDL_Color core = blendFireColor(heat, std::min(1.0f, p.alpha * 1.1f), 150, 80); float velLen = std::sqrt(p.vx*p.vx + p.vy*p.vy); SDL_FPoint dir = velLen > 0.001f ? SDL_FPoint{p.vx/velLen,p.vy/velLen} : SDL_FPoint{0.0f,-1.0f}; SDL_FPoint perp{-dir.y, dir.x}; const float baseW = std::max(0.8f, p.size * 0.55f); const float tipW = baseW * 0.35f; const float tailLen = p.size * (3.0f + (1.0f - p.alpha) * 1.8f); SDL_FPoint base{p.x,p.y}; SDL_FPoint tip{p.x + dir.x*tailLen, p.y + dir.y*tailLen}; SDL_Vertex tail[4]; tail[0] = makeV(base.x + perp.x * baseW, base.y + perp.y * baseW, tailBase); tail[1] = makeV(base.x - perp.x * baseW, base.y - perp.y * baseW, tailBase); tail[2] = makeV(tip.x + perp.x * tipW, tip.y + perp.y * tipW, tailTip); tail[3] = makeV(tip.x - perp.x * tipW, tip.y - perp.y * tipW, tailTip); SDL_RenderGeometry(renderer, nullptr, tail, 4, quadIdx, 6); const float glowAlong = p.size * 0.95f; const float glowAcross = p.size * 0.6f; SDL_Vertex glowV[4]; glowV[0] = makeV(base.x + dir.x * glowAlong, base.y + dir.y * glowAlong, glow); glowV[1] = makeV(base.x - dir.x * glowAlong, base.y - dir.y * glowAlong, glow); glowV[2] = makeV(base.x + perp.x * glowAcross, base.y + perp.y * glowAcross, glow); glowV[3] = makeV(base.x - perp.x * glowAcross, base.y - perp.y * glowAcross, glow); SDL_RenderGeometry(renderer, nullptr, glowV, 4, quadIdx, 6); const float coreW = p.size * 0.35f; const float coreH = p.size * 0.9f; SDL_Vertex coreV[4]; coreV[0] = makeV(base.x + perp.x * coreW, base.y + perp.y * coreW, core); coreV[1] = makeV(base.x - perp.x * coreW, base.y - perp.y * coreW, core); coreV[2] = makeV(base.x + dir.x * coreH, base.y + dir.y * coreH, core); coreV[3] = makeV(base.x - dir.x * coreH, base.y - dir.y * coreH, core); SDL_RenderGeometry(renderer, nullptr, coreV, 4, quadIdx, 6); } } SDL_SetRenderDrawBlendMode(renderer, previousBlend); } double getLogoAnimCounter() { return logoAnimCounter; } int getHoveredButton() { return hoveredButton; } void spawn(float x, float y) { fireworks.emplace_back(x, y); } } // namespace AppFireworks