reestructuració
This commit is contained in:
215
source/core/system/defaults.hpp
Normal file
215
source/core/system/defaults.hpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_ScaleMode
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "ui/notifier.hpp" // Para Notifier::Position
|
||||
|
||||
namespace Defaults::Game {
|
||||
constexpr float WIDTH = 320.0F;
|
||||
constexpr float HEIGHT = 256.0F;
|
||||
constexpr int NAME_ENTRY_IDLE_TIME = 10;
|
||||
constexpr int NAME_ENTRY_TOTAL_TIME = 60;
|
||||
constexpr bool HIT_STOP = false;
|
||||
constexpr int HIT_STOP_MS = 500;
|
||||
constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00";
|
||||
|
||||
constexpr float PLAY_AREA_X = 0.0F;
|
||||
constexpr float PLAY_AREA_Y = 0.0F;
|
||||
constexpr float PLAY_AREA_W = 320.0F;
|
||||
constexpr float PLAY_AREA_H = 216.0F;
|
||||
} // namespace Defaults::Game
|
||||
|
||||
namespace Defaults::Fade {
|
||||
constexpr const char* COLOR = "1F2B30";
|
||||
constexpr float NUM_SQUARES_WIDTH = 160.0F;
|
||||
constexpr float NUM_SQUARES_HEIGHT = 128.0F;
|
||||
constexpr int RANDOM_SQUARES_DURATION_MS = 1;
|
||||
constexpr int POST_DURATION_MS = 80;
|
||||
constexpr float VENETIAN_SIZE = 12.0F;
|
||||
} // namespace Defaults::Fade
|
||||
|
||||
namespace Defaults::Scoreboard {
|
||||
constexpr float RECT_X = 0.0F;
|
||||
constexpr float RECT_Y = 216.0F;
|
||||
constexpr float RECT_W = 320.0F;
|
||||
constexpr float RECT_H = 40.0F;
|
||||
constexpr bool SEPARATOR_AUTOCOLOR = true;
|
||||
constexpr const char* SEPARATOR_COLOR = "0D1A2B";
|
||||
constexpr const char* EASY_COLOR = "4B692F";
|
||||
constexpr const char* NORMAL_COLOR = "2E3F47";
|
||||
constexpr const char* HARD_COLOR = "76428A";
|
||||
constexpr bool TEXT_AUTOCOLOR = true;
|
||||
constexpr const char* TEXT_COLOR1 = "FFFFFF";
|
||||
constexpr const char* TEXT_COLOR2 = "FFFFFF";
|
||||
constexpr int SKIP_COUNTDOWN_VALUE = 8;
|
||||
} // namespace Defaults::Scoreboard
|
||||
|
||||
namespace Defaults::Title {
|
||||
constexpr int PRESS_START_POSITION = 180;
|
||||
constexpr float DURATION_S = 14.0F;
|
||||
constexpr int ARCADE_EDITION_POSITION = 123;
|
||||
constexpr int TITLE_C_C_POSITION = 80;
|
||||
constexpr const char* BG_COLOR = "41526F";
|
||||
} // namespace Defaults::Title
|
||||
|
||||
namespace Defaults::Background {
|
||||
constexpr const char* ATTENUATE_COLOR = "FFFFFF00";
|
||||
} // namespace Defaults::Background
|
||||
|
||||
namespace Defaults::Balloon {
|
||||
struct BalloonSettings {
|
||||
float vel;
|
||||
float grav;
|
||||
constexpr BalloonSettings(float v, float g)
|
||||
: vel(v),
|
||||
grav(g) {}
|
||||
};
|
||||
|
||||
constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
||||
BalloonSettings(165.0F, 320.0F),
|
||||
BalloonSettings(222.0F, 360.0F),
|
||||
BalloonSettings(282.0F, 360.0F),
|
||||
BalloonSettings(327.0F, 360.0F),
|
||||
}};
|
||||
|
||||
constexpr std::array<const char*, 4> COLORS = {
|
||||
"blue",
|
||||
"orange",
|
||||
"red",
|
||||
"green"};
|
||||
|
||||
constexpr bool BOUNCING_SOUND = false;
|
||||
} // namespace Defaults::Balloon
|
||||
|
||||
namespace Defaults::Notification {
|
||||
constexpr Notifier::Position POS_V = Notifier::Position::TOP;
|
||||
constexpr Notifier::Position POS_H = Notifier::Position::LEFT;
|
||||
constexpr bool SOUND = false;
|
||||
constexpr const char* COLOR = "303030";
|
||||
} // namespace Defaults::Notification
|
||||
|
||||
namespace Defaults::ServiceMenu {
|
||||
constexpr const char* TITLE_COLOR = "99FF62";
|
||||
constexpr const char* TEXT_COLOR = "FFFFFF";
|
||||
constexpr const char* SELECTED_COLOR = "FFDC44";
|
||||
constexpr const char* BG_COLOR = "000F00F5";
|
||||
constexpr bool DROP_SHADOW = false;
|
||||
} // namespace Defaults::ServiceMenu
|
||||
|
||||
namespace Defaults::ServiceMenu::WindowMessage {
|
||||
constexpr const char* BG_COLOR = "141E32F0";
|
||||
constexpr const char* BORDER_COLOR = "6496C8FF";
|
||||
constexpr const char* TITLE_COLOR = "6496C8FF";
|
||||
constexpr const char* TEXT_COLOR = "DCDCDCFF";
|
||||
constexpr float PADDING = 15.0F;
|
||||
constexpr float LINE_SPACING = 5.0F;
|
||||
constexpr float TITLE_SEPARATOR_SPACING = 10.0F;
|
||||
constexpr float MIN_WIDTH = 250.0F;
|
||||
constexpr float MIN_HEIGHT = 32.0F;
|
||||
constexpr float MAX_WIDTH_RATIO = 0.8F;
|
||||
constexpr float MAX_HEIGHT_RATIO = 0.8F;
|
||||
constexpr float TEXT_SAFETY_MARGIN = 15.0F;
|
||||
constexpr float ANIMATION_DURATION = 0.3F;
|
||||
} // namespace Defaults::ServiceMenu::WindowMessage
|
||||
|
||||
namespace Defaults::Intro {
|
||||
constexpr const char* BG_COLOR = "4664BD";
|
||||
constexpr const char* CARD_COLOR = "CBDBFC";
|
||||
constexpr const char* SHADOW_COLOR = "00000080";
|
||||
constexpr int TEXT_DISTANCE_FROM_BOTTOM = 48;
|
||||
} // namespace Defaults::Intro
|
||||
|
||||
namespace Defaults::Debug {
|
||||
constexpr const char* COLOR = "00FFFF";
|
||||
} // namespace Defaults::Debug
|
||||
|
||||
namespace Defaults::Resource {
|
||||
constexpr const char* COLOR = "FFFFFF";
|
||||
} // namespace Defaults::Resource
|
||||
|
||||
namespace Defaults::Tabe {
|
||||
constexpr float MIN_SPAWN_TIME = 2.0F;
|
||||
constexpr float MAX_SPAWN_TIME = 3.0F;
|
||||
} // namespace Defaults::Tabe
|
||||
|
||||
namespace Defaults::Player::DefaultShirt {
|
||||
constexpr const char* PLAYER0_DARKEST = "028ECFFF";
|
||||
constexpr const char* PLAYER0_DARK = "0297DBFF";
|
||||
constexpr const char* PLAYER0_BASE = "029FE8FF";
|
||||
constexpr const char* PLAYER0_LIGHT = "03A9F4FF";
|
||||
|
||||
constexpr const char* PLAYER1_DARKEST = "8E8E8EFF";
|
||||
constexpr const char* PLAYER1_DARK = "AEADADFF";
|
||||
constexpr const char* PLAYER1_BASE = "E4E4E4FF";
|
||||
constexpr const char* PLAYER1_LIGHT = "F7F1F1FF";
|
||||
} // namespace Defaults::Player::DefaultShirt
|
||||
|
||||
namespace Defaults::Player::OneCoffeeShirt {
|
||||
constexpr const char* PLAYER0_DARKEST = "3D9C70FF";
|
||||
constexpr const char* PLAYER0_DARK = "4FA370FF";
|
||||
constexpr const char* PLAYER0_BASE = "5DDE70FF";
|
||||
constexpr const char* PLAYER0_LIGHT = "7DF25CFF";
|
||||
|
||||
constexpr const char* PLAYER1_DARKEST = "2E8B57FF";
|
||||
constexpr const char* PLAYER1_DARK = "3CB371FF";
|
||||
constexpr const char* PLAYER1_BASE = "48D181FF";
|
||||
constexpr const char* PLAYER1_LIGHT = "55EF8DFF";
|
||||
} // namespace Defaults::Player::OneCoffeeShirt
|
||||
|
||||
namespace Defaults::Player::TwoCoffeeShirt {
|
||||
constexpr const char* PLAYER0_DARKEST = "D6A41AFF";
|
||||
constexpr const char* PLAYER0_DARK = "E3AE1BFF";
|
||||
constexpr const char* PLAYER0_BASE = "EFB71DFF";
|
||||
constexpr const char* PLAYER0_LIGHT = "FCC11EFF";
|
||||
|
||||
constexpr const char* PLAYER1_DARKEST = "E08500FF";
|
||||
constexpr const char* PLAYER1_DARK = "FA7D00FF";
|
||||
constexpr const char* PLAYER1_BASE = "FAA200FF";
|
||||
constexpr const char* PLAYER1_LIGHT = "FA8500FF";
|
||||
} // namespace Defaults::Player::TwoCoffeeShirt
|
||||
|
||||
namespace Defaults::Player::OutlineColor {
|
||||
constexpr const char* PLAYER0 = "66323FFF";
|
||||
constexpr const char* PLAYER1 = "422028FF";
|
||||
} // namespace Defaults::Player::OutlineColor
|
||||
|
||||
namespace Defaults::Window {
|
||||
constexpr const char* CAPTION = "© 2025 Coffee Crisis Arcade Edition — JailDesigner";
|
||||
constexpr int ZOOM = 2;
|
||||
constexpr int MAX_ZOOM = 2;
|
||||
} // namespace Defaults::Window
|
||||
|
||||
namespace Defaults::Video {
|
||||
constexpr SDL_ScaleMode SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
|
||||
constexpr bool FULLSCREEN = false;
|
||||
constexpr bool VSYNC = true;
|
||||
constexpr bool INTEGER_SCALE = true;
|
||||
constexpr bool GPU_ACCELERATION = true;
|
||||
constexpr bool SHADER_ENABLED = false;
|
||||
constexpr bool SUPERSAMPLING = false;
|
||||
constexpr bool LINEAR_UPSCALE = false;
|
||||
constexpr int DOWNSCALE_ALGO = 1;
|
||||
} // namespace Defaults::Video
|
||||
|
||||
namespace Defaults::Music {
|
||||
constexpr bool ENABLED = true;
|
||||
constexpr int VOLUME = 100;
|
||||
} // namespace Defaults::Music
|
||||
|
||||
namespace Defaults::Sound {
|
||||
constexpr bool ENABLED = true;
|
||||
constexpr int VOLUME = 100;
|
||||
} // namespace Defaults::Sound
|
||||
|
||||
namespace Defaults::Audio {
|
||||
constexpr bool ENABLED = true;
|
||||
constexpr int VOLUME = 100;
|
||||
} // namespace Defaults::Audio
|
||||
|
||||
namespace Defaults::Settings {
|
||||
constexpr bool AUTOFIRE = true;
|
||||
constexpr bool SHUTDOWN_ENABLED = false;
|
||||
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
||||
} // namespace Defaults::Settings
|
||||
68
source/core/system/demo.cpp
Normal file
68
source/core/system/demo.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "demo.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_IOStream, SDL_IOFromConstMem, SDL_IOFromFile, SDL_ReadIO, SDL_WriteIO, SDL_CloseIO
|
||||
|
||||
#include <iostream> // Para std::cout
|
||||
#include <stdexcept> // Para runtime_error
|
||||
|
||||
#include "resource_helper.hpp" // Para ResourceHelper
|
||||
#include "utils.hpp" // Para getFileName
|
||||
|
||||
// Carga el fichero de datos para la demo
|
||||
auto loadDemoDataFromFile(const std::string& file_path) -> DemoData {
|
||||
DemoData dd;
|
||||
|
||||
SDL_IOStream* file = nullptr;
|
||||
|
||||
// Intentar cargar desde ResourceHelper primero
|
||||
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||
if (!resource_data.empty()) {
|
||||
file = SDL_IOFromConstMem(resource_data.data(), resource_data.size());
|
||||
} else {
|
||||
// Fallback a filesystem directo
|
||||
file = SDL_IOFromFile(file_path.c_str(), "r+b");
|
||||
}
|
||||
|
||||
if (file == nullptr) {
|
||||
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
|
||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||
}
|
||||
|
||||
// Lee todos los datos del fichero y los deja en el destino
|
||||
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
||||
DemoKeys dk = DemoKeys();
|
||||
SDL_ReadIO(file, &dk, sizeof(DemoKeys));
|
||||
dd.push_back(dk);
|
||||
}
|
||||
|
||||
// Cierra el fichero
|
||||
SDL_CloseIO(file);
|
||||
|
||||
return dd;
|
||||
}
|
||||
|
||||
#ifdef RECORDING
|
||||
// Guarda el fichero de datos para la demo
|
||||
bool saveDemoFile(const std::string& file_path, const DemoData& dd) {
|
||||
auto success = true;
|
||||
auto file = SDL_IOFromFile(file_path.c_str(), "w+b");
|
||||
|
||||
if (file) {
|
||||
// Guarda los datos
|
||||
for (const auto& data : dd) {
|
||||
if (SDL_WriteIO(file, &data, sizeof(DemoKeys)) != sizeof(DemoKeys)) {
|
||||
std::cout << "Error al escribir el fichero " << getFileName(file_path) << '\n';
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cierra el fichero
|
||||
SDL_CloseIO(file);
|
||||
} else {
|
||||
std::cout << "Error: Unable to save " << getFileName(file_path) << " file! " << SDL_GetError() << '\n';
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif // RECORDING
|
||||
55
source/core/system/demo.hpp
Normal file
55
source/core/system/demo.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para Uint8
|
||||
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
// --- Constantes ---
|
||||
constexpr int TOTAL_DEMO_DATA = 2000;
|
||||
|
||||
// --- Estructuras ---
|
||||
struct DemoKeys {
|
||||
Uint8 left;
|
||||
Uint8 right;
|
||||
Uint8 no_input;
|
||||
Uint8 fire;
|
||||
Uint8 fire_left;
|
||||
Uint8 fire_right;
|
||||
|
||||
explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0)
|
||||
: left(l),
|
||||
right(r),
|
||||
no_input(ni),
|
||||
fire(f),
|
||||
fire_left(fl),
|
||||
fire_right(fr) {}
|
||||
};
|
||||
|
||||
// --- Tipos ---
|
||||
using DemoData = std::vector<DemoKeys>;
|
||||
|
||||
struct Demo {
|
||||
bool enabled = false; // Indica si está activo el modo demo
|
||||
bool recording = false; // Indica si está activado el modo para grabar la demo
|
||||
float elapsed_s = 0.0F; // Segundos transcurridos de demo
|
||||
int index = 0; // Contador para el modo demo
|
||||
DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo
|
||||
std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo
|
||||
|
||||
Demo() = default;
|
||||
|
||||
Demo(bool e, bool r, int c, const DemoKeys& k, const std::vector<DemoData>& d)
|
||||
: enabled(e),
|
||||
recording(r),
|
||||
index(c),
|
||||
keys(k),
|
||||
data(d) {}
|
||||
};
|
||||
|
||||
// --- Funciones ---
|
||||
auto loadDemoDataFromFile(const std::string& file_path) -> DemoData;
|
||||
|
||||
#ifdef RECORDING
|
||||
bool saveDemoFile(const std::string& file_path, const DemoData& dd);
|
||||
#endif
|
||||
469
source/core/system/director.cpp
Normal file
469
source/core/system/director.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
// IWYU pragma: no_include <bits/chrono.h>
|
||||
#include "director.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_SetLogPriority, SDL_LogCategory, SDL_LogPriority, SDL_Quit
|
||||
|
||||
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
|
||||
#include <ctime> // Para time
|
||||
#include <fstream> // Para ifstream, ofstream
|
||||
#include <iostream> // Para basic_ostream, operator<<, cerr
|
||||
#include <memory> // Para make_unique, unique_ptr
|
||||
#include <stdexcept> // Para runtime_error
|
||||
#include <string> // Para allocator, basic_string, char_traits, operator+, string, operator==
|
||||
|
||||
#include "asset.hpp" // Para Asset
|
||||
#include "audio.hpp" // Para Audio
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "global_events.hpp" // Para GlobalEvents::handle
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para setLanguage
|
||||
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable
|
||||
#include "options.hpp" // Para Settings, loadFromFile, saveToFile, settings, setConfigFile, setControllersFile
|
||||
#include "param.hpp" // Para loadParamsFromFile
|
||||
#include "player.hpp" // Para Player
|
||||
#include "resource.hpp" // Para Resource
|
||||
#include "resource_helper.hpp" // Para initializeResourceSystem
|
||||
#include "screen.hpp" // Para Screen
|
||||
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
||||
#include "sections/credits.hpp" // Para Credits
|
||||
#include "sections/game.hpp" // Para Game
|
||||
#include "sections/hiscore_table.hpp" // Para HiScoreTable
|
||||
#include "sections/instructions.hpp" // Para Instructions
|
||||
#include "sections/intro.hpp" // Para Intro
|
||||
#include "sections/logo.hpp" // Para Logo
|
||||
#include "sections/title.hpp" // Para Title
|
||||
#include "shutdown.hpp" // Para resultToString, shutdownSystem, ShutdownResult
|
||||
#include "system_utils.hpp" // Para createApplicationFolder, resultToString, Result
|
||||
#include "ui/notifier.hpp" // Para Notifier
|
||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
||||
|
||||
// Constructor
|
||||
Director::Director() {
|
||||
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
|
||||
|
||||
// Establece el nivel de prioridad de la categoría de registro
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR);
|
||||
|
||||
// Inicia la semilla aleatoria usando el tiempo actual en segundos
|
||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||
|
||||
std::cout << "Game start\n";
|
||||
|
||||
// Obtener la ruta del ejecutable desde SDL
|
||||
const char* base_path = SDL_GetBasePath();
|
||||
executable_path_ = (base_path != nullptr) ? base_path : "";
|
||||
|
||||
// Crea la carpeta del sistema donde guardar los datos persistentes
|
||||
createSystemFolder("jailgames");
|
||||
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
|
||||
|
||||
// Establecer sección inicial según modo de compilación
|
||||
#ifdef RECORDING
|
||||
Section::name = Section::Name::GAME;
|
||||
Section::options = Section::Options::GAME_PLAY_1P;
|
||||
#elif _DEBUG
|
||||
loadDebugConfig();
|
||||
#else
|
||||
Section::name = Section::Name::LOGO;
|
||||
Section::options = Section::Options::NONE;
|
||||
#endif
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
Director::~Director() {
|
||||
// Libera las secciones primero: sus destructores pueden tocar Audio/Resource/Screen,
|
||||
// que close() destruye a continuación.
|
||||
resetActiveSection();
|
||||
close();
|
||||
}
|
||||
|
||||
// Inicializa todo
|
||||
void Director::init() {
|
||||
// Configuración inicial de parametros
|
||||
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
||||
|
||||
// Determinar ruta del pack según la plataforma
|
||||
#ifdef MACOS_BUNDLE
|
||||
std::string pack_path = executable_path_ + "../Resources/resources.pack";
|
||||
#else
|
||||
std::string pack_path = executable_path_ + "resources.pack";
|
||||
#endif
|
||||
|
||||
// Inicializar sistema de recursos con o sin fallback según el tipo de build
|
||||
#ifdef RELEASE_BUILD
|
||||
// Release: Sin fallback - Solo resources.pack (estricto)
|
||||
ResourceHelper::initializeResourceSystem(pack_path, false);
|
||||
#else
|
||||
// Desarrollo: Con fallback - Puede usar data/ si falta el pack (flexible)
|
||||
ResourceHelper::initializeResourceSystem(pack_path, true);
|
||||
#endif
|
||||
|
||||
loadAssets(); // Crea el índice de archivos
|
||||
|
||||
Input::init(Asset::get()->getPath("gamecontrollerdb.txt"), Asset::get()->getPath("controllers.json")); // Carga configuración de controles
|
||||
|
||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
||||
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
||||
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
||||
Options::loadFromFile(); // Carga el archivo de configuración
|
||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
||||
loadParams(); // Carga los parámetros del programa
|
||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||
|
||||
// Inicialización de subsistemas principales
|
||||
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
||||
|
||||
Screen::init(); // Inicializa la pantalla y el sistema de renderizado
|
||||
|
||||
Audio::init(); // Activa el sistema de audio
|
||||
|
||||
#ifdef _DEBUG
|
||||
Resource::init(debug_config.resource_loading == "lazy" ? Resource::LoadingMode::LAZY_LOAD : Resource::LoadingMode::PRELOAD);
|
||||
#else
|
||||
Resource::init(Resource::LoadingMode::PRELOAD);
|
||||
#endif
|
||||
ServiceMenu::init(); // Inicializa el menú de servicio
|
||||
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
||||
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
|
||||
}
|
||||
|
||||
// Cierra todo y libera recursos del sistema y de los singletons
|
||||
void Director::close() {
|
||||
// Guarda las opciones actuales en el archivo de configuración
|
||||
Options::saveToFile();
|
||||
|
||||
// Libera los singletons y recursos en orden inverso al de inicialización
|
||||
Notifier::destroy(); // Libera el sistema de notificaciones
|
||||
ServiceMenu::destroy(); // Libera el sistema de menú de servicio
|
||||
Input::destroy(); // Libera el sistema de entrada
|
||||
Resource::destroy(); // Libera el sistema de recursos gráficos y de texto
|
||||
Audio::destroy(); // Libera el sistema de audio
|
||||
Screen::destroy(); // Libera el sistema de pantalla y renderizado
|
||||
Asset::destroy(); // Libera el gestor de archivos
|
||||
|
||||
std::cout << "\nBye!\n";
|
||||
|
||||
// Libera todos los recursos de SDL
|
||||
SDL_Quit();
|
||||
|
||||
// Apaga el sistema
|
||||
shutdownSystem(Section::options == Section::Options::SHUTDOWN);
|
||||
}
|
||||
|
||||
// Carga los parametros
|
||||
void Director::loadParams() {
|
||||
// Carga los parametros para configurar el juego
|
||||
#ifdef ANBERNIC
|
||||
const std::string PARAM_FILE_PATH = Asset::get()->getPath("param_320x240.txt");
|
||||
#else
|
||||
const std::string PARAM_FILE_PATH = Asset::get()->getPath(Options::settings.params_file);
|
||||
#endif
|
||||
loadParamsFromFile(PARAM_FILE_PATH);
|
||||
}
|
||||
|
||||
// Carga el fichero de puntuaciones
|
||||
void Director::loadScoreFile() {
|
||||
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
|
||||
#ifdef _DEBUG
|
||||
manager->clear();
|
||||
#else
|
||||
manager->loadFromFile(Asset::get()->getPath("score.bin"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Carga el indice de ficheros desde un fichero
|
||||
void Director::loadAssets() {
|
||||
#ifdef MACOS_BUNDLE
|
||||
const std::string PREFIX = "/../Resources";
|
||||
#else
|
||||
const std::string PREFIX;
|
||||
#endif
|
||||
|
||||
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
||||
std::string config_path = executable_path_ + PREFIX + "/config/assets.txt";
|
||||
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
||||
|
||||
// Si falta algun fichero, sale del programa
|
||||
if (!Asset::get()->check()) {
|
||||
throw std::runtime_error("Falta algun fichero");
|
||||
}
|
||||
}
|
||||
|
||||
// Carga debug.yaml desde la carpeta del sistema (solo en _DEBUG)
|
||||
void Director::loadDebugConfig() {
|
||||
const std::string DEBUG_FILE = system_folder_ + "/debug.yaml";
|
||||
|
||||
std::ifstream file(DEBUG_FILE);
|
||||
if (!file.good()) {
|
||||
// Crear fichero por defecto
|
||||
std::ofstream out(DEBUG_FILE);
|
||||
if (out.is_open()) {
|
||||
out << "# Coffee Crisis Arcade Edition - Debug Configuration\n";
|
||||
out << "# This file is only read in DEBUG builds.\n";
|
||||
out << "#\n";
|
||||
out << "# initial_section: logo, intro, title, game, credits, instructions, hiscore\n";
|
||||
out << "# initial_options: none, 1p, 2p, both\n";
|
||||
out << "# initial_stage: 0-based stage index (only when section is game)\n";
|
||||
out << "# show_render_info: show FPS/driver/preset overlay\n";
|
||||
out << "# resource_loading: preload, lazy\n";
|
||||
out << "\n";
|
||||
out << "initial_section: game\n";
|
||||
out << "initial_options: 1p\n";
|
||||
out << "initial_stage: 0\n";
|
||||
out << "show_render_info: true\n";
|
||||
out << "resource_loading: preload\n";
|
||||
out.close();
|
||||
}
|
||||
// Usar defaults de DebugConfig
|
||||
} else {
|
||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(content);
|
||||
if (yaml.contains("initial_section")) {
|
||||
try {
|
||||
debug_config.initial_section = yaml["initial_section"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (yaml.contains("initial_options")) {
|
||||
try {
|
||||
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (yaml.contains("initial_stage")) {
|
||||
try {
|
||||
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (yaml.contains("show_render_info")) {
|
||||
try {
|
||||
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (yaml.contains("resource_loading")) {
|
||||
try {
|
||||
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
|
||||
} catch (...) {}
|
||||
}
|
||||
} catch (...) {
|
||||
std::cout << "Error parsing debug.yaml, using defaults" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Mapear strings a enums
|
||||
const auto& sec = debug_config.initial_section;
|
||||
if (sec == "logo") {
|
||||
Section::name = Section::Name::LOGO;
|
||||
} else if (sec == "intro") {
|
||||
Section::name = Section::Name::INTRO;
|
||||
} else if (sec == "title") {
|
||||
Section::name = Section::Name::TITLE;
|
||||
} else if (sec == "game") {
|
||||
Section::name = Section::Name::GAME;
|
||||
} else if (sec == "credits") {
|
||||
Section::name = Section::Name::CREDITS;
|
||||
} else if (sec == "instructions") {
|
||||
Section::name = Section::Name::INSTRUCTIONS;
|
||||
} else if (sec == "hiscore") {
|
||||
Section::name = Section::Name::HI_SCORE_TABLE;
|
||||
} else {
|
||||
Section::name = Section::Name::GAME;
|
||||
}
|
||||
|
||||
const auto& opt = debug_config.initial_options;
|
||||
if (opt == "none") {
|
||||
Section::options = Section::Options::NONE;
|
||||
} else if (opt == "1p") {
|
||||
Section::options = Section::Options::GAME_PLAY_1P;
|
||||
} else if (opt == "2p") {
|
||||
Section::options = Section::Options::GAME_PLAY_2P;
|
||||
} else if (opt == "both") {
|
||||
Section::options = Section::Options::GAME_PLAY_BOTH;
|
||||
} else {
|
||||
Section::options = Section::Options::GAME_PLAY_1P;
|
||||
}
|
||||
}
|
||||
|
||||
// Crea la carpeta del sistema donde guardar datos
|
||||
void Director::createSystemFolder(const std::string& folder) {
|
||||
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
|
||||
|
||||
if (result != SystemUtils::Result::SUCCESS) {
|
||||
std::cerr << "Error creando carpeta del sistema: "
|
||||
<< SystemUtils::resultToString(result) << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// Libera todos los unique_ptr de sección (solo uno tiene propiedad a la vez)
|
||||
void Director::resetActiveSection() {
|
||||
logo_.reset();
|
||||
intro_.reset();
|
||||
title_.reset();
|
||||
game_.reset();
|
||||
instructions_.reset();
|
||||
hi_score_table_.reset();
|
||||
credits_.reset();
|
||||
}
|
||||
|
||||
// Destruye la sección anterior y construye la nueva cuando Section::name cambia
|
||||
void Director::handleSectionTransition() {
|
||||
// RESET: recarga recursos y vuelve a LOGO (el propio reset() cambia Section::name)
|
||||
if (Section::name == Section::Name::RESET) {
|
||||
resetActiveSection(); // libera recursos actuales antes del reload
|
||||
reset();
|
||||
}
|
||||
|
||||
if (Section::name == last_built_section_name_) {
|
||||
return; // ya tenemos la sección correcta viva
|
||||
}
|
||||
|
||||
// Destruye la sección anterior
|
||||
resetActiveSection();
|
||||
|
||||
// Construye la nueva
|
||||
switch (Section::name) {
|
||||
case Section::Name::LOGO:
|
||||
logo_ = std::make_unique<Logo>();
|
||||
break;
|
||||
|
||||
case Section::Name::INTRO:
|
||||
intro_ = std::make_unique<Intro>();
|
||||
break;
|
||||
|
||||
case Section::Name::TITLE:
|
||||
title_ = std::make_unique<Title>();
|
||||
break;
|
||||
|
||||
case Section::Name::GAME: {
|
||||
Player::Id player_id = Player::Id::PLAYER1;
|
||||
switch (Section::options) {
|
||||
case Section::Options::GAME_PLAY_1P:
|
||||
player_id = Player::Id::PLAYER1;
|
||||
break;
|
||||
case Section::Options::GAME_PLAY_2P:
|
||||
player_id = Player::Id::PLAYER2;
|
||||
break;
|
||||
case Section::Options::GAME_PLAY_BOTH:
|
||||
player_id = Player::Id::BOTH_PLAYERS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
const int CURRENT_STAGE = debug_config.initial_stage;
|
||||
#else
|
||||
constexpr int CURRENT_STAGE = 0;
|
||||
#endif
|
||||
game_ = std::make_unique<Game>(player_id, CURRENT_STAGE, Game::DEMO_OFF);
|
||||
break;
|
||||
}
|
||||
|
||||
case Section::Name::GAME_DEMO: {
|
||||
const auto PLAYER_ID = static_cast<Player::Id>((rand() % 2) + 1);
|
||||
constexpr auto CURRENT_STAGE = 0;
|
||||
game_ = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, Game::DEMO_ON);
|
||||
break;
|
||||
}
|
||||
|
||||
case Section::Name::INSTRUCTIONS:
|
||||
instructions_ = std::make_unique<Instructions>();
|
||||
break;
|
||||
|
||||
case Section::Name::CREDITS:
|
||||
credits_ = std::make_unique<Credits>();
|
||||
break;
|
||||
|
||||
case Section::Name::HI_SCORE_TABLE:
|
||||
hi_score_table_ = std::make_unique<HiScoreTable>();
|
||||
break;
|
||||
|
||||
case Section::Name::RESET:
|
||||
case Section::Name::QUIT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
last_built_section_name_ = Section::name;
|
||||
}
|
||||
|
||||
// Reinicia objetos y vuelve a la sección inicial
|
||||
void Director::reset() {
|
||||
Options::saveToFile();
|
||||
Options::loadFromFile();
|
||||
Lang::setLanguage(Options::settings.language);
|
||||
Audio::get()->stopMusic();
|
||||
Audio::get()->stopAllSounds();
|
||||
Resource::get()->reload();
|
||||
ServiceMenu::get()->reset();
|
||||
Section::name = Section::Name::LOGO;
|
||||
}
|
||||
|
||||
// Avanza un frame de la sección activa (llamado desde SDL_AppIterate)
|
||||
auto Director::iterate() -> SDL_AppResult {
|
||||
if (Section::name == Section::Name::QUIT) {
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
|
||||
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
||||
handleSectionTransition();
|
||||
|
||||
// Ejecuta un frame de la sección activa
|
||||
if (logo_) {
|
||||
logo_->iterate();
|
||||
} else if (intro_) {
|
||||
intro_->iterate();
|
||||
} else if (title_) {
|
||||
title_->iterate();
|
||||
} else if (game_) {
|
||||
game_->iterate();
|
||||
} else if (instructions_) {
|
||||
instructions_->iterate();
|
||||
} else if (hi_score_table_) {
|
||||
hi_score_table_->iterate();
|
||||
} else if (credits_) {
|
||||
credits_->iterate();
|
||||
}
|
||||
|
||||
return (Section::name == Section::Name::QUIT) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
// Procesa un evento SDL (llamado desde SDL_AppEvent)
|
||||
auto Director::handleEvent(SDL_Event& event) -> SDL_AppResult {
|
||||
// Eventos globales (SDL_EVENT_QUIT, resize, render target reset, hotplug, service menu, ratón)
|
||||
GlobalEvents::handle(event);
|
||||
|
||||
// Reenvía a la sección activa
|
||||
if (logo_) {
|
||||
logo_->handleEvent(event);
|
||||
} else if (intro_) {
|
||||
intro_->handleEvent(event);
|
||||
} else if (title_) {
|
||||
title_->handleEvent(event);
|
||||
} else if (game_) {
|
||||
game_->handleEvent(event);
|
||||
} else if (instructions_) {
|
||||
instructions_->handleEvent(event);
|
||||
} else if (hi_score_table_) {
|
||||
hi_score_table_->handleEvent(event);
|
||||
} else if (credits_) {
|
||||
credits_->handleEvent(event);
|
||||
}
|
||||
|
||||
return (Section::name == Section::Name::QUIT) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
// Apaga el sistema de forma segura
|
||||
void Director::shutdownSystem(bool should_shutdown) {
|
||||
if (should_shutdown) {
|
||||
auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps
|
||||
|
||||
if (result != SystemShutdown::ShutdownResult::SUCCESS) {
|
||||
std::cerr << SystemShutdown::resultToString(result) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
87
source/core/system/director.hpp
Normal file
87
source/core/system/director.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_AppResult, SDL_Event
|
||||
|
||||
#include <memory> // Para unique_ptr
|
||||
#include <string> // Para string
|
||||
|
||||
#include "section.hpp" // Para Section::Name
|
||||
|
||||
namespace Lang {
|
||||
enum class Code : int;
|
||||
}
|
||||
|
||||
// Declaraciones adelantadas de las secciones
|
||||
class Logo;
|
||||
class Intro;
|
||||
class Title;
|
||||
class Game;
|
||||
class Instructions;
|
||||
class HiScoreTable;
|
||||
class Credits;
|
||||
|
||||
// --- Clase Director: gestor principal de la aplicación ---
|
||||
class Director {
|
||||
public:
|
||||
// --- Constructor y destructor ---
|
||||
Director();
|
||||
~Director();
|
||||
|
||||
// --- Callbacks para SDL_MAIN_USE_CALLBACKS ---
|
||||
auto iterate() -> SDL_AppResult; // Avanza un frame de la sección activa
|
||||
auto handleEvent(SDL_Event& event) -> SDL_AppResult; // Procesa un evento SDL
|
||||
|
||||
// --- Debug config (accesible desde otras clases) ---
|
||||
struct DebugConfig {
|
||||
std::string initial_section;
|
||||
std::string initial_options;
|
||||
int initial_stage = 0;
|
||||
bool show_render_info = true;
|
||||
std::string resource_loading;
|
||||
|
||||
DebugConfig()
|
||||
: initial_section("game"),
|
||||
initial_options("1p"),
|
||||
resource_loading("preload") {}
|
||||
};
|
||||
static inline DebugConfig debug_config;
|
||||
|
||||
private:
|
||||
// --- Variables internas ---
|
||||
std::string executable_path_; // Ruta del ejecutable
|
||||
std::string system_folder_; // Carpeta del sistema para almacenar datos
|
||||
|
||||
// --- Sección activa (una y sólo una viva en cada momento) ---
|
||||
std::unique_ptr<Logo> logo_;
|
||||
std::unique_ptr<Intro> intro_;
|
||||
std::unique_ptr<Title> title_;
|
||||
std::unique_ptr<Game> game_;
|
||||
std::unique_ptr<Instructions> instructions_;
|
||||
std::unique_ptr<HiScoreTable> hi_score_table_;
|
||||
std::unique_ptr<Credits> credits_;
|
||||
Section::Name last_built_section_name_ = Section::Name::RESET;
|
||||
|
||||
// --- Inicialización y cierre del sistema ---
|
||||
void init(); // Inicializa la aplicación
|
||||
static void close(); // Cierra y libera recursos
|
||||
|
||||
// --- Configuración inicial ---
|
||||
static void loadParams(); // Carga los parámetros del programa
|
||||
static void loadScoreFile(); // Carga el fichero de puntuaciones
|
||||
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
|
||||
void loadDebugConfig(); // Carga debug.yaml (solo en _DEBUG)
|
||||
|
||||
// --- Gestión de entrada y archivos ---
|
||||
void loadAssets(); // Crea el índice de archivos disponibles
|
||||
|
||||
// --- Gestión de secciones ---
|
||||
void handleSectionTransition(); // Destruye la sección anterior y construye la nueva si Section::name ha cambiado
|
||||
void resetActiveSection(); // Libera todos los unique_ptr de sección
|
||||
static void reset(); // Reinicia objetos y vuelve a la sección inicial
|
||||
|
||||
// --- Gestión de archivos de idioma ---
|
||||
auto getLangFile(Lang::Code code) -> std::string; // Obtiene un fichero de idioma según el código
|
||||
|
||||
// --- Apagado del sistema ---
|
||||
static void shutdownSystem(bool should_shutdown); // Apaga el sistema
|
||||
};
|
||||
69
source/core/system/global_events.cpp
Normal file
69
source/core/system/global_events.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "global_events.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_EventType, SDL_Event, SDL_LogInfo, SDL_LogCategory
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <iostream> // Para std::cout
|
||||
#include <string> // Para allocator, operator+, string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "input.hpp" // Para Input
|
||||
#include "lang.hpp" // Para getText
|
||||
#include "mouse.hpp" // Para handleEvent
|
||||
#include "options.hpp" // Para GamepadManager, gamepad_manager
|
||||
#include "screen.hpp" // Para Screen
|
||||
#include "section.hpp" // Para Name, Options, name, options
|
||||
#include "ui/notifier.hpp" // Para Notifier
|
||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
||||
|
||||
namespace GlobalEvents {
|
||||
// Comprueba los eventos de Input y muestra notificaciones
|
||||
void handleInputEvents(const SDL_Event& event) {
|
||||
static auto* input_ = Input::get();
|
||||
auto message = input_->handleEvent(event);
|
||||
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reemplazo de palabras clave por texto localizado
|
||||
size_t pos;
|
||||
while ((pos = message.find(" CONNECTED")) != std::string::npos) {
|
||||
message.replace(pos, std::string(" CONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] CONNECTED"));
|
||||
}
|
||||
while ((pos = message.find(" DISCONNECTED")) != std::string::npos) {
|
||||
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
||||
}
|
||||
|
||||
Options::gamepad_manager.assignAndLinkGamepads();
|
||||
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
||||
Notifier::get()->show({message});
|
||||
ServiceMenu::get()->refresh();
|
||||
}
|
||||
|
||||
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||
void handle(const SDL_Event& event) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT: // Evento de salida de la aplicación
|
||||
Section::name = Section::Name::QUIT;
|
||||
Section::options = Section::Options::NONE;
|
||||
return;
|
||||
|
||||
case SDL_EVENT_RENDER_DEVICE_RESET:
|
||||
case SDL_EVENT_RENDER_TARGETS_RESET:
|
||||
std::cout << "SDL_RENDER_TARGETS_RESET" << '\n';
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
Screen::initShaders();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ServiceMenu::get()->handleEvent(event);
|
||||
Mouse::handleEvent(event);
|
||||
handleInputEvents(event);
|
||||
}
|
||||
} // namespace GlobalEvents
|
||||
9
source/core/system/global_events.hpp
Normal file
9
source/core/system/global_events.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// --- Namespace GlobalEvents: maneja eventos globales del juego ---
|
||||
namespace GlobalEvents {
|
||||
// --- Funciones ---
|
||||
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||
} // namespace GlobalEvents
|
||||
48
source/core/system/section.hpp
Normal file
48
source/core/system/section.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Namespace section: define los estados/secciones principales del programa,
|
||||
así como las opciones y modos especiales (como el Attract Mode).
|
||||
Proporciona variables globales para gestionar el flujo entre secciones.
|
||||
*/
|
||||
|
||||
namespace Section {
|
||||
// --- Enumeraciones de secciones del programa ---
|
||||
enum class Name {
|
||||
RESET, // Inicialización
|
||||
LOGO, // Pantalla de logo
|
||||
INTRO, // Introducción
|
||||
TITLE, // Pantalla de título/menú principal
|
||||
GAME, // Juego principal
|
||||
HI_SCORE_TABLE, // Tabla de récords
|
||||
GAME_DEMO, // Modo demo
|
||||
INSTRUCTIONS, // Instrucciones
|
||||
CREDITS, // Créditos
|
||||
QUIT, // Salir del juego
|
||||
};
|
||||
|
||||
// --- Opciones para la sección actual ---
|
||||
enum class Options {
|
||||
GAME_PLAY_1P, // Iniciar el juego con el jugador 1
|
||||
GAME_PLAY_2P, // Iniciar el juego con el jugador 2
|
||||
GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores
|
||||
TITLE_TIME_OUT, // Timeout en el título
|
||||
TITLE_1, // Opción 1 en el título
|
||||
TITLE_2, // Opción 2 en el título
|
||||
RELOAD, // Recargar sección
|
||||
HI_SCORE_AFTER_PLAYING, // Mostrar récord tras jugar
|
||||
SHUTDOWN, // Apagar el sistema
|
||||
NONE, // Sin opción
|
||||
};
|
||||
|
||||
// --- Modos para el Attract Mode ---
|
||||
enum class AttractMode {
|
||||
TITLE_TO_DEMO, // Pasar de título a demo
|
||||
TITLE_TO_LOGO, // Pasar de título a logo
|
||||
};
|
||||
|
||||
// --- Variables globales de estado ---
|
||||
inline Name name = Name::RESET;
|
||||
inline Options options = Options::NONE;
|
||||
inline AttractMode attract_mode = AttractMode::TITLE_TO_DEMO;
|
||||
} // namespace Section
|
||||
157
source/core/system/shutdown.cpp
Normal file
157
source/core/system/shutdown.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "shutdown.hpp"
|
||||
|
||||
#include <sys/types.h> // Para pid_t
|
||||
|
||||
#include <cstdlib> // Para WEXITSTATUS
|
||||
#include <iostream> // Para char_traits, basic_ostream, operator<<, cerr
|
||||
#include <vector> // Para vector
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/wait.h> // Para waitpid
|
||||
#include <unistd.h> // Para _exit, execvp, fork
|
||||
#endif
|
||||
|
||||
namespace SystemShutdown {
|
||||
|
||||
#ifndef _WIN32
|
||||
// Función auxiliar para sistemas Unix-like
|
||||
auto executeUnixShutdown(const char* command, const std::vector<char*>& args) -> ShutdownResult {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
// Proceso hijo
|
||||
execvp(command, args.data());
|
||||
// Si llegamos aquí, execvp falló
|
||||
std::cerr << "Error: No se pudo ejecutar " << command << '\n';
|
||||
_exit(1);
|
||||
} else if (pid > 0) {
|
||||
// Proceso padre
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
return (WEXITSTATUS(status) == 0) ? ShutdownResult::SUCCESS : ShutdownResult::ERROR_SYSTEM_CALL;
|
||||
} else {
|
||||
return ShutdownResult::ERROR_FORK_FAILED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Implementación de las funciones públicas
|
||||
auto shutdownSystem() -> ShutdownResult {
|
||||
ShutdownConfig config;
|
||||
return shutdownSystem(config);
|
||||
}
|
||||
|
||||
auto shutdownSystem(int delay_seconds, bool force_apps) -> ShutdownResult {
|
||||
ShutdownConfig config;
|
||||
config.delay_seconds = delay_seconds;
|
||||
config.force_close_apps = force_apps;
|
||||
return shutdownSystem(config);
|
||||
}
|
||||
|
||||
auto shutdownSystem(const ShutdownConfig& config) -> ShutdownResult {
|
||||
#ifdef _WIN32
|
||||
// Windows: Usar CreateProcess
|
||||
STARTUPINFOA si = {0};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
si.cb = sizeof(si);
|
||||
|
||||
// Crear comando con el delay especificado
|
||||
std::string command = "shutdown.exe /s /t " + std::to_string(config.delay_seconds);
|
||||
if (config.force_close_apps) {
|
||||
command += " /f";
|
||||
}
|
||||
|
||||
// CreateProcess necesita un array de char modificable
|
||||
char* cmd_buffer = new char[command.length() + 1];
|
||||
strcpy(cmd_buffer, command.c_str());
|
||||
|
||||
bool success = CreateProcessA(
|
||||
NULL, // lpApplicationName
|
||||
cmd_buffer, // lpCommandLine
|
||||
NULL, // lpProcessAttributes
|
||||
NULL, // lpThreadAttributes
|
||||
FALSE, // bInheritHandles
|
||||
0, // dwCreationFlags
|
||||
NULL, // lpEnvironment
|
||||
NULL, // lpCurrentDirectory
|
||||
&si, // lpStartupInfo
|
||||
&pi // lpProcessInformation
|
||||
);
|
||||
|
||||
delete[] cmd_buffer;
|
||||
|
||||
if (success) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return ShutdownResult::SUCCESS;
|
||||
} else {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_ACCESS_DENIED) {
|
||||
return ShutdownResult::ERROR_PERMISSION;
|
||||
}
|
||||
return ShutdownResult::ERROR_SYSTEM_CALL;
|
||||
}
|
||||
|
||||
#elif __APPLE__
|
||||
// macOS - apagado inmediato
|
||||
std::vector<char*> args = {
|
||||
const_cast<char*>("shutdown"),
|
||||
const_cast<char*>("-h"),
|
||||
const_cast<char*>("now"),
|
||||
nullptr};
|
||||
|
||||
return executeUnixShutdown("shutdown", args);
|
||||
|
||||
#elif __linux__
|
||||
// Linux - apagado inmediato
|
||||
std::vector<char*> args = {
|
||||
const_cast<char*>("shutdown"),
|
||||
const_cast<char*>("-h"),
|
||||
const_cast<char*>("now"),
|
||||
nullptr};
|
||||
|
||||
return executeUnixShutdown("shutdown", args);
|
||||
|
||||
#else
|
||||
return ShutdownResult::ERROR_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto resultToString(ShutdownResult result) -> const char* {
|
||||
switch (result) {
|
||||
case ShutdownResult::SUCCESS:
|
||||
return "Apagado iniciado exitosamente";
|
||||
case ShutdownResult::ERROR_PERMISSION:
|
||||
return "Error: Permisos insuficientes";
|
||||
case ShutdownResult::ERROR_SYSTEM_CALL:
|
||||
return "Error: Fallo en la llamada al sistema";
|
||||
case ShutdownResult::ERROR_FORK_FAILED:
|
||||
return "Error: No se pudo crear proceso hijo";
|
||||
case ShutdownResult::ERROR_UNSUPPORTED:
|
||||
return "Error: Sistema operativo no soportado";
|
||||
default:
|
||||
return "Error desconocido";
|
||||
}
|
||||
}
|
||||
|
||||
auto isShutdownSupported() -> bool {
|
||||
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto getRequiredPermissions() -> const char* {
|
||||
#ifdef _WIN32
|
||||
return "Requiere permisos de Administrador en Windows";
|
||||
#elif defined(__APPLE__) || defined(__linux__)
|
||||
return "Requiere permisos de root/sudo en Unix";
|
||||
#else
|
||||
return "Sistema no soportado";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace SystemShutdown
|
||||
33
source/core/system/shutdown.hpp
Normal file
33
source/core/system/shutdown.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
// --- Namespace SystemShutdown: utilidad multiplataforma para apagar el sistema de forma segura ---
|
||||
namespace SystemShutdown {
|
||||
|
||||
// --- Enums ---
|
||||
enum class ShutdownResult {
|
||||
SUCCESS = 0, // Éxito
|
||||
ERROR_PERMISSION, // Error de permisos insuficientes
|
||||
ERROR_SYSTEM_CALL, // Error en la llamada al sistema
|
||||
ERROR_FORK_FAILED, // Error al crear proceso hijo (Unix)
|
||||
ERROR_UNSUPPORTED // Sistema operativo no soportado
|
||||
};
|
||||
|
||||
// --- Estructuras ---
|
||||
struct ShutdownConfig {
|
||||
int delay_seconds{5}; // Segundos de retraso antes del apagado
|
||||
bool force_close_apps{true}; // Forzar cierre de aplicaciones
|
||||
const char* shutdown_message{"El sistema se apagará..."}; // Mensaje mostrado durante el apagado
|
||||
|
||||
// Constructor con valores por defecto
|
||||
ShutdownConfig() = default;
|
||||
};
|
||||
|
||||
// --- Funciones ---
|
||||
auto shutdownSystem() -> ShutdownResult; // Apaga el sistema con configuración por defecto
|
||||
auto shutdownSystem(const ShutdownConfig& config) -> ShutdownResult; // Apaga el sistema con configuración personalizada
|
||||
auto shutdownSystem(int delay_seconds, bool force_apps = true) -> ShutdownResult; // Apaga el sistema con parámetros simples
|
||||
auto resultToString(ShutdownResult result) -> const char*; // Convierte un código de resultado a string descriptivo
|
||||
auto isShutdownSupported() -> bool; // Verifica si el sistema actual soporta apagado programático
|
||||
auto getRequiredPermissions() -> const char*; // Obtiene información sobre los permisos necesarios
|
||||
|
||||
} // namespace SystemShutdown
|
||||
19
source/core/system/stage_interface.hpp
Normal file
19
source/core/system/stage_interface.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Interfaz para acceso a información de fases.
|
||||
* Proporciona una API mínima para componentes que necesitan interactuar con datos de fases
|
||||
* sin requerir acceso a toda la funcionalidad de StageManager.
|
||||
*/
|
||||
class IStageInfo {
|
||||
public:
|
||||
virtual ~IStageInfo() = default;
|
||||
|
||||
// Interfaz de recolección de poder
|
||||
[[nodiscard]] virtual auto canCollectPower() const -> bool = 0;
|
||||
virtual void enablePowerCollection() = 0;
|
||||
virtual void addPower(int amount) = 0;
|
||||
|
||||
// Ajuste de comportamiento del gameplay
|
||||
[[nodiscard]] virtual auto getCurrentMenaceLevel() const -> int = 0;
|
||||
};
|
||||
191
source/core/system/system_utils.cpp
Normal file
191
source/core/system/system_utils.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "system_utils.hpp"
|
||||
|
||||
#include <sys/stat.h> // Para stat, mkdir, S_ISDIR
|
||||
|
||||
#include <cerrno> // Para EACCES, EEXIST, ENAMETOOLONG, errno
|
||||
#include <cstdlib> // Para getenv, size_t
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
// Evitar conflictos con macros de Windows
|
||||
#ifdef ERROR_ALREADY_EXISTS
|
||||
#undef ERROR_ALREADY_EXISTS
|
||||
#endif
|
||||
#else
|
||||
#include <pwd.h> // Para getpwuid, passwd
|
||||
#include <unistd.h> // Para getuid
|
||||
#endif
|
||||
|
||||
namespace SystemUtils {
|
||||
|
||||
// Función auxiliar para crear una carpeta individual
|
||||
auto createSingleFolder(const std::string& path, int permissions) -> Result {
|
||||
struct stat st = {.st_dev = 0};
|
||||
|
||||
// Verificar si ya existe
|
||||
if (stat(path.c_str(), &st) == 0) {
|
||||
return Result::SUCCESS; // Ya existe, no es error por defecto
|
||||
}
|
||||
|
||||
// Intentar crear la carpeta
|
||||
int result;
|
||||
#ifdef _WIN32
|
||||
result = _mkdir(path.c_str());
|
||||
#else
|
||||
result = mkdir(path.c_str(), permissions);
|
||||
#endif
|
||||
|
||||
if (result == -1) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
return Result::PERMISSION_DENIED;
|
||||
case EEXIST:
|
||||
return Result::ALREADY_EXISTS;
|
||||
case ENAMETOOLONG:
|
||||
return Result::PATH_TOO_LONG;
|
||||
default:
|
||||
return Result::UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
// Función auxiliar para crear carpetas padre recursivamente
|
||||
auto createParentFolders(const std::string& path, int permissions) -> Result {
|
||||
size_t pos = 0;
|
||||
|
||||
while ((pos = path.find('/', pos + 1)) != std::string::npos) {
|
||||
std::string parent = path.substr(0, pos);
|
||||
if (!parent.empty() && !folderExists(parent)) {
|
||||
Result result = createSingleFolder(parent, permissions);
|
||||
if (result != Result::SUCCESS && result != Result::ALREADY_EXISTS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result::SUCCESS;
|
||||
}
|
||||
|
||||
auto createApplicationFolder(const std::string& app_name, std::string& out_path) -> Result {
|
||||
FolderConfig config;
|
||||
return createApplicationFolder(app_name, out_path, config);
|
||||
}
|
||||
|
||||
auto createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) -> Result {
|
||||
out_path = getApplicationDataPath(app_name);
|
||||
return createFolder(out_path, config);
|
||||
}
|
||||
|
||||
auto createFolder(const std::string& path) -> Result {
|
||||
FolderConfig config;
|
||||
return createFolder(path, config);
|
||||
}
|
||||
|
||||
auto createFolder(const std::string& path, const FolderConfig& config) -> Result {
|
||||
if (path.empty()) {
|
||||
return Result::INVALID_PATH;
|
||||
}
|
||||
|
||||
// Verificar si ya existe y si eso es un error
|
||||
if (folderExists(path) && config.fail_if_exists) {
|
||||
return Result::ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
// Crear carpetas padre si es necesario
|
||||
if (config.create_parents) {
|
||||
Result parent_result = createParentFolders(path, config.permissions);
|
||||
if (parent_result != Result::SUCCESS) {
|
||||
return parent_result;
|
||||
}
|
||||
}
|
||||
|
||||
// Crear la carpeta final
|
||||
return createSingleFolder(path, config.permissions);
|
||||
}
|
||||
|
||||
auto getApplicationDataPath(const std::string& app_name) -> std::string {
|
||||
#ifdef _WIN32
|
||||
char* appdata = getenv("APPDATA");
|
||||
if (appdata) {
|
||||
return std::string(appdata) + "/" + app_name;
|
||||
}
|
||||
return "C:/Users/Default/AppData/Roaming/" + app_name;
|
||||
|
||||
#elif __APPLE__
|
||||
std::string home = getHomeDirectory();
|
||||
return home + "/Library/Application Support/" + app_name;
|
||||
|
||||
#elif __linux__
|
||||
std::string home = getHomeDirectory();
|
||||
return home + "/.config/" + app_name;
|
||||
|
||||
#else
|
||||
// Fallback genérico
|
||||
std::string home = getHomeDirectory();
|
||||
return home + "/." + app_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto folderExists(const std::string& path) -> bool {
|
||||
struct stat st = {.st_dev = 0};
|
||||
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
|
||||
}
|
||||
|
||||
auto resultToString(Result result) -> const char* {
|
||||
switch (result) {
|
||||
case Result::SUCCESS:
|
||||
return "Operación exitosa";
|
||||
case Result::PERMISSION_DENIED:
|
||||
return "Error: Permisos insuficientes";
|
||||
case Result::PATH_TOO_LONG:
|
||||
return "Error: Ruta demasiado larga";
|
||||
case Result::ALREADY_EXISTS:
|
||||
return "Error: La carpeta ya existe";
|
||||
case Result::INVALID_PATH:
|
||||
return "Error: Ruta inválida";
|
||||
case Result::UNKNOWN_ERROR:
|
||||
return "Error desconocido";
|
||||
default:
|
||||
return "Error no identificado";
|
||||
}
|
||||
}
|
||||
|
||||
auto getHomeDirectory() -> std::string {
|
||||
#ifdef _WIN32
|
||||
char* userprofile = getenv("USERPROFILE");
|
||||
if (userprofile) {
|
||||
return std::string(userprofile);
|
||||
}
|
||||
return "C:/Users/Default";
|
||||
#else
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if ((pw != nullptr) && (pw->pw_dir != nullptr)) {
|
||||
return {pw->pw_dir};
|
||||
}
|
||||
|
||||
// Fallback
|
||||
char* home = getenv("HOME");
|
||||
if (home != nullptr) {
|
||||
return {home};
|
||||
}
|
||||
return "/tmp";
|
||||
#endif
|
||||
}
|
||||
|
||||
auto getTempDirectory() -> std::string {
|
||||
#ifdef _WIN32
|
||||
char* temp = getenv("TEMP");
|
||||
if (temp) {
|
||||
return std::string(temp);
|
||||
}
|
||||
return "C:/Windows/Temp";
|
||||
#else
|
||||
return "/tmp";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace SystemUtils
|
||||
38
source/core/system/system_utils.hpp
Normal file
38
source/core/system/system_utils.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// --- Namespace SystemUtils: utilidades multiplataforma para operaciones del sistema ---
|
||||
namespace SystemUtils {
|
||||
// --- Enums ---
|
||||
enum class Result { // Códigos de resultado para operaciones del sistema
|
||||
SUCCESS = 0,
|
||||
PERMISSION_DENIED, // Sin permisos para crear la carpeta
|
||||
PATH_TOO_LONG, // Ruta demasiado larga
|
||||
ALREADY_EXISTS, // Ya existe (solo si se considera error)
|
||||
INVALID_PATH, // Ruta inválida
|
||||
UNKNOWN_ERROR // Error desconocido
|
||||
};
|
||||
|
||||
// --- Estructuras ---
|
||||
struct FolderConfig { // Configuración para creación de carpetas
|
||||
bool create_parents{true}; // Crear carpetas padre si no existen
|
||||
bool fail_if_exists{false}; // Fallar si la carpeta ya existe
|
||||
int permissions{0755}; // Permisos Unix (ignorado en Windows)
|
||||
|
||||
// Constructor con valores por defecto
|
||||
FolderConfig() = default;
|
||||
};
|
||||
|
||||
// --- Funciones ---
|
||||
auto createApplicationFolder(const std::string& app_name, std::string& out_path) -> Result; // Crea la carpeta del sistema donde guardar datos de la aplicación
|
||||
auto createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) -> Result; // Crea la carpeta del sistema con configuración personalizada
|
||||
auto createFolder(const std::string& path) -> Result; // Crea una carpeta en la ruta especificada
|
||||
auto createFolder(const std::string& path, const FolderConfig& config) -> Result; // Crea una carpeta con configuración personalizada
|
||||
auto getApplicationDataPath(const std::string& app_name) -> std::string; // Obtiene la ruta de datos de la aplicación (sin crearla)
|
||||
auto folderExists(const std::string& path) -> bool; // Verifica si una carpeta existe
|
||||
auto resultToString(Result result) -> const char*; // Convierte un código de resultado a string descriptivo
|
||||
auto getHomeDirectory() -> std::string; // Obtiene el directorio home del usuario
|
||||
auto getTempDirectory() -> std::string; // Obtiene el directorio temporal del sistema
|
||||
|
||||
} // namespace SystemUtils
|
||||
Reference in New Issue
Block a user