#pragma once #include // Para SDL_FRect, SDL_FlipMode #include // Para size_t #include // Para pair #include // Para shared_ptr, unique_ptr #include // Para basic_string, string #include // Para move, pair #include // Para vector #include "animated_sprite.hpp" // for AnimatedSprite #include "bullet.hpp" // for Bullet #include "enter_name.hpp" // for EnterName #include "input.hpp" // for Input #include "manage_hiscore_table.hpp" // for Table #include "scoreboard.hpp" // for Scoreboard #include "utils.hpp" // for Circle class IStageInfo; class Texture; // --- Clase Player: jugador principal del juego --- // // Esta clase gestiona todos los aspectos de un jugador durante el juego, // incluyendo movimiento, disparos, animaciones y estados especiales. // // Funcionalidades principales: // • Sistema de disparo de dos líneas: funcional (cooldown) + visual (animaciones) // • Estados de animación: normal → aiming → recoiling → threat_pose → normal // • Movimiento time-based: compatibilidad con deltaTime para fluidez variable // • Power-ups e invulnerabilidad: coffee machine, extra hits, parpadeos // • Sistema de puntuación: multipliers, high scores, entrada de nombres // • Estados de juego: playing, rolling, continue, entering_name, etc. // // El sistema de disparo utiliza duraciones configurables mediante constantes // para facilitar el ajuste del gameplay y la sensación de disparo. class Player { public: // --- Constantes --- static constexpr int WIDTH = 32; // Anchura static constexpr int HEIGHT = 32; // Altura // --- Estructuras --- struct BulletColorPair { Bullet::Color normal_color; // Color de bala sin power-up Bullet::Color powered_color; // Color de bala con power-up }; // --- Enums --- enum class Id : int { NO_PLAYER = -1, // Sin jugador BOTH_PLAYERS = 0, // Ambos jugadores PLAYER1 = 1, // Jugador 1 PLAYER2 = 2 // Jugador 2 }; enum class State { // Estados de movimiento WALKING_LEFT, // Caminando hacia la izquierda WALKING_RIGHT, // Caminando hacia la derecha WALKING_STOP, // Parado, sin moverse // Estados de disparo FIRING_UP, // Disparando hacia arriba FIRING_LEFT, // Disparando hacia la izquierda FIRING_RIGHT, // Disparando hacia la derecha FIRING_NONE, // No está disparando // Estados de retroceso tras disparar RECOILING_UP, // Retroceso tras disparar hacia arriba RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha // Estados de enfriamiento tras disparar COOLING_UP, // Enfriando tras disparar hacia arriba COOLING_LEFT, // Enfriando tras disparar hacia la izquierda COOLING_RIGHT, // Enfriando tras disparar hacia la derecha // Estados generales del jugador PLAYING, // Está jugando activamente CONTINUE, // Cuenta atrás para continuar tras perder CONTINUE_TIME_OUT, // Se ha terminado la cuenta atras para continuar y se retira al jugador de la zona de juego WAITING, // Esperando para entrar a jugar ENTERING_NAME, // Introduciendo nombre para la tabla de puntuaciones SHOWING_NAME, // Mostrando el nombre introducido ROLLING, // El jugador está dando vueltas y rebotando LYING_ON_THE_FLOOR_FOREVER, // El jugador está inconsciente para siempre en el suelo (demo) GAME_OVER, // Fin de la partida, no puede jugar CELEBRATING, // Celebrando victoria (pose de victoria) ENTERING_NAME_GAME_COMPLETED, // Introduciendo nombre tras completar el juego LEAVING_SCREEN, // Saliendo de la pantalla (animación) ENTERING_SCREEN, // Entrando a la pantalla (animación) CREDITS, // Estado para mostrar los créditos del juego TITLE_ANIMATION, // Animacion para el titulo TITLE_HIDDEN, // Animacion para el titulo RECOVER, // Al aceptar continuar RESPAWNING, // Tras continuar y dar las gracias, otorga inmunidad y vuelve al juego }; // --- Estructuras --- struct Config { Id id; // Identificador del jugador float x; // Posición X inicial int y; // Posición Y inicial bool demo; // Modo demo SDL_FRect* play_area; // Área de juego (puntero para mantener referencia) std::vector> texture; // Texturas del jugador std::vector> animations; // Animaciones del jugador Table* hi_score_table; // Tabla de puntuaciones (puntero para referencia) int* glowing_entry; // Entrada brillante (puntero para mantener referencia) IStageInfo* stage_info; // Gestor de pantallas (puntero) }; // --- Constructor y destructor --- Player(const Config& config); ~Player() = default; // --- Inicialización y ciclo de vida --- void init(); // Inicializa el jugador void update(float delta_time); // Actualiza estado, animación y contadores (time-based) void render(); // Dibuja el jugador en pantalla // --- Entrada y control --- void setInput(Input::Action action); // Procesa entrada general void setInputPlaying(Input::Action action); // Procesa entrada en modo jugando void setInputEnteringName(Input::Action action); // Procesa entrada al introducir nombre // --- Movimiento y animación --- void move(float delta_time); // Mueve el jugador (time-based) void setAnimation(float delta_time); // Establece la animación según el estado (time-based) // --- Texturas y animaciones --- void setPlayerTextures(const std::vector>& texture); // Cambia las texturas del jugador // --- Gameplay: Puntuación y power-ups --- void addScore(int score, int lowest_hi_score_entry); // Añade puntos void incScoreMultiplier(); // Incrementa el multiplicador void decScoreMultiplier(); // Decrementa el multiplicador // --- Estados de juego --- void setPlayingState(State state); // Cambia el estado de juego void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad void setPowerUp(); // Activa el modo PowerUp void updatePowerUp(float delta_time); // Actualiza el valor de PowerUp void giveExtraHit(); // Concede un toque extra al jugador void removeExtraHit(); // Quita el toque extra al jugador void decContinueCounter(); // Decrementa el contador de continuar void setWalkingState(State state) { walking_state_ = state; } // Establece el estado de caminar void startFiringSystem(int cooldown_frames); // Inicia el sistema de disparo void setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; } // Establece el panel del marcador void addCredit(); void passShowingName(); // --- Estado del juego: Consultas (is* methods) --- [[nodiscard]] auto isLyingOnTheFloorForever() const -> bool { return playing_state_ == State::LYING_ON_THE_FLOOR_FOREVER; } [[nodiscard]] auto isCelebrating() const -> bool { return playing_state_ == State::CELEBRATING; } [[nodiscard]] auto isContinue() const -> bool { return playing_state_ == State::CONTINUE; } [[nodiscard]] auto isDying() const -> bool { return playing_state_ == State::ROLLING; } [[nodiscard]] auto isEnteringName() const -> bool { return playing_state_ == State::ENTERING_NAME; } [[nodiscard]] auto isShowingName() const -> bool { return playing_state_ == State::SHOWING_NAME; } [[nodiscard]] auto isEnteringNameGameCompleted() const -> bool { return playing_state_ == State::ENTERING_NAME_GAME_COMPLETED; } [[nodiscard]] auto isLeavingScreen() const -> bool { return playing_state_ == State::LEAVING_SCREEN; } [[nodiscard]] auto isGameOver() const -> bool { return playing_state_ == State::GAME_OVER; } [[nodiscard]] auto isPlaying() const -> bool { return playing_state_ == State::PLAYING; } [[nodiscard]] auto isWaiting() const -> bool { return playing_state_ == State::WAITING; } [[nodiscard]] auto isTitleHidden() const -> bool { return playing_state_ == State::TITLE_HIDDEN; } // --- Estados específicos: Consultas adicionales --- [[nodiscard]] auto canFire() const -> bool { return can_fire_new_system_; } // Usa nuevo sistema [[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; } [[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; } [[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; } [[nodiscard]] auto qualifiesForHighScore() const -> bool { return qualifies_for_high_score_; } [[nodiscard]] auto isInvulnerable() const -> bool { return invulnerable_; } [[nodiscard]] auto isPowerUp() const -> bool { return power_up_; } [[nodiscard]] auto isInBulletColorToggleMode() const -> bool { return in_power_up_ending_phase_; } // --- Getters: Propiedades y valores --- // Posición y dimensiones [[nodiscard]] auto getPosX() const -> int { return static_cast(pos_x_); } [[nodiscard]] auto getPosY() const -> int { return pos_y_; } [[nodiscard]] static auto getWidth() -> int { return WIDTH; } [[nodiscard]] static auto getHeight() -> int { return HEIGHT; } // Jugador y identificación [[nodiscard]] auto getId() const -> Player::Id { return id_; } [[nodiscard]] auto getName() const -> const std::string& { return name_; } [[nodiscard]] auto getPlayingState() const -> State { return playing_state_; } auto getCollider() -> Circle& { return collider_; } [[nodiscard]] auto getZOrder() const -> size_t { return z_order_; } void setZOrder(size_t z_order) { z_order_ = z_order; } // Puntuación y juego [[nodiscard]] auto getScore() const -> int { return score_; } [[nodiscard]] auto getScoreMultiplier() const -> float { return score_multiplier_; } [[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ <= 1; } [[nodiscard]] auto getScoreBoardPanel() const -> Scoreboard::Id { return scoreboard_panel_; } // Power-ups y estado especial [[nodiscard]] auto getCoffees() const -> int { return coffees_; } [[nodiscard]] auto getPowerUpCounter() const -> int { return power_up_counter_; } [[nodiscard]] auto getInvulnerableCounter() const -> int { return invulnerable_counter_; } [[nodiscard]] auto getBulletColor() const -> Bullet::Color; // Devuelve el color actual de bala según el estado auto getNextBulletColor() -> Bullet::Color; // Devuelve el color para la próxima bala (alterna si está en modo toggle) void setBulletColors(Bullet::Color normal, Bullet::Color powered); // Establece los colores de bala para este jugador [[nodiscard]] auto getBulletSoundFile() const -> std::string { return bullet_sound_file_; } // Devuelve el archivo de sonido de bala void setBulletSoundFile(const std::string& filename); // Establece el archivo de sonido de bala para este jugador // Contadores y timers [[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; } [[nodiscard]] auto getRecordName() const -> std::string { return enter_name_ ? enter_name_->getFinalName() : "xxx"; } [[nodiscard]] auto getLastEnterName() const -> std::string { return last_enter_name_; } // --- Configuración e interfaz externa --- void setName(const std::string& name) { name_ = name; } void setGamepad(std::shared_ptr gamepad) { gamepad_ = std::move(gamepad); } [[nodiscard]] auto getGamepad() const -> std::shared_ptr { return gamepad_; } void setUsesKeyboard(bool value) { uses_keyboard_ = value; } [[nodiscard]] auto getUsesKeyboard() const -> bool { return uses_keyboard_; } [[nodiscard]] auto getController() const -> int { return controller_index_; } // Demo file management [[nodiscard]] auto getDemoFile() const -> size_t { return demo_file_; } void setDemoFile(size_t demo_file) { demo_file_ = demo_file; } private: // --- Constantes de física y movimiento --- static constexpr float BASE_SPEED = 90.0F; // Velocidad base del jugador (pixels/segundo) // --- Constantes de power-ups y estados especiales --- static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp (frames) static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable (frames) static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad // --- Constantes del sistema de disparo (obsoletas - usar nuevo sistema) --- static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado // --- Constantes de estados de espera --- static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera // --- Constantes del nuevo sistema de disparo de dos líneas --- static constexpr float AIMING_DURATION_FACTOR = 0.5F; // 50% del cooldown funcional static constexpr float RECOILING_DURATION_MULTIPLIER = 4.0F; // 4 veces la duración de aiming static constexpr float THREAT_POSE_DURATION = 50.0F / 60.0F; // 50 frames = ~0.833s (duración base) static constexpr float MIN_THREAT_POSE_DURATION = 6.0F / 60.0F; // 6 frames = ~0.1s (duración mínima) // --- Objetos y punteros --- std::unique_ptr player_sprite_; // Sprite para dibujar el jugador std::unique_ptr power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope std::unique_ptr enter_name_; // Clase utilizada para introducir el nombre std::shared_ptr gamepad_ = nullptr; // Dispositivo asociado Table* hi_score_table_ = nullptr; // Tabla de máximas puntuaciones int* glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar IStageInfo* stage_info_; // Informacion de la pantalla actual // --- Variables de estado --- SDL_FRect play_area_; // Rectángulo con la zona de juego Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador std::string name_; // Nombre del jugador std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones Scoreboard::Id scoreboard_panel_ = Scoreboard::Id::LEFT; // Panel del marcador asociado al jugador Id id_; // Identificador para el jugador State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar State playing_state_ = State::WAITING; // Estado del jugador en el juego BulletColorPair bullet_colors_ = {.normal_color = Bullet::Color::YELLOW, .powered_color = Bullet::Color::GREEN}; // Par de colores de balas para este jugador std::string bullet_sound_file_ = "bullet1p.wav"; // Archivo de sonido de bala para este jugador float pos_x_ = 0.0F; // Posición en el eje X float default_pos_x_; // Posición inicial para el jugador float vel_x_ = 0.0F; // Cantidad de píxeles a desplazarse en el eje X float score_multiplier_ = 1.0F; // Multiplicador de puntos int pos_y_ = 0; // Posición en el eje Y int default_pos_y_; // Posición inicial para el jugador int vel_y_ = 0; // Cantidad de píxeles a desplazarse en el eje Y float invulnerable_time_accumulator_ = 0.0F; // Acumulador de tiempo para invulnerabilidad (time-based) float power_up_time_accumulator_ = 0.0F; // Acumulador de tiempo para power-up (time-based) float continue_time_accumulator_ = 0.0F; // Acumulador de tiempo para continue counter (time-based) float name_entry_time_accumulator_ = 0.0F; // Acumulador de tiempo para name entry counter (time-based) float showing_name_time_accumulator_ = 0.0F; // Acumulador de tiempo para showing name (time-based) float waiting_time_accumulator_ = 0.0F; // Acumulador de tiempo para waiting movement (time-based) float step_time_accumulator_ = 0.0F; // Acumulador de tiempo para step counter (time-based) // ======================================== // NUEVO SISTEMA DE DISPARO DE DOS LÍNEAS // ======================================== // LÍNEA 1: SISTEMA FUNCIONAL (CanFire) float fire_cooldown_timer_ = 0.0F; // Tiempo restante hasta poder disparar otra vez bool can_fire_new_system_ = true; // true si puede disparar ahora mismo // LÍNEA 2: SISTEMA VISUAL (Animaciones) enum class VisualFireState { NORMAL, // Brazo en posición neutral AIMING, // Brazo alzado (disparando) RECOILING, // Brazo en retroceso THREAT_POSE // Posición amenazante }; VisualFireState visual_fire_state_ = VisualFireState::NORMAL; float visual_state_timer_ = 0.0F; // Tiempo en el estado visual actual float aiming_duration_ = 0.0F; // Duración del estado AIMING float recoiling_duration_ = 0.0F; // Duración del estado RECOILING int invulnerable_counter_ = INVULNERABLE_COUNTER; // Contador para la invulnerabilidad int score_ = 0; // Puntos del jugador int coffees_ = 0; // Indica cuántos cafés lleva acumulados int power_up_counter_ = POWERUP_COUNTER; // Temporizador para el modo PowerUp int power_up_x_offset_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador int continue_counter_ = 10; // Contador para poder continuar int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse size_t demo_file_ = 0; // Indice del fichero de datos para el modo demo size_t z_order_ = 0; // Orden de dibujado en la pantalla float name_entry_idle_time_accumulator_ = 0.0F; // Tiempo idle acumulado para poner nombre (milisegundos) float name_entry_total_time_accumulator_ = 0.0F; // Tiempo total acumulado poniendo nombre (milisegundos) int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente int credits_used_ = 0; // Indica el número de veces que ha continuado int waiting_counter_ = 0; // Contador para el estado de espera bool qualifies_for_high_score_ = false; // Indica si tiene una puntuación que le permite entrar en la tabla de records bool invulnerable_ = true; // Indica si el jugador es invulnerable bool extra_hit_ = false; // Indica si el jugador tiene un toque extra bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp bool power_sprite_visible_ = false; // Indica si el sprite de power-up debe ser visible bool in_power_up_ending_phase_ = false; // Indica si está en la fase final del power-up (alternando colores) bool bullet_color_toggle_ = false; // Para alternar entre verde y amarillo en fase final bool demo_ = false; // Para que el jugador sepa si está en el modo demostración bool game_completed_ = false; // Indica si ha completado el juego bool uses_keyboard_ = false; // Indica si usa el teclado como dispositivo de control bool recover_sound_triggered_ = false; // Indica si ya ha sonado el sonido en el estado RECOVER // --- Métodos internos --- void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador void shiftSprite(); // Recoloca el sprite // --- Setters internos --- void setController(int index) { controller_index_ = index; } void setFiringState(State state) { firing_state_ = state; } void setInvulnerableCounter(int value) { invulnerable_counter_ = value; } void setPowerUpCounter(int value) { power_up_counter_ = value; } void setScore(int score) { score_ = score; } void setScoreMultiplier(float value) { score_multiplier_ = value; } // --- Actualizadores de estado (time-based) --- void updateInvulnerable(float delta_time); // Monitoriza el estado de invulnerabilidad void updateContinueCounter(float delta_time); // Actualiza el contador de continue void updateEnterNameCounter(float delta_time); // Actualiza el contador de entrar nombre void updateShowingName(float delta_time); // Actualiza el estado SHOWING_NAME void decNameEntryCounter(); // Decrementa el contador de entrar nombre // --- Utilidades generales --- void updateScoreboard(); // Actualiza el panel del marcador void setScoreboardMode(Scoreboard::Mode mode) const; // Cambia el modo del marcador void playSound(const std::string& name) const; // Hace sonar un sonido [[nodiscard]] auto isRenderable() const -> bool; // Indica si se puede dibujar el objeto void addScoreToScoreBoard() const; // Añade una puntuación a la tabla de records // --- Sistema de disparo (nuevo - dos líneas) --- void updateFireSystem(float delta_time); // Método principal del nuevo sistema de disparo void updateFunctionalLine(float delta_time); // Actualiza la línea funcional (CanFire) void updateVisualLine(float delta_time); // Actualiza la línea visual (Animaciones) void updateFiringStateFromVisual(); // Sincroniza firing_state_ con visual_fire_state_ void transitionToRecoilingNew(); // Transición AIMING → RECOILING void transitionToThreatPose(); // Transición RECOILING → THREAT_POSE void transitionToNormalNew(); // Transición THREAT_POSE → NORMAL // --- Manejadores de movimiento --- void handlePlayingMovement(float delta_time); // Gestiona el movimiento durante el juego void handleRecoverMovement(); // Comprueba si ha acabado la animación de recuperación void updateStepCounter(float delta_time); // Incrementa o ajusta el contador de pasos void setInputBasedOnPlayerId(); // Asocia las entradas de control según el jugador // --- Manejadores de estados especiales --- void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" void handleRollingBoundaryCollision(); // Detecta colisiones con límites durante rodamiento void handleRollingGroundCollision(); // Gestiona interacción con el suelo durante rodamiento void handleRollingStop(); // Detiene el movimiento del objeto rodante void handleRollingBounce(); // Aplica lógica de rebote durante rodamiento void handleContinueTimeOut(); // Gestiona tiempo de espera en pantalla "Continuar" // --- Manejadores de transiciones de pantalla --- void handleTitleAnimation(float delta_time); // Ejecuta animación del título void handleLeavingScreen(float delta_time); // Lógica para salir de pantalla void handleEnteringScreen(float delta_time); // Lógica para entrar en pantalla void handlePlayer1Entering(float delta_time); // Entrada del Jugador 1 void handlePlayer2Entering(float delta_time); // Entrada del Jugador 2 // --- Manejadores de pantallas especiales --- void handleCreditsMovement(float delta_time); // Movimiento en pantalla de créditos void handleCreditsRightMovement(); // Movimiento hacia la derecha en créditos void handleCreditsLeftMovement(); // Movimiento hacia la izquierda en créditos void handleWaitingMovement(float delta_time); // Animación del jugador saludando void updateWalkingStateForCredits(); // Actualiza estado de caminata en créditos // --- Utilidades de animación --- [[nodiscard]] auto computeAnimation() const -> std::pair; // Calcula animación de movimiento y disparo };