Fase 9a: extraer CollisionSystem de GameScene a modulo aparte
GameScene::detectar_col*_* (3 funciones de deteccion de gameplay,
~170 LOC) salen a Systems::Collision en
source/game/systems/collision_system.{hpp,cpp}.
API:
- struct Systems::Collision::Context: agrupa todo lo que las
detecciones leen/modifican (ships, enemies, bullets, hit_timer,
score, lives, debris, floating_score, match_config) y un callback
on_player_hit para delegar la muerte del jugador.
- Funciones libres: detectBulletEnemy, detectShipEnemy,
detectBulletPlayer y detectAll.
GameScene::update construye el Context y llama detectAll. La
funcion GameScene::tocado se inyecta via lambda. El cuerpo de update
queda mas legible y separa fisica de gameplay (lo decide el solver)
de fisica rigida (lo decide PhysicsWorld).
GameScene.cpp: 1429 -> 1274 LOC. Smoke test xvfb OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "core/system/scene_context.hpp"
|
||||
#include "core/system/global_events.hpp"
|
||||
#include "game/stage_system/stage_loader.hpp"
|
||||
#include "game/systems/collision_system.hpp"
|
||||
|
||||
// Using declarations per simplificar el codi
|
||||
using SceneManager::SceneContext;
|
||||
@@ -504,9 +505,21 @@ void GameScene::update(float delta_time) {
|
||||
bullet.update(delta_time);
|
||||
}
|
||||
|
||||
detectar_col·lisions_bales_enemics();
|
||||
detectar_col·lisio_naus_enemics();
|
||||
detectar_col·lisions_bales_jugadors();
|
||||
{
|
||||
Systems::Collision::Context col_ctx{
|
||||
.ships = ships_,
|
||||
.enemies = enemies_,
|
||||
.bullets = bullets_,
|
||||
.hit_timer_per_player = hit_timer_per_player_,
|
||||
.score_per_player = score_per_player_,
|
||||
.lives_per_player = lives_per_player_,
|
||||
.debris_manager = debris_manager_,
|
||||
.floating_score_manager = floating_score_manager_,
|
||||
.match_config = match_config_,
|
||||
.on_player_hit = [this](uint8_t pid) { tocado(pid); },
|
||||
};
|
||||
Systems::Collision::detectAll(col_ctx);
|
||||
}
|
||||
debris_manager_.update(delta_time);
|
||||
floating_score_manager_.update(delta_time);
|
||||
break;
|
||||
@@ -972,174 +985,6 @@ std::string GameScene::buildScoreboard() const {
|
||||
return score_p1 + " " + vides_p1 + " LEVEL " + stage_str + " " + score_p2 + " " + vides_p2;
|
||||
}
|
||||
|
||||
void GameScene::detectar_col·lisions_bales_enemics() {
|
||||
// Amplificador per hitbox més generós (115%)
|
||||
constexpr float AMPLIFIER = Defaults::Game::COLLISION_BULLET_ENEMY_AMPLIFIER;
|
||||
|
||||
// Velocidad de explosión reduïda per efecte suau
|
||||
constexpr float VELOCITAT_EXPLOSIO = 80.0F; // px/s (en lloc de 80.0f per defecte)
|
||||
|
||||
// Iterar per todas las balas i enemigos
|
||||
for (auto& bullet : bullets_) {
|
||||
for (auto& enemy : enemies_) {
|
||||
// Comprovar colisión utilitzant la interfície genèrica
|
||||
if (Physics::check_collision(bullet, enemy, AMPLIFIER)) {
|
||||
// *** COL·LISIÓ DETECTADA ***
|
||||
|
||||
const Vec2& pos_enemic = enemy.getCenter();
|
||||
|
||||
// 1. Calculate score for enemy type
|
||||
int points = 0;
|
||||
switch (enemy.getType()) {
|
||||
case EnemyType::PENTAGON:
|
||||
points = Defaults::Enemies::Scoring::PENTAGON_SCORE;
|
||||
break;
|
||||
case EnemyType::QUADRAT:
|
||||
points = Defaults::Enemies::Scoring::QUADRAT_SCORE;
|
||||
break;
|
||||
case EnemyType::MOLINILLO:
|
||||
points = Defaults::Enemies::Scoring::MOLINILLO_SCORE;
|
||||
break;
|
||||
}
|
||||
|
||||
// 2. Add score to the player who shot it
|
||||
uint8_t owner_id = bullet.getOwnerId();
|
||||
score_per_player_[owner_id] += points;
|
||||
|
||||
// 3. Create floating score number
|
||||
floating_score_manager_.crear(points, pos_enemic);
|
||||
|
||||
// 4. Destruir enemy (marca como inactiu)
|
||||
enemy.destruir();
|
||||
|
||||
// 2. Crear explosión de fragments
|
||||
Vec2 vel_enemic = enemy.getVelocityVector();
|
||||
debris_manager_.explode(
|
||||
enemy.getShape(), // Forma vectorial del pentágono
|
||||
pos_enemic, // Posición central
|
||||
0.0F, // Angle (enemy té rotación interna)
|
||||
1.0F, // Escala normal
|
||||
VELOCITAT_EXPLOSIO, // 50 px/s (explosión suau)
|
||||
enemy.getBrightness(), // Heredar brightness
|
||||
vel_enemic, // Heredar velocity
|
||||
enemy.getRotationDelta(), // Heredar velocity angular (trayectorias curvas)
|
||||
0.0F // Sin herencia visual (rotación aleatoria)
|
||||
);
|
||||
|
||||
// 3. Desactivar bullet
|
||||
bullet.desactivar();
|
||||
|
||||
// 4. Eixir del bucle intern (bullet solo destrueix 1 enemy)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::detectar_col·lisio_naus_enemics() {
|
||||
// Amplificador per hitbox generós (80%)
|
||||
constexpr float AMPLIFIER = Defaults::Game::COLLISION_SHIP_ENEMY_AMPLIFIER;
|
||||
|
||||
// Check collision for BOTH players
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
// Skip collisions if player is dead or invulnerable
|
||||
if (hit_timer_per_player_[i] > 0.0F) {
|
||||
continue;
|
||||
}
|
||||
if (!ships_[i].isAlive()) {
|
||||
continue;
|
||||
}
|
||||
if (ships_[i].isInvulnerable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check collision with all active enemies
|
||||
for (const auto& enemy : enemies_) {
|
||||
// Skip collision if enemy is invulnerable
|
||||
if (enemy.isInvulnerable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Comprovar colisión utilitzant la interfície genèrica
|
||||
if (Physics::check_collision(ships_[i], enemy, AMPLIFIER)) {
|
||||
tocado(i); // Trigger death sequence for player i
|
||||
break; // Only one collision per player per frame
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::detectar_col·lisions_bales_jugadors() {
|
||||
// Skip if friendly fire disabled
|
||||
if (!Defaults::Game::FRIENDLY_FIRE_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Amplificador per hitbox exacte (100%)
|
||||
constexpr float AMPLIFIER = Defaults::Game::COLLISION_BULLET_PLAYER_AMPLIFIER;
|
||||
|
||||
// Check all active bullets
|
||||
for (auto& bullet : bullets_) {
|
||||
if (!bullet.esta_activa()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip bullets in grace period (prevents instant self-collision)
|
||||
if (bullet.getGraceTimer() > 0.0F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t bullet_owner = bullet.getOwnerId();
|
||||
|
||||
// Check collision with BOTH players
|
||||
for (uint8_t player_id = 0; player_id < 2; player_id++) {
|
||||
// Skip if player is dead, invulnerable, or inactive
|
||||
if (hit_timer_per_player_[player_id] > 0.0F) {
|
||||
continue;
|
||||
}
|
||||
if (!ships_[player_id].isAlive()) {
|
||||
continue;
|
||||
}
|
||||
if (ships_[player_id].isInvulnerable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip inactive players
|
||||
bool jugador_actiu = (player_id == 0) ? match_config_.jugador1_actiu
|
||||
: match_config_.jugador2_actiu;
|
||||
if (!jugador_actiu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Comprovar colisión utilitzant la interfície genèrica
|
||||
if (Physics::check_collision(bullet, ships_[player_id], AMPLIFIER)) {
|
||||
// *** FRIENDLY FIRE HIT ***
|
||||
|
||||
if (bullet_owner == player_id) {
|
||||
// CASE 1: Self-hit (own bullet)
|
||||
// Player loses 1 life, no gain
|
||||
tocado(player_id);
|
||||
} else {
|
||||
// CASE 2: Teammate hit
|
||||
// Victim loses 1 life
|
||||
tocado(player_id);
|
||||
|
||||
// Attacker gains 1 life (no sin)
|
||||
lives_per_player_[bullet_owner]++;
|
||||
}
|
||||
|
||||
// Play distinct sound
|
||||
Audio::get()->playSound(Defaults::Sound::FRIENDLY_FIRE_HIT, Audio::Group::GAME);
|
||||
|
||||
// Deactivate bullet
|
||||
bullet.desactivar();
|
||||
|
||||
break; // Bullet only hits once per frame
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [NEW] Stage system helper methods
|
||||
|
||||
void GameScene::dibuixar_missatge_stage(const std::string& message) {
|
||||
|
||||
Reference in New Issue
Block a user