Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ed7316948 | |||
| 2228153d59 | |||
| 7315032ff2 | |||
| 3fc6795593 | |||
| 16924cf503 | |||
| 705a9fc7cd | |||
| b164c11ba7 | |||
| 1817d00881 | |||
| 8dcf473f31 | |||
| 8f191f02fa | |||
| 1077c13fd0 | |||
| d5a4caa86e | |||
| f3bad9f4ed | |||
| 32f22c99db | |||
| cd14ae22c5 | |||
| 1fdc29e9d2 | |||
| 0a740a5be2 | |||
| 9e1b2b8960 | |||
| ed3724193e |
143
CHANGELOG.md
Normal file
143
CHANGELOG.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to JailDoctor's Dilemma are documented here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.11] - 2026-03-31
|
||||||
|
|
||||||
|
### Novedades
|
||||||
|
- **PaletteManager:** refactorización de `Screen`, responsabilidades de gestión de paletas extraídas a clase propia
|
||||||
|
- **Consola 2.1:** la consola puede cambiar de paleta por nombre (`Screen` devuelve lista de paletas)
|
||||||
|
- **Zoom configurable:** `Screen` permite establecer el nivel de zoom directamente desde consola
|
||||||
|
- **Autocompletar en consola:** autocompletado de comandos con Tab (incluyendo soporte para armadura de lagarto)
|
||||||
|
- Generación automática de tabla de tab-completions en la consola
|
||||||
|
|
||||||
|
### Correcciones
|
||||||
|
- Fix: al entrar a GAME con la consola abierta, el jugador no tenía los inputs deshabilitados
|
||||||
|
- Fix: al hacer restart con la música del attract mode sonando, la música no paraba al ir al logo
|
||||||
|
- Fix: en modo debug, protección para que el jugador no caiga infinitamente si sale de pantalla
|
||||||
|
- Corregido el case en algunas respuestas de la consola
|
||||||
|
- Corregido Makefile
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.10] - 2026-03-30
|
||||||
|
|
||||||
|
### Novedades
|
||||||
|
- **Consola 2.0:** rediseño completo de la consola de desarrollador
|
||||||
|
- Efecto typewriter al mostrar texto
|
||||||
|
- Separación de líneas automática
|
||||||
|
- Cambio de skin
|
||||||
|
- Historial y navegación mejorada
|
||||||
|
- Comandos para cheats, control de escena, debug, audio y shaders
|
||||||
|
- Teclas de función operativas con la consola abierta
|
||||||
|
- Límite de caracteres ampliado
|
||||||
|
- La consola ya no pausa al jugador
|
||||||
|
- Reorganización del sistema de comandos y aliases (`show info`, `hide info`, etc.)
|
||||||
|
- **RenderInfo:** nueva clase con animación para mostrar info de renderizado en pantalla
|
||||||
|
- **Soporte multi-shader:** comandos y teclas para manejar el nuevo diseño de shaders (SPIRV/SPIR-V)
|
||||||
|
- **Modo kiosko:** defaults y restricciones de comandos para modo kiosk
|
||||||
|
- **Supersampling Lanczos:** implementación de escalado Lanczos en el supersampling
|
||||||
|
- **Driver GPU configurable:** permite elegir driver de GPU o ninguno desde consola
|
||||||
|
- Cheats accesibles desde la consola
|
||||||
|
- Cambio y reinicio de escena desde la consola
|
||||||
|
- Posición e habitación inicial de debug configurables desde consola y fichero
|
||||||
|
- `Debug` carga posición e habitación inicial desde fichero
|
||||||
|
- Comandos de audio configurables desde consola
|
||||||
|
- Renderizado del dispositivo GPU en info_debug
|
||||||
|
- `Screen` optimizado (`textureToRenderer()`)
|
||||||
|
- Eliminado soporte para argumentos de línea de comandos
|
||||||
|
- Eliminado `Options::console`
|
||||||
|
- Help de consola organizado
|
||||||
|
|
||||||
|
### Correcciones
|
||||||
|
- Fix: vsync off no funcionaba en Wayland
|
||||||
|
- Fix: en TITLE, la consola no bloqueaba la pulsación del 1 al 4 y entraba a opciones
|
||||||
|
- Fix: dos logs de consola con formato incorrecto
|
||||||
|
- Fix: lógica para abrir y entrar a la jail (ahora usa número de habitación, no nombre)
|
||||||
|
- Corregido `compile_spirv.cmake` y la `system_folder` para shaders
|
||||||
|
- Corregido carácter de caret que se había perdido
|
||||||
|
- Eliminados acentos en títulos de habitaciones que causaban problemas con fuentes
|
||||||
|
- Revisadas y corregidas traducciones
|
||||||
|
- Corregidos ficheros `.fnt`
|
||||||
|
- Corrección en `Screen` para `std::setprecision()` (faltaba `#include <iomanip>`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.09] - 2025-03-01
|
||||||
|
|
||||||
|
### Novedades
|
||||||
|
- **Refactorización a singletons:** `Screen`, `Input`, `Audio`, `Resource::Cache`, `Resource::List`, `Director`, `Cheevos`, `Debug` convertidos a singletons thread-safe
|
||||||
|
- **Smart pointers:** uso de `std::shared_ptr` y `std::unique_ptr` para gestión de recursos y sprites
|
||||||
|
- **Surfaces 8-bit indexadas:** nuevo sistema de renderizado con color indexado y paletas intercambiables
|
||||||
|
- **Sistema de notificaciones rediseñado:** nuevo engine de notificaciones con control de offset
|
||||||
|
- **Modos de vídeo mejorados:** la ventana mantiene posición al cambiar tamaño o activar borde; puede crecer según el escritorio
|
||||||
|
- **ItemTracker:** nuevo singleton para rastrear ítems recogidos
|
||||||
|
- **globalEvents:** nuevo sistema de eventos globales SDL
|
||||||
|
- **Barra de progreso en carga de recursos** (actualización cada 5 ítems para mayor rendimiento con vsync)
|
||||||
|
- **Métodos show/hide ventana:** métodos para mostrar u ocultar la ventana
|
||||||
|
- Afinada la clase `Options`
|
||||||
|
- Actualizada a la última versión de `jail_audio`
|
||||||
|
- Implementados shaders
|
||||||
|
- Nueva tipografía añadida
|
||||||
|
- Parametros de ficheros `.ani` migrados a snake_case
|
||||||
|
- Música de Title y attract mode restaurada
|
||||||
|
- Eliminado sistema online completo
|
||||||
|
|
||||||
|
### Correcciones
|
||||||
|
- Fix: notificaciones ya no ensucian la pantalla de carga
|
||||||
|
- Fix: no pintaba el efecto de carga del borde en `LoadingScreen`
|
||||||
|
- Fix: bug con el puntero a `ScoreboardData`
|
||||||
|
- Fix: carga de opciones y recursos corregida
|
||||||
|
- Eliminados acentos problemáticos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.08] - 2024-02-22
|
||||||
|
|
||||||
|
### Novedades
|
||||||
|
- Posibilidad de saltar la pantalla de carga ya completada desde el menú de título
|
||||||
|
- El `gamestate_title` puede empezar en diferentes estados
|
||||||
|
- Pantalla de carga con fade de paleta
|
||||||
|
- GIF loader: dibujado correcto de GIFs en pantalla
|
||||||
|
- Añadida `paleta.cpp`/`.h` y `gif.c`
|
||||||
|
|
||||||
|
### Correcciones
|
||||||
|
- Corregido bug en el fade de paleta (el canal azul no se propagaba)
|
||||||
|
- Arreglada la separación entre el título y el fade
|
||||||
|
- Online deshabilitado por defecto al crear el fichero de configuración
|
||||||
|
- Tiempo de la pantalla de carga aumentado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.07] - 2022-12-02
|
||||||
|
|
||||||
|
### Novedades
|
||||||
|
- El nombre de la habitación se pinta a partir de una textura
|
||||||
|
- Añadido Batman a FEEL THE HEAT
|
||||||
|
- Cielo de la Jail actualizado
|
||||||
|
- Retocada la pantalla de título
|
||||||
|
- Sprite de PACO modificado
|
||||||
|
- Nombre del enemigo diskette cambiado a floppy
|
||||||
|
- Cambios cosméticos en algunas habitaciones (BE CAREFUL WITH THE FUSE renombrado)
|
||||||
|
- El color de fondo de la habitación se pinta en la textura del mapa
|
||||||
|
- Optimizaciones en intro y title
|
||||||
|
- Preparación para compatibilidad con consolas
|
||||||
|
- Actualizado `jail_audio` a la última versión
|
||||||
|
- Eliminados la mayor parte de accesos a `vector::at()`
|
||||||
|
|
||||||
|
### Correcciones
|
||||||
|
- Corregido bug: en la jail se rellenaban las vidas mientras estaba activa la pausa
|
||||||
|
- Corregido memory leak en `texture.cpp`
|
||||||
|
- Corregido bug en apertura de la Jail
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [v1.0] - 2022-11-13
|
||||||
|
|
||||||
|
Versión de lanzamiento inicial.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*El formato de este changelog sigue [Keep a Changelog](https://keepachangelog.com/).*
|
||||||
@@ -34,31 +34,32 @@ set(APP_SOURCES
|
|||||||
|
|
||||||
# Core - Input
|
# Core - Input
|
||||||
source/core/input/global_inputs.cpp
|
source/core/input/global_inputs.cpp
|
||||||
source/core/input/input.cpp
|
|
||||||
source/core/input/input_types.cpp
|
source/core/input/input_types.cpp
|
||||||
|
source/core/input/input.cpp
|
||||||
source/core/input/mouse.cpp
|
source/core/input/mouse.cpp
|
||||||
|
|
||||||
# Core - Rendering
|
# Core - Rendering
|
||||||
source/core/rendering/gif.cpp
|
source/core/rendering/gif.cpp
|
||||||
|
source/core/rendering/palette_manager.cpp
|
||||||
source/core/rendering/pixel_reveal.cpp
|
source/core/rendering/pixel_reveal.cpp
|
||||||
source/core/rendering/render_info.cpp
|
source/core/rendering/render_info.cpp
|
||||||
source/core/rendering/screen.cpp
|
source/core/rendering/screen.cpp
|
||||||
source/core/rendering/surface.cpp
|
|
||||||
source/core/rendering/sprite/animated_sprite.cpp
|
source/core/rendering/sprite/animated_sprite.cpp
|
||||||
source/core/rendering/sprite/dissolve_sprite.cpp
|
source/core/rendering/sprite/dissolve_sprite.cpp
|
||||||
source/core/rendering/sprite/moving_sprite.cpp
|
source/core/rendering/sprite/moving_sprite.cpp
|
||||||
source/core/rendering/sprite/sprite.cpp
|
source/core/rendering/sprite/sprite.cpp
|
||||||
|
source/core/rendering/surface.cpp
|
||||||
source/core/rendering/text.cpp
|
source/core/rendering/text.cpp
|
||||||
|
|
||||||
# Core - Locale
|
# Core - Locale
|
||||||
source/core/locale/locale.cpp
|
source/core/locale/locale.cpp
|
||||||
|
|
||||||
# Core - Resources
|
# Core - Resources
|
||||||
source/core/resources/resource_list.cpp
|
|
||||||
source/core/resources/resource_cache.cpp
|
source/core/resources/resource_cache.cpp
|
||||||
source/core/resources/resource_pack.cpp
|
|
||||||
source/core/resources/resource_loader.cpp
|
|
||||||
source/core/resources/resource_helper.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
|
source/core/resources/resource_list.cpp
|
||||||
|
source/core/resources/resource_loader.cpp
|
||||||
|
source/core/resources/resource_pack.cpp
|
||||||
|
|
||||||
# Core - System
|
# Core - System
|
||||||
source/core/system/director.cpp
|
source/core/system/director.cpp
|
||||||
@@ -78,9 +79,9 @@ set(APP_SOURCES
|
|||||||
source/game/gameplay/enemy_manager.cpp
|
source/game/gameplay/enemy_manager.cpp
|
||||||
source/game/gameplay/item_manager.cpp
|
source/game/gameplay/item_manager.cpp
|
||||||
source/game/gameplay/item_tracker.cpp
|
source/game/gameplay/item_tracker.cpp
|
||||||
source/game/gameplay/room.cpp
|
|
||||||
source/game/gameplay/room_loader.cpp
|
source/game/gameplay/room_loader.cpp
|
||||||
source/game/gameplay/room_tracker.cpp
|
source/game/gameplay/room_tracker.cpp
|
||||||
|
source/game/gameplay/room.cpp
|
||||||
source/game/gameplay/scoreboard.cpp
|
source/game/gameplay/scoreboard.cpp
|
||||||
source/game/gameplay/stats.cpp
|
source/game/gameplay/stats.cpp
|
||||||
source/game/gameplay/tilemap_renderer.cpp
|
source/game/gameplay/tilemap_renderer.cpp
|
||||||
@@ -89,8 +90,8 @@ set(APP_SOURCES
|
|||||||
source/game/scenes/credits.cpp
|
source/game/scenes/credits.cpp
|
||||||
source/game/scenes/ending.cpp
|
source/game/scenes/ending.cpp
|
||||||
source/game/scenes/ending2.cpp
|
source/game/scenes/ending2.cpp
|
||||||
source/game/scenes/game.cpp
|
|
||||||
source/game/scenes/game_over.cpp
|
source/game/scenes/game_over.cpp
|
||||||
|
source/game/scenes/game.cpp
|
||||||
source/game/scenes/loading_screen.cpp
|
source/game/scenes/loading_screen.cpp
|
||||||
source/game/scenes/logo.cpp
|
source/game/scenes/logo.cpp
|
||||||
source/game/scenes/title.cpp
|
source/game/scenes/title.cpp
|
||||||
|
|||||||
61
Makefile
61
Makefile
@@ -84,57 +84,58 @@ LINUX_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-linux.tar.g
|
|||||||
APP_SOURCES := \
|
APP_SOURCES := \
|
||||||
source/main.cpp \
|
source/main.cpp \
|
||||||
source/core/audio/audio.cpp \
|
source/core/audio/audio.cpp \
|
||||||
source/core/input/input.cpp \
|
|
||||||
source/core/input/input_types.cpp \
|
|
||||||
source/core/input/mouse.cpp \
|
|
||||||
source/core/input/global_inputs.cpp \
|
source/core/input/global_inputs.cpp \
|
||||||
source/core/rendering/screen.cpp \
|
source/core/input/input_types.cpp \
|
||||||
source/core/rendering/surface.cpp \
|
source/core/input/input.cpp \
|
||||||
source/core/rendering/sprite/sprite.cpp \
|
source/core/input/mouse.cpp \
|
||||||
source/core/rendering/sprite/animated_sprite.cpp \
|
source/core/locale/locale.cpp \
|
||||||
source/core/rendering/sprite/moving_sprite.cpp \
|
|
||||||
source/core/rendering/sprite/dissolve_sprite.cpp \
|
|
||||||
source/core/rendering/text.cpp \
|
|
||||||
source/core/rendering/gif.cpp \
|
source/core/rendering/gif.cpp \
|
||||||
|
source/core/rendering/palette_manager.cpp \
|
||||||
source/core/rendering/pixel_reveal.cpp \
|
source/core/rendering/pixel_reveal.cpp \
|
||||||
source/core/rendering/render_info.cpp \
|
source/core/rendering/render_info.cpp \
|
||||||
|
source/core/rendering/screen.cpp \
|
||||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp \
|
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp \
|
||||||
source/core/locale/locale.cpp \
|
source/core/rendering/sprite/animated_sprite.cpp \
|
||||||
source/core/resources/resource_list.cpp \
|
source/core/rendering/sprite/dissolve_sprite.cpp \
|
||||||
|
source/core/rendering/sprite/moving_sprite.cpp \
|
||||||
|
source/core/rendering/sprite/sprite.cpp \
|
||||||
|
source/core/rendering/surface.cpp \
|
||||||
|
source/core/rendering/text.cpp \
|
||||||
source/core/resources/resource_cache.cpp \
|
source/core/resources/resource_cache.cpp \
|
||||||
source/core/resources/resource_helper.cpp \
|
source/core/resources/resource_helper.cpp \
|
||||||
|
source/core/resources/resource_list.cpp \
|
||||||
source/core/resources/resource_loader.cpp \
|
source/core/resources/resource_loader.cpp \
|
||||||
source/core/resources/resource_pack.cpp \
|
source/core/resources/resource_pack.cpp \
|
||||||
source/core/system/director.cpp \
|
|
||||||
source/core/system/debug.cpp \
|
source/core/system/debug.cpp \
|
||||||
|
source/core/system/director.cpp \
|
||||||
source/core/system/global_events.cpp \
|
source/core/system/global_events.cpp \
|
||||||
source/game/options.cpp \
|
|
||||||
source/game/entities/player.cpp \
|
|
||||||
source/game/entities/enemy.cpp \
|
source/game/entities/enemy.cpp \
|
||||||
source/game/entities/item.cpp \
|
source/game/entities/item.cpp \
|
||||||
source/game/gameplay/room.cpp \
|
source/game/entities/player.cpp \
|
||||||
|
source/game/gameplay/cheevos.cpp \
|
||||||
source/game/gameplay/collision_map.cpp \
|
source/game/gameplay/collision_map.cpp \
|
||||||
source/game/gameplay/enemy_manager.cpp \
|
source/game/gameplay/enemy_manager.cpp \
|
||||||
source/game/gameplay/item_manager.cpp \
|
source/game/gameplay/item_manager.cpp \
|
||||||
source/game/gameplay/room_loader.cpp \
|
|
||||||
source/game/gameplay/tilemap_renderer.cpp \
|
|
||||||
source/game/gameplay/scoreboard.cpp \
|
|
||||||
source/game/gameplay/cheevos.cpp \
|
|
||||||
source/game/gameplay/item_tracker.cpp \
|
source/game/gameplay/item_tracker.cpp \
|
||||||
|
source/game/gameplay/room_loader.cpp \
|
||||||
source/game/gameplay/room_tracker.cpp \
|
source/game/gameplay/room_tracker.cpp \
|
||||||
|
source/game/gameplay/room.cpp \
|
||||||
|
source/game/gameplay/scoreboard.cpp \
|
||||||
source/game/gameplay/stats.cpp \
|
source/game/gameplay/stats.cpp \
|
||||||
source/game/scenes/logo.cpp \
|
source/game/gameplay/tilemap_renderer.cpp \
|
||||||
source/game/scenes/loading_screen.cpp \
|
source/game/options.cpp \
|
||||||
source/game/scenes/title.cpp \
|
source/game/scenes/credits.cpp \
|
||||||
source/game/scenes/game.cpp \
|
|
||||||
source/game/scenes/game_over.cpp \
|
|
||||||
source/game/scenes/ending.cpp \
|
source/game/scenes/ending.cpp \
|
||||||
source/game/scenes/ending2.cpp \
|
source/game/scenes/ending2.cpp \
|
||||||
source/game/scenes/credits.cpp \
|
source/game/scenes/game_over.cpp \
|
||||||
source/game/ui/notifier.cpp \
|
source/game/scenes/game.cpp \
|
||||||
|
source/game/scenes/loading_screen.cpp \
|
||||||
|
source/game/scenes/logo.cpp \
|
||||||
|
source/game/scenes/title.cpp \
|
||||||
source/game/ui/console.cpp \
|
source/game/ui/console.cpp \
|
||||||
source/utils/utils.cpp \
|
source/game/ui/notifier.cpp \
|
||||||
source/utils/delta_timer.cpp
|
source/utils/delta_timer.cpp \
|
||||||
|
source/utils/utils.cpp
|
||||||
|
|
||||||
# All sources combined
|
# All sources combined
|
||||||
ALL_SOURCES := $(APP_SOURCES)
|
ALL_SOURCES := $(APP_SOURCES)
|
||||||
@@ -298,6 +299,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()) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
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 initFromContent(const std::string& content); // Crea e inicializa desde contenido en memoria (pack)
|
||||||
static void destroy(); // Destruye el singleton
|
static void destroy(); // Destruye el singleton
|
||||||
static auto get() -> Locale*; // Devuelve el singleton
|
static auto get() -> Locale*; // Devuelve el singleton
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|||||||
104
source/core/rendering/palette_manager.cpp
Normal file
104
source/core/rendering/palette_manager.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "core/rendering/palette_manager.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/rendering/surface.hpp"
|
||||||
|
#include "core/resources/resource_cache.hpp"
|
||||||
|
#include "game/options.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
|
||||||
|
PaletteManager::PaletteManager(
|
||||||
|
std::vector<std::string> raw_paths,
|
||||||
|
const std::string& initial_name,
|
||||||
|
std::shared_ptr<Surface> game_surface,
|
||||||
|
std::shared_ptr<Surface> border_surface,
|
||||||
|
OnChangeCallback on_change)
|
||||||
|
: palettes_(std::move(raw_paths)),
|
||||||
|
game_surface_(std::move(game_surface)),
|
||||||
|
border_surface_(std::move(border_surface)),
|
||||||
|
on_change_(std::move(on_change)) {
|
||||||
|
current_ = findIndex(initial_name);
|
||||||
|
|
||||||
|
// Leer y aplicar paleta inicial directamente desde el archivo
|
||||||
|
// (Resource::Cache aún no está disponible en este punto del ciclo de vida)
|
||||||
|
const auto initial_palette = readPalFile(palettes_.at(current_));
|
||||||
|
game_surface_->setPalette(initial_palette);
|
||||||
|
border_surface_->setPalette(initial_palette);
|
||||||
|
|
||||||
|
// Procesar la lista: conservar solo los nombres de archivo (sin ruta)
|
||||||
|
processPathList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaletteManager::next() {
|
||||||
|
if (++current_ == palettes_.size()) {
|
||||||
|
current_ = 0;
|
||||||
|
}
|
||||||
|
apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaletteManager::previous() {
|
||||||
|
current_ = (current_ > 0) ? current_ - 1 : palettes_.size() - 1;
|
||||||
|
apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PaletteManager::setByName(const std::string& name) -> bool {
|
||||||
|
const std::string upper_name = toUpper(name + ".pal");
|
||||||
|
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||||
|
if (toUpper(palettes_[i]) == upper_name) {
|
||||||
|
current_ = i;
|
||||||
|
apply();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PaletteManager::getNames() 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PaletteManager::getCurrentName() const -> std::string {
|
||||||
|
std::string name = palettes_.at(current_);
|
||||||
|
const size_t pos = name.find(".pal");
|
||||||
|
if (pos != std::string::npos) { name.erase(pos, 4); }
|
||||||
|
std::ranges::transform(name, name.begin(), ::toupper);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaletteManager::apply() {
|
||||||
|
game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_)));
|
||||||
|
border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_)));
|
||||||
|
|
||||||
|
Options::video.palette = getCurrentName();
|
||||||
|
|
||||||
|
if (on_change_) {
|
||||||
|
on_change_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PaletteManager::findIndex(const std::string& name) const -> size_t {
|
||||||
|
const std::string upper_name = toUpper(name + ".pal");
|
||||||
|
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||||
|
if (toUpper(getFileName(palettes_[i])) == upper_name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaletteManager::processPathList() {
|
||||||
|
for (auto& palette : palettes_) {
|
||||||
|
palette = getFileName(palette);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
source/core/rendering/palette_manager.hpp
Normal file
37
source/core/rendering/palette_manager.hpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Surface;
|
||||||
|
|
||||||
|
class PaletteManager {
|
||||||
|
public:
|
||||||
|
using OnChangeCallback = std::function<void()>;
|
||||||
|
|
||||||
|
PaletteManager(
|
||||||
|
std::vector<std::string> raw_paths,
|
||||||
|
const std::string& initial_name,
|
||||||
|
std::shared_ptr<Surface> game_surface,
|
||||||
|
std::shared_ptr<Surface> border_surface,
|
||||||
|
OnChangeCallback on_change = nullptr);
|
||||||
|
|
||||||
|
void next(); // Avanza a la siguiente paleta
|
||||||
|
void previous(); // Retrocede a la paleta anterior
|
||||||
|
auto setByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe
|
||||||
|
[[nodiscard]] auto getNames() const -> std::vector<std::string>; // Nombres disponibles (mayúsculas, sin .pal)
|
||||||
|
[[nodiscard]] auto getCurrentName() const -> std::string; // Nombre de la paleta actual (mayúsculas, sin .pal)
|
||||||
|
|
||||||
|
private:
|
||||||
|
void apply(); // Aplica la paleta actual a ambas surfaces
|
||||||
|
[[nodiscard]] auto findIndex(const std::string& name) const -> size_t; // Localiza paleta por nombre en el vector
|
||||||
|
void processPathList(); // Extrae nombres de archivo de las rutas completas
|
||||||
|
|
||||||
|
std::vector<std::string> palettes_;
|
||||||
|
size_t current_{0};
|
||||||
|
std::shared_ptr<Surface> game_surface_;
|
||||||
|
std::shared_ptr<Surface> border_surface_;
|
||||||
|
OnChangeCallback on_change_;
|
||||||
|
};
|
||||||
@@ -42,8 +42,7 @@ auto Screen::get() -> Screen* {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Screen::Screen()
|
Screen::Screen() {
|
||||||
: palettes_(Resource::List::get()->getListByType(Resource::List::Type::PALETTE)) {
|
|
||||||
// Arranca SDL VIDEO, crea la ventana y el renderizador
|
// Arranca SDL VIDEO, crea la ventana y el renderizador
|
||||||
initSDLVideo();
|
initSDLVideo();
|
||||||
if (Options::video.fullscreen) { SDL_HideCursor(); }
|
if (Options::video.fullscreen) { SDL_HideCursor(); }
|
||||||
@@ -55,7 +54,6 @@ Screen::Screen()
|
|||||||
|
|
||||||
// Ajusta los tamaños
|
// Ajusta los tamaños
|
||||||
game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height};
|
game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height};
|
||||||
current_palette_ = findPalette(Options::video.palette);
|
|
||||||
|
|
||||||
// Define el color del borde para el modo de pantalla completa
|
// Define el color del borde para el modo de pantalla completa
|
||||||
border_color_ = static_cast<Uint8>(PaletteColor::BLACK);
|
border_color_ = static_cast<Uint8>(PaletteColor::BLACK);
|
||||||
@@ -76,19 +74,27 @@ Screen::Screen()
|
|||||||
}
|
}
|
||||||
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
// Cargar la paleta una sola vez
|
// Crea las surfaces (PaletteManager aplicará la paleta inicial en su constructor)
|
||||||
auto initial_palette = readPalFile(palettes_.at(current_palette_));
|
|
||||||
|
|
||||||
// Crea la surface donde se dibujan los graficos del juego
|
|
||||||
game_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);
|
game_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);
|
||||||
game_surface_->setPalette(initial_palette);
|
|
||||||
game_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
|
game_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
|
||||||
|
|
||||||
// Crea la surface para el borde de colores
|
|
||||||
border_surface_ = std::make_shared<Surface>(Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2));
|
border_surface_ = std::make_shared<Surface>(Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2));
|
||||||
border_surface_->setPalette(initial_palette);
|
|
||||||
border_surface_->clear(border_color_);
|
border_surface_->clear(border_color_);
|
||||||
|
|
||||||
|
// Crea el gestor de paletas; aplica la paleta inicial a ambas surfaces
|
||||||
|
palette_manager_ = std::make_unique<PaletteManager>(
|
||||||
|
Resource::List::get()->getListByType(Resource::List::Type::PALETTE),
|
||||||
|
Options::video.palette,
|
||||||
|
game_surface_,
|
||||||
|
border_surface_,
|
||||||
|
[this]() {
|
||||||
|
// Actualizar caché ARGB del borde cuando cambia la paleta
|
||||||
|
if (border_is_solid_) {
|
||||||
|
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
||||||
|
border_argb_color_ = border_pixel_buffer_[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Cachear el color ARGB inicial del borde (borde sólido por defecto)
|
// Cachear el color ARGB inicial del borde (borde sólido por defecto)
|
||||||
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
||||||
border_argb_color_ = border_pixel_buffer_[0];
|
border_argb_color_ = border_pixel_buffer_[0];
|
||||||
@@ -99,9 +105,6 @@ Screen::Screen()
|
|||||||
// Crea el objeto de texto para la pantalla de carga
|
// Crea el objeto de texto para la pantalla de carga
|
||||||
createText();
|
createText();
|
||||||
|
|
||||||
// Extrae el nombre de las paletas desde su ruta
|
|
||||||
processPaletteList();
|
|
||||||
|
|
||||||
// Renderizar una vez la textura vacía para que tenga contenido válido
|
// Renderizar una vez la textura vacía para que tenga contenido válido
|
||||||
// antes de inicializar los shaders (evita pantalla negra)
|
// antes de inicializar los shaders (evita pantalla negra)
|
||||||
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
|
||||||
@@ -194,6 +197,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;
|
||||||
@@ -342,55 +360,10 @@ void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cambia la paleta
|
// Cambia la paleta
|
||||||
void Screen::nextPalette() {
|
void Screen::nextPalette() { palette_manager_->next(); }
|
||||||
++current_palette_;
|
|
||||||
if (current_palette_ == static_cast<int>(palettes_.size())) {
|
|
||||||
current_palette_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPalete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cambia la paleta
|
// Cambia la paleta
|
||||||
void Screen::previousPalette() {
|
void Screen::previousPalette() { palette_manager_->previous(); }
|
||||||
if (current_palette_ > 0) {
|
|
||||||
--current_palette_;
|
|
||||||
} else {
|
|
||||||
current_palette_ = static_cast<Uint8>(palettes_.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
setPalete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece la paleta
|
|
||||||
void Screen::setPalete() { // NOLINT(readability-convert-member-functions-to-static)
|
|
||||||
game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_)));
|
|
||||||
border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_palette_)));
|
|
||||||
|
|
||||||
Options::video.palette = palettes_.at(current_palette_);
|
|
||||||
|
|
||||||
// Eliminar ".gif"
|
|
||||||
size_t pos = Options::video.palette.find(".pal");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
Options::video.palette.erase(pos, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convertir a mayúsculas
|
|
||||||
std::ranges::transform(Options::video.palette, Options::video.palette.begin(), ::toupper);
|
|
||||||
|
|
||||||
// Actualizar caché si el borde es sólido (la paleta cambia el valor ARGB del color)
|
|
||||||
if (border_is_solid_) {
|
|
||||||
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
|
||||||
border_argb_color_ = border_pixel_buffer_[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extrae los nombres de las paletas
|
|
||||||
void Screen::processPaletteList() {
|
|
||||||
for (auto& palette : palettes_) {
|
|
||||||
palette = getFileName(palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copia la surface a la textura
|
// Copia la surface a la textura
|
||||||
void Screen::surfaceToTexture() { // NOLINT(readability-convert-member-functions-to-static)
|
void Screen::surfaceToTexture() { // NOLINT(readability-convert-member-functions-to-static)
|
||||||
@@ -470,17 +443,11 @@ void Screen::renderOverlays() {
|
|||||||
if (Console::get() != nullptr) { Console::get()->render(); } // Console (encima)
|
if (Console::get() != nullptr) { Console::get()->render(); } // Console (encima)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Localiza la paleta dentro del vector de paletas
|
// Cambia a una paleta por nombre (case-insensitive); devuelve false si no existe
|
||||||
auto Screen::findPalette(const std::string& name) -> size_t { // NOLINT(readability-convert-member-functions-to-static)
|
auto Screen::setPaletteByName(const std::string& name) -> bool { return palette_manager_->setByName(name); }
|
||||||
std::string upper_name = toUpper(name + ".pal");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < palettes_.size(); ++i) {
|
// Devuelve los nombres de paletas disponibles (mayúsculas, sin extensión .pal)
|
||||||
if (toUpper(getFileName(palettes_[i])) == upper_name) {
|
auto Screen::getPaletteNames() const -> std::vector<std::string> { return palette_manager_->getNames(); }
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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); }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <utility> // Para std::pair
|
#include <utility> // Para std::pair
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "core/rendering/palette_manager.hpp" // Para PaletteManager
|
||||||
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType, ShaderBackend
|
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType, ShaderBackend
|
||||||
#include "utils/utils.hpp" // Para Color
|
#include "utils/utils.hpp" // Para Color
|
||||||
class Surface;
|
class Surface;
|
||||||
@@ -41,6 +42,7 @@ class Screen {
|
|||||||
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
|
||||||
|
|
||||||
@@ -54,7 +56,8 @@ class Screen {
|
|||||||
// 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
|
auto setPaletteByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe
|
||||||
|
[[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>; // Nombres disponibles (mayúsculas, sin .pal)
|
||||||
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
||||||
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
||||||
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
||||||
@@ -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:
|
||||||
@@ -119,11 +123,9 @@ class Screen {
|
|||||||
void renderNotifications() const; // Dibuja las notificaciones
|
void renderNotifications() const; // Dibuja las notificaciones
|
||||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||||
void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador
|
void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador
|
||||||
void processPaletteList(); // Extrae los nombres de las paletas
|
|
||||||
void surfaceToTexture(); // Copia la surface a la textura
|
void surfaceToTexture(); // Copia la surface a la textura
|
||||||
void textureToRenderer(); // Copia la textura al renderizador
|
void textureToRenderer(); // Copia la textura al renderizador
|
||||||
void renderOverlays(); // Renderiza todos los overlays
|
void renderOverlays(); // Renderiza todos los overlays
|
||||||
auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas
|
|
||||||
void initShaders(); // Inicializa los shaders
|
void initShaders(); // Inicializa los shaders
|
||||||
void applyCurrentPostFXPreset(); // Aplica los parámetros del preset PostFX actual al backend
|
void applyCurrentPostFXPreset(); // Aplica los parámetros del preset PostFX actual al backend
|
||||||
void applyCurrentCrtPiPreset(); // Aplica los parámetros del preset CrtPi actual al backend
|
void applyCurrentCrtPiPreset(); // Aplica los parámetros del preset CrtPi actual al backend
|
||||||
@@ -164,8 +166,7 @@ class Screen {
|
|||||||
|
|
||||||
// Paletas y colores
|
// Paletas y colores
|
||||||
Uint8 border_color_{0}; // Color del borde
|
Uint8 border_color_{0}; // Color del borde
|
||||||
std::vector<std::string> palettes_; // Listado de ficheros de paleta disponibles
|
std::unique_ptr<PaletteManager> palette_manager_; // Gestor de paletas de color
|
||||||
Uint8 current_palette_{0}; // Índice para el vector de paletas
|
|
||||||
|
|
||||||
// Estado y configuración
|
// Estado y configuración
|
||||||
bool notifications_enabled_{false}; // Indica si se muestran las notificaciones
|
bool notifications_enabled_{false}; // Indica si se muestran las notificaciones
|
||||||
|
|||||||
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 ---
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ Game::Game(Mode mode)
|
|||||||
|
|
||||||
GameControl::refresh_player_color = [this]() -> void { player_->setColor(); };
|
GameControl::refresh_player_color = [this]() -> void { player_->setColor(); };
|
||||||
Console::get()->on_toggle = [this](bool open) { player_->setIgnoreInput(open); };
|
Console::get()->on_toggle = [this](bool open) { player_->setIgnoreInput(open); };
|
||||||
|
if (Console::get()->isActive()) { player_->setIgnoreInput(true); }
|
||||||
GameControl::change_player_skin = [this](int skin_num) -> void {
|
GameControl::change_player_skin = [this](int skin_num) -> void {
|
||||||
Options::game.player_skin = skin_num;
|
Options::game.player_skin = skin_num;
|
||||||
player_->setSkin(skin_num);
|
player_->setSkin(skin_num);
|
||||||
@@ -882,9 +883,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,13 +564,24 @@ 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)
|
||||||
@@ -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,7 +669,18 @@ 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
|
||||||
|
},
|
||||||
|
.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
|
#endif
|
||||||
}},
|
}},
|
||||||
|
|
||||||
@@ -605,7 +688,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
|||||||
// 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#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;
|
||||||
@@ -65,6 +68,7 @@ class Console {
|
|||||||
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