c45e524109
Pase automático de clang-tidy --fix sobre el conjunto de checks que son puro transform de sintaxis y no rompen API. Invocado con --format-style=none para que clang-tidy NO arrastre clang-format sobre las líneas tocadas (evita la regla NamespaceIndentation: All del .clang-format reformateando solo trozos del archivo). Checks aplicados: - modernize-use-trailing-return-type (193 hits): 'int foo()' → 'auto foo() -> int'. Estilo coherente con la convención del proyecto. - modernize-use-default-member-init (36 hits): inicialización de miembros pasa de la lista del constructor a la declaración. Reduce duplicación cuando hay varios constructores con los mismos defaults. - modernize-use-auto (6 hits): tipos largos sustituidos por auto donde el tipo es evidente del contexto (new T, dynamic_cast, etc). - modernize-use-starts-ends-with (2 hits): s.rfind(x) == 0 → s.starts_with(x), aprovechando C++20. - performance-enum-size (10 hits): enums pequeños declaran tipo subyacente (uint8_t / similar) para reducir tamaño y precisar layout. NO aplicado en este pase (riesgo de cambios semánticos o de API): - readability-identifier-naming (renames pueden romper callsites parciales) - readability-convert-member-functions-to-static (cambia firma) - readability-use-anyofallof (reescribe loops, side effects) - readability-function-cognitive-complexity (requiere refactor manual) - bugs reales (bugprone-*, clang-diagnostic-*) → uno a uno Cambios manuales asociados: - SDLManager::clear() ahora devuelve bool: propaga el resultado de beginFrame al caller para que Director::runFrameLoop salte draw+present cuando la swapchain no esté disponible (ventana minimizada). Antes la función ignoraba el [[nodiscard]] del beginFrame y los vértices se acumulaban en el batch sin nadie que los consumiera. - vector_text.cpp: borrada la línea suelta "// Test pre-commit hook" que quedó como cruft. clang-tidy crashea en LLVM 19.1 con performance-noexcept-move-constructor (recursión infinita en ExceptionSpecAnalyzer al procesar std::set); check deshabilitado en .clang-tidy con comentario explicativo. Build limpio, smoke test OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
141 lines
4.4 KiB
C++
141 lines
4.4 KiB
C++
// bullet.cpp - Implementación de projectils de la ship
|
||
// © 2026 JailDesigner
|
||
|
||
#include "game/entities/bullet.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <cstdint>
|
||
#include <iostream>
|
||
|
||
#include "core/audio/audio.hpp"
|
||
#include "core/defaults.hpp"
|
||
#include "core/entities/entity.hpp"
|
||
#include "core/graphics/shape_loader.hpp"
|
||
#include "core/rendering/shape_renderer.hpp"
|
||
#include "core/types.hpp"
|
||
#include "game/constants.hpp"
|
||
|
||
namespace {
|
||
// Velocidad escalar de las balas (px/s). Conserva el feel del Pascal original
|
||
// (7 px/frame × 20 FPS = 140 px/s).
|
||
constexpr float BULLET_SPEED = 140.0F;
|
||
} // namespace
|
||
|
||
Bullet::Bullet(Rendering::Renderer* renderer)
|
||
: Entity(renderer)
|
||
{
|
||
// Brightness específico para balas
|
||
brightness_ = Defaults::Brightness::BALA;
|
||
|
||
// Configuración del cuerpo físico.
|
||
// Las balas son cinemáticas: no colisionan con otros bodies ni paredes.
|
||
// El gameplay (GameScene) gestiona los hits con check_collision y la
|
||
// salida del PLAYAREA. Por eso radius=0 en el world (no participa en
|
||
// resolveBodyCollisions ni resolveBoundsCollisions).
|
||
body_.setMass(0.5F); // Ligera (no afecta a nadie, pero por consistencia)
|
||
body_.radius = 0.0F; // Sin colisión física (cinemática pura)
|
||
body_.restitution = 0.0F; // Irrelevante (no rebota)
|
||
body_.linear_damping = 0.0F; // Sin fricción (movimiento rectilíneo uniforme)
|
||
body_.angular_damping = 0.0F;
|
||
|
||
// Cargar shape compartida desde archivo
|
||
shape_ = Graphics::ShapeLoader::load("bullet.shp");
|
||
if (!shape_ || !shape_->isValid()) {
|
||
std::cerr << "[Bullet] Error: no s'ha pogut load bullet.shp" << '\n';
|
||
}
|
||
}
|
||
|
||
void Bullet::init() {
|
||
// Inicialment inactiva
|
||
esta_ = false;
|
||
center_ = {.x = 0.0F, .y = 0.0F};
|
||
angle_ = 0.0F;
|
||
grace_timer_ = 0.0F;
|
||
|
||
// Reset del cuerpo físico
|
||
body_.position = Vec2{};
|
||
body_.velocity = Vec2{};
|
||
body_.angle = 0.0F;
|
||
body_.angular_velocity = 0.0F;
|
||
body_.clearAccumulators();
|
||
}
|
||
|
||
void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) {
|
||
// Activar bullet
|
||
esta_ = true;
|
||
|
||
// Almacenar propietario (0=P1, 1=P2)
|
||
owner_id_ = owner_id;
|
||
|
||
// Activar grace period (prevents instant self-collision)
|
||
grace_timer_ = Defaults::Game::BULLET_GRACE_PERIOD;
|
||
|
||
// Posición y orientación iniciales = ship
|
||
center_ = position;
|
||
angle_ = angle;
|
||
|
||
// Sincronizar el body físico: posición + velocidad cartesiana
|
||
// angle - PI/2 porque angle=0 apunta hacia arriba (eje Y negativo SDL)
|
||
body_.position = position;
|
||
body_.angle = angle;
|
||
const float DIR_X = std::cos(angle - (Constants::PI / 2.0F));
|
||
const float DIR_Y = std::sin(angle - (Constants::PI / 2.0F));
|
||
body_.velocity = Vec2{.x = DIR_X * BULLET_SPEED, .y = DIR_Y * BULLET_SPEED};
|
||
body_.angular_velocity = 0.0F;
|
||
body_.clearAccumulators();
|
||
|
||
// Reproducir sonido de disparo láser
|
||
Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
|
||
}
|
||
|
||
void Bullet::update(float delta_time) {
|
||
if (!esta_) {
|
||
return;
|
||
}
|
||
|
||
// Decrementar grace timer
|
||
if (grace_timer_ > 0.0F) {
|
||
grace_timer_ -= delta_time;
|
||
grace_timer_ = std::max(grace_timer_, 0.0F);
|
||
}
|
||
|
||
// El movimiento real lo hace PhysicsWorld::update() (integración).
|
||
// Aquí solo lógica de estado: detectar salida del PLAYAREA y desactivar.
|
||
float min_x;
|
||
float max_x;
|
||
float min_y;
|
||
float max_y;
|
||
Constants::obtenir_limits_zona_segurs(Defaults::Entities::BULLET_RADIUS,
|
||
min_x,
|
||
max_x,
|
||
min_y,
|
||
max_y);
|
||
|
||
if (body_.position.x < min_x || body_.position.x > max_x ||
|
||
body_.position.y < min_y || body_.position.y > max_y) {
|
||
desactivar();
|
||
}
|
||
}
|
||
|
||
void Bullet::postUpdate(float /*delta_time*/) {
|
||
// Sincronizar mirror desde body_ tras la integración del world.
|
||
center_ = body_.position;
|
||
// angle_ no cambia (las balas no rotan visualmente).
|
||
}
|
||
|
||
void Bullet::desactivar() {
|
||
esta_ = false;
|
||
// Detener el cuerpo físico para que no acumule deriva mientras inactiva.
|
||
body_.velocity = Vec2{};
|
||
body_.angular_velocity = 0.0F;
|
||
}
|
||
|
||
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_,
|
||
/*rotation_3d=*/nullptr, Defaults::Palette::BULLET);
|
||
}
|
||
}
|