Files
orni-attack/source/game/entities/bullet.cpp
T
JailDesigner 707fd29b97 refactor: eliminar Rotation3D i el seu camí de codi (codi mort)
L'struct Rotation3D, la funció apply3dRotation i el paràmetre opcional
rotation_3d de renderShape mai s'activaven en cap caller:
- Ship, Enemy i Bullet passaven explícitament nullptr.
- Title scene, logo scene, starfield, vector_text i ship_animator
  usaven el default nullptr (set els 7 callers).

CLAUDE.md descriu un sistema 3D del title screen que ja no està viu —
el comentari en ship_animator.cpp aclareix que la perspectiva s'ha
bakeat dins de la shape, així que la rotació dinàmica era residu
històric.

Esborrats: struct Rotation3D + ctors + hasRotation(), apply3dRotation(),
la branca rotation_3d a transformPoint() i el seu paràmetre, el
paràmetre rotation_3d de renderShape, i els arguments nullptr als
3 callers d'entitats.

Hallazgo #16 de CODE_REVIEW.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 16:50:03 +02:00

139 lines
4.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
is_active_ = 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
is_active_ = 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 (!is_active_) {
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::getSafePlayAreaBounds(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() {
is_active_ = 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 (is_active_ && shape_) {
// Les bales roten segons l'angle de trayectòria (estático tras disparo)
Rendering::renderShape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_, Defaults::Palette::BULLET);
}
}