efecte maquina d'escriure per als textos d'entrada de fase
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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!";
|
||||
}
|
||||
|
||||
|
||||
@@ -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: ";
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user