// demo_pilot.hpp - IA de piloto para el attract mode (demo jugándose sola) // © 2026 JailDesigner // // Calcula, cada frame, los controles de la nave (rotar/empujar/disparar) leyendo // el estado de juego en solo-lectura. No lee Input ni muta entidades: GameScene // aplica el Control resultante via Ship::applyMovement + fireBullet. // // El comportamiento busca parecer "humano", no óptimo: apuntado con lead y un // pequeño error, cadencia de disparo con cooldown, esquiva por radio de peligro // y sesgo hacia el centro para no pegarse a las paredes. #pragma once #include #include #include #include #include "core/defaults/entities.hpp" #include "core/system/scene_context.hpp" #include "game/constants.hpp" #include "game/entities/bullet.hpp" #include "game/entities/enemy.hpp" #include "game/entities/ship.hpp" namespace Systems::Demo { // Escenari curat del attract mode: stage de arranque + nombre de naus IA. // Tots inclouen estrelles (que disparen) per donar feina al pilot. struct Scenario { std::uint8_t stage; // stage_id de stages.yaml on arrenca la demo std::uint8_t players; // 1 o 2 naus controlades per la IA }; inline constexpr std::array SCENARIOS = {{ {.stage = 5, .players = 1}, // estrelles + mix, 1 jugador {.stage = 8, .players = 1}, // pressió alta, 1 jugador {.stage = 6, .players = 2}, // densitat alta, 2 jugadors (foc amic ON) {.stage = 10, .players = 2}, // repte final, 2 jugadors (foc amic ON) }}; // Escenari per a l'índex donat (cíclic). [[nodiscard]] inline auto scenario(std::uint8_t index) -> Scenario { return SCENARIOS.at(index % SCENARIOS.size()); } // Control de la nave para un frame. struct Control { bool left{false}; bool right{false}; bool thrust{false}; bool shoot{false}; }; class DemoPilot { public: [[nodiscard]] auto compute(const Ship& ship, const Ship* teammate, // altra nau (nullptr si no n'hi ha): evita foc amic const std::array& enemies, const std::array(Defaults::Entities::MAX_BULLETS_TOTAL)>& bullets, const SDL_FRect& play_area, float delta_time) -> Control; private: float retarget_timer_{0.0F}; // re-evalúa objetivo cada RETARGET_INTERVAL float fire_cooldown_{0.0F}; // tiempo restante hasta poder disparar float aim_jitter_{0.0F}; // error de apuntado, refrescado al retargetar int target_idx_{-1}; // índice del enemigo objetivo (o -1) }; } // namespace Systems::Demo