feat(locale): sistema i18n YAML amb català i anglès
This commit is contained in:
@@ -20,6 +20,7 @@ namespace ConfigYaml {
|
||||
Config::PlayerBindings& player1 = engine_config.player1;
|
||||
Config::PlayerBindings& player2 = engine_config.player2;
|
||||
bool& console = engine_config.console;
|
||||
std::string& locale = engine_config.locale;
|
||||
} // namespace
|
||||
|
||||
// ========== FUNCIONS AUXILIARS PER CONVERSIÓ DE CONTROLES ==========
|
||||
@@ -208,6 +209,9 @@ namespace ConfigYaml {
|
||||
rendering.render_width = Defaults::Rendering::RENDER_WIDTH_DEFAULT;
|
||||
rendering.render_height = Defaults::Rendering::RENDER_HEIGHT_DEFAULT;
|
||||
|
||||
// Idioma
|
||||
locale = "ca";
|
||||
|
||||
// Version
|
||||
version = std::string(Project::VERSION);
|
||||
}
|
||||
@@ -446,6 +450,16 @@ namespace ConfigYaml {
|
||||
loadPlayer1ControlsFromYaml(yaml);
|
||||
loadPlayer2ControlsFromYaml(yaml);
|
||||
|
||||
// Idioma (opcional; valors admesos: "ca" | "en")
|
||||
if (yaml.contains("locale")) {
|
||||
try {
|
||||
auto val = yaml["locale"].get_value<std::string>();
|
||||
locale = (val == "ca" || val == "en") ? val : "ca";
|
||||
} catch (...) {
|
||||
locale = "ca";
|
||||
}
|
||||
}
|
||||
|
||||
if (console) {
|
||||
std::cout << "Config carregada correctament desde: " << config_file_path
|
||||
<< '\n';
|
||||
@@ -532,6 +546,9 @@ namespace ConfigYaml {
|
||||
file << " render_height: " << rendering.render_height
|
||||
<< " # Parell amb render_width (720, 900, 1080, 1440, 2160)\n\n";
|
||||
|
||||
file << "# IDIOMA\n";
|
||||
file << "locale: " << locale << " # ca | en\n\n";
|
||||
|
||||
// Guardar controls de jugadors
|
||||
savePlayer1ControlsToYaml(file);
|
||||
savePlayer2ControlsToYaml(file);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/input/input.hpp"
|
||||
#include "core/locale/locale.hpp"
|
||||
#include "core/system/scene_context.hpp"
|
||||
#include "game/stage_system/stage_loader.hpp"
|
||||
#include "game/systems/collision_system.hpp"
|
||||
@@ -608,7 +609,7 @@ void GameScene::drawGameOverState() {
|
||||
firework_manager_.draw();
|
||||
floating_score_manager_.draw();
|
||||
|
||||
const std::string GAME_OVER_TEXT = "GAME OVER";
|
||||
const std::string GAME_OVER_TEXT = Locale::get().text("game_screen.game_over");
|
||||
constexpr float SCALE = Defaults::Game::GameOverScreen::TEXT_SCALE;
|
||||
constexpr float SPACING = Defaults::Game::GameOverScreen::TEXT_SPACING;
|
||||
|
||||
@@ -710,7 +711,7 @@ void GameScene::drawLevelCompletedState() {
|
||||
debris_manager_.draw();
|
||||
firework_manager_.draw();
|
||||
floating_score_manager_.draw();
|
||||
drawStageMessage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED);
|
||||
drawStageMessage(Locale::get().text("stage.completed"));
|
||||
drawScoreboard();
|
||||
}
|
||||
|
||||
@@ -814,7 +815,7 @@ auto GameScene::buildScoreboard() const -> std::string {
|
||||
|
||||
// Format: "123456 03 LEVEL 01 654321 02"
|
||||
// Nota: dos espais entre seccions, mantenir ambdós slots siempre visibles
|
||||
return score_p1 + " " + vides_p1 + " LEVEL " + stage_str + " " + score_p2 + " " + vides_p2;
|
||||
return score_p1 + " " + vides_p1 + " " + Locale::get().text("hud.level") + stage_str + " " + score_p2 + " " + vides_p2;
|
||||
}
|
||||
|
||||
// [NEW] Stage system helper methods
|
||||
@@ -946,7 +947,7 @@ void GameScene::drawContinue() {
|
||||
constexpr float SPACING = 4.0F;
|
||||
|
||||
// "CONTINUE" text (using constants)
|
||||
const std::string CONTINUE_TEXT = "CONTINUE";
|
||||
const std::string CONTINUE_TEXT = Locale::get().text("game_screen.continue");
|
||||
float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE;
|
||||
float y_ratio_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_Y_RATIO;
|
||||
|
||||
@@ -966,7 +967,10 @@ void GameScene::drawContinue() {
|
||||
|
||||
// "CONTINUES LEFT" (conditional + using constants)
|
||||
if (!Defaults::Game::INFINITE_CONTINUES) {
|
||||
const std::string CONTINUES_TEXT = "CONTINUES LEFT: " + std::to_string(Defaults::Game::MAX_CONTINUES - continues_used_);
|
||||
const std::string CONTINUES_TEXT = localeSubstitute(
|
||||
Locale::get().text("game_screen.continues_left"),
|
||||
"{n}",
|
||||
std::to_string(Defaults::Game::MAX_CONTINUES - continues_used_));
|
||||
float escala_info = Defaults::Game::ContinueScreen::INFO_TEXT_SCALE;
|
||||
float y_ratio_info = Defaults::Game::ContinueScreen::INFO_TEXT_Y_RATIO;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/graphics/shape_loader.hpp"
|
||||
#include "core/input/input.hpp"
|
||||
#include "core/locale/locale.hpp"
|
||||
#include "core/math/easing.hpp"
|
||||
#include "core/rendering/shape_renderer.hpp"
|
||||
#include "core/system/scene_context.hpp"
|
||||
@@ -602,7 +603,7 @@ void TitleScene::draw() {
|
||||
mostrar_text = (std::sin(FASE) > 0.0F);
|
||||
}
|
||||
if (mostrar_text) {
|
||||
const std::string MAIN_TEXT = "PRESS START TO PLAY";
|
||||
const std::string MAIN_TEXT = Locale::get().text("title.press_start");
|
||||
const float MAIN_SCALE = Defaults::Title::Layout::PRESS_START_SCALE;
|
||||
const float CENTRE_X = Defaults::Game::WIDTH / 2.0F;
|
||||
const float CENTRE_Y = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -78,24 +77,4 @@ namespace StageSystem {
|
||||
}
|
||||
};
|
||||
|
||||
// Constants per messages de transición
|
||||
namespace Constants {
|
||||
// Pool de messages per start 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!";
|
||||
} // namespace Constants
|
||||
|
||||
} // namespace StageSystem
|
||||
|
||||
@@ -10,164 +10,164 @@
|
||||
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/locale/locale.hpp"
|
||||
#include "stage_config.hpp"
|
||||
|
||||
namespace StageSystem {
|
||||
|
||||
StageManager::StageManager(const StageSystemConfig* config)
|
||||
: config_(config)
|
||||
{
|
||||
if (config_ == nullptr) {
|
||||
std::cerr << "[StageManager] Error: config es null" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::init() {
|
||||
stage_actual_ = 1;
|
||||
loadStage(stage_actual_);
|
||||
changeState(EstatStage::INIT_HUD);
|
||||
|
||||
std::cout << "[StageManager] Inicialitzat a stage " << static_cast<int>(stage_actual_)
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
void StageManager::update(float delta_time, bool pause_spawn) {
|
||||
switch (estat_) {
|
||||
case EstatStage::INIT_HUD:
|
||||
processInitHud(delta_time);
|
||||
break;
|
||||
|
||||
case EstatStage::LEVEL_START:
|
||||
processLevelStart(delta_time);
|
||||
break;
|
||||
|
||||
case EstatStage::PLAYING:
|
||||
processPlaying(delta_time, pause_spawn);
|
||||
break;
|
||||
|
||||
case EstatStage::LEVEL_COMPLETED:
|
||||
processLevelCompleted(delta_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::markStageCompleted() {
|
||||
std::cout << "[StageManager] Stage " << static_cast<int>(stage_actual_) << " completat!"
|
||||
<< '\n';
|
||||
changeState(EstatStage::LEVEL_COMPLETED);
|
||||
}
|
||||
|
||||
auto StageManager::isGameComplete() const -> bool {
|
||||
return stage_actual_ >= config_->metadata.total_stages &&
|
||||
estat_ == EstatStage::LEVEL_COMPLETED &&
|
||||
timer_transicio_ <= 0.0F;
|
||||
}
|
||||
|
||||
auto StageManager::getCurrentConfig() const -> const StageConfig* {
|
||||
return config_->findStage(stage_actual_);
|
||||
}
|
||||
|
||||
void StageManager::changeState(EstatStage nou_estat) {
|
||||
estat_ = nou_estat;
|
||||
|
||||
// Set timer based on state type
|
||||
if (nou_estat == EstatStage::INIT_HUD) {
|
||||
timer_transicio_ = Defaults::Game::INIT_HUD_DURATION;
|
||||
} else 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];
|
||||
|
||||
// [NOU] Iniciar música al entrar en LEVEL_START (después de INIT_HUD)
|
||||
// Solo si no está sonant ya (per evitar reset en loops posteriors)
|
||||
if (Audio::getMusicState() != Audio::MusicState::PLAYING) {
|
||||
Audio::get()->playMusic("game.ogg");
|
||||
StageManager::StageManager(const StageSystemConfig* config)
|
||||
: config_(config) {
|
||||
if (config_ == nullptr) {
|
||||
std::cerr << "[StageManager] Error: config es null" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[StageManager] Canvi de state: ";
|
||||
switch (nou_estat) {
|
||||
case EstatStage::INIT_HUD:
|
||||
std::cout << "INIT_HUD";
|
||||
break;
|
||||
case EstatStage::LEVEL_START:
|
||||
std::cout << "LEVEL_START";
|
||||
break;
|
||||
case EstatStage::PLAYING:
|
||||
std::cout << "PLAYING";
|
||||
break;
|
||||
case EstatStage::LEVEL_COMPLETED:
|
||||
std::cout << "LEVEL_COMPLETED";
|
||||
break;
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
void StageManager::processInitHud(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
changeState(EstatStage::LEVEL_START);
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::processLevelStart(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
changeState(EstatStage::PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::processPlaying(float delta_time, bool pause_spawn) {
|
||||
|
||||
// Update spawn controller (pauses when pause_spawn = true)
|
||||
// Note: The actual enemy array update happens in GameScene::update()
|
||||
// This is just for internal timekeeping
|
||||
(void)delta_time; // Spawn controller is updated externally
|
||||
(void)pause_spawn; // Passed to spawn_controller_.update() by GameScene
|
||||
}
|
||||
|
||||
void StageManager::processLevelCompleted(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
// Advance to next stage
|
||||
stage_actual_++;
|
||||
|
||||
// Loop back to stage 1 after final stage
|
||||
if (stage_actual_ > config_->metadata.total_stages) {
|
||||
stage_actual_ = 1;
|
||||
std::cout << "[StageManager] Todas las stages completades! Tornant a stage 1"
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
// Load next stage
|
||||
void StageManager::init() {
|
||||
stage_actual_ = 1;
|
||||
loadStage(stage_actual_);
|
||||
changeState(EstatStage::LEVEL_START);
|
||||
}
|
||||
}
|
||||
changeState(EstatStage::INIT_HUD);
|
||||
|
||||
void StageManager::loadStage(uint8_t stage_id) {
|
||||
const StageConfig* stage_config = config_->findStage(stage_id);
|
||||
if (stage_config == nullptr) {
|
||||
std::cerr << "[StageManager] Error: no es pot trobar stage " << static_cast<int>(stage_id)
|
||||
std::cout << "[StageManager] Inicialitzat a stage " << static_cast<int>(stage_actual_)
|
||||
<< '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure spawn controller
|
||||
spawn_controller_.configure(stage_config);
|
||||
spawn_controller_.start();
|
||||
void StageManager::update(float delta_time, bool pause_spawn) {
|
||||
switch (estat_) {
|
||||
case EstatStage::INIT_HUD:
|
||||
processInitHud(delta_time);
|
||||
break;
|
||||
|
||||
std::cout << "[StageManager] Carregat stage " << static_cast<int>(stage_id) << ": "
|
||||
<< static_cast<int>(stage_config->total_enemies) << " enemigos" << '\n';
|
||||
}
|
||||
case EstatStage::LEVEL_START:
|
||||
processLevelStart(delta_time);
|
||||
break;
|
||||
|
||||
case EstatStage::PLAYING:
|
||||
processPlaying(delta_time, pause_spawn);
|
||||
break;
|
||||
|
||||
case EstatStage::LEVEL_COMPLETED:
|
||||
processLevelCompleted(delta_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::markStageCompleted() {
|
||||
std::cout << "[StageManager] Stage " << static_cast<int>(stage_actual_) << " completat!"
|
||||
<< '\n';
|
||||
changeState(EstatStage::LEVEL_COMPLETED);
|
||||
}
|
||||
|
||||
auto StageManager::isGameComplete() const -> bool {
|
||||
return stage_actual_ >= config_->metadata.total_stages &&
|
||||
estat_ == EstatStage::LEVEL_COMPLETED &&
|
||||
timer_transicio_ <= 0.0F;
|
||||
}
|
||||
|
||||
auto StageManager::getCurrentConfig() const -> const StageConfig* {
|
||||
return config_->findStage(stage_actual_);
|
||||
}
|
||||
|
||||
void StageManager::changeState(EstatStage nou_estat) {
|
||||
estat_ = nou_estat;
|
||||
|
||||
// Set timer based on state type
|
||||
if (nou_estat == EstatStage::INIT_HUD) {
|
||||
timer_transicio_ = Defaults::Game::INIT_HUD_DURATION;
|
||||
} else 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) {
|
||||
const std::size_t POOL = Locale::get().count("stage.start");
|
||||
const std::size_t INDEX = (POOL == 0) ? 0 : static_cast<std::size_t>(std::rand()) % POOL;
|
||||
missatge_level_start_actual_ = Locale::get().text("stage.start." + std::to_string(INDEX));
|
||||
|
||||
// [NOU] Iniciar música al entrar en LEVEL_START (después de INIT_HUD)
|
||||
// Solo si no está sonant ya (per evitar reset en loops posteriors)
|
||||
if (Audio::getMusicState() != Audio::MusicState::PLAYING) {
|
||||
Audio::get()->playMusic("game.ogg");
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[StageManager] Canvi de state: ";
|
||||
switch (nou_estat) {
|
||||
case EstatStage::INIT_HUD:
|
||||
std::cout << "INIT_HUD";
|
||||
break;
|
||||
case EstatStage::LEVEL_START:
|
||||
std::cout << "LEVEL_START";
|
||||
break;
|
||||
case EstatStage::PLAYING:
|
||||
std::cout << "PLAYING";
|
||||
break;
|
||||
case EstatStage::LEVEL_COMPLETED:
|
||||
std::cout << "LEVEL_COMPLETED";
|
||||
break;
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
void StageManager::processInitHud(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
changeState(EstatStage::LEVEL_START);
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::processLevelStart(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
changeState(EstatStage::PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::processPlaying(float delta_time, bool pause_spawn) {
|
||||
// Update spawn controller (pauses when pause_spawn = true)
|
||||
// Note: The actual enemy array update happens in GameScene::update()
|
||||
// This is just for internal timekeeping
|
||||
(void)delta_time; // Spawn controller is updated externally
|
||||
(void)pause_spawn; // Passed to spawn_controller_.update() by GameScene
|
||||
}
|
||||
|
||||
void StageManager::processLevelCompleted(float delta_time) {
|
||||
timer_transicio_ -= delta_time;
|
||||
|
||||
if (timer_transicio_ <= 0.0F) {
|
||||
// Advance to next stage
|
||||
stage_actual_++;
|
||||
|
||||
// Loop back to stage 1 after final stage
|
||||
if (stage_actual_ > config_->metadata.total_stages) {
|
||||
stage_actual_ = 1;
|
||||
std::cout << "[StageManager] Todas las stages completades! Tornant a stage 1"
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
// Load next stage
|
||||
loadStage(stage_actual_);
|
||||
changeState(EstatStage::LEVEL_START);
|
||||
}
|
||||
}
|
||||
|
||||
void StageManager::loadStage(uint8_t stage_id) {
|
||||
const StageConfig* stage_config = config_->findStage(stage_id);
|
||||
if (stage_config == nullptr) {
|
||||
std::cerr << "[StageManager] Error: no es pot trobar stage " << static_cast<int>(stage_id)
|
||||
<< '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure spawn controller
|
||||
spawn_controller_.configure(stage_config);
|
||||
spawn_controller_.start();
|
||||
|
||||
std::cout << "[StageManager] Carregat stage " << static_cast<int>(stage_id) << ": "
|
||||
<< static_cast<int>(stage_config->total_enemies) << " enemigos" << '\n';
|
||||
}
|
||||
|
||||
} // namespace StageSystem
|
||||
|
||||
Reference in New Issue
Block a user