efecte maquina d'escriure per als textos d'entrada de fase

This commit is contained in:
2025-12-09 19:38:29 +01:00
parent 94a7a38cdd
commit 64ab08973c
5 changed files with 96 additions and 13 deletions

View File

@@ -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<size_t>(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<float>(x), static_cast<float>(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);
}

View File

@@ -3,6 +3,7 @@
#pragma once
#include <array>
#include <cstdint>
#include <string>
#include <vector>
@@ -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<const char*, 12> 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!";
}

View File

@@ -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<size_t>(std::rand()) % Constants::MISSATGES_LEVEL_START.size();
missatge_level_start_actual_ = Constants::MISSATGES_LEVEL_START[index];
}
std::cout << "[StageManager] Canvi d'estat: ";

View File

@@ -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);