Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16924cf503 | |||
| 705a9fc7cd | |||
| b164c11ba7 | |||
| 1817d00881 | |||
| 8dcf473f31 | |||
| 8f191f02fa | |||
| 1077c13fd0 | |||
| d5a4caa86e | |||
| f3bad9f4ed | |||
| 32f22c99db | |||
| cd14ae22c5 | |||
| 1fdc29e9d2 | |||
| 0a740a5be2 | |||
| 9e1b2b8960 | |||
| ed3724193e |
2
Makefile
2
Makefile
@@ -298,6 +298,8 @@ macos_release:
|
|||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
$(RMFILE) tmp.dmg
|
$(RMFILE) tmp.dmg
|
||||||
$(RMFILE) "$(DIST_DIR)"/rw.*
|
$(RMFILE) "$(DIST_DIR)"/rw.*
|
||||||
|
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
|
||||||
|
$(RMFILE) "$(MACOS_APPLE_SILICON_RELEASE)"
|
||||||
|
|
||||||
# Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macOS
|
# Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macOS
|
||||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Input::Input(std::string game_controller_db_path)
|
|||||||
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
|
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
|
||||||
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
|
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
|
||||||
{Action::TOGGLE_INFO, KeyState{.scancode = SDL_SCANCODE_F12}},
|
{Action::TOGGLE_INFO, KeyState{.scancode = SDL_SCANCODE_F12}},
|
||||||
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_TAB}}};
|
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_GRAVE}}};
|
||||||
|
|
||||||
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
|
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||||
@@ -15,6 +16,12 @@ void Locale::init(const std::string& file_path) { // NOLINT(readability-convert
|
|||||||
Locale::instance->loadFromFile(file_path);
|
Locale::instance->loadFromFile(file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [SINGLETON] Crea el objeto desde contenido en memoria (para release con pack)
|
||||||
|
void Locale::initFromContent(const std::string& content) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
Locale::instance = new Locale();
|
||||||
|
Locale::instance->loadFromContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
// [SINGLETON] Destruye el objeto con esta función estática
|
// [SINGLETON] Destruye el objeto con esta función estática
|
||||||
void Locale::destroy() {
|
void Locale::destroy() {
|
||||||
delete Locale::instance;
|
delete Locale::instance;
|
||||||
@@ -55,6 +62,24 @@ void Locale::flatten(const void* node_ptr, const std::string& prefix) { // NOLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Carga las traducciones desde contenido YAML en memoria
|
||||||
|
void Locale::loadFromContent(const std::string& content) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
if (content.empty()) {
|
||||||
|
std::cerr << "Locale: contenido vacío, sin traducciones cargadas\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::istringstream stream(content);
|
||||||
|
auto yaml = fkyaml::node::deserialize(stream);
|
||||||
|
flatten(&yaml, "");
|
||||||
|
|
||||||
|
std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde pack\n";
|
||||||
|
} catch (const fkyaml::exception& e) {
|
||||||
|
std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Carga las traducciones desde el fichero YAML indicado
|
// Carga las traducciones desde el fichero YAML indicado
|
||||||
void Locale::loadFromFile(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
|
void Locale::loadFromFile(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
if (file_path.empty()) {
|
if (file_path.empty()) {
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
// No se permite cambio de idioma en caliente.
|
// No se permite cambio de idioma en caliente.
|
||||||
class Locale {
|
class Locale {
|
||||||
public:
|
public:
|
||||||
static void init(const std::string& file_path); // Crea e inicializa el singleton
|
static void init(const std::string& file_path); // Crea e inicializa el singleton
|
||||||
static void destroy(); // Destruye el singleton
|
static void initFromContent(const std::string& content); // Crea e inicializa desde contenido en memoria (pack)
|
||||||
static auto get() -> Locale*; // Devuelve el singleton
|
static void destroy(); // Destruye el singleton
|
||||||
|
static auto get() -> Locale*; // Devuelve el singleton
|
||||||
|
|
||||||
// Devuelve la traducción de la clave dada.
|
// Devuelve la traducción de la clave dada.
|
||||||
// Si la clave no existe, devuelve la propia clave como fallback.
|
// Si la clave no existe, devuelve la propia clave como fallback.
|
||||||
@@ -19,6 +20,7 @@ class Locale {
|
|||||||
private:
|
private:
|
||||||
Locale() = default;
|
Locale() = default;
|
||||||
void loadFromFile(const std::string& file_path);
|
void loadFromFile(const std::string& file_path);
|
||||||
|
void loadFromContent(const std::string& content);
|
||||||
void flatten(const void* node_ptr, const std::string& prefix); // Aplana nodos YAML anidados
|
void flatten(const void* node_ptr, const std::string& prefix); // Aplana nodos YAML anidados
|
||||||
|
|
||||||
static Locale* instance;
|
static Locale* instance;
|
||||||
|
|||||||
@@ -194,6 +194,21 @@ auto Screen::incWindowZoom() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Establece el zoom directamente; false si fuera del rango [1, max_zoom] o en pantalla completa
|
||||||
|
auto Screen::setWindowZoom(int zoom) -> bool {
|
||||||
|
if (Options::video.fullscreen) { return false; }
|
||||||
|
if (zoom < 1 || zoom > Options::window.max_zoom) { return false; }
|
||||||
|
if (zoom == Options::window.zoom) { return false; }
|
||||||
|
Options::window.zoom = zoom;
|
||||||
|
setVideoMode(Options::video.fullscreen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve el zoom máximo permitido según la pantalla actual
|
||||||
|
auto Screen::getMaxZoom() const -> int {
|
||||||
|
return Options::window.max_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
// Cambia el color del borde
|
// Cambia el color del borde
|
||||||
void Screen::setBorderColor(Uint8 color) {
|
void Screen::setBorderColor(Uint8 color) {
|
||||||
border_color_ = color;
|
border_color_ = color;
|
||||||
@@ -482,6 +497,33 @@ auto Screen::findPalette(const std::string& name) -> size_t { // NOLINT(readabi
|
|||||||
return static_cast<size_t>(0);
|
return static_cast<size_t>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cambia a una paleta por nombre (case-insensitive); devuelve false si no existe
|
||||||
|
bool Screen::setPaletteByName(const std::string& name) {
|
||||||
|
const std::string upper_name = toUpper(name + ".pal");
|
||||||
|
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||||
|
if (toUpper(getFileName(palettes_[i])) == upper_name) {
|
||||||
|
current_palette_ = static_cast<Uint8>(i);
|
||||||
|
setPalete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve los nombres de paletas disponibles (mayúsculas, sin extensión .pal)
|
||||||
|
auto Screen::getPaletteNames() const -> std::vector<std::string> {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
names.reserve(palettes_.size());
|
||||||
|
for (const auto& p : palettes_) {
|
||||||
|
std::string name = p;
|
||||||
|
const size_t pos = name.find(".pal");
|
||||||
|
if (pos != std::string::npos) { name.erase(pos, 4); }
|
||||||
|
std::ranges::transform(name, name.begin(), ::toupper);
|
||||||
|
names.push_back(std::move(name));
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
// Limpia la game_surface_
|
// Limpia la game_surface_
|
||||||
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
|
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ class Screen {
|
|||||||
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
|
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
|
||||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
||||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||||
auto decWindowZoom() -> bool; // Reduce el tamaño de la ventana
|
auto decWindowZoom() -> bool; // Reduce el tamaño de la ventana
|
||||||
auto incWindowZoom() -> bool; // Aumenta el tamaño de la ventana
|
auto incWindowZoom() -> bool; // Aumenta el tamaño de la ventana
|
||||||
|
auto setWindowZoom(int zoom) -> bool; // Establece zoom directo; false si fuera de [1, max_zoom]
|
||||||
void show(); // Muestra la ventana
|
void show(); // Muestra la ventana
|
||||||
void hide(); // Oculta la ventana
|
void hide(); // Oculta la ventana
|
||||||
|
|
||||||
@@ -52,17 +53,19 @@ class Screen {
|
|||||||
void toggleBorder(); // Cambia entre borde visible y no visible
|
void toggleBorder(); // Cambia entre borde visible y no visible
|
||||||
|
|
||||||
// Paletas y PostFX
|
// Paletas y PostFX
|
||||||
void nextPalette(); // Cambia a la siguiente paleta
|
void nextPalette(); // Cambia a la siguiente paleta
|
||||||
void previousPalette(); // Cambia a la paleta anterior
|
void previousPalette(); // Cambia a la paleta anterior
|
||||||
void setPalete(); // Establece la paleta actual
|
void setPalete(); // Establece la paleta actual
|
||||||
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
bool setPaletteByName(const std::string& name); // Cambia a paleta por nombre; false si no existe
|
||||||
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
[[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>; // Nombres disponibles (mayúsculas, sin .pal)
|
||||||
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
||||||
void reloadCrtPi(); // Recarga el shader CrtPi del preset actual sin toggle
|
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
||||||
void setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
|
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
||||||
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
void reloadCrtPi(); // Recarga el shader CrtPi del preset actual sin toggle
|
||||||
void setActiveShader(Rendering::ShaderType type); // Cambia el shader de post-procesado activo
|
void setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
|
||||||
void nextShader(); // Cicla al siguiente shader disponible (para futura UI)
|
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
||||||
|
void setActiveShader(Rendering::ShaderType type); // Cambia el shader de post-procesado activo
|
||||||
|
void nextShader(); // Cicla al siguiente shader disponible (para futura UI)
|
||||||
|
|
||||||
// Surfaces y notificaciones
|
// Surfaces y notificaciones
|
||||||
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces
|
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces
|
||||||
@@ -79,6 +82,7 @@ class Screen {
|
|||||||
[[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
|
[[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
|
||||||
[[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; }
|
[[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; }
|
||||||
[[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; }
|
[[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; }
|
||||||
|
[[nodiscard]] auto getMaxZoom() const -> int;
|
||||||
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int>;
|
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,59 +1,633 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
static const uint8_t kupscale_frag_spv[] = {
|
static const uint8_t kupscale_frag_spv[] = {
|
||||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
|
0x03,
|
||||||
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
0x02,
|
||||||
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
|
0x23,
|
||||||
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
|
0x07,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
|
0x01,
|
||||||
0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
|
0x00,
|
||||||
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
|
0x0b,
|
||||||
0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00,
|
0x00,
|
||||||
0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x63, 0x70,
|
0x0d,
|
||||||
0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65,
|
0x00,
|
||||||
0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00,
|
0x14,
|
||||||
0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c,
|
0x00,
|
||||||
0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69,
|
0x00,
|
||||||
0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00,
|
0x00,
|
||||||
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x5f,
|
0x00,
|
||||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
|
0x00,
|
||||||
0x0d, 0x00, 0x00, 0x00, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x05, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x75, 0x76,
|
0x11,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
|
0x02,
|
||||||
0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
|
0x01,
|
||||||
0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
|
0x00,
|
||||||
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00,
|
0x0b,
|
||||||
0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
|
0x06,
|
||||||
0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
0x01,
|
||||||
0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00,
|
0x47,
|
||||||
0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
0x4c,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
|
0x53,
|
||||||
0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x4c,
|
||||||
0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
0x2e,
|
||||||
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
|
0x73,
|
||||||
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
|
0x74,
|
||||||
0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
0x64,
|
||||||
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
0x2e,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00,
|
0x34,
|
||||||
0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
|
0x35,
|
||||||
0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
|
0x30,
|
||||||
0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
|
0x00,
|
||||||
0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
|
0x00,
|
||||||
0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
|
0x00,
|
||||||
0x38, 0x00, 0x01, 0x00
|
0x0e,
|
||||||
};
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0f,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x6d,
|
||||||
|
0x61,
|
||||||
|
0x69,
|
||||||
|
0x6e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x11,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xc2,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0a,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x4c,
|
||||||
|
0x5f,
|
||||||
|
0x47,
|
||||||
|
0x4f,
|
||||||
|
0x4f,
|
||||||
|
0x47,
|
||||||
|
0x4c,
|
||||||
|
0x45,
|
||||||
|
0x5f,
|
||||||
|
0x63,
|
||||||
|
0x70,
|
||||||
|
0x70,
|
||||||
|
0x5f,
|
||||||
|
0x73,
|
||||||
|
0x74,
|
||||||
|
0x79,
|
||||||
|
0x6c,
|
||||||
|
0x65,
|
||||||
|
0x5f,
|
||||||
|
0x6c,
|
||||||
|
0x69,
|
||||||
|
0x6e,
|
||||||
|
0x65,
|
||||||
|
0x5f,
|
||||||
|
0x64,
|
||||||
|
0x69,
|
||||||
|
0x72,
|
||||||
|
0x65,
|
||||||
|
0x63,
|
||||||
|
0x74,
|
||||||
|
0x69,
|
||||||
|
0x76,
|
||||||
|
0x65,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x4c,
|
||||||
|
0x5f,
|
||||||
|
0x47,
|
||||||
|
0x4f,
|
||||||
|
0x4f,
|
||||||
|
0x47,
|
||||||
|
0x4c,
|
||||||
|
0x45,
|
||||||
|
0x5f,
|
||||||
|
0x69,
|
||||||
|
0x6e,
|
||||||
|
0x63,
|
||||||
|
0x6c,
|
||||||
|
0x75,
|
||||||
|
0x64,
|
||||||
|
0x65,
|
||||||
|
0x5f,
|
||||||
|
0x64,
|
||||||
|
0x69,
|
||||||
|
0x72,
|
||||||
|
0x65,
|
||||||
|
0x63,
|
||||||
|
0x74,
|
||||||
|
0x69,
|
||||||
|
0x76,
|
||||||
|
0x65,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x6d,
|
||||||
|
0x61,
|
||||||
|
0x69,
|
||||||
|
0x6e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x6f,
|
||||||
|
0x75,
|
||||||
|
0x74,
|
||||||
|
0x5f,
|
||||||
|
0x63,
|
||||||
|
0x6f,
|
||||||
|
0x6c,
|
||||||
|
0x6f,
|
||||||
|
0x72,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0d,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x73,
|
||||||
|
0x63,
|
||||||
|
0x65,
|
||||||
|
0x6e,
|
||||||
|
0x65,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x11,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x76,
|
||||||
|
0x5f,
|
||||||
|
0x75,
|
||||||
|
0x76,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x1e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0d,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x21,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0d,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x22,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x47,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x11,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x1e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x13,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x21,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x16,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x20,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x17,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x20,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3b,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x19,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x0a,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x1b,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x0b,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0a,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x20,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0c,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0b,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3b,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0c,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0d,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x17,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0f,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x20,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0f,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3b,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x11,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x36,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xf8,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3d,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0b,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0d,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3d,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x0f,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x12,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x11,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x57,
|
||||||
|
0x00,
|
||||||
|
0x05,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x13,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0e,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x12,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x3e,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x13,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xfd,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x38,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00};
|
||||||
static const size_t kupscale_frag_spv_size = 628;
|
static const size_t kupscale_frag_spv_size = 628;
|
||||||
|
|||||||
@@ -189,8 +189,13 @@ Director::Director() {
|
|||||||
|
|
||||||
// Inicializa el sistema de localización (antes de Cheevos que usa textos traducidos)
|
// Inicializa el sistema de localización (antes de Cheevos que usa textos traducidos)
|
||||||
#ifdef RELEASE_BUILD
|
#ifdef RELEASE_BUILD
|
||||||
std::string locale_path = executable_path_ + PREFIX + "/data/locale/" + Options::language + ".yaml";
|
{
|
||||||
Locale::init(locale_path);
|
// En release el locale está en el pack, no en el filesystem
|
||||||
|
std::string locale_key = Resource::List::get()->get(Options::language + ".yaml"); // NOLINT(readability-static-accessed-through-instance)
|
||||||
|
auto locale_bytes = Resource::Helper::loadFile(locale_key);
|
||||||
|
std::string locale_content(locale_bytes.begin(), locale_bytes.end());
|
||||||
|
Locale::initFromContent(locale_content);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
Locale::init(Resource::List::get()->get(Options::language + ".yaml")); // NOLINT(readability-static-accessed-through-instance)
|
Locale::init(Resource::List::get()->get(Options::language + ".yaml")); // NOLINT(readability-static-accessed-through-instance)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -880,6 +880,10 @@ auto Player::handleLandingFromAir(float displacement, const SDL_FRect& projectio
|
|||||||
|
|
||||||
// No hay colisión
|
// No hay colisión
|
||||||
y_ += displacement;
|
y_ += displacement;
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Guarda por si en debug el jugador se sale de la pantalla, para que no esté cayendo infinitamente
|
||||||
|
if (y_ > PlayArea::BOTTOM + HEIGHT) { y_ = PlayArea::TOP + 2; }
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ class Player {
|
|||||||
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo
|
[[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo
|
||||||
void setPaused(bool value) { is_paused_ = value; } // Pone el jugador en modo pausa
|
void setPaused(bool value) { is_paused_ = value; } // Pone el jugador en modo pausa
|
||||||
void setIgnoreInput(bool value) { ignore_input_ = value; } // Ignora inputs del jugador (física sigue activa)
|
void setIgnoreInput(bool value) { ignore_input_ = value; } // Ignora inputs del jugador (física sigue activa)
|
||||||
|
[[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; }
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// --- Funciones de debug ---
|
// --- Funciones de debug ---
|
||||||
|
|||||||
@@ -882,9 +882,11 @@ void Game::checkEndGameCheevos() { // NOLINT(readability-convert-member-functio
|
|||||||
|
|
||||||
// Inicializa al jugador
|
// Inicializa al jugador
|
||||||
void Game::initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room) { // NOLINT(readability-convert-member-functions-to-static)
|
void Game::initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room) { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
|
const bool IGNORE_INPUT = player_ != nullptr && player_->getIgnoreInput();
|
||||||
std::string player_animations = (Options::game.player_skin == 2) ? "player2.yaml" : "player.yaml";
|
std::string player_animations = (Options::game.player_skin == 2) ? "player2.yaml" : "player.yaml";
|
||||||
const Player::Data PLAYER{.spawn_data = spawn_point, .animations_path = player_animations, .room = std::move(room)};
|
const Player::Data PLAYER{.spawn_data = spawn_point, .animations_path = player_animations, .room = std::move(room)};
|
||||||
player_ = std::make_shared<Player>(PLAYER);
|
player_ = std::make_shared<Player>(PLAYER);
|
||||||
|
if (IGNORE_INPUT) { player_->setIgnoreInput(true); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea la textura para poner el nombre de la habitación
|
// Crea la textura para poner el nombre de la habitación
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <cctype> // Para toupper
|
#include <cctype> // Para toupper
|
||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
#include <iostream> // Para std::cout
|
#include <iostream> // Para std::cout
|
||||||
|
#include <sstream> // Para std::istringstream
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
@@ -27,9 +28,16 @@
|
|||||||
|
|
||||||
// ── Sistema de comandos ────────────────────────────────────────────────────────
|
// ── Sistema de comandos ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Mapa de completions: {ruta_completa_en_mayúsculas, {opciones}}
|
||||||
|
// Ej: {"CHEAT OPEN THE", {"JAIL"}}
|
||||||
|
using CompletionMap = std::vector<std::pair<std::string_view, std::vector<std::string_view>>>;
|
||||||
|
|
||||||
struct ConsoleCommand {
|
struct ConsoleCommand {
|
||||||
std::string_view keyword;
|
std::string_view keyword;
|
||||||
std::function<std::string(const std::vector<std::string>& args)> execute;
|
std::function<std::string(const std::vector<std::string>& args)> execute;
|
||||||
|
bool instant{false}; // Si true, muestra la respuesta sin efecto typewriter
|
||||||
|
bool hidden{false}; // Si true, no aparece en el autocompletado (TAB)
|
||||||
|
CompletionMap completions{}; // Árbol de sub-argumentos para TAB; cargado en el constructor de Console
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convierte la entrada a uppercase y la divide en tokens por espacios
|
// Convierte la entrada a uppercase y la divide en tokens por espacios
|
||||||
@@ -69,7 +77,7 @@ static auto parseTokens(const std::string& input) -> std::vector<std::string> {
|
|||||||
(toggle_fn); \
|
(toggle_fn); \
|
||||||
return label " OFF"; \
|
return label " OFF"; \
|
||||||
} \
|
} \
|
||||||
return "Usage: " label " [ON|OFF]"; \
|
return "usage: " label " [on|off]"; \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texto de ayuda común para HELP y ?
|
// Texto de ayuda común para HELP y ?
|
||||||
@@ -128,6 +136,13 @@ static void printHelp() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// En Release, los comandos de truco (CHEAT) son ocultos en el autocompletado
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static constexpr bool CHEAT_HIDDEN = false;
|
||||||
|
#else
|
||||||
|
static constexpr bool CHEAT_HIDDEN = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Tabla de comandos disponibles
|
// Tabla de comandos disponibles
|
||||||
static const std::vector<ConsoleCommand> COMMANDS = {
|
static const std::vector<ConsoleCommand> COMMANDS = {
|
||||||
// SS [ON|OFF|SIZE|UPSCALE [NEAREST|LINEAR]|DOWNSCALE [BILINEAR|LANCZOS2|LANCZOS3]] — Supersampling
|
// SS [ON|OFF|SIZE|UPSCALE [NEAREST|LINEAR]|DOWNSCALE [BILINEAR|LANCZOS2|LANCZOS3]] — Supersampling
|
||||||
@@ -154,7 +169,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Screen::get()->setLinearUpscale(true);
|
Screen::get()->setLinearUpscale(true);
|
||||||
return "Upscale: Linear";
|
return "Upscale: Linear";
|
||||||
}
|
}
|
||||||
return "Usage: SS UPSCALE [NEAREST|LINEAR]";
|
return "usage: ss upscale [nearest|linear]";
|
||||||
}
|
}
|
||||||
if (!args.empty() && args[0] == "DOWNSCALE") {
|
if (!args.empty() && args[0] == "DOWNSCALE") {
|
||||||
if (args.size() == 1) {
|
if (args.size() == 1) {
|
||||||
@@ -164,7 +179,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (args[1] == "BILINEAR") { algo = 0; }
|
if (args[1] == "BILINEAR") { algo = 0; }
|
||||||
if (args[1] == "LANCZOS2") { algo = 1; }
|
if (args[1] == "LANCZOS2") { algo = 1; }
|
||||||
if (args[1] == "LANCZOS3") { algo = 2; }
|
if (args[1] == "LANCZOS3") { algo = 2; }
|
||||||
if (algo == -1) { return "Usage: SS DOWNSCALE [BILINEAR|LANCZOS2|LANCZOS3]"; }
|
if (algo == -1) { return "usage: ss downscale [bilinear|lanczos2|lanczos3]"; }
|
||||||
if (Options::video.downscale_algo == algo) {
|
if (Options::video.downscale_algo == algo) {
|
||||||
return std::string("Downscale already ") + std::string(DOWNSCALE_NAMES[static_cast<size_t>(algo)]);
|
return std::string("Downscale already ") + std::string(DOWNSCALE_NAMES[static_cast<size_t>(algo)]);
|
||||||
}
|
}
|
||||||
@@ -185,14 +200,19 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Screen::get()->toggleSupersampling();
|
Screen::get()->toggleSupersampling();
|
||||||
return "PostFX Supersampling OFF";
|
return "PostFX Supersampling OFF";
|
||||||
}
|
}
|
||||||
return "Usage: SS [ON|OFF|SIZE|UPSCALE [NEAREST|LINEAR]|DOWNSCALE [BILINEAR|LANCZOS2|LANCZOS3]]";
|
return "usage: ss [on|off|size|upscale [nearest|linear]|downscale [bilinear|lanczos2|lanczos3]]";
|
||||||
}},
|
},
|
||||||
|
.completions = {
|
||||||
|
{"SS", {"ON", "OFF", "SIZE", "UPSCALE", "DOWNSCALE"}},
|
||||||
|
{"SS UPSCALE", {"NEAREST", "LINEAR"}},
|
||||||
|
{"SS DOWNSCALE", {"BILINEAR", "LANCZOS2", "LANCZOS3"}},
|
||||||
|
}},
|
||||||
|
|
||||||
// SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI] — Toggle/cicla/selecciona shader (F4 / Shift+F4)
|
// SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI] — Toggle/cicla/selecciona shader (F4 / Shift+F4)
|
||||||
{.keyword = "SHADER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "SHADER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
Screen::get()->toggleShaders();
|
Screen::get()->toggleShaders();
|
||||||
return std::string("Shaders ") + (Options::video.postfx ? "ON" : "OFF");
|
return std::string("Shader ") + (Options::video.postfx ? "ON" : "OFF");
|
||||||
}
|
}
|
||||||
if (args[0] == "ON") {
|
if (args[0] == "ON") {
|
||||||
if (Options::video.postfx) { return "Shader already ON"; }
|
if (Options::video.postfx) { return "Shader already ON"; }
|
||||||
@@ -206,11 +226,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
}
|
}
|
||||||
if (args[0] == "POSTFX") {
|
if (args[0] == "POSTFX") {
|
||||||
Screen::get()->setActiveShader(Rendering::ShaderType::POSTFX);
|
Screen::get()->setActiveShader(Rendering::ShaderType::POSTFX);
|
||||||
return "Shader: POSTFX";
|
return "Shader: PostFX";
|
||||||
}
|
}
|
||||||
if (args[0] == "CRTPI") {
|
if (args[0] == "CRTPI") {
|
||||||
Screen::get()->setActiveShader(Rendering::ShaderType::CRTPI);
|
Screen::get()->setActiveShader(Rendering::ShaderType::CRTPI);
|
||||||
return "Shader: CRTPI";
|
return "Shader: CrtPi";
|
||||||
}
|
}
|
||||||
if (args[0] == "NEXT") {
|
if (args[0] == "NEXT") {
|
||||||
// SHADER NEXT PRESET → cicla presets del shader activo
|
// SHADER NEXT PRESET → cicla presets del shader activo
|
||||||
@@ -232,16 +252,20 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
return "PostFX preset: " +
|
return "PostFX preset: " +
|
||||||
Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name;
|
Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name;
|
||||||
}
|
}
|
||||||
// SHADER NEXT → cicla entre tipos de shader (POSTFX ↔ CRTPI)
|
// SHADER NEXT → cicla entre tipos de shader (PostFX ↔ CrtPi)
|
||||||
Screen::get()->nextShader();
|
Screen::get()->nextShader();
|
||||||
return std::string("Shader: ") +
|
return std::string("Shader: ") +
|
||||||
(Options::current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX");
|
(Options::current_shader == Rendering::ShaderType::CRTPI ? "CrtPi" : "PostFX");
|
||||||
}
|
}
|
||||||
return "Usage: SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI]";
|
return "usage: shader [on|off|next [preset]|postfx|crtpi]";
|
||||||
}},
|
},
|
||||||
|
.completions = {
|
||||||
|
{"SHADER", {"ON", "OFF", "NEXT", "POSTFX", "CRTPI"}},
|
||||||
|
{"SHADER NEXT", {"PRESET"}},
|
||||||
|
}},
|
||||||
|
|
||||||
// BORDER [ON|OFF] — Borde decorativo (B)
|
// BORDER [ON|OFF] — Borde decorativo (B)
|
||||||
{.keyword = "BORDER", .execute = BOOL_TOGGLE_CMD("Border", Options::video.border.enabled, Screen::get()->toggleBorder())},
|
{.keyword = "BORDER", .execute = BOOL_TOGGLE_CMD("Border", Options::video.border.enabled, Screen::get()->toggleBorder()), .completions = {{"BORDER", {"ON", "OFF"}}}},
|
||||||
|
|
||||||
// FULLSCREEN [ON|OFF [PLEASE]] — Pantalla completa (F3); OFF bloqueado en kiosk sin PLEASE
|
// FULLSCREEN [ON|OFF [PLEASE]] — Pantalla completa (F3); OFF bloqueado en kiosk sin PLEASE
|
||||||
{.keyword = "FULLSCREEN", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "FULLSCREEN", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -268,12 +292,13 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Screen::get()->toggleVideoMode();
|
Screen::get()->toggleVideoMode();
|
||||||
return std::string("Fullscreen ") + (Options::video.fullscreen ? "ON" : "OFF");
|
return std::string("Fullscreen ") + (Options::video.fullscreen ? "ON" : "OFF");
|
||||||
}
|
}
|
||||||
return "Usage: FULLSCREEN [ON|OFF]";
|
return "usage: fullscreen [on|off]";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"FULLSCREEN", {"ON", "OFF"}}}},
|
||||||
|
|
||||||
// ZOOM UP/DOWN — Zoom de ventana (F1/F2)
|
// ZOOM UP/DOWN — Zoom de ventana (F1/F2)
|
||||||
{.keyword = "ZOOM", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "ZOOM", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (args.empty()) { return "Usage: ZOOM [UP|DOWN]"; }
|
if (args.empty()) { return "usage: zoom [up|down|<1-" + std::to_string(Screen::get()->getMaxZoom()) + ">]"; }
|
||||||
if (args[0] == "UP") {
|
if (args[0] == "UP") {
|
||||||
if (!Screen::get()->incWindowZoom()) { return "Max zoom reached"; }
|
if (!Screen::get()->incWindowZoom()) { return "Max zoom reached"; }
|
||||||
return "Zoom " + std::to_string(Options::window.zoom);
|
return "Zoom " + std::to_string(Options::window.zoom);
|
||||||
@@ -282,15 +307,27 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (!Screen::get()->decWindowZoom()) { return "Min zoom reached"; }
|
if (!Screen::get()->decWindowZoom()) { return "Min zoom reached"; }
|
||||||
return "Zoom " + std::to_string(Options::window.zoom);
|
return "Zoom " + std::to_string(Options::window.zoom);
|
||||||
}
|
}
|
||||||
return "Usage: ZOOM [UP|DOWN]";
|
// Zoom numérico directo
|
||||||
}},
|
try {
|
||||||
|
const int N = std::stoi(args[0]);
|
||||||
|
const int MAX = Screen::get()->getMaxZoom();
|
||||||
|
if (N < 1 || N > MAX) {
|
||||||
|
return "Zoom must be between 1 and " + std::to_string(MAX);
|
||||||
|
}
|
||||||
|
if (N == Options::window.zoom) { return "Zoom already " + std::to_string(N); }
|
||||||
|
Screen::get()->setWindowZoom(N);
|
||||||
|
return "Zoom " + std::to_string(Options::window.zoom);
|
||||||
|
} catch (...) {}
|
||||||
|
return "usage: zoom [up|down|<1-" + std::to_string(Screen::get()->getMaxZoom()) + ">]";
|
||||||
|
},
|
||||||
|
.completions = {{"ZOOM", {"UP", "DOWN"}}}},
|
||||||
|
|
||||||
// INTSCALE [ON|OFF] — Escalado entero (F7)
|
// INTSCALE [ON|OFF] — Escalado entero (F7)
|
||||||
{.keyword = "INTSCALE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "INTSCALE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
const bool ON = args.empty() ? !Options::video.integer_scale
|
const bool ON = args.empty() ? !Options::video.integer_scale
|
||||||
: (args[0] == "ON");
|
: (args[0] == "ON");
|
||||||
if (!args.empty() && args[0] != "ON" && args[0] != "OFF") {
|
if (!args.empty() && args[0] != "ON" && args[0] != "OFF") {
|
||||||
return "Usage: INTSCALE [ON|OFF]";
|
return "usage: intscale [on|off]";
|
||||||
}
|
}
|
||||||
if (ON == Options::video.integer_scale) {
|
if (ON == Options::video.integer_scale) {
|
||||||
return std::string("IntScale already ") + (ON ? "ON" : "OFF");
|
return std::string("IntScale already ") + (ON ? "ON" : "OFF");
|
||||||
@@ -298,10 +335,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Screen::get()->toggleIntegerScale();
|
Screen::get()->toggleIntegerScale();
|
||||||
Screen::get()->setVideoMode(Options::video.fullscreen);
|
Screen::get()->setVideoMode(Options::video.fullscreen);
|
||||||
return std::string("IntScale ") + (Options::video.integer_scale ? "ON" : "OFF");
|
return std::string("IntScale ") + (Options::video.integer_scale ? "ON" : "OFF");
|
||||||
}},
|
},
|
||||||
|
.completions = {{"INTSCALE", {"ON", "OFF"}}}},
|
||||||
|
|
||||||
// VSYNC [ON|OFF] — Sincronización vertical
|
// VSYNC [ON|OFF] — Sincronización vertical
|
||||||
{.keyword = "VSYNC", .execute = BOOL_TOGGLE_CMD("VSync", Options::video.vertical_sync, Screen::get()->toggleVSync())},
|
{.keyword = "VSYNC", .execute = BOOL_TOGGLE_CMD("VSync", Options::video.vertical_sync, Screen::get()->toggleVSync()), .completions = {{"VSYNC", {"ON", "OFF"}}}},
|
||||||
|
|
||||||
// DRIVER [LIST|AUTO|<nombre>] — Driver GPU (aplica en el próximo arranque)
|
// DRIVER [LIST|AUTO|<nombre>] — Driver GPU (aplica en el próximo arranque)
|
||||||
{.keyword = "DRIVER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "DRIVER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -357,20 +395,31 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Options::video.gpu_preferred_driver = driver_lower;
|
Options::video.gpu_preferred_driver = driver_lower;
|
||||||
Options::saveToFile();
|
Options::saveToFile();
|
||||||
return "Driver: " + driver_lower + " (restart)";
|
return "Driver: " + driver_lower + " (restart)";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"DRIVER", {"LIST", "AUTO", "NONE"}}}},
|
||||||
|
|
||||||
// PALETTE NEXT/PREV — Paleta de colores (F5/F6)
|
// PALETTE NEXT/PREV/<nombre> — Paleta de colores (F5/F6 o por nombre)
|
||||||
{.keyword = "PALETTE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "PALETTE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (args.empty()) { return "Usage: PALETTE [NEXT|PREV]"; }
|
const auto palName = []() -> std::string {
|
||||||
|
std::string name = Options::video.palette;
|
||||||
|
std::ranges::transform(name, name.begin(), ::tolower);
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
if (args.empty()) { return "usage: palette [next|prev|<name>]"; }
|
||||||
if (args[0] == "NEXT") {
|
if (args[0] == "NEXT") {
|
||||||
Screen::get()->nextPalette();
|
Screen::get()->nextPalette();
|
||||||
return "Palette: " + Options::video.palette;
|
return "Palette: " + palName();
|
||||||
}
|
}
|
||||||
if (args[0] == "PREV") {
|
if (args[0] == "PREV") {
|
||||||
Screen::get()->previousPalette();
|
Screen::get()->previousPalette();
|
||||||
return "Palette: " + Options::video.palette;
|
return "Palette: " + palName();
|
||||||
}
|
}
|
||||||
return "Usage: PALETTE [NEXT|PREV]";
|
if (!Screen::get()->setPaletteByName(args[0])) {
|
||||||
|
std::string arg_lower = args[0];
|
||||||
|
std::ranges::transform(arg_lower, arg_lower.begin(), ::tolower);
|
||||||
|
return "Unknown palette: " + arg_lower;
|
||||||
|
}
|
||||||
|
return "Palette: " + palName();
|
||||||
}},
|
}},
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@@ -389,15 +438,16 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
GameControl::toggle_debug_mode();
|
GameControl::toggle_debug_mode();
|
||||||
return "Debug mode OFF";
|
return "Debug mode OFF";
|
||||||
}
|
}
|
||||||
if (!args.empty()) { return "Usage: DEBUG [ON|OFF]"; }
|
if (!args.empty()) { return "usage: debug [on|off]"; }
|
||||||
GameControl::toggle_debug_mode();
|
GameControl::toggle_debug_mode();
|
||||||
return std::string("Debug mode ") + (Debug::get()->isEnabled() ? "ON" : "OFF");
|
return std::string("Debug mode ") + (Debug::get()->isEnabled() ? "ON" : "OFF");
|
||||||
}},
|
},
|
||||||
|
.completions = {{"DEBUG", {"ON", "OFF"}}}},
|
||||||
|
|
||||||
// ROOM <num>|NEXT|PREV — Cambia a la habitación indicada (1-60); solo en escena GAME
|
// ROOM <num>|NEXT|PREV — Cambia a la habitación indicada (1-60); solo en escena GAME
|
||||||
{.keyword = "ROOM", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "ROOM", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; }
|
if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; }
|
||||||
if (args.empty()) { return "Usage: ROOM <1-60>|NEXT|PREV"; }
|
if (args.empty()) { return "usage: room <1-60>|next|prev"; }
|
||||||
int num = 0;
|
int num = 0;
|
||||||
if (args[0] == "NEXT" || args[0] == "PREV") {
|
if (args[0] == "NEXT" || args[0] == "PREV") {
|
||||||
if (!GameControl::get_current_room) { return "Game not initialized"; }
|
if (!GameControl::get_current_room) { return "Game not initialized"; }
|
||||||
@@ -409,7 +459,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
num = std::stoi(args[0]);
|
num = std::stoi(args[0]);
|
||||||
} catch (...) { return "Usage: ROOM <1-60>|NEXT|PREV"; }
|
} catch (...) { return "usage: room <1-60>|next|prev"; }
|
||||||
}
|
}
|
||||||
if (num < 1 || num > 60) { return "Room must be between 1 and 60"; }
|
if (num < 1 || num > 60) { return "Room must be between 1 and 60"; }
|
||||||
char buf[16];
|
char buf[16];
|
||||||
@@ -418,7 +468,8 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
return std::string("Room: ") + buf;
|
return std::string("Room: ") + buf;
|
||||||
}
|
}
|
||||||
return std::string("Room not found: ") + buf;
|
return std::string("Room not found: ") + buf;
|
||||||
}},
|
},
|
||||||
|
.completions = {{"ROOM", {"NEXT", "PREV"}}}},
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -433,31 +484,39 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Notifier::get()->show({Locale::get()->get("achievements.header"), Locale::get()->get("achievements.c1")}, Notifier::Style::CHEEVO, -1, false); // NOLINT(readability-static-accessed-through-instance)
|
Notifier::get()->show({Locale::get()->get("achievements.header"), Locale::get()->get("achievements.c1")}, Notifier::Style::CHEEVO, -1, false); // NOLINT(readability-static-accessed-through-instance)
|
||||||
return "Cheevo notification shown";
|
return "Cheevo notification shown";
|
||||||
}
|
}
|
||||||
if (args.empty() || args[0] != "INFO") { return "Usage: SHOW [INFO|NOTIFICATION|CHEEVO]"; }
|
if (args.empty() || args[0] != "INFO") { return "usage: show [info|notification|cheevo]"; }
|
||||||
#else
|
#else
|
||||||
if (args.empty() || args[0] != "INFO") { return "Usage: SHOW [INFO]"; }
|
if (args.empty() || args[0] != "INFO") { return "usage: show [info]"; }
|
||||||
#endif
|
#endif
|
||||||
if (RenderInfo::get()->isActive()) { return "Info overlay already ON"; }
|
if (RenderInfo::get()->isActive()) { return "Info overlay already ON"; }
|
||||||
RenderInfo::get()->toggle();
|
RenderInfo::get()->toggle();
|
||||||
return "Info overlay ON";
|
return "Info overlay ON";
|
||||||
}},
|
},
|
||||||
|
.completions = {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
{"SHOW", {"INFO", "NOTIFICATION", "CHEEVO"}},
|
||||||
|
#else
|
||||||
|
{"SHOW", {"INFO"}},
|
||||||
|
#endif
|
||||||
|
}},
|
||||||
|
|
||||||
// HIDE INFO — disponible en Release
|
// HIDE INFO — disponible en Release
|
||||||
{.keyword = "HIDE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "HIDE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (args.empty() || args[0] != "INFO") { return "Usage: HIDE [INFO]"; }
|
if (args.empty() || args[0] != "INFO") { return "usage: hide [info]"; }
|
||||||
if (!RenderInfo::get()->isActive()) { return "Info overlay already OFF"; }
|
if (!RenderInfo::get()->isActive()) { return "Info overlay already OFF"; }
|
||||||
RenderInfo::get()->toggle();
|
RenderInfo::get()->toggle();
|
||||||
return "Info overlay OFF";
|
return "Info overlay OFF";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"HIDE", {"INFO"}}}},
|
||||||
|
|
||||||
// CHEAT <subcomando> — Trucos de juego; solo en escena GAME; no aparece en ayuda en builds Release
|
// CHEAT <subcomando> — Trucos de juego; solo en escena GAME; no aparece en ayuda en builds Release
|
||||||
{.keyword = "CHEAT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "CHEAT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; }
|
if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; }
|
||||||
if (args.empty()) { return "Usage: CHEAT [INFINITE LIVES|INVINCIBILITY|OPEN THE JAIL|CLOSE THE JAIL]"; }
|
if (args.empty()) { return "usage: cheat [infinite lives|invincibility|open the jail|close the jail]"; }
|
||||||
|
|
||||||
// CHEAT INFINITE LIVES [ON|OFF]
|
// CHEAT INFINITE LIVES [ON|OFF]
|
||||||
if (args[0] == "INFINITE") {
|
if (args[0] == "INFINITE") {
|
||||||
if (args.size() < 2 || args[1] != "LIVES") { return "Usage: CHEAT INFINITE LIVES [ON|OFF]"; }
|
if (args.size() < 2 || args[1] != "LIVES") { return "usage: cheat infinite lives [on|off]"; }
|
||||||
auto& cheat = Options::cheats.infinite_lives;
|
auto& cheat = Options::cheats.infinite_lives;
|
||||||
using State = Options::Cheat::State;
|
using State = Options::Cheat::State;
|
||||||
const std::vector<std::string> REST(args.begin() + 2, args.end());
|
const std::vector<std::string> REST(args.begin() + 2, args.end());
|
||||||
@@ -470,7 +529,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (cheat == State::DISABLED) { return "Infinite lives already OFF"; }
|
if (cheat == State::DISABLED) { return "Infinite lives already OFF"; }
|
||||||
cheat = State::DISABLED;
|
cheat = State::DISABLED;
|
||||||
} else {
|
} else {
|
||||||
return "Usage: CHEAT INFINITE LIVES [ON|OFF]";
|
return "usage: cheat infinite lives [on|off]";
|
||||||
}
|
}
|
||||||
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
||||||
return std::string("Infinite lives ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
return std::string("Infinite lives ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
||||||
@@ -489,7 +548,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (cheat == State::DISABLED) { return "Invincibility already OFF"; }
|
if (cheat == State::DISABLED) { return "Invincibility already OFF"; }
|
||||||
cheat = State::DISABLED;
|
cheat = State::DISABLED;
|
||||||
} else {
|
} else {
|
||||||
return "Usage: CHEAT INVINCIBILITY [ON|OFF]";
|
return "usage: cheat invincibility [on|off]";
|
||||||
}
|
}
|
||||||
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
||||||
return std::string("Invincibility ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
return std::string("Invincibility ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
||||||
@@ -497,7 +556,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
|
|
||||||
// CHEAT OPEN THE JAIL
|
// CHEAT OPEN THE JAIL
|
||||||
if (args[0] == "OPEN") {
|
if (args[0] == "OPEN") {
|
||||||
if (args.size() < 3 || args[1] != "THE" || args[2] != "JAIL") { return "Usage: CHEAT OPEN THE JAIL"; }
|
if (args.size() < 3 || args[1] != "THE" || args[2] != "JAIL") { return "usage: cheat open the jail"; }
|
||||||
if (Options::cheats.jail_is_open == Options::Cheat::State::ENABLED) { return "Jail already open"; }
|
if (Options::cheats.jail_is_open == Options::Cheat::State::ENABLED) { return "Jail already open"; }
|
||||||
Options::cheats.jail_is_open = Options::Cheat::State::ENABLED;
|
Options::cheats.jail_is_open = Options::Cheat::State::ENABLED;
|
||||||
return "Jail opened";
|
return "Jail opened";
|
||||||
@@ -505,14 +564,25 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
|
|
||||||
// CHEAT CLOSE THE JAIL
|
// CHEAT CLOSE THE JAIL
|
||||||
if (args[0] == "CLOSE") {
|
if (args[0] == "CLOSE") {
|
||||||
if (args.size() < 3 || args[1] != "THE" || args[2] != "JAIL") { return "Usage: CHEAT CLOSE THE JAIL"; }
|
if (args.size() < 3 || args[1] != "THE" || args[2] != "JAIL") { return "usage: cheat close the jail"; }
|
||||||
if (Options::cheats.jail_is_open == Options::Cheat::State::DISABLED) { return "Jail already closed"; }
|
if (Options::cheats.jail_is_open == Options::Cheat::State::DISABLED) { return "Jail already closed"; }
|
||||||
Options::cheats.jail_is_open = Options::Cheat::State::DISABLED;
|
Options::cheats.jail_is_open = Options::Cheat::State::DISABLED;
|
||||||
return "Jail closed";
|
return "Jail closed";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Usage: CHEAT [INFINITE LIVES|INVINCIBILITY|OPEN THE JAIL|CLOSE THE JAIL]";
|
return "usage: cheat [infinite lives|invincibility|open the jail|close the jail]";
|
||||||
}},
|
},
|
||||||
|
.hidden = CHEAT_HIDDEN,
|
||||||
|
.completions = {
|
||||||
|
{"CHEAT", {"INFINITE", "INVINCIBILITY", "OPEN", "CLOSE"}},
|
||||||
|
{"CHEAT INFINITE", {"LIVES"}},
|
||||||
|
{"CHEAT INFINITE LIVES", {"ON", "OFF"}},
|
||||||
|
{"CHEAT INVINCIBILITY", {"ON", "OFF"}},
|
||||||
|
{"CHEAT OPEN", {"THE"}},
|
||||||
|
{"CHEAT OPEN THE", {"JAIL"}},
|
||||||
|
{"CHEAT CLOSE", {"THE"}},
|
||||||
|
{"CHEAT CLOSE THE", {"JAIL"}},
|
||||||
|
}},
|
||||||
|
|
||||||
// SET PLAYER SKIN <1|2> — Cambia la skin del jugador (disponible en todos los builds, GAME)
|
// SET PLAYER SKIN <1|2> — Cambia la skin del jugador (disponible en todos los builds, GAME)
|
||||||
// SET INITIAL [ROOM|POS] — Guarda habitación/posición actual como inicio (solo _DEBUG, GAME)
|
// SET INITIAL [ROOM|POS] — Guarda habitación/posición actual como inicio (solo _DEBUG, GAME)
|
||||||
@@ -525,7 +595,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
try {
|
try {
|
||||||
num = std::stoi(args[2]);
|
num = std::stoi(args[2]);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
if (num < 1 || num > 2) { return "Usage: SET PLAYER SKIN <1|2>"; }
|
if (num < 1 || num > 2) { return "usage: set player skin <1|2>"; }
|
||||||
if (!GameControl::change_player_skin) { return "Game not initialized"; }
|
if (!GameControl::change_player_skin) { return "Game not initialized"; }
|
||||||
GameControl::change_player_skin(num);
|
GameControl::change_player_skin(num);
|
||||||
return "Player skin: " + std::to_string(num);
|
return "Player skin: " + std::to_string(num);
|
||||||
@@ -538,27 +608,29 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (args.size() >= 3) {
|
if (args.size() >= 3) {
|
||||||
if (args[2] == "GAME") {
|
if (args[2] == "GAME") {
|
||||||
target = SceneManager::Scene::GAME;
|
target = SceneManager::Scene::GAME;
|
||||||
name = "GAME";
|
name = "game";
|
||||||
} else if (args[2] == "LOGO") {
|
} else if (args[2] == "LOGO") {
|
||||||
target = SceneManager::Scene::LOGO;
|
target = SceneManager::Scene::LOGO;
|
||||||
name = "LOGO";
|
name = "logo";
|
||||||
} else if (args[2] == "LOADING") {
|
} else if (args[2] == "LOADING") {
|
||||||
target = SceneManager::Scene::LOADING_SCREEN;
|
target = SceneManager::Scene::LOADING_SCREEN;
|
||||||
name = "LOADING";
|
name = "loading";
|
||||||
} else if (args[2] == "TITLE") {
|
} else if (args[2] == "TITLE") {
|
||||||
target = SceneManager::Scene::TITLE;
|
target = SceneManager::Scene::TITLE;
|
||||||
name = "TITLE";
|
name = "title";
|
||||||
} else if (args[2] == "CREDITS") {
|
} else if (args[2] == "CREDITS") {
|
||||||
target = SceneManager::Scene::CREDITS;
|
target = SceneManager::Scene::CREDITS;
|
||||||
name = "CREDITS";
|
name = "credits";
|
||||||
} else if (args[2] == "ENDING") {
|
} else if (args[2] == "ENDING") {
|
||||||
target = SceneManager::Scene::ENDING;
|
target = SceneManager::Scene::ENDING;
|
||||||
name = "ENDING";
|
name = "ending";
|
||||||
} else if (args[2] == "ENDING2") {
|
} else if (args[2] == "ENDING2") {
|
||||||
target = SceneManager::Scene::ENDING2;
|
target = SceneManager::Scene::ENDING2;
|
||||||
name = "ENDING2";
|
name = "ending2";
|
||||||
} else {
|
} else {
|
||||||
return "Unknown scene: " + args[2];
|
std::string scene_lower = args[2];
|
||||||
|
std::ranges::transform(scene_lower, scene_lower.begin(), ::tolower);
|
||||||
|
return "Unknown scene: " + scene_lower;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug::get()->setInitialScene(target);
|
Debug::get()->setInitialScene(target);
|
||||||
@@ -570,23 +642,23 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
|
|
||||||
// SET ITEMS <0-200> — Fija el contador de items recogidos
|
// SET ITEMS <0-200> — Fija el contador de items recogidos
|
||||||
if (args[0] == "ITEMS") {
|
if (args[0] == "ITEMS") {
|
||||||
if (args.size() < 2) { return "Usage: SET ITEMS <0-200>"; }
|
if (args.size() < 2) { return "usage: set items <0-200>"; }
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
count = std::stoi(args[1]);
|
count = std::stoi(args[1]);
|
||||||
} catch (...) { return "Usage: SET ITEMS <0-200>"; }
|
} catch (...) { return "usage: set items <0-200>"; }
|
||||||
if (count < 0 || count > 200) { return "Items must be between 0 and 200"; }
|
if (count < 0 || count > 200) { return "Items must be between 0 and 200"; }
|
||||||
if (!GameControl::set_items) { return "Game not initialized"; }
|
if (!GameControl::set_items) { return "Game not initialized"; }
|
||||||
GameControl::set_items(count);
|
GameControl::set_items(count);
|
||||||
return "Items: " + std::to_string(count);
|
return "Items: " + std::to_string(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.empty() || args[0] != "INITIAL") { return "Usage: SET INITIAL [ROOM|POS|SCENE] | SET ITEMS <0-200> | SET PLAYER SKIN <1|2>"; }
|
if (args.empty() || args[0] != "INITIAL") { return "usage: set initial [room|pos|scene] | set items <0-200> | set player skin <1|2>"; }
|
||||||
|
|
||||||
const bool DO_ROOM = args.size() == 1 || (args.size() >= 2 && args[1] == "ROOM");
|
const bool DO_ROOM = args.size() == 1 || (args.size() >= 2 && args[1] == "ROOM");
|
||||||
const bool DO_POS = args.size() == 1 || (args.size() >= 2 && args[1] == "POS");
|
const bool DO_POS = args.size() == 1 || (args.size() >= 2 && args[1] == "POS");
|
||||||
|
|
||||||
if (!DO_ROOM && !DO_POS) { return "Usage: SET INITIAL [ROOM|POS|SCENE]"; }
|
if (!DO_ROOM && !DO_POS) { return "usage: set initial [room|pos|scene]"; }
|
||||||
if (!GameControl::set_initial_room || !GameControl::set_initial_pos) { return "Game not initialized"; }
|
if (!GameControl::set_initial_room || !GameControl::set_initial_pos) { return "Game not initialized"; }
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
@@ -597,15 +669,26 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
#else
|
#else
|
||||||
return "Usage: SET PLAYER SKIN <1|2>";
|
return "usage: set player skin <1|2>";
|
||||||
#endif
|
#endif
|
||||||
}},
|
},
|
||||||
|
.completions = {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
{"SET", {"PLAYER", "INITIAL", "ITEMS"}},
|
||||||
|
{"SET PLAYER", {"SKIN"}},
|
||||||
|
{"SET INITIAL", {"ROOM", "POS", "SCENE"}},
|
||||||
|
{"SET INITIAL SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2"}},
|
||||||
|
#else
|
||||||
|
{"SET", {"PLAYER"}},
|
||||||
|
{"SET PLAYER", {"SKIN"}},
|
||||||
|
#endif
|
||||||
|
}},
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// SCENE [LOGO|LOADING|TITLE|CREDITS|GAME|ENDING|ENDING2|RESTART] — Cambiar o reiniciar escena; solo en Debug
|
// SCENE [LOGO|LOADING|TITLE|CREDITS|GAME|ENDING|ENDING2|RESTART] — Cambiar o reiniciar escena; solo en Debug
|
||||||
{.keyword = "SCENE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "SCENE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (Options::kiosk.enabled) { return "Not allowed in kiosk mode"; }
|
if (Options::kiosk.enabled) { return "Not allowed in kiosk mode"; }
|
||||||
if (args.empty()) { return "Usage: SCENE [LOGO|LOADING|TITLE|CREDITS|GAME|ENDING|ENDING2|RESTART]"; }
|
if (args.empty()) { return "usage: scene [logo|loading|title|credits|game|ending|ending2|restart]"; }
|
||||||
|
|
||||||
// RESTART: reinicia la escena actual (funciona desde cualquier escena)
|
// RESTART: reinicia la escena actual (funciona desde cualquier escena)
|
||||||
if (args[0] == "RESTART") {
|
if (args[0] == "RESTART") {
|
||||||
@@ -633,14 +716,17 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (args[0] == "ENDING") { return GO_TO(SceneManager::Scene::ENDING, "Ending"); }
|
if (args[0] == "ENDING") { return GO_TO(SceneManager::Scene::ENDING, "Ending"); }
|
||||||
if (args[0] == "ENDING2") { return GO_TO(SceneManager::Scene::ENDING2, "Ending 2"); }
|
if (args[0] == "ENDING2") { return GO_TO(SceneManager::Scene::ENDING2, "Ending 2"); }
|
||||||
return "Unknown scene: " + args[0];
|
return "Unknown scene: " + args[0];
|
||||||
}},
|
},
|
||||||
|
.completions = {{"SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2", "RESTART"}}}},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// RESTART — Reiniciar desde el principio (equivale a SCENE LOGO)
|
// RESTART — Reiniciar desde el principio (equivale a SCENE LOGO)
|
||||||
{.keyword = "RESTART", .execute = [](const std::vector<std::string>&) -> std::string {
|
{.keyword = "RESTART", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||||
SceneManager::current = SceneManager::Scene::LOGO;
|
SceneManager::current = SceneManager::Scene::LOGO;
|
||||||
|
Audio::get()->stopMusic();
|
||||||
return "Restarting...";
|
return "Restarting...";
|
||||||
}},
|
},
|
||||||
|
.instant = true},
|
||||||
|
|
||||||
// KIOSK [ON|OFF PLEASE|PLEASE] — Modo kiosko
|
// KIOSK [ON|OFF PLEASE|PLEASE] — Modo kiosko
|
||||||
{.keyword = "KIOSK", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "KIOSK", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -659,8 +745,9 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
if (!Options::video.fullscreen) { Screen::get()->toggleVideoMode(); }
|
if (!Options::video.fullscreen) { Screen::get()->toggleVideoMode(); }
|
||||||
return "Kiosk mode ON";
|
return "Kiosk mode ON";
|
||||||
}
|
}
|
||||||
return "Usage: KIOSK [ON]";
|
return "usage: kiosk [on]";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"KIOSK", {"ON"}}}},
|
||||||
|
|
||||||
// AUDIO [ON|OFF|VOL <0-100>] — Audio maestro (estado + volumen)
|
// AUDIO [ON|OFF|VOL <0-100>] — Audio maestro (estado + volumen)
|
||||||
{.keyword = "AUDIO", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "AUDIO", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -688,10 +775,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Options::audio.volume = static_cast<float>(VAL) / 100.0F;
|
Options::audio.volume = static_cast<float>(VAL) / 100.0F;
|
||||||
Audio::get()->enable(Options::audio.enabled);
|
Audio::get()->enable(Options::audio.enabled);
|
||||||
return "Audio vol:" + std::to_string(VAL);
|
return "Audio vol:" + std::to_string(VAL);
|
||||||
} catch (...) { return "Usage: AUDIO VOL <0-100>"; }
|
} catch (...) { return "usage: audio vol <0-100>"; }
|
||||||
}
|
}
|
||||||
return "Usage: AUDIO [ON|OFF|VOL N]";
|
return "usage: audio [on|off|vol n]";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"AUDIO", {"ON", "OFF", "VOL"}}}},
|
||||||
|
|
||||||
// MUSIC [ON|OFF|VOL <0-100>] — Volumen e interruptor de música
|
// MUSIC [ON|OFF|VOL <0-100>] — Volumen e interruptor de música
|
||||||
{.keyword = "MUSIC", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "MUSIC", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -723,10 +811,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Audio::get()->setMusicVolume(Options::audio.music.volume);
|
Audio::get()->setMusicVolume(Options::audio.music.volume);
|
||||||
}
|
}
|
||||||
return "Music vol:" + std::to_string(VAL);
|
return "Music vol:" + std::to_string(VAL);
|
||||||
} catch (...) { return "Usage: MUSIC VOL <0-100>"; }
|
} catch (...) { return "usage: music vol <0-100>"; }
|
||||||
}
|
}
|
||||||
return "Usage: MUSIC [ON|OFF|VOL N]";
|
return "usage: music [on|off|vol n]";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"MUSIC", {"ON", "OFF", "VOL"}}}},
|
||||||
|
|
||||||
// SOUND [ON|OFF|VOL <0-100>] — Volumen e interruptor de efectos de sonido
|
// SOUND [ON|OFF|VOL <0-100>] — Volumen e interruptor de efectos de sonido
|
||||||
{.keyword = "SOUND", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "SOUND", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -758,10 +847,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
Audio::get()->setSoundVolume(Options::audio.sound.volume);
|
Audio::get()->setSoundVolume(Options::audio.sound.volume);
|
||||||
}
|
}
|
||||||
return "Sound vol:" + std::to_string(VAL);
|
return "Sound vol:" + std::to_string(VAL);
|
||||||
} catch (...) { return "Usage: SOUND VOL <0-100>"; }
|
} catch (...) { return "usage: sound vol <0-100>"; }
|
||||||
}
|
}
|
||||||
return "Usage: SOUND [ON|OFF|VOL N]";
|
return "usage: sound [on|off|vol n]";
|
||||||
}},
|
},
|
||||||
|
.completions = {{"SOUND", {"ON", "OFF", "VOL"}}}},
|
||||||
|
|
||||||
// EXIT / QUIT — Cerrar la aplicacion (bloqueado en kiosk)
|
// EXIT / QUIT — Cerrar la aplicacion (bloqueado en kiosk)
|
||||||
{.keyword = "EXIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "EXIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
@@ -770,14 +860,16 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
}
|
}
|
||||||
SceneManager::current = SceneManager::Scene::QUIT;
|
SceneManager::current = SceneManager::Scene::QUIT;
|
||||||
return "Quitting...";
|
return "Quitting...";
|
||||||
}},
|
},
|
||||||
|
.instant = true},
|
||||||
{.keyword = "QUIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
{.keyword = "QUIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||||
if (Options::kiosk.enabled && (args.empty() || args[0] != "PLEASE")) {
|
if (Options::kiosk.enabled && (args.empty() || args[0] != "PLEASE")) {
|
||||||
return "Not allowed in kiosk mode";
|
return "Not allowed in kiosk mode";
|
||||||
}
|
}
|
||||||
SceneManager::current = SceneManager::Scene::QUIT;
|
SceneManager::current = SceneManager::Scene::QUIT;
|
||||||
return "Quitting...";
|
return "Quitting...";
|
||||||
}},
|
},
|
||||||
|
.instant = true},
|
||||||
|
|
||||||
// SIZE — Devuelve el tamaño actual de la ventana en píxeles
|
// SIZE — Devuelve el tamaño actual de la ventana en píxeles
|
||||||
{.keyword = "SIZE", .execute = [](const std::vector<std::string>&) -> std::string {
|
{.keyword = "SIZE", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||||
@@ -787,17 +879,77 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
return std::to_string(w) + "x" + std::to_string(h);
|
return std::to_string(w) + "x" + std::to_string(h);
|
||||||
}},
|
}},
|
||||||
|
|
||||||
// HELP / ? — Muestra ayuda en la terminal del sistema
|
// HELP / ? — Muestra ayuda en la terminal del sistema y lista de comandos en consola
|
||||||
{.keyword = "HELP", .execute = [](const std::vector<std::string>&) -> std::string {
|
{.keyword = "HELP", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||||
printHelp();
|
printHelp();
|
||||||
return "Help printed to terminal";
|
std::string result =
|
||||||
|
"Commands:\n"
|
||||||
|
"fullscreen, zoom, intscale, vsync, driver, palette, audio, music, sound, set, restart, kiosk, exit, quit, show, hide, size, help\n";
|
||||||
|
#ifdef _DEBUG
|
||||||
|
result +=
|
||||||
|
"\nDebug commands:\n"
|
||||||
|
"debug, room, scene, cheat\n";
|
||||||
|
#endif
|
||||||
|
result += "-- more info on the terminal";
|
||||||
|
return result;
|
||||||
}},
|
}},
|
||||||
{.keyword = "?", .execute = [](const std::vector<std::string>&) -> std::string {
|
{.keyword = "?", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||||
printHelp();
|
printHelp();
|
||||||
return "Help printed to terminal";
|
std::string result =
|
||||||
|
"Commands:\n"
|
||||||
|
"fullscreen, zoom, intscale, vsync, driver, palette, audio, music, sound, set, restart, kiosk, exit, quit, show, hide, size, help\n";
|
||||||
|
#ifdef _DEBUG
|
||||||
|
result +=
|
||||||
|
"\nDebug commands:\n"
|
||||||
|
"debug, room, scene, cheat\n";
|
||||||
|
#endif
|
||||||
|
result += "-- more info on the terminal";
|
||||||
|
return result;
|
||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Calcula la altura total de la consola para N líneas de mensaje (+ 1 línea de input)
|
||||||
|
static auto calcTargetHeight(int num_msg_lines) -> float {
|
||||||
|
constexpr int TEXT_SIZE = 6;
|
||||||
|
constexpr int PADDING_IN_V = TEXT_SIZE / 2;
|
||||||
|
return static_cast<float>((TEXT_SIZE * (num_msg_lines + 1)) + (PADDING_IN_V * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide text en líneas respetando los \n existentes y haciendo word-wrap por ancho en píxeles
|
||||||
|
auto Console::wrapText(const std::string& text) const -> std::vector<std::string> {
|
||||||
|
constexpr int PADDING_IN_H = 6; // TEXT_SIZE; simétrico a ambos lados
|
||||||
|
const int MAX_PX = static_cast<int>(Options::game.width) - (2 * PADDING_IN_H);
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
std::istringstream segment_stream(text);
|
||||||
|
std::string segment;
|
||||||
|
|
||||||
|
while (std::getline(segment_stream, segment)) {
|
||||||
|
if (segment.empty()) {
|
||||||
|
result.emplace_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string current_line;
|
||||||
|
std::istringstream word_stream(segment);
|
||||||
|
std::string word;
|
||||||
|
while (word_stream >> word) {
|
||||||
|
const std::string TEST = current_line.empty() ? word : (current_line + ' ' + word);
|
||||||
|
if (text_->length(TEST) <= MAX_PX) {
|
||||||
|
current_line = TEST;
|
||||||
|
} else {
|
||||||
|
if (!current_line.empty()) { result.push_back(current_line); }
|
||||||
|
current_line = word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!current_line.empty()) { result.push_back(current_line); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.empty()) { result.emplace_back(); }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Singleton ─────────────────────────────────────────────────────────────────
|
// ── Singleton ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// [SINGLETON]
|
// [SINGLETON]
|
||||||
@@ -822,12 +974,18 @@ auto Console::get() -> Console* {
|
|||||||
// Constructor
|
// Constructor
|
||||||
Console::Console(const std::string& font_name)
|
Console::Console(const std::string& font_name)
|
||||||
: text_(Resource::Cache::get()->getText(font_name)) {
|
: text_(Resource::Cache::get()->getText(font_name)) {
|
||||||
const int TEXT_SIZE = 6;
|
msg_lines_ = {std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION)};
|
||||||
const int PADDING_IN_V = TEXT_SIZE / 2;
|
height_ = calcTargetHeight(static_cast<int>(msg_lines_.size()));
|
||||||
height_ = static_cast<float>((TEXT_SIZE * 2) + (PADDING_IN_V * 2));
|
target_height_ = height_;
|
||||||
y_ = -height_;
|
y_ = -height_;
|
||||||
|
|
||||||
msg_line_ = std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION);
|
// Construir mapa de autocompletado a partir de COMMANDS
|
||||||
|
for (const auto& cmd : COMMANDS) {
|
||||||
|
for (const auto& [path, opts] : cmd.completions) {
|
||||||
|
auto& vec = tab_completions_[std::string(path)];
|
||||||
|
for (const auto& opt : opts) { vec.emplace_back(opt); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildSurface();
|
buildSurface();
|
||||||
}
|
}
|
||||||
@@ -849,9 +1007,9 @@ void Console::buildSurface() {
|
|||||||
// Redibuja el texto dinámico sobre la surface (fondo + borde + líneas)
|
// Redibuja el texto dinámico sobre la surface (fondo + borde + líneas)
|
||||||
void Console::redrawText() {
|
void Console::redrawText() {
|
||||||
const float WIDTH = Options::game.width;
|
const float WIDTH = Options::game.width;
|
||||||
const int TEXT_SIZE = 6;
|
constexpr int TEXT_SIZE = 6;
|
||||||
const int PADDING_IN_H = TEXT_SIZE;
|
constexpr int PADDING_IN_H = TEXT_SIZE;
|
||||||
const int PADDING_IN_V = TEXT_SIZE / 2;
|
constexpr int PADDING_IN_V = TEXT_SIZE / 2;
|
||||||
|
|
||||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
auto previous_renderer = Screen::get()->getRendererSurface();
|
||||||
Screen::get()->setRendererSurface(surface_);
|
Screen::get()->setRendererSurface(surface_);
|
||||||
@@ -861,13 +1019,21 @@ void Console::redrawText() {
|
|||||||
SDL_FRect rect = {.x = 0, .y = 0, .w = WIDTH, .h = height_};
|
SDL_FRect rect = {.x = 0, .y = 0, .w = WIDTH, .h = height_};
|
||||||
surface_->drawRectBorder(&rect, BORDER_COLOR);
|
surface_->drawRectBorder(&rect, BORDER_COLOR);
|
||||||
|
|
||||||
// Línea 1: mensajes
|
// Líneas de mensaje con efecto typewriter (solo muestra los primeros typewriter_chars_)
|
||||||
text_->writeColored(PADDING_IN_H, PADDING_IN_V, msg_line_, MSG_COLOR);
|
int y_pos = PADDING_IN_V;
|
||||||
|
int remaining = typewriter_chars_;
|
||||||
|
for (const auto& line : msg_lines_) {
|
||||||
|
if (remaining <= 0) { break; }
|
||||||
|
const int VISIBLE = std::min(remaining, static_cast<int>(line.size()));
|
||||||
|
text_->writeColored(PADDING_IN_H, y_pos, line.substr(0, VISIBLE), MSG_COLOR);
|
||||||
|
remaining -= VISIBLE;
|
||||||
|
y_pos += TEXT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
// Línea 2: prompt + input + cursor
|
// Línea de input (siempre la última)
|
||||||
const bool SHOW_CURSOR = cursor_visible_ && (static_cast<int>(input_line_.size()) < MAX_LINE_CHARS);
|
const bool SHOW_CURSOR = cursor_visible_ && (static_cast<int>(input_line_.size()) < MAX_LINE_CHARS);
|
||||||
const std::string INPUT_STR = "> " + input_line_ + (SHOW_CURSOR ? "_" : "");
|
const std::string INPUT_STR = "> " + input_line_ + (SHOW_CURSOR ? "_" : "");
|
||||||
text_->writeColored(PADDING_IN_H, PADDING_IN_V + TEXT_SIZE, INPUT_STR, BORDER_COLOR);
|
text_->writeColored(PADDING_IN_H, y_pos, INPUT_STR, BORDER_COLOR);
|
||||||
|
|
||||||
Screen::get()->setRendererSurface(previous_renderer);
|
Screen::get()->setRendererSurface(previous_renderer);
|
||||||
}
|
}
|
||||||
@@ -888,6 +1054,44 @@ void Console::update(float delta_time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Efecto typewriter: revelar letras una a una (solo cuando ACTIVE)
|
||||||
|
if (status_ == Status::ACTIVE) {
|
||||||
|
int total_chars = 0;
|
||||||
|
for (const auto& line : msg_lines_) { total_chars += static_cast<int>(line.size()); }
|
||||||
|
if (typewriter_chars_ < total_chars) {
|
||||||
|
typewriter_timer_ += delta_time;
|
||||||
|
while (typewriter_timer_ >= TYPEWRITER_CHAR_DELAY && typewriter_chars_ < total_chars) {
|
||||||
|
typewriter_timer_ -= TYPEWRITER_CHAR_DELAY;
|
||||||
|
++typewriter_chars_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animación de altura (resize cuando msg_lines_ cambia); solo en ACTIVE
|
||||||
|
if (status_ == Status::ACTIVE && height_ != target_height_) {
|
||||||
|
const float PREV_HEIGHT = height_;
|
||||||
|
if (height_ < target_height_) {
|
||||||
|
height_ = std::min(height_ + SLIDE_SPEED * delta_time, target_height_);
|
||||||
|
} else {
|
||||||
|
height_ = std::max(height_ - SLIDE_SPEED * delta_time, target_height_);
|
||||||
|
}
|
||||||
|
// Actualizar el Notifier incrementalmente con el delta de altura
|
||||||
|
if (Notifier::get() != nullptr) {
|
||||||
|
const int DELTA_PX = static_cast<int>(height_) - static_cast<int>(PREV_HEIGHT);
|
||||||
|
if (DELTA_PX > 0) {
|
||||||
|
Notifier::get()->addYOffset(DELTA_PX);
|
||||||
|
notifier_offset_applied_ += DELTA_PX;
|
||||||
|
} else if (DELTA_PX < 0) {
|
||||||
|
Notifier::get()->removeYOffset(-DELTA_PX);
|
||||||
|
notifier_offset_applied_ += DELTA_PX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reconstruir la Surface al nuevo tamaño (pequeña: 256×~18-72px)
|
||||||
|
const float WIDTH = Options::game.width;
|
||||||
|
surface_ = std::make_shared<Surface>(WIDTH, height_);
|
||||||
|
sprite_->setSurface(surface_);
|
||||||
|
}
|
||||||
|
|
||||||
// Redibujar texto cada frame
|
// Redibujar texto cada frame
|
||||||
redrawText();
|
redrawText();
|
||||||
|
|
||||||
@@ -905,6 +1109,9 @@ void Console::update(float delta_time) {
|
|||||||
if (y_ <= -height_) {
|
if (y_ <= -height_) {
|
||||||
y_ = -height_;
|
y_ = -height_;
|
||||||
status_ = Status::HIDDEN;
|
status_ = Status::HIDDEN;
|
||||||
|
// Resetear el mensaje una vez completamente oculta
|
||||||
|
msg_lines_ = {std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION)};
|
||||||
|
target_height_ = calcTargetHeight(static_cast<int>(msg_lines_.size()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -914,6 +1121,7 @@ void Console::update(float delta_time) {
|
|||||||
|
|
||||||
SDL_FRect rect = {.x = 0, .y = y_, .w = Options::game.width, .h = height_};
|
SDL_FRect rect = {.x = 0, .y = y_, .w = Options::game.width, .h = height_};
|
||||||
sprite_->setPosition(rect);
|
sprite_->setPosition(rect);
|
||||||
|
sprite_->setClip({.x = 0.0F, .y = 0.0F, .w = Options::game.width, .h = height_});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderiza la consola
|
// Renderiza la consola
|
||||||
@@ -928,21 +1136,36 @@ void Console::render() {
|
|||||||
void Console::toggle() {
|
void Console::toggle() {
|
||||||
switch (status_) {
|
switch (status_) {
|
||||||
case Status::HIDDEN:
|
case Status::HIDDEN:
|
||||||
|
// Al abrir: la consola siempre empieza con 1 línea de mensaje (altura base)
|
||||||
|
target_height_ = calcTargetHeight(static_cast<int>(msg_lines_.size()));
|
||||||
|
height_ = target_height_;
|
||||||
|
y_ = -height_;
|
||||||
status_ = Status::RISING;
|
status_ = Status::RISING;
|
||||||
input_line_.clear();
|
input_line_.clear();
|
||||||
cursor_timer_ = 0.0F;
|
cursor_timer_ = 0.0F;
|
||||||
cursor_visible_ = true;
|
cursor_visible_ = true;
|
||||||
|
// El mensaje inicial ("JDD Console v1.0") aparece completo, sin typewriter
|
||||||
|
typewriter_chars_ = static_cast<int>(msg_lines_[0].size());
|
||||||
|
typewriter_timer_ = 0.0F;
|
||||||
SDL_StartTextInput(SDL_GetKeyboardFocus());
|
SDL_StartTextInput(SDL_GetKeyboardFocus());
|
||||||
if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(static_cast<int>(height_)); }
|
if (Notifier::get() != nullptr) {
|
||||||
|
const int OFFSET = static_cast<int>(height_);
|
||||||
|
Notifier::get()->addYOffset(OFFSET);
|
||||||
|
notifier_offset_applied_ = OFFSET;
|
||||||
|
}
|
||||||
if (on_toggle) { on_toggle(true); }
|
if (on_toggle) { on_toggle(true); }
|
||||||
break;
|
break;
|
||||||
case Status::ACTIVE:
|
case Status::ACTIVE:
|
||||||
|
// Al cerrar: mantener el texto visible hasta que esté completamente oculta
|
||||||
status_ = Status::VANISHING;
|
status_ = Status::VANISHING;
|
||||||
msg_line_ = std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION);
|
target_height_ = height_; // No animar durante VANISHING
|
||||||
history_index_ = -1;
|
history_index_ = -1;
|
||||||
saved_input_.clear();
|
saved_input_.clear();
|
||||||
SDL_StopTextInput(SDL_GetKeyboardFocus());
|
SDL_StopTextInput(SDL_GetKeyboardFocus());
|
||||||
if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(static_cast<int>(height_)); }
|
if (Notifier::get() != nullptr) {
|
||||||
|
Notifier::get()->removeYOffset(notifier_offset_applied_);
|
||||||
|
notifier_offset_applied_ = 0;
|
||||||
|
}
|
||||||
if (on_toggle) { on_toggle(false); }
|
if (on_toggle) { on_toggle(false); }
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -956,15 +1179,19 @@ void Console::handleEvent(const SDL_Event& event) {
|
|||||||
if (status_ != Status::ACTIVE) { return; }
|
if (status_ != Status::ACTIVE) { return; }
|
||||||
|
|
||||||
if (event.type == SDL_EVENT_TEXT_INPUT) {
|
if (event.type == SDL_EVENT_TEXT_INPUT) {
|
||||||
|
// Filtrar caracteres de control (tab, newline, etc.)
|
||||||
|
if (static_cast<unsigned char>(event.text.text[0]) < 32) { return; }
|
||||||
if (static_cast<int>(input_line_.size()) < MAX_LINE_CHARS) {
|
if (static_cast<int>(input_line_.size()) < MAX_LINE_CHARS) {
|
||||||
input_line_ += event.text.text;
|
input_line_ += event.text.text;
|
||||||
}
|
}
|
||||||
|
tab_matches_.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN) {
|
if (event.type == SDL_EVENT_KEY_DOWN) {
|
||||||
switch (event.key.scancode) {
|
switch (event.key.scancode) {
|
||||||
case SDL_SCANCODE_BACKSPACE:
|
case SDL_SCANCODE_BACKSPACE:
|
||||||
|
tab_matches_.clear();
|
||||||
if (!input_line_.empty()) { input_line_.pop_back(); }
|
if (!input_line_.empty()) { input_line_.pop_back(); }
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_RETURN:
|
case SDL_SCANCODE_RETURN:
|
||||||
@@ -973,6 +1200,7 @@ void Console::handleEvent(const SDL_Event& event) {
|
|||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_UP:
|
case SDL_SCANCODE_UP:
|
||||||
// Navegar hacia atrás en el historial
|
// Navegar hacia atrás en el historial
|
||||||
|
tab_matches_.clear();
|
||||||
if (history_index_ < static_cast<int>(history_.size()) - 1) {
|
if (history_index_ < static_cast<int>(history_.size()) - 1) {
|
||||||
if (history_index_ == -1) { saved_input_ = input_line_; }
|
if (history_index_ == -1) { saved_input_ = input_line_; }
|
||||||
++history_index_;
|
++history_index_;
|
||||||
@@ -981,6 +1209,7 @@ void Console::handleEvent(const SDL_Event& event) {
|
|||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_DOWN:
|
case SDL_SCANCODE_DOWN:
|
||||||
// Navegar hacia el presente en el historial
|
// Navegar hacia el presente en el historial
|
||||||
|
tab_matches_.clear();
|
||||||
if (history_index_ >= 0) {
|
if (history_index_ >= 0) {
|
||||||
--history_index_;
|
--history_index_;
|
||||||
input_line_ = (history_index_ == -1)
|
input_line_ = (history_index_ == -1)
|
||||||
@@ -988,6 +1217,56 @@ void Console::handleEvent(const SDL_Event& event) {
|
|||||||
: history_[static_cast<size_t>(history_index_)];
|
: history_[static_cast<size_t>(history_index_)];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SDL_SCANCODE_TAB: {
|
||||||
|
if (tab_matches_.empty()) {
|
||||||
|
// Calcular el input actual en mayúsculas
|
||||||
|
std::string upper;
|
||||||
|
for (unsigned char c : input_line_) { upper += static_cast<char>(std::toupper(c)); }
|
||||||
|
|
||||||
|
const size_t space_pos = upper.rfind(' ');
|
||||||
|
if (space_pos == std::string::npos) {
|
||||||
|
// Modo comando: ciclar keywords que empiecen por el prefijo
|
||||||
|
for (const auto& cmd : COMMANDS) {
|
||||||
|
if (cmd.hidden) { continue; }
|
||||||
|
if (upper.empty() || cmd.keyword.starts_with(upper)) {
|
||||||
|
tab_matches_.emplace_back(cmd.keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const std::string base_cmd = upper.substr(0, space_pos);
|
||||||
|
const std::string sub_prefix = upper.substr(space_pos + 1);
|
||||||
|
if (base_cmd == "PALETTE" && Screen::get() != nullptr) {
|
||||||
|
// NEXT/PREV primero, luego todos los nombres de paleta disponibles
|
||||||
|
for (const auto* sv : {"NEXT", "PREV"}) {
|
||||||
|
if (sub_prefix.empty() || std::string_view{sv}.starts_with(sub_prefix)) {
|
||||||
|
tab_matches_.emplace_back("PALETTE " + std::string(sv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& name : Screen::get()->getPaletteNames()) {
|
||||||
|
if (sub_prefix.empty() || std::string_view{name}.starts_with(sub_prefix)) {
|
||||||
|
tab_matches_.emplace_back("PALETTE " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto it = tab_completions_.find(base_cmd);
|
||||||
|
if (it != tab_completions_.end()) {
|
||||||
|
for (const auto& arg : it->second) {
|
||||||
|
if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) {
|
||||||
|
tab_matches_.emplace_back(base_cmd + " " + arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab_index_ = -1;
|
||||||
|
}
|
||||||
|
if (tab_matches_.empty()) { break; }
|
||||||
|
tab_index_ = (tab_index_ + 1) % static_cast<int>(tab_matches_.size());
|
||||||
|
std::string result = tab_matches_[static_cast<size_t>(tab_index_)];
|
||||||
|
for (char& c : result) { c = static_cast<char>(std::tolower(static_cast<unsigned char>(c))); }
|
||||||
|
input_line_ = result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1009,25 +1288,44 @@ void Console::processCommand() {
|
|||||||
if (!TOKENS.empty()) {
|
if (!TOKENS.empty()) {
|
||||||
const std::string& cmd = TOKENS[0];
|
const std::string& cmd = TOKENS[0];
|
||||||
const std::vector<std::string> ARGS(TOKENS.begin() + 1, TOKENS.end());
|
const std::vector<std::string> ARGS(TOKENS.begin() + 1, TOKENS.end());
|
||||||
|
std::string result;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
bool instant = false;
|
||||||
for (const auto& command : COMMANDS) {
|
for (const auto& command : COMMANDS) {
|
||||||
if (command.keyword == cmd) {
|
if (command.keyword == cmd) {
|
||||||
msg_line_ = command.execute(ARGS);
|
result = command.execute(ARGS);
|
||||||
|
instant = command.instant;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
msg_line_ = "Unknown: " + cmd;
|
std::string cmd_lower = cmd;
|
||||||
|
std::ranges::transform(cmd_lower, cmd_lower.begin(), ::tolower);
|
||||||
|
result = "Unknown: " + cmd_lower;
|
||||||
}
|
}
|
||||||
if (static_cast<int>(msg_line_.size()) > MAX_LINE_CHARS) {
|
|
||||||
msg_line_.resize(MAX_LINE_CHARS);
|
// Word-wrap automático según el ancho disponible en píxeles
|
||||||
|
msg_lines_ = wrapText(result);
|
||||||
|
|
||||||
|
// Actualizar la altura objetivo para animar el resize
|
||||||
|
target_height_ = calcTargetHeight(static_cast<int>(msg_lines_.size()));
|
||||||
|
|
||||||
|
// Typewriter: instantáneo si el comando lo requiere, letra a letra si no
|
||||||
|
if (instant) {
|
||||||
|
int total = 0;
|
||||||
|
for (const auto& l : msg_lines_) { total += static_cast<int>(l.size()); }
|
||||||
|
typewriter_chars_ = total;
|
||||||
|
} else {
|
||||||
|
typewriter_chars_ = 0;
|
||||||
}
|
}
|
||||||
|
typewriter_timer_ = 0.0F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input_line_.clear();
|
input_line_.clear();
|
||||||
history_index_ = -1;
|
history_index_ = -1;
|
||||||
saved_input_.clear();
|
saved_input_.clear();
|
||||||
|
tab_matches_.clear();
|
||||||
cursor_timer_ = 0.0F;
|
cursor_timer_ = 0.0F;
|
||||||
cursor_visible_ = true;
|
cursor_visible_ = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <deque> // Para deque (historial)
|
#include <deque> // Para deque (historial)
|
||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
|
#include <unordered_map> // Para unordered_map (tab_completions_)
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
class Surface;
|
class Surface;
|
||||||
class Sprite;
|
class Sprite;
|
||||||
@@ -44,15 +46,16 @@ class Console {
|
|||||||
static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK
|
static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK
|
||||||
static constexpr Uint8 BORDER_COLOR = 9; // PaletteColor::BRIGHT_GREEN
|
static constexpr Uint8 BORDER_COLOR = 9; // PaletteColor::BRIGHT_GREEN
|
||||||
static constexpr Uint8 MSG_COLOR = 8; // PaletteColor::GREEN
|
static constexpr Uint8 MSG_COLOR = 8; // PaletteColor::GREEN
|
||||||
static constexpr float SLIDE_SPEED = 120.0F;
|
static constexpr float SLIDE_SPEED = 180.0F;
|
||||||
|
|
||||||
// Constantes de consola
|
// Constantes de consola
|
||||||
static constexpr std::string_view CONSOLE_NAME = "JDD Console";
|
static constexpr std::string_view CONSOLE_NAME = "JDD Console";
|
||||||
static constexpr std::string_view CONSOLE_VERSION = "v1.0";
|
static constexpr std::string_view CONSOLE_VERSION = "v2.1";
|
||||||
static constexpr int MAX_LINE_CHARS = 32;
|
static constexpr int MAX_LINE_CHARS = 32;
|
||||||
static constexpr int MAX_HISTORY_SIZE = 20;
|
static constexpr int MAX_HISTORY_SIZE = 20;
|
||||||
static constexpr float CURSOR_ON_TIME = 0.5F;
|
static constexpr float CURSOR_ON_TIME = 0.5F;
|
||||||
static constexpr float CURSOR_OFF_TIME = 0.3F;
|
static constexpr float CURSOR_OFF_TIME = 0.3F;
|
||||||
|
static constexpr float TYPEWRITER_CHAR_DELAY = 0.01F; // segundos entre letra y letra
|
||||||
|
|
||||||
// [SINGLETON]
|
// [SINGLETON]
|
||||||
static Console* console;
|
static Console* console;
|
||||||
@@ -62,9 +65,10 @@ class Console {
|
|||||||
~Console() = default;
|
~Console() = default;
|
||||||
|
|
||||||
// Métodos privados
|
// Métodos privados
|
||||||
void buildSurface(); // Crea la Surface con el aspecto visual
|
void buildSurface(); // Crea la Surface con el aspecto visual
|
||||||
void redrawText(); // Redibuja el texto dinámico (msg + input + cursor)
|
void redrawText(); // Redibuja el texto dinámico (msg + input + cursor)
|
||||||
void processCommand(); // Procesa el comando introducido por el usuario
|
void processCommand(); // Procesa el comando introducido por el usuario
|
||||||
|
[[nodiscard]] auto wrapText(const std::string& text) const -> std::vector<std::string>; // Word-wrap por ancho en píxeles
|
||||||
|
|
||||||
// Objetos de renderizado
|
// Objetos de renderizado
|
||||||
std::shared_ptr<Text> text_;
|
std::shared_ptr<Text> text_;
|
||||||
@@ -77,13 +81,26 @@ class Console {
|
|||||||
float height_{0.0F}; // Altura del panel
|
float height_{0.0F}; // Altura del panel
|
||||||
|
|
||||||
// Estado de la entrada de texto
|
// Estado de la entrada de texto
|
||||||
std::string msg_line_; // inicializado en constructor con CONSOLE_NAME + CONSOLE_VERSION
|
std::vector<std::string> msg_lines_; // Líneas de mensaje (1 o más)
|
||||||
std::string input_line_;
|
std::string input_line_;
|
||||||
float cursor_timer_{0.0F};
|
float cursor_timer_{0.0F};
|
||||||
bool cursor_visible_{true};
|
bool cursor_visible_{true};
|
||||||
|
|
||||||
|
// Efecto typewriter
|
||||||
|
int typewriter_chars_{0}; // Caracteres de msg_lines_ actualmente visibles
|
||||||
|
float typewriter_timer_{0.0F};
|
||||||
|
|
||||||
|
// Animación de altura dinámica
|
||||||
|
float target_height_{0.0F}; // Altura objetivo (según número de líneas de mensaje)
|
||||||
|
int notifier_offset_applied_{0}; // Acumulador del offset enviado al Notifier
|
||||||
|
|
||||||
// Historial de comandos (navegable con flechas arriba/abajo)
|
// Historial de comandos (navegable con flechas arriba/abajo)
|
||||||
std::deque<std::string> history_;
|
std::deque<std::string> history_;
|
||||||
int history_index_{-1}; // -1 = en la entrada actual (presente)
|
int history_index_{-1}; // -1 = en la entrada actual (presente)
|
||||||
std::string saved_input_; // guarda input_line_ al empezar a navegar
|
std::string saved_input_; // guarda input_line_ al empezar a navegar
|
||||||
|
|
||||||
|
// Estado de autocompletado (TAB)
|
||||||
|
std::vector<std::string> tab_matches_; // Comandos que coinciden con el prefijo actual
|
||||||
|
int tab_index_{-1}; // Índice actual en tab_matches_
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> tab_completions_; // Mapa pre-calculado en constructor
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
namespace Texts {
|
namespace Texts {
|
||||||
constexpr const char* WINDOW_CAPTION = "© 2022 JailDoctor's Dilemma — JailDesigner";
|
constexpr const char* WINDOW_CAPTION = "© 2022 JailDoctor's Dilemma — JailDesigner";
|
||||||
constexpr const char* COPYRIGHT = "@2022 JailDesigner";
|
constexpr const char* COPYRIGHT = "@2022 JailDesigner";
|
||||||
constexpr const char* VERSION = "1.10"; // Versión por defecto
|
constexpr const char* VERSION = "1.11"; // Versión por defecto
|
||||||
} // namespace Texts
|
} // namespace Texts
|
||||||
|
|
||||||
// Tamaño de bloque
|
// Tamaño de bloque
|
||||||
|
|||||||
Reference in New Issue
Block a user