9e54dde490
Reporte completo de la pasada de revisión arquitectónica realizada por el subagente Plan tras cerrar el ciclo de lint. Organiza los 40 hallazgos en 8 áreas (escenas, sistemas, entidades, renderizado, configuración, naming, headers, bugs latentes), cada uno con prioridad, tipo (estructural/opinable), file:line, problema, propuesta y esfuerzo estimado. Incluye: - Top picks de bugs latentes y limpieza con impacto - Lista de hallazgos que requieren verificación profunda - Lista de hallazgos opinables (potencialmente rechazables) - Resumen de lo ya resuelto en chore/lint - Plan de ataque sugerido para iterar Permite continuar el code review en otro equipo sin perder el contexto. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
26 KiB
26 KiB
Auditoría arquitectónica — Orni Attack
Pasada de revisión de diseño y arquitectura (no de estilo) sobre la rama
maintras cerrar el ciclo de lint (cppcheck/clang-tidy a 0 hits). Generada el 2026-05-20 por el subagentePlany verificada parcialmente. Las referenciasfile:lineson relativas a la raíz del repo.
Cómo usar este documento
- Prioridad indica urgencia subjetiva (alta/media/baja).
- Tipo distingue:
- [estructural] = bug latente, deuda técnica medible, código muerto
- [opinable] = cambio de estilo/diseño que el usuario puede rechazar legítimamente
- Esfuerzo: small (1 commit < 30 min) / medium (1-3 commits) / large (varias sesiones).
Antes de actuar sobre un hallazgo, re-verificar que sigue siendo válido (el código habrá cambiado).
Hallazgos por área
A. Escenas
1. Scene no declara init(), pero GameScene lo expone — [PRIORIDAD: alta, estructural]
- Dónde:
source/core/system/scene.hpp:31-40,source/game/scenes/game_scene.hpp:47,source/game/scenes/game_scene.cpp:64(init()se llama desde el ctor). - Problema: La interfaz
ScenedeclarahandleEvent/update/draw/isFinishedcomo obligatorios.GameScene::init()es un método público adicional, pero el Director nunca lo llama —GameScenelo invoca desde su propio ctor. El comentario del header dice "init() — llamado por Director tras crear la escena", pero eso no ocurre.TitleSceneyLogoSceneno tienen ese método: hacen todo en el ctor. Inconsistencia de lifecycle. - Propuesta: (a) eliminar
init()y mover su contenido al ctor (lo más simple), o (b) añadirlo a la interfazSceney llamarlo desde el Director trasbuildScene. Lo coherente es (a). - Esfuerzo: small
2. No existe clase base MenuScene para el patrón timer + state-machine + skip-button — [PRIORIDAD: media, opinable]
- Dónde:
source/game/scenes/title_scene.cpp:368-388,source/game/scenes/logo_scene.cpp:219-280, partes degame_scene.cpp(INIT_HUD/LEVEL_START). - Problema: Las tres escenas implementan la misma idea: un enum
State, un timer acumulado, unswitchpor estado, una transición "skip".checkSkipButtonPressed()aparece enTitleSceneyLogoScenecon la misma signatura estática. - Propuesta: Mixin pequeño
TimedStateScene<EnumT>conenterState(EnumT),time_in_state(), helpers de "esperar N segundos y avanzar". Al menos consolidarcheckSkipButtonPressed()y la parejatemps_estat_actual_ + cambio de estadoen un helper. - Esfuerzo: medium
3. TitleScene::initTitle() duplica dos veces el cálculo de bounding box + posicionado — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/scenes/title_scene.cpp:104-254(función de 150 líneas). - Problema: Carga "ORNI" (108-166) y "ATTACK!" (178-241) con el mismo código copiado.
inicialitzarJailgames()(256-325) hace lo mismo por tercera vez. Tres copias del cómputo de bounding box. - Propuesta: Extraer
loadLetterRow(filenames, scale, y_position, espai) -> std::vector<LetraLogo>. - Esfuerzo: small
4. PLAYER_JOIN_PHASE en TitleScene mezcla animación + audio + late join — [PRIORIDAD: media, opinable]
- Dónde:
source/game/scenes/title_scene.cpp:451-483. - Problema:
updatePlayerJoinPhaseStatehace varias cosas no relacionadas. La lógica de "late join" reaprovechacheckStartGameButtonPressed()(que mutamatch_config_— efecto secundario no advertido por la firma). - Propuesta: Renombrar
checkStartGameButtonPressed()atryActivatePlayersOnStart(). Separar la lógica de "late join" en un helper. - Esfuerzo: small
5. SceneContext::actual es estado global redundante — [PRIORIDAD: baja, opinable]
- Dónde:
source/core/system/scene_context.hpp:81, escrita endirector.cpp:267,275yglobal_events.cpp:29,58. - Problema:
inline SceneContext::SceneType actualcomo variable global, sincronizada manualmente. No la consulta nadie excepto el propio Director (que leecontext.nextScene(), noactual). - Propuesta: Verificación profunda. Si nadie la consulta, eliminarla.
- Esfuerzo: small
B. Sistemas
6. StageManager::update(dt, pause_spawn) tiene pause_spawn muerto — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/stage_system/stage_manager.hpp:28,source/game/stage_system/stage_manager.cpp:128-135,game_scene.cpp:359-374,423-424. - Problema: El parámetro
pause_spawnexiste en la API pero ningún caller lo pasa con sentido — la pausa real ocurre por otra ruta (spawn_controller_.update(dt, enemies, PAUSE_SPAWN)). El comentario enstage_manager.cpp:131-134ya admite que no se usa. - Propuesta: Quitar el parámetro.
- Esfuerzo: small
7. SpawnController hardcoda std::array<Enemy, 15> en su firma — [PRIORIDAD: media, estructural]
- Dónde:
source/game/stage_system/spawn_controller.hpp:33,37,39;.cpp:44,89,96. - Problema: Literal
15en lugar deDefaults::Entities::MAX_ORNIS. Acoplamiento silencioso por número. - Propuesta: Usar
std::array<Enemy, Defaults::Entities::MAX_ORNIS>&o un aliasusing EnemyArray = ...;. - Esfuerzo: small
8. Systems::Collision::Context y Systems::ContinueScreen::Context son "god structs" — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/systems/collision_system.hpp:31-43,source/game/systems/continue_system.hpp:25-38. - Problema: Cada
Contextagrupa entre 9 y 12 referencias mutables a estado deGameScene+ unstd::functioncallback. Punto único donde cualquier cambio de campo en GameScene rompe el módulo. Los sistemas pueden mutar todo (no hay distinción "lo que leo" vs "lo que modifico"). La separaciónGameScene↔Systems::*es nominal. - Propuesta: Que
detectAlldevuelva una struct "outcome" (qué balas se desactivaron, qué enemigos murieron, qué jugador fue hit) y que GameScene aplique el resultado. Si es demasiado, al menos partir el Context enIn(lectura) eOut(escritura). - Esfuerzo: large
9. CollisionSystem y PhysicsWorld se solapan — [PRIORIDAD: media, opinable]
- Dónde:
source/game/systems/collision_system.cpp(todo),source/core/physics/physics_world.cpp:118-179. - Problema:
PhysicsWorld::resolveBodyCollisionsresuelve colisiones cuerpo-cuerpo con impulsos elásticos;CollisionSystem::detectBulletEnemyitera otra vez los mismos pares con un amplifier distinto y aplica daño. Dos bucles O(n²) sobre el mismo conjunto. El comentario engame_scene.cpp:159explica la sutileza pero es frágil. - Propuesta: Verificación profunda. ¿Deberían las balas vivir fuera del world directamente?
- Esfuerzo: medium
10. init_hud_animator es solo 3 funciones libres en un namespace — [PRIORIDAD: baja, opinable]
- Dónde:
source/game/systems/init_hud_animator.hpp,.cpp(99 líneas). - Problema: A diferencia de
CollisionSystem/ContinueSystem, no tieneContext; son 3 funciones puras. La nomenclatura sugiere algo más grande. - Propuesta: Mover a
source/core/rendering/o renombrarsystems/agameplay/. - Esfuerzo: small
C. Entidades
11. Ship expone tres métodos equivalentes: isAlive(), isHit(), isActive() — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/entities/ship.hpp:27,38,39. - Problema:
isActive() = !is_hit_,isAlive() = !is_hit_,isHit() = is_hit_. Tres formas de preguntar lo mismo, usadas en sitios distintos. - Propuesta: Quedarse con
isActive()(override de Entity) y reescribir los call-sites. - Esfuerzo: small
12. Entity::renderer_ raw pointer; ctor por defecto deja la entidad inservible — [PRIORIDAD: media, estructural]
- Dónde:
source/core/entities/entity.hpp:56,67,source/game/entities/ship.hpp:15-16(Ship() : Entity(nullptr) {}), idem enenemy.hpp:38-39,bullet.hpp:15-16. - Problema: El ctor por defecto existe solo para soportar
std::array<Ship, 2>, pero dejarenderer_nullptr. Si alguien dibuja antes de la reasignación, segfault.renderer_no tiene in-class initializer. - Propuesta: (a) Inicializador en miembro
Rendering::Renderer* renderer_{nullptr};. (b) Guard endraw()pararenderer_ == nullptr. (c) Considerarstd::array<std::optional<Ship>, 2>. - Esfuerzo: small (a)+(b); medium (c)
13. Duplicación de estado: center_/angle_ en Entity vs body_.position/body_.angle en RigidBody — [PRIORIDAD: media, estructural]
- Dónde:
source/core/entities/entity.hpp:58-59,64;source/game/entities/ship.cpp:131-135(postUpdate sincroniza),enemy.cpp:214-219,bullet.cpp:121-125. - Problema: Cada entidad mantiene
center_/angle_como mirror del body. Cualquier código que leagetCenter()duranteupdate()(antes delpostUpdate) verá el valor del frame anterior. El patrón está documentado pero es frágil. - Propuesta: Hacer que
getCenter()devuelvabody_.positiondirectamente (eliminandocenter_). - Esfuerzo: medium
14. Entity::renderer_ protected: con acceso directo + getter por referencia mutable a body_ — [PRIORIDAD: media, estructural]
- Dónde:
source/core/entities/entity.hpp:51-52(getBody() -> RigidBody&),:54-64. - Problema: Encapsulación abierta + getters que devuelven referencias no-const al
body_. El contrato de quién mutabody_.velocity/positionno está claro. - Propuesta: Documentar el contrato. Hacer
body_privado con API estricta (registerInWorld(world)). - Esfuerzo: medium
15. Enemy tiene drotacio_/rotacio_ (rotación visual) Y body_.angle/angular_velocity — [PRIORIDAD: baja, estructural]
- Dónde:
source/game/entities/enemy.hpp:92-93,.cpp:210-212. - Problema: El body de un enemy nunca rota (angular_velocity siempre 0), pero la visual sí. Dos representaciones de orientación viviendo en la misma entidad.
- Propuesta: Eliminar
body_.angle/angular_velocitypara enemies (no se usa). - Esfuerzo: small
D. Renderizado y recursos
16. Rotation3D (struct + parámetro en renderShape) es código muerto — [PRIORIDAD: media, estructural]
- Dónde:
source/core/rendering/shape_renderer.hpp:18-35,52,.cpp:13-78. - Problema: La función
renderShapeaceptaconst Rotation3D* rotation_3d = nullptr. Todos los callers pasannullptr(ship.cpp:161,enemy.cpp:232,bullet.cpp:137). 28 líneas de código muerto. - Propuesta: Eliminar
Rotation3D, el parámetro y el branch. - Esfuerzo: small
17. Resource::Helper es una fachada delgada sobre Resource::Loader — [PRIORIDAD: media, estructural]
- Dónde:
source/core/resources/resource_helper.hpp,.cpp(80 líneas);resource_loader.hpp(54 líneas). - Problema:
Helper::loadFile()esLoader::get().loadResource(normalizePath(...)). La única "lógica real" de Helper esnormalizePath(). Duplicación de API. - Propuesta: Mover
normalizePathaResource::Loadery eliminarHelper. - Esfuerzo: small
18. ShapeLoader::resolvePath parece código muerto — [PRIORIDAD: baja, opinable]
- Dónde:
source/core/graphics/shape_loader.hpp:36,.cpp:70-83. - Problema:
resolvePathestá declarada como helper privado, pero no se llama desde ningún sitio. - Propuesta: Eliminar
resolvePathyBASE_PATH. - Esfuerzo: small
19. ShapeLoader::cache sin política de invalidación — [PRIORIDAD: baja, estructural]
- Dónde:
source/core/graphics/shape_loader.cpp:13,15-60. - Problema: Caché estática
unordered_map<string, shared_ptr<Shape>>.clearCache()existe pero nadie la llama. Sin límite de tamaño. - Propuesta: Documentar en el header que la caché vive toda la vida del proceso.
- Esfuerzo: small
20. GpuFrameRenderer::device() y isInsideFrame() son getters públicos sin callers — [PRIORIDAD: media, estructural]
- Dónde:
source/core/rendering/gpu/gpu_frame_renderer.hpp:96,97. SimilarSDLManager::getScaleFactor()(línea 42). - Problema: API pública sin clientes.
- Propuesta: Eliminar o documentar como API reservada.
- Esfuerzo: small
E. Configuración
21. Options::physics, Options::audio, Options::gameplay se cargan desde YAML pero nunca se consultan en runtime — [PRIORIDAD: alta, estructural]
- Dónde:
Options::physicsse rellena enoptions.cpp:194-201, parse 286-291, write 597-603. Único call-site lector:director.cpp:109(informativo). Ship/Enemy/Bullet leenDefaults::Physics::*directamente.Options::audiodocumentado como "deliberadamente desacoplado" (audio.hpp:22-25);director.cpp:235-244construyeAudio::ConfigdesdeDefaults::Audio.Options::gameplay(max_enemies, max_bullets): las arrays son compile-time (std::array<Enemy, MAX_ORNIS>).
- Problema: El usuario edita su
config.yaml, cambiaphysics.rotation_speed: 6.0, reinicia el juego — el juego sigue usando el default. Prometemos configurabilidad que no entregamos. - Propuesta: O bien (a) conectar Options→runtime, o (b) eliminar los campos muertos. Híbrido: borrar
audio/gameplay, conectarphysics(apoya Fase 10). - Esfuerzo: small (eliminar) o medium (conectar correctamente).
22. defaults.hpp mezcla constantes de cinco dominios distintos en un solo archivo de 530 líneas — [PRIORIDAD: media, estructural]
- Dónde:
source/core/defaults.hpp(todo). - Problema: Contiene Window, Game, Zones, Entities, Palette, Ship, Physics, Math, Brightness, Rendering, Audio, Music, Sound, Controls, Enemies (con sub-namespaces), Title, FloatingScore. 22 archivos lo incluyen. Editar una constante de Audio fuerza recompilación de todo. Dos
namespace Gameseparados (líneas 22-25 y 132-210). - Propuesta: Partir en
defaults/window.hpp,defaults/game.hpp,defaults/zones.hpp, etc. Condefaults.hppcomo umbrella. - Esfuerzo: medium
23. Defaults::Window::WIDTH = 1280 y Defaults::Game::WIDTH = 1280 son duplicados sin relación enforced — [PRIORIDAD: media, estructural]
- Dónde:
source/core/defaults.hpp:10vs:23. - Problema: Dos
WIDTHdistintos pero con el mismo valor. Nadie obliga a que coincidan. Si alguien quisiera arrancar a 1366×768 cambiandoWindow::WIDTH, el cálculozoom_factor = (float)width / Window::WIDTH(sdl_manager.cpp:101) usaWindow::WIDTH, noGame::WIDTH— bug latente. - Propuesta: Decidir la fuente de verdad. Si es
Game::{WIDTH,HEIGHT}, derivarWindow::WIDTH = Game::WIDTH. - Esfuerzo: small
24. game/constants.hpp contiene aliases legacy mayoritariamente muertos — [PRIORIDAD: baja, estructural]
- Dónde:
source/game/constants.hpp:10-23. - Problema:
Constants::MARGIN_*,MAX_ORNIS,MAX_BALES,VELOCITAT,VELOCITAT_MAX— no se usan. Solo se usan los helpers (PI, isInPlayArea, getSafePlayAreaBounds, getPlayAreaCenter). - Propuesta: Eliminar las constantes muertas.
- Esfuerzo: small
25. Defaults::Physics::{ENEMY_SPEED, BULLET_SPEED, VELOCITY_SCALE} legacy de Pascal son código muerto — [PRIORIDAD: media, estructural]
- Dónde:
source/core/defaults.hpp:218-220. - Problema: Los valores están en unidades/frame, comentados como "actuales del juego", pero la migración a SDL3 los reemplazó (
bullet.cpp:22define su propioBULLET_SPEED = 140.0F; enemy speeds vienen deDefaults::Enemies::{Pentagon,Cuadrado,Molinillo}::VELOCITAT). - Propuesta: Eliminar.
- Esfuerzo: small
F. Naming y consistencia de idioma
26. Variables del estado de juego mezclan catalán y castellano — [PRIORIDAD: baja, opinable]
- Dónde:
source/game/scenes/game_scene.hpp:67-77,source/game/scenes/title_scene.hpp:65-84. - Problema: Conviven
hit_timer_per_player_(inglés),lives_per_player_(inglés),match_config_.jugador1_actiu(catalán). En TitleScene:estat_actual_(catalán) junto afactor_lerp_. No hay convención clara dentro de la misma clase. - Propuesta: Si el usuario quiere preservar el sabor, reglar: identificadores expuestos en headers públicos en catalán, detalles internos en inglés.
- Esfuerzo: medium
27. Dos namespaces Constants no relacionados — [PRIORIDAD: baja, opinable]
- Dónde:
source/game/constants.hpp:8(namespace Constants) ysource/game/stage_system/stage_config.hpp:82(namespace StageSystem::Constants). - Problema: Mismo nombre, scopes distintos. Lecturalmente confuso.
- Propuesta: Renombrar el del stage system a
StageSystem::Messages. - Esfuerzo: small
G. Headers y dependencias
28. core/ depende de game/ — violación de capas — [PRIORIDAD: alta, estructural]
- Dónde:
source/core/system/director.cpp:25-28incluyegame/scenes/*.hppygame/options.hppsource/core/rendering/sdl_manager.cpp:16incluyegame/options.hppsource/core/system/debug_overlay.cpp:8incluyegame/options.hppsource/core/input/input.cpp:11incluyegame/options.hpp
- Problema: La estructura sugiere un layered design, pero
core/lee y mutagame::Options. Si se intenta convertircore/en librería estática reusable, no compila. - Propuesta: Mover
Optionsasource/core/config/, o invertir la dependencia conIConfigProvider*. Relacionado con #21. - Esfuerzo: medium
29. continue_system.hpp forward-declara enum class GameOverState definida a nivel global en game_scene.hpp — [PRIORIDAD: media, estructural]
- Dónde:
source/game/scenes/game_scene.hpp:28-32(declaración en namespace global, NO dentro de la clase),source/game/systems/continue_system.hpp:18-20. - Problema: Si la subyacente cambia de
uint8_taint, las dos declaraciones divergen silenciosamente. - Propuesta: Mover
GameOverStateasource/game/systems/game_over_state.hpp(header de tipos puros). - Esfuerzo: small
30. core/defaults.hpp se incluye en 22 archivos arrastrando ~530 líneas — [PRIORIDAD: media, estructural]
- Dónde: Ver #22.
- Problema: Tiempo de compilación incremental innecesariamente alto.
- Propuesta: Ver #22.
- Esfuerzo: medium
H. Bugs latentes / code smells
31. hit_timer_per_player_ usa el valor mágico 999.0F como sentinela "jugador permanentemente inactivo" — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/scenes/game_scene.cpp:140,229,230,239,312,332. - Problema: El timer combina tres estados en un float:
== 0.0F= vivo,> 0 && < 3.0= secuencia de muerte,== 999.0F= sin vidas. Si en algún momento se decrementa accidentalmente, el sentinela se pierde. - Propuesta: Introducir un enum
PlayerStatus { ALIVE, DYING, OUT }separado del timer; el timer solo cuentaDYING. - Esfuerzo: medium
32. std::rand() en todo el gameplay; std::mt19937 solo en LogoScene; reseed solo en GameScene::init — [PRIORIDAD: media, estructural]
- Dónde:
std::srandengame_scene.cpp:80;std::randenspawn_controller.cpp:131,enemy.cpp:133-447,stage_manager.cpp:84,debris_manager.cpp:121-352.std::mt19937enlogo_scene.cpp:171-173. - Problema: Mezcla de PRNG.
std::rand()tiene calidad mala y es global.srandsolo se llama una vez por partida; el logo y el título usan la seed por defecto. Repetibilidad accidental. - Propuesta: Centralizar un
Random(single mt19937 con seeds explícitas). Al menossranden main/Director para que se siembre antes. - Esfuerzo: small (centralizar srand) o medium (reemplazar todos).
33. LineRenderer mantiene dos globales mutables: g_current_line_color y g_current_line_thickness — [PRIORIDAD: media, estructural]
- Dónde:
source/core/rendering/line_renderer.cpp:11,15. - Problema: Estado mutable global en la TU. Si alguna escena cambia color y se olvida de restaurarlo, contamina las siguientes. Misma problemática con
g_current_scale_factorencoordinate_transform.cpp:9. - Propuesta: Moverlos a miembros del
GpuFrameRenderer. El color global puede vivir como uniform de pipeline. - Esfuerzo: medium
34. GameScene::init() reasigna enemies_[i] = Enemy(...) duplicando el trabajo del constructor — [PRIORIDAD: alta, estructural]
- Dónde:
source/game/scenes/game_scene.cpp:56-59(ctor) vs:149-153(init). - Problema: En el ctor se hace
enemy = Enemy(sdl.getRenderer()). Inmediatamente después se llamainit()(línea 64), que reasigna otra vez. Trabajo perdido. Inconsistencia con bullets (que solo se inicializan una vez). - Propuesta: Eliminar la inicialización en el ctor o en init. Decidir cuál es la fuente única.
- Esfuerzo: small
35. runCollisionDetections() reconstruye un Context enorme cada frame — [PRIORIDAD: media, estructural]
- Dónde:
source/game/scenes/game_scene.cpp:470-484, similarstepContinueScreen(:252-282). - Problema: Cada frame en PLAYING se construye una struct con 9 referencias + un
std::function(que aloca en heap por la captura[this]). 60 allocs/segundo delstd::function. - Propuesta: Cachear el Context como miembro o pasar el callback como puntero a función miembro.
- Esfuerzo: small
36. SpawnController::generateSpawnEvents precomputa toda la cola pero update() solo procesa secuencialmente — [PRIORIDAD: media, estructural]
- Dónde:
source/game/stage_system/spawn_controller.cpp:54-82. - Problema: El flag
event.spawnejatparece sobrante (el index ya marca progreso). - Propuesta: Verificación profunda. Simplificable.
- Esfuerzo: small
37. Director::run() es estático pero usa estado de instancia via singletons — [PRIORIDAD: baja, estructural]
- Dónde:
source/core/system/director.hpp:21-25,.cpp:41-48,150. - Problema:
executable_path_se asigna en ctor pero nadie lo lee.system_folder_se usa en ctor pero no después. El patrón "Director con state" es decorativo. - Propuesta: O hacer
run()no-estática, o eliminar los miembros y moverrun()a una función libreApp::main(). - Esfuerzo: small
Otros
38. MatchConfig::jugador{1,2}_actiu mutación desde dos sistemas independientes sin notificar — [PRIORIDAD: media, estructural]
- Dónde:
source/game/scenes/game_scene.cpp:912-916(joinPlayer),source/game/systems/continue_system.cpp:29-33(revivePlayer). - Problema:
MatchConfigviaja como referencia mutable por losContexty se modifica desde múltiples sistemas. Rompe la intuición de queMatchConfiges "la configuración decidida en TitleScene". - Propuesta: Renombrar el campo a
player_active_runtimeo mover el flag a otro lado (ej. dentro delives_per_player_: si lives > 0 → activo). - Esfuerzo: medium
39. Inconsistencia: LogoScene::~LogoScene() llama stopAllSounds(), TitleScene::~TitleScene() llama stopMusic(), GameScene::~GameScene() es default — [PRIORIDAD: baja, opinable]
- Dónde:
source/game/scenes/logo_scene.cpp:62-66,title_scene.cpp:99-102,game_scene.hpp:38. - Problema: Cada escena gestiona limpieza de audio a su manera. Si una olvida un sonido en loop, fuga.
- Propuesta: Decidir si el Director debe llamar
Audio::stopAllSounds()ystopMusic()antes de destruir cada escena. - Esfuerzo: small
40. Bullet::BULLET_SPEED = 140.0F en anonymous namespace, no en defaults.hpp — [PRIORIDAD: baja, opinable]
- Dónde:
source/game/entities/bullet.cpp:22-23. - Problema: Constante de gameplay escondida en una TU privada. Si alguien quiere tunear, mira en defaults.hpp y no la encuentra (ahí hay otro
BULLET_SPEEDdiferente y muerto, ver #25). - Propuesta: Mover a
Defaults::Entities::BULLET_SPEED_PXS; eliminar el legacy de Pascal. - Esfuerzo: small
Resumen ejecutivo
Top picks "bug latente / deuda clara"
| # | Hallazgo | Esfuerzo |
|---|---|---|
| 21 | Options::physics/audio/gameplay se cargan del YAML pero nadie las consulta |
small-medium |
| 28 | core/ depende de game/ (4 ficheros incluyen game/options.hpp) |
medium |
| 11 | Ship::isAlive() == isActive() == !isHit() (tres alias del mismo bool) |
small |
| 34 | GameScene construye enemies_ dos veces (en ctor y en init) |
small |
| 31 | hit_timer_per_player_ = 999.0F como sentinela |
medium |
| 16 | Rotation3D es código muerto (28 líneas que nunca se ejecutan) |
small |
| 1 | GameScene::init() no encaja en el lifecycle de Scene |
small |
Top picks "limpieza con impacto"
| # | Hallazgo | Esfuerzo |
|---|---|---|
| 22 | defaults.hpp (530 líneas, 22 includes) dividido en subarchivos |
medium |
| 13 | Entity::center_/angle_ mirror de body_.position/angle |
medium |
| 25 | Constantes legacy del Pascal muertas en Defaults::Physics |
small |
| 18 | ShapeLoader::resolvePath + BASE_PATH sin callers |
small |
Hallazgos que requieren verificación más profunda antes de actuar
- #5 (
SceneManager::actual) - #9 (separación CollisionSystem/PhysicsWorld)
- #20 (getters dead public)
- #36 (
event.spawnejat)
Hallazgos puramente opinables que el usuario podría rechazar legítimamente
- #2, #4, #10, #14, #15, #19, #26, #27, #37, #39, #40
Hallazgos estructurales claros (bug latente o deuda técnica medible)
- #1, #6, #11, #12, #16, #21, #28, #31, #34
Ya identificados y resueltos en la rama chore/lint (no incluidos arriba)
- Patrón draw-by-state de
GameScene(refactorizado: complejidad 59 → 3) - Complejidad de
TitleScene::update(68 → 4) - Duplicado
LetraLogoTitle/Logo - Double
isActive()en Bullet (redeclaración tras rename) - Refactor de
DebrisManager::explode(39 → 3) Options::loadXXXConfigFromYamlboilerplate (con templatesreadField)PhysicsWorld::resolveBodyCollisions(35 → 5)
Plan de ataque sugerido
- #21 (en curso): borrar
Options::audioyOptions::gameplay, conectarOptions::physicsa runtime - #16, #18, #24, #25 (dead code, commits limpios y rápidos)
- #34 (eliminar doble inicialización de
enemies_) - #11 (consolidar
isActive/isAlive/isHit) - #1 (limpiar lifecycle de
Scene) - #22 + #30 (partir
defaults.hppen subarchivos) - #28 (mover
Optionsacore/) - #31 (enum
PlayerStatusreemplazando 999.0F) - #13 (eliminar mirror
center_/angle_)
A partir de ahí, decidir si seguir con el resto o pasar a otras prioridades (SDL_CALLBACKS, tuning de físicas).