From 64ab08973cc46d364b10c37375b845ee7540dd90 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 9 Dec 2025 19:38:29 +0100 Subject: [PATCH] efecte maquina d'escriure per als textos d'entrada de fase --- source/core/defaults.hpp | 8 ++- source/game/escenes/escena_joc.cpp | 67 +++++++++++++++++++--- source/game/stage_system/stage_config.hpp | 18 +++++- source/game/stage_system/stage_manager.cpp | 14 ++++- source/game/stage_system/stage_manager.hpp | 2 + 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/source/core/defaults.hpp b/source/core/defaults.hpp index 07302b5..bbcfa24 100644 --- a/source/core/defaults.hpp +++ b/source/core/defaults.hpp @@ -79,7 +79,13 @@ constexpr int STARTING_LIVES = 3; // Initial lives constexpr float DEATH_DURATION = 3.0f; // Seconds of death animation constexpr float GAME_OVER_DURATION = 5.0f; // Seconds to display game over constexpr float COLLISION_SHIP_ENEMY_AMPLIFIER = 0.80f; // 80% hitbox (generous) -constexpr float STAGE_TRANSITION_DURATION = 3.0f; // Seconds for LEVEL_START/COMPLETED transitions +// Transición LEVEL_START (mensajes aleatorios PRE-level) +constexpr float LEVEL_START_DURATION = 3.0f; // Duración total +constexpr float LEVEL_START_TYPING_RATIO = 0.3f; // 30% escribiendo, 70% mostrando + +// Transición LEVEL_COMPLETED (mensaje "GOOD JOB COMMANDER!") +constexpr float LEVEL_COMPLETED_DURATION = 3.0f; // Duración total +constexpr float LEVEL_COMPLETED_TYPING_RATIO = 0.0f; // 0.0 = sin typewriter (directo) } // namespace Game // Física (valores actuales del juego, sincronizados con joc_asteroides.cpp) diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp index 544fcad..118fb58 100644 --- a/source/game/escenes/escena_joc.cpp +++ b/source/game/escenes/escena_joc.cpp @@ -368,7 +368,7 @@ void EscenaJoc::dibuixar() { gestor_puntuacio_.dibuixar(); // [EXISTING] Draw intro message and score - dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_START); + dibuixar_missatge_stage(stage_manager_->get_missatge_level_start()); dibuixar_marcador(); break; @@ -689,16 +689,67 @@ void EscenaJoc::detectar_col·lisio_nau_enemics() { // [NEW] Stage system helper methods void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) { - constexpr float escala = 1.0f; + constexpr float escala_base = 1.0f; constexpr float spacing = 2.0f; - - float text_width = text_.get_text_width(missatge, escala, spacing); - float text_height = text_.get_text_height(escala); + constexpr float max_width_ratio = 0.9f; // 90% del ancho disponible const SDL_FRect& play_area = Defaults::Zones::PLAYAREA; - float x = play_area.x + (play_area.w - text_width) / 2.0f; + const float max_width = play_area.w * max_width_ratio; // 558px + + // ========== TYPEWRITER EFFECT (PARAMETRIZED) ========== + // Get state-specific timing configuration + float total_time; + float typing_ratio; + + if (stage_manager_->get_estat() == StageSystem::EstatStage::LEVEL_START) { + total_time = Defaults::Game::LEVEL_START_DURATION; + typing_ratio = Defaults::Game::LEVEL_START_TYPING_RATIO; + } else { // LEVEL_COMPLETED + total_time = Defaults::Game::LEVEL_COMPLETED_DURATION; + typing_ratio = Defaults::Game::LEVEL_COMPLETED_TYPING_RATIO; + } + + // Calculate progress from timer (0.0 at start → 1.0 at end) + float remaining_time = stage_manager_->get_timer_transicio(); + float progress = 1.0f - (remaining_time / total_time); + + // Determine how many characters to show + size_t visible_chars; + + if (typing_ratio > 0.0f && progress < typing_ratio) { + // Typewriter phase: show partial text + float typing_progress = progress / typing_ratio; // Normalize to 0.0-1.0 + visible_chars = static_cast(missatge.length() * typing_progress); + if (visible_chars == 0 && progress > 0.0f) { + visible_chars = 1; // Show at least 1 character after first frame + } + } else { + // Display phase: show complete text + // (Either after typing phase, or immediately if typing_ratio == 0.0) + visible_chars = missatge.length(); + } + + // Create partial message (substring for typewriter) + std::string partial_message = missatge.substr(0, visible_chars); + // =================================================== + + // Calculate text width at base scale (using FULL message for position calculation) + float text_width_at_base = text_.get_text_width(missatge, escala_base, spacing); + + // Auto-scale if text exceeds max width + float escala = (text_width_at_base <= max_width) + ? escala_base + : max_width / text_width_at_base; + + // Recalculate dimensions with final scale (using FULL message for centering) + float full_text_width = text_.get_text_width(missatge, escala, spacing); + float text_height = text_.get_text_height(escala); + + // Calculate position as if FULL text was there (for fixed position typewriter) + float x = play_area.x + (play_area.w - full_text_width) / 2.0f; float y = play_area.y + (play_area.h * 0.25f) - (text_height / 2.0f); - Punt pos = {static_cast(x), static_cast(y)}; - text_.render(missatge, pos, escala, spacing); + // Render only the partial message (typewriter effect) + Punt pos = {x, y}; + text_.render(partial_message, pos, escala, spacing); } diff --git a/source/game/stage_system/stage_config.hpp b/source/game/stage_system/stage_config.hpp index 26ff3d4..3b514ae 100644 --- a/source/game/stage_system/stage_config.hpp +++ b/source/game/stage_system/stage_config.hpp @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -77,7 +78,22 @@ struct ConfigSistemaStages { // Constants per missatges de transició namespace Constants { - constexpr const char* MISSATGE_LEVEL_START = "ENEMY INCOMING"; + // Pool de missatges per inici de level (selecció aleatòria) + inline constexpr std::array MISSATGES_LEVEL_START = { + "ORNI ALERT!", + "INCOMING ORNIS!", + "ROLLING THREAT!", + "ENEMY WAVE!", + "WAVE OF ORNIS DETECTED!", + "NEXT SWARM APPROACHING!", + "BRACE FOR THE NEXT WAVE!", + "ANOTHER ATTACK INCOMING!", + "SENSORS DETECT HOSTILE ORNIS...", + "UNIDENTIFIED ROLLING OBJECTS INBOUND!", + "ENEMY FORCES MOBILIZING!", + "PREPARE FOR IMPACT!" + }; + constexpr const char* MISSATGE_LEVEL_COMPLETED = "GOOD JOB COMMANDER!"; } diff --git a/source/game/stage_system/stage_manager.cpp b/source/game/stage_system/stage_manager.cpp index f23e2c4..e3bee87 100644 --- a/source/game/stage_system/stage_manager.cpp +++ b/source/game/stage_system/stage_manager.cpp @@ -63,9 +63,17 @@ const ConfigStage* StageManager::get_config_actual() const { void StageManager::canviar_estat(EstatStage nou_estat) { estat_ = nou_estat; - // Reset transition timer for LEVEL_START and LEVEL_COMPLETED - if (nou_estat == EstatStage::LEVEL_START || nou_estat == EstatStage::LEVEL_COMPLETED) { - timer_transicio_ = Defaults::Game::STAGE_TRANSITION_DURATION; + // Set timer based on state type + if (nou_estat == EstatStage::LEVEL_START) { + timer_transicio_ = Defaults::Game::LEVEL_START_DURATION; + } else if (nou_estat == EstatStage::LEVEL_COMPLETED) { + timer_transicio_ = Defaults::Game::LEVEL_COMPLETED_DURATION; + } + + // Select random message when entering LEVEL_START + if (nou_estat == EstatStage::LEVEL_START) { + size_t index = static_cast(std::rand()) % Constants::MISSATGES_LEVEL_START.size(); + missatge_level_start_actual_ = Constants::MISSATGES_LEVEL_START[index]; } std::cout << "[StageManager] Canvi d'estat: "; diff --git a/source/game/stage_system/stage_manager.hpp b/source/game/stage_system/stage_manager.hpp index d65125f..552a7a8 100644 --- a/source/game/stage_system/stage_manager.hpp +++ b/source/game/stage_system/stage_manager.hpp @@ -35,6 +35,7 @@ class StageManager { uint8_t get_stage_actual() const { return stage_actual_; } const ConfigStage* get_config_actual() const; float get_timer_transicio() const { return timer_transicio_; } + const std::string& get_missatge_level_start() const { return missatge_level_start_actual_; } // Spawn control (delegate to SpawnController) SpawnController& get_spawn_controller() { return spawn_controller_; } @@ -47,6 +48,7 @@ class StageManager { EstatStage estat_; uint8_t stage_actual_; // 1-10 float timer_transicio_; // Timer for LEVEL_START/LEVEL_COMPLETED (3.0s → 0.0s) + std::string missatge_level_start_actual_; // Missatge seleccionat per al level actual // State transitions void canviar_estat(EstatStage nou_estat);