diff --git a/source/core/defaults.hpp b/source/core/defaults.hpp index d4894e4..8e5644f 100644 --- a/source/core/defaults.hpp +++ b/source/core/defaults.hpp @@ -107,6 +107,17 @@ constexpr float ENEMY_RADIUS = 20.0F; constexpr float BULLET_RADIUS = 3.0F; } // namespace Entities +// Paleta semántica por tipo de entidad. Si una entity declara color, lo +// pasa al pipeline con alpha=255 (sentinela "color válido"); si no, se +// usa el color global del oscilador (g_current_line_color). +namespace Palette { +constexpr SDL_Color SHIP = {.r = 255, .g = 255, .b = 255, .a = 255}; // Blanco neutro +constexpr SDL_Color BULLET = {.r = 120, .g = 255, .b = 140, .a = 255}; // Verde laser +constexpr SDL_Color PENTAGON = {.r = 120, .g = 170, .b = 255, .a = 255}; // Azul "esquivador" +constexpr SDL_Color QUADRAT = {.r = 255, .g = 110, .b = 110, .a = 255}; // Rojo "tank" +constexpr SDL_Color MOLINILLO = {.r = 255, .g = 130, .b = 255, .a = 255}; // Magenta agresivo +} // namespace Palette + // Ship (nave del player) namespace Ship { // Invulnerabilidad post-respawn diff --git a/source/core/rendering/line_renderer.cpp b/source/core/rendering/line_renderer.cpp index a421ff8..cd3513f 100644 --- a/source/core/rendering/line_renderer.cpp +++ b/source/core/rendering/line_renderer.cpp @@ -16,7 +16,8 @@ float g_current_line_thickness = 1.5F; void linea(Renderer* renderer, int x1, int y1, int x2, int y2, float brightness, - float thickness) { + float thickness, + SDL_Color color) { if (renderer == nullptr) { return; } @@ -28,10 +29,11 @@ void linea(Renderer* renderer, const float FX2 = static_cast(x2); const float FY2 = static_cast(y2); - // Color: aplicar brightness al color oscilatorio global. Convertir 0..255 → 0..1. - const float R = (static_cast(g_current_line_color.r) * brightness) / 255.0F; - const float G = (static_cast(g_current_line_color.g) * brightness) / 255.0F; - const float B = (static_cast(g_current_line_color.b) * brightness) / 255.0F; + // color.alpha==0 → usar global del oscilador. alpha>0 → color directo. + const SDL_Color SOURCE = (color.a > 0) ? color : g_current_line_color; + const float R = (static_cast(SOURCE.r) * brightness) / 255.0F; + const float G = (static_cast(SOURCE.g) * brightness) / 255.0F; + const float B = (static_cast(SOURCE.b) * brightness) / 255.0F; const float W = (thickness > 0.0F) ? thickness : g_current_line_thickness; diff --git a/source/core/rendering/line_renderer.hpp b/source/core/rendering/line_renderer.hpp index 59b4843..e1468f2 100644 --- a/source/core/rendering/line_renderer.hpp +++ b/source/core/rendering/line_renderer.hpp @@ -18,10 +18,13 @@ namespace Rendering { // Dibuja una línea entre dos puntos en coordenadas lógicas (1280×720). // brightness: factor de brillo (0.0..1.0, default 1.0 = brillo máximo). // thickness: grosor en píxeles lógicos. Si <= 0 usa g_current_line_thickness. +// color: si alpha==0, se usa el color global del oscilador; si alpha>0 se +// usa este color directo (paleta semántica por entidad). void linea(Renderer* renderer, int x1, int y1, int x2, int y2, float brightness = 1.0F, - float thickness = 0.0F); + float thickness = 0.0F, + SDL_Color color = {0, 0, 0, 0}); // Color global de las líneas (lo actualiza ColorOscillator vía SDLManager). void setLineColor(SDL_Color color); diff --git a/source/core/rendering/shape_renderer.cpp b/source/core/rendering/shape_renderer.cpp index c5a23f0..2a99b7a 100644 --- a/source/core/rendering/shape_renderer.cpp +++ b/source/core/rendering/shape_renderer.cpp @@ -79,40 +79,31 @@ void render_shape(Rendering::Renderer* renderer, float scale, float progress, float brightness, - const Rotation3D* rotation_3d) { - // Verificar que la shape es vàlida + const Rotation3D* rotation_3d, + SDL_Color color) { if (!shape || !shape->isValid()) { return; } - - // Si progress < 1.0, no draw (tot o res) if (progress < 1.0F) { return; } - // Obtenir el centro de la shape para transformacions - const Vec2& shape_centre = shape->getCenter(); + const Vec2& SHAPE_CENTRE = shape->getCenter(); - // Iterar sobre todas las primitives for (const auto& primitive : shape->get_primitives()) { if (primitive.type == Graphics::PrimitiveType::POLYLINE) { - // POLYLINE: connectar points consecutius + // POLYLINE: conectar puntos consecutivos. for (size_t i = 0; i < primitive.points.size() - 1; i++) { - Vec2 p1 = transform_point(primitive.points[i], shape_centre, position, angle, scale, rotation_3d); - Vec2 p2 = transform_point(primitive.points[i + 1], shape_centre, position, angle, scale, rotation_3d); - - linea(renderer, static_cast(p1.x), static_cast(p1.y), - static_cast(p2.x), static_cast(p2.y), brightness); - } - } else { // PrimitiveType::LINE - // LINE: exactament 2 points - if (primitive.points.size() >= 2) { - Vec2 p1 = transform_point(primitive.points[0], shape_centre, position, angle, scale, rotation_3d); - Vec2 p2 = transform_point(primitive.points[1], shape_centre, position, angle, scale, rotation_3d); - - linea(renderer, static_cast(p1.x), static_cast(p1.y), - static_cast(p2.x), static_cast(p2.y), brightness); + const Vec2 P1 = transform_point(primitive.points[i], SHAPE_CENTRE, position, angle, scale, rotation_3d); + const Vec2 P2 = transform_point(primitive.points[i + 1], SHAPE_CENTRE, position, angle, scale, rotation_3d); + linea(renderer, static_cast(P1.x), static_cast(P1.y), + static_cast(P2.x), static_cast(P2.y), brightness, 0.0F, color); } + } else if (primitive.points.size() >= 2) { // LINE + const Vec2 P1 = transform_point(primitive.points[0], SHAPE_CENTRE, position, angle, scale, rotation_3d); + const Vec2 P2 = transform_point(primitive.points[1], SHAPE_CENTRE, position, angle, scale, rotation_3d); + linea(renderer, static_cast(P1.x), static_cast(P1.y), + static_cast(P2.x), static_cast(P2.y), brightness, 0.0F, color); } } } diff --git a/source/core/rendering/shape_renderer.hpp b/source/core/rendering/shape_renderer.hpp index 6952b5a..8b6940a 100644 --- a/source/core/rendering/shape_renderer.hpp +++ b/source/core/rendering/shape_renderer.hpp @@ -49,6 +49,7 @@ void render_shape(Rendering::Renderer* renderer, float scale = 1.0F, float progress = 1.0F, float brightness = 1.0F, - const Rotation3D* rotation_3d = nullptr); + const Rotation3D* rotation_3d = nullptr, + SDL_Color color = {0, 0, 0, 0}); // alpha==0 → usa global oscilador } // namespace Rendering diff --git a/source/game/effects/debris.hpp b/source/game/effects/debris.hpp index b8fd88d..e0126fe 100644 --- a/source/game/effects/debris.hpp +++ b/source/game/effects/debris.hpp @@ -2,6 +2,8 @@ // © 2025 Port a C++20 con SDL3 #pragma once +#include + #include "core/types.hpp" namespace Effects { @@ -31,7 +33,8 @@ struct Debris { float factor_shrink; // Factor de reducció per segon (0.0-1.0) // Rendering - float brightness; // Factor de brightness (0.0-1.0, heretat de l'objecte original) + float brightness; // Factor de brightness (0.0-1.0, heretat de l'objecte original) + SDL_Color color{}; // Color heredado del padre. alpha==0 → usa global oscilador }; } // namespace Effects diff --git a/source/game/effects/debris_manager.cpp b/source/game/effects/debris_manager.cpp index c43cbf1..46478c1 100644 --- a/source/game/effects/debris_manager.cpp +++ b/source/game/effects/debris_manager.cpp @@ -53,7 +53,8 @@ void DebrisManager::explode(const std::shared_ptr& shape, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, - const std::string& sound) { + const std::string& sound, + SDL_Color color) { if (!shape || !shape->isValid()) { return; } @@ -195,8 +196,9 @@ void DebrisManager::explode(const std::shared_ptr& shape, debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA; debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; - // 8. Heredar brightness + // 8. Heredar brightness y color del padre debris->brightness = brightness; + debris->color = color; // 9. Activar debris->active = true; @@ -308,13 +310,13 @@ void DebrisManager::draw() const { continue; } - // Dibuixar segment de línia con brightness heretat + // Dibujar segmento con brightness y color heredados del padre. Rendering::linea(renderer_, static_cast(debris.p1.x), static_cast(debris.p1.y), static_cast(debris.p2.x), static_cast(debris.p2.y), - debris.brightness); + debris.brightness, 0.0F, debris.color); } } diff --git a/source/game/effects/debris_manager.hpp b/source/game/effects/debris_manager.hpp index 0793569..89acddd 100644 --- a/source/game/effects/debris_manager.hpp +++ b/source/game/effects/debris_manager.hpp @@ -41,7 +41,8 @@ class DebrisManager { const Vec2& velocitat_objecte = {.x = 0.0F, .y = 0.0F}, float velocitat_angular = 0.0F, float factor_herencia_visual = 0.0F, - const std::string& sound = Defaults::Sound::EXPLOSION); + const std::string& sound = Defaults::Sound::EXPLOSION, + SDL_Color color = {0, 0, 0, 0}); // alpha==0 → fragmentos usan oscilador global // Actualitzar todos los fragments active void update(float delta_time); diff --git a/source/game/entities/bullet.cpp b/source/game/entities/bullet.cpp index 3f3abe3..9a52f56 100644 --- a/source/game/entities/bullet.cpp +++ b/source/game/entities/bullet.cpp @@ -137,6 +137,7 @@ void Bullet::desactivar() { void Bullet::draw() const { if (esta_ && shape_) { // Les bales roten segons l'angle de trayectòria (estático tras disparo) - Rendering::render_shape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_); + Rendering::render_shape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_, + /*rotation_3d=*/nullptr, Defaults::Palette::BULLET); } } diff --git a/source/game/entities/enemy.cpp b/source/game/entities/enemy.cpp index 32dbdc8..98a3083 100644 --- a/source/game/entities/enemy.cpp +++ b/source/game/entities/enemy.cpp @@ -230,7 +230,14 @@ void Enemy::draw() const { return; } const float SCALE = computeCurrentScale(); - Rendering::render_shape(renderer_, shape_, center_, rotacio_, SCALE, 1.0F, brightness_); + SDL_Color color{}; + switch (type_) { + case EnemyType::PENTAGON: color = Defaults::Palette::PENTAGON; break; + case EnemyType::QUADRAT: color = Defaults::Palette::QUADRAT; break; + case EnemyType::MOLINILLO: color = Defaults::Palette::MOLINILLO; break; + } + Rendering::render_shape(renderer_, shape_, center_, rotacio_, SCALE, 1.0F, brightness_, + /*rotation_3d=*/nullptr, color); } void Enemy::destruir() { diff --git a/source/game/entities/ship.cpp b/source/game/entities/ship.cpp index 184ccc5..087e1ef 100644 --- a/source/game/entities/ship.cpp +++ b/source/game/entities/ship.cpp @@ -160,5 +160,6 @@ void Ship::draw() const { const float VISUAL_PUSH = SPEED / 33.33F; const float SCALE = 1.0F + (VISUAL_PUSH / 12.0F); - Rendering::render_shape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_); + Rendering::render_shape(renderer_, shape_, center_, angle_, SCALE, 1.0F, brightness_, + /*rotation_3d=*/nullptr, Defaults::Palette::SHIP); } diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index c7ee347..864fa66 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -753,7 +753,8 @@ void GameScene::tocado(uint8_t player_id) { vel_nau_80, // Heredar 80% velocity 0.0F, // Nave: trayectorias rectas (sin drotacio) 0.0F, // Sin herencia visual (rotación aleatoria) - Defaults::Sound::EXPLOSION2 // Sonido alternativo para la explosión + Defaults::Sound::EXPLOSION2, // Sonido alternativo para la explosión + Defaults::Palette::SHIP // Debris hereda color de la nave ); // Start death timer (non-zero to avoid re-triggering) diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index 4f82427..fa1d4d7 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -41,7 +41,13 @@ void detectBulletEnemy(Context& ctx) { ctx.score_per_player[owner_id] += points; ctx.floating_score_manager.crear(points, POS_ENEMIC); - // 2. Destruir enemy + crear explosión + // 2. Destruir enemy + crear explosión (debris hereda color del enemy) + SDL_Color enemy_color{}; + switch (enemy.getType()) { + case EnemyType::PENTAGON: enemy_color = Defaults::Palette::PENTAGON; break; + case EnemyType::QUADRAT: enemy_color = Defaults::Palette::QUADRAT; break; + case EnemyType::MOLINILLO: enemy_color = Defaults::Palette::MOLINILLO; break; + } enemy.destruir(); Vec2 vel_enemic = enemy.getVelocityVector(); ctx.debris_manager.explode( @@ -53,7 +59,9 @@ void detectBulletEnemy(Context& ctx) { enemy.getBrightness(), vel_enemic, enemy.getRotationDelta(), - 0.0F // sin herencia visual + 0.0F, // sin herencia visual + Defaults::Sound::EXPLOSION, + enemy_color ); // 3. Desactivar bullet (solo destruye 1 enemy)