Files
orni-attack/source/game/entities/bullet.cpp
T

120 lines
4.4 KiB
C++

// bullet.cpp - Implementación de projectils de la ship
// © 2026 JailDesigner
#include "game/entities/bullet.hpp"
#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"
#include "game/entities/bullet_config.hpp"
#include "game/entities/bullet_registry.hpp"
Bullet::Bullet(Rendering::Renderer* renderer)
: Entity(renderer),
config_(&BulletRegistry::get()) {
brightness_ = Defaults::Brightness::BALA;
// Cinemàtiques pures: no col·lisionen al PhysicsWorld (body_.radius = 0).
// El gameplay (GameScene) gestiona els hits via checkCollisionSwept i la
// sortida del PLAYAREA.
body_.setMass(config_->physics.mass);
body_.radius = 0.0F;
body_.restitution = config_->physics.restitution;
body_.linear_damping = config_->physics.linear_damping;
body_.angular_damping = config_->physics.angular_damping;
shape_ = Graphics::ShapeLoader::load(config_->shape.path);
if (!shape_ || !shape_->isValid()) {
std::cerr << "[Bullet] Error: no s'ha pogut carregar " << config_->shape.path << '\n';
}
// Radi de col·lisió derivat del cercle circumscrit de la shape.
const float BOUNDING = (shape_ != nullptr) ? shape_->getBoundingRadius() : 0.0F;
collision_radius_ = BOUNDING * config_->shape.scale * config_->shape.collision_factor;
}
void Bullet::init() {
is_active_ = false;
center_ = {.x = 0.0F, .y = 0.0F};
prev_position_ = {.x = 0.0F, .y = 0.0F};
angle_ = 0.0F;
body_.position = Vec2{};
body_.velocity = Vec2{};
body_.angle = 0.0F;
body_.angular_velocity = 0.0F;
body_.clearAccumulators();
}
void Bullet::fire(const Vec2& position, float angle, uint8_t owner_id, float bullet_speed, const BulletConfig* cfg) {
is_active_ = true;
owner_id_ = owner_id;
// Si no es passa cfg, restaurem al config per defecte (BulletRegistry::get):
// els slots són reutilitzables i una bala que abans ha estat disparada amb
// una variant (p.ex. bullet_long d'enemic) ha de tornar al bullet.shp del
// player quan aquest la reutilitza.
const BulletConfig* effective = (cfg != nullptr) ? cfg : &BulletRegistry::get();
if (effective != config_) {
config_ = effective;
shape_ = Graphics::ShapeLoader::load(config_->shape.path);
if (!shape_ || !shape_->isValid()) {
std::cerr << "[Bullet] Error: no s'ha pogut carregar " << config_->shape.path << '\n';
}
const float BOUNDING = (shape_ != nullptr) ? shape_->getBoundingRadius() : 0.0F;
collision_radius_ = BOUNDING * config_->shape.scale * config_->shape.collision_factor;
body_.setMass(config_->physics.mass);
body_.restitution = config_->physics.restitution;
body_.linear_damping = config_->physics.linear_damping;
body_.angular_damping = config_->physics.angular_damping;
}
center_ = position;
prev_position_ = position; // spawn: swept degenera a punt-cercle
angle_ = angle;
// Sincronizar el body físic: posició + velocitat cartesiana.
// angle - PI/2 perquè angle=0 apunta cap amunt (eje Y negatiu 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();
Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
}
void Bullet::update(float /*delta_time*/) {
// No-op: la desactivació per fora-de-zone viu a
// Systems::Collision::desactivateOutOfBoundsBullets() perquè així té accés
// al DebrisManager i pot generar el "trencament" visual de la bala alhora.
// El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_.
}
void Bullet::postUpdate(float /*delta_time*/) {
prev_position_ = center_;
center_ = body_.position;
}
void Bullet::desactivar() {
is_active_ = false;
body_.velocity = Vec2{};
body_.angular_velocity = 0.0F;
}
void Bullet::draw() const {
if (is_active_ && shape_) {
Rendering::renderShape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_, config_->colors.normal);
}
}