Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c689507982 | |||
| 417643018f | |||
| 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
|
||||
source/core/input/global_inputs.cpp
|
||||
source/core/input/input.cpp
|
||||
source/core/input/input_types.cpp
|
||||
source/core/input/input.cpp
|
||||
source/core/input/mouse.cpp
|
||||
|
||||
# Core - Rendering
|
||||
source/core/rendering/gif.cpp
|
||||
source/core/rendering/palette_manager.cpp
|
||||
source/core/rendering/pixel_reveal.cpp
|
||||
source/core/rendering/render_info.cpp
|
||||
source/core/rendering/screen.cpp
|
||||
source/core/rendering/surface.cpp
|
||||
source/core/rendering/sprite/animated_sprite.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
|
||||
|
||||
# Core - Locale
|
||||
source/core/locale/locale.cpp
|
||||
|
||||
# Core - Resources
|
||||
source/core/resources/resource_list.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_list.cpp
|
||||
source/core/resources/resource_loader.cpp
|
||||
source/core/resources/resource_pack.cpp
|
||||
|
||||
# Core - System
|
||||
source/core/system/director.cpp
|
||||
@@ -78,9 +79,9 @@ set(APP_SOURCES
|
||||
source/game/gameplay/enemy_manager.cpp
|
||||
source/game/gameplay/item_manager.cpp
|
||||
source/game/gameplay/item_tracker.cpp
|
||||
source/game/gameplay/room.cpp
|
||||
source/game/gameplay/room_loader.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/tilemap_renderer.cpp
|
||||
@@ -89,8 +90,8 @@ set(APP_SOURCES
|
||||
source/game/scenes/credits.cpp
|
||||
source/game/scenes/ending.cpp
|
||||
source/game/scenes/ending2.cpp
|
||||
source/game/scenes/game.cpp
|
||||
source/game/scenes/game_over.cpp
|
||||
source/game/scenes/game.cpp
|
||||
source/game/scenes/loading_screen.cpp
|
||||
source/game/scenes/logo.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 := \
|
||||
source/main.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/rendering/screen.cpp \
|
||||
source/core/rendering/surface.cpp \
|
||||
source/core/rendering/sprite/sprite.cpp \
|
||||
source/core/rendering/sprite/animated_sprite.cpp \
|
||||
source/core/rendering/sprite/moving_sprite.cpp \
|
||||
source/core/rendering/sprite/dissolve_sprite.cpp \
|
||||
source/core/rendering/text.cpp \
|
||||
source/core/input/input_types.cpp \
|
||||
source/core/input/input.cpp \
|
||||
source/core/input/mouse.cpp \
|
||||
source/core/locale/locale.cpp \
|
||||
source/core/rendering/gif.cpp \
|
||||
source/core/rendering/palette_manager.cpp \
|
||||
source/core/rendering/pixel_reveal.cpp \
|
||||
source/core/rendering/render_info.cpp \
|
||||
source/core/rendering/screen.cpp \
|
||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp \
|
||||
source/core/locale/locale.cpp \
|
||||
source/core/resources/resource_list.cpp \
|
||||
source/core/rendering/sprite/animated_sprite.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_helper.cpp \
|
||||
source/core/resources/resource_list.cpp \
|
||||
source/core/resources/resource_loader.cpp \
|
||||
source/core/resources/resource_pack.cpp \
|
||||
source/core/system/director.cpp \
|
||||
source/core/system/debug.cpp \
|
||||
source/core/system/director.cpp \
|
||||
source/core/system/global_events.cpp \
|
||||
source/game/options.cpp \
|
||||
source/game/entities/player.cpp \
|
||||
source/game/entities/enemy.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/enemy_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/room_loader.cpp \
|
||||
source/game/gameplay/room_tracker.cpp \
|
||||
source/game/gameplay/room.cpp \
|
||||
source/game/gameplay/scoreboard.cpp \
|
||||
source/game/gameplay/stats.cpp \
|
||||
source/game/scenes/logo.cpp \
|
||||
source/game/scenes/loading_screen.cpp \
|
||||
source/game/scenes/title.cpp \
|
||||
source/game/scenes/game.cpp \
|
||||
source/game/scenes/game_over.cpp \
|
||||
source/game/gameplay/tilemap_renderer.cpp \
|
||||
source/game/options.cpp \
|
||||
source/game/scenes/credits.cpp \
|
||||
source/game/scenes/ending.cpp \
|
||||
source/game/scenes/ending2.cpp \
|
||||
source/game/scenes/credits.cpp \
|
||||
source/game/ui/notifier.cpp \
|
||||
source/game/scenes/game_over.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/utils/utils.cpp \
|
||||
source/utils/delta_timer.cpp
|
||||
source/game/ui/notifier.cpp \
|
||||
source/utils/delta_timer.cpp \
|
||||
source/utils/utils.cpp
|
||||
|
||||
# All sources combined
|
||||
ALL_SOURCES := $(APP_SOURCES)
|
||||
@@ -298,6 +299,8 @@ macos_release:
|
||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||
$(RMFILE) tmp.dmg
|
||||
$(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
|
||||
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
|
||||
@@ -32,7 +32,7 @@ assets:
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/zx-spectrum-adjusted.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/zxarne-5-2.pal
|
||||
path: ${PREFIX}/data/palette/zxarne-5.2.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/black-and-white.pal
|
||||
- type: PALETTE
|
||||
@@ -46,15 +46,33 @@ assets:
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/pico-8.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/sweetie-16.pal
|
||||
path: ${PREFIX}/data/palette/sweetie.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/island-joy-16.pal
|
||||
path: ${PREFIX}/data/palette/island-joy.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/lost-century.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/na16.pal
|
||||
path: ${PREFIX}/data/palette/na.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/steam-lords.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/winds-seed-pc98.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/psychic-fibre.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/shido-cyberneon.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/darkseed.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/antiquity.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/bubblegum.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/vanilla-milkshake.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/aged-terracotta.pal
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/h16da.pal
|
||||
|
||||
# LOCALE
|
||||
locale:
|
||||
|
||||
19
data/palette/aged-terracotta.pal
Normal file
19
data/palette/aged-terracotta.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
52 49 40
|
||||
80 73 57
|
||||
92 104 82
|
||||
108 116 76
|
||||
125 130 73
|
||||
163 158 85
|
||||
202 181 103
|
||||
119 63 53
|
||||
132 86 64
|
||||
160 119 84
|
||||
188 153 120
|
||||
214 193 157
|
||||
234 220 193
|
||||
247 240 221
|
||||
255 251 237
|
||||
255 255 255
|
||||
19
data/palette/antiquity.pal
Normal file
19
data/palette/antiquity.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
32 32 32
|
||||
45 33 30
|
||||
69 41 35
|
||||
109 61 41
|
||||
177 107 74
|
||||
232 159 110
|
||||
232 190 130
|
||||
93 117 87
|
||||
142 146 87
|
||||
112 123 136
|
||||
138 167 172
|
||||
229 93 77
|
||||
241 134 108
|
||||
210 103 48
|
||||
222 154 40
|
||||
232 216 165
|
||||
19
data/palette/bubblegum.pal
Normal file
19
data/palette/bubblegum.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
67 0 103
|
||||
22 23 26
|
||||
127 6 34
|
||||
0 40 89
|
||||
148 33 106
|
||||
35 73 117
|
||||
214 36 17
|
||||
255 38 116
|
||||
0 120 153
|
||||
255 132 38
|
||||
255 128 164
|
||||
104 174 212
|
||||
16 210 117
|
||||
255 209 0
|
||||
191 255 60
|
||||
250 253 255
|
||||
19
data/palette/darkseed.pal
Normal file
19
data/palette/darkseed.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
0 0 0
|
||||
0 20 24
|
||||
0 32 36
|
||||
0 44 56
|
||||
20 52 68
|
||||
68 52 68
|
||||
88 60 72
|
||||
108 76 68
|
||||
128 96 88
|
||||
108 112 108
|
||||
136 128 120
|
||||
164 148 132
|
||||
196 172 156
|
||||
216 176 168
|
||||
236 212 208
|
||||
252 252 252
|
||||
19
data/palette/h16da.pal
Normal file
19
data/palette/h16da.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
31 32 37
|
||||
46 51 77
|
||||
76 82 116
|
||||
124 143 178
|
||||
128 77 83
|
||||
191 125 133
|
||||
114 51 76
|
||||
192 165 169
|
||||
82 103 93
|
||||
108 154 154
|
||||
108 154 154
|
||||
226 217 228
|
||||
243 200 147
|
||||
229 152 125
|
||||
192 165 169
|
||||
226 217 228
|
||||
@@ -1,19 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
255 255 255
|
||||
109 247 193
|
||||
17 173 193
|
||||
96 108 129
|
||||
57 52 87
|
||||
30 136 117
|
||||
91 179 97
|
||||
161 229 90
|
||||
247 228 118
|
||||
249 146 82
|
||||
203 77 104
|
||||
106 55 113
|
||||
201 36 100
|
||||
203 77 104
|
||||
96 108 129
|
||||
30 136 117
|
||||
17 173 193
|
||||
155 156 130
|
||||
91 179 97
|
||||
249 146 82
|
||||
244 140 182
|
||||
247 182 158
|
||||
155 156 130
|
||||
161 229 90
|
||||
109 247 193
|
||||
247 228 118
|
||||
255 255 255
|
||||
@@ -1,19 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
209 177 135
|
||||
199 123 88
|
||||
174 93 64
|
||||
121 68 74
|
||||
75 61 68
|
||||
186 145 88
|
||||
146 116 65
|
||||
77 69 57
|
||||
119 116 59
|
||||
179 165 85
|
||||
210 201 165
|
||||
140 171 161
|
||||
75 114 110
|
||||
87 72 82
|
||||
121 68 74
|
||||
75 114 110
|
||||
174 93 64
|
||||
119 116 59
|
||||
146 116 65
|
||||
132 120 117
|
||||
199 123 88
|
||||
186 145 88
|
||||
171 155 142
|
||||
179 165 85
|
||||
140 171 161
|
||||
209 177 135
|
||||
210 201 165
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
140 143 174
|
||||
88 69 99
|
||||
62 33 55
|
||||
154 99 72
|
||||
215 155 125
|
||||
245 237 186
|
||||
192 199 65
|
||||
100 125 52
|
||||
228 148 58
|
||||
157 48 59
|
||||
210 100 113
|
||||
112 55 127
|
||||
126 196 193
|
||||
52 133 157
|
||||
23 67 75
|
||||
31 14 28
|
||||
62 33 55
|
||||
23 67 75
|
||||
157 48 59
|
||||
112 55 127
|
||||
88 69 99
|
||||
154 99 72
|
||||
100 125 52
|
||||
52 133 157
|
||||
210 100 113
|
||||
140 143 174
|
||||
228 148 58
|
||||
215 155 125
|
||||
126 196 193
|
||||
192 199 65
|
||||
245 237 186
|
||||
@@ -3,17 +3,17 @@ JASC-PAL
|
||||
16
|
||||
0 0 0
|
||||
29 43 83
|
||||
126 37 83
|
||||
0 135 81
|
||||
171 82 54
|
||||
95 87 79
|
||||
194 195 199
|
||||
255 241 232
|
||||
255 0 77
|
||||
255 163 0
|
||||
255 236 39
|
||||
0 228 54
|
||||
41 173 255
|
||||
131 118 156
|
||||
255 0 77
|
||||
171 82 54
|
||||
255 119 168
|
||||
194 195 199
|
||||
0 228 54
|
||||
0 135 81
|
||||
95 87 79
|
||||
255 241 232
|
||||
255 236 39
|
||||
255 163 0
|
||||
255 204 170
|
||||
126 37 83
|
||||
|
||||
19
data/palette/psychic-fibre.pal
Normal file
19
data/palette/psychic-fibre.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
10 8 25
|
||||
49 50 67
|
||||
69 59 70
|
||||
87 84 117
|
||||
130 105 128
|
||||
164 111 114
|
||||
185 115 113
|
||||
205 95 105
|
||||
229 76 81
|
||||
201 55 73
|
||||
144 161 168
|
||||
140 147 137
|
||||
195 150 145
|
||||
236 151 134
|
||||
235 171 145
|
||||
219 182 167
|
||||
@@ -2,18 +2,18 @@ JASC-PAL
|
||||
0100
|
||||
16
|
||||
17 17 37
|
||||
82 75 109
|
||||
176 201 196
|
||||
255 252 241
|
||||
36 34 114
|
||||
52 112 190
|
||||
159 32 98
|
||||
255 94 57
|
||||
150 58 191
|
||||
255 94 57
|
||||
159 32 98
|
||||
255 105 246
|
||||
176 201 196
|
||||
44 126 75
|
||||
160 195 95
|
||||
67 152 196
|
||||
147 255 229
|
||||
210 133 55
|
||||
254 245 107
|
||||
255 252 241
|
||||
82 75 109
|
||||
|
||||
@@ -2,18 +2,18 @@ JASC-PAL
|
||||
0100
|
||||
16
|
||||
15 11 56
|
||||
97 106 130
|
||||
173 180 183
|
||||
249 255 236
|
||||
122 87 22
|
||||
40 19 160
|
||||
74 107 255
|
||||
160 35 17
|
||||
237 23 95
|
||||
115 16 147
|
||||
238 20 181
|
||||
115 16 147
|
||||
39 139 97
|
||||
157 255 38
|
||||
27 105 167
|
||||
71 233 223
|
||||
122 87 22
|
||||
27 105 167
|
||||
247 229 77
|
||||
173 180 183
|
||||
249 255 236
|
||||
97 106 130
|
||||
|
||||
19
data/palette/shido-cyberneon.pal
Normal file
19
data/palette/shido-cyberneon.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
0 3 60
|
||||
0 82 96
|
||||
0 56 132
|
||||
42 46 121
|
||||
255 0 78
|
||||
177 5 133
|
||||
172 41 206
|
||||
255 92 255
|
||||
10 255 82
|
||||
0 157 74
|
||||
0 247 255
|
||||
0 138 197
|
||||
78 110 168
|
||||
255 255 255
|
||||
173 212 250
|
||||
96 0 136
|
||||
@@ -1,19 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
33 59 37
|
||||
58 96 74
|
||||
79 119 84
|
||||
161 159 124
|
||||
119 116 79
|
||||
119 92 79
|
||||
96 59 58
|
||||
59 33 55
|
||||
23 14 25
|
||||
47 33 59
|
||||
59 33 55
|
||||
33 59 37
|
||||
67 58 96
|
||||
96 59 58
|
||||
79 82 119
|
||||
58 96 74
|
||||
119 92 79
|
||||
79 119 84
|
||||
101 115 140
|
||||
119 116 79
|
||||
124 148 161
|
||||
161 159 124
|
||||
160 185 186
|
||||
192 209 204
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
26 28 44
|
||||
93 39 93
|
||||
177 62 83
|
||||
239 125 87
|
||||
255 205 117
|
||||
167 240 112
|
||||
56 183 100
|
||||
37 113 121
|
||||
41 54 111
|
||||
59 93 201
|
||||
65 166 246
|
||||
115 239 247
|
||||
244 244 244
|
||||
148 176 194
|
||||
86 108 134
|
||||
51 60 87
|
||||
@@ -2,18 +2,18 @@ JASC-PAL
|
||||
0100
|
||||
16
|
||||
26 28 44
|
||||
41 54 111
|
||||
51 60 87
|
||||
86 108 134
|
||||
59 93 201
|
||||
37 113 121
|
||||
93 39 93
|
||||
41 54 111
|
||||
177 62 83
|
||||
239 125 87
|
||||
93 39 93
|
||||
148 176 194
|
||||
56 183 100
|
||||
167 240 112
|
||||
37 113 121
|
||||
65 166 246
|
||||
115 239 247
|
||||
239 125 87
|
||||
255 205 117
|
||||
148 176 194
|
||||
167 240 112
|
||||
244 244 244
|
||||
86 108 134
|
||||
19
data/palette/vanilla-milkshake.pal
Normal file
19
data/palette/vanilla-milkshake.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
40 40 46
|
||||
108 86 113
|
||||
217 200 191
|
||||
249 130 132
|
||||
176 169 228
|
||||
172 204 228
|
||||
179 227 218
|
||||
254 170 228
|
||||
135 168 137
|
||||
176 235 147
|
||||
233 245 157
|
||||
255 230 198
|
||||
222 163 139
|
||||
255 195 132
|
||||
255 247 160
|
||||
255 247 228
|
||||
19
data/palette/winds-seed-pc98.pal
Normal file
19
data/palette/winds-seed-pc98.pal
Normal file
@@ -0,0 +1,19 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
16
|
||||
0 0 0
|
||||
50 1 50
|
||||
50 50 171
|
||||
35 103 239
|
||||
254 1 69
|
||||
119 70 2
|
||||
118 50 118
|
||||
239 152 152
|
||||
1 137 84
|
||||
1 186 152
|
||||
152 186 220
|
||||
253 253 253
|
||||
254 239 69
|
||||
186 118 84
|
||||
254 205 205
|
||||
84 1 103
|
||||
@@ -119,12 +119,12 @@ namespace GlobalInputs {
|
||||
|
||||
void handleNextPalette() {
|
||||
Screen::get()->nextPalette();
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); // NOLINT(readability-static-accessed-through-instance)
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
|
||||
void handlePreviousPalette() {
|
||||
Screen::get()->previousPalette();
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); // NOLINT(readability-static-accessed-through-instance)
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
|
||||
void handleToggleIntegerScale() {
|
||||
|
||||
@@ -52,7 +52,7 @@ Input::Input(std::string game_controller_db_path)
|
||||
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
|
||||
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
|
||||
{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
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
// [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
|
||||
void Locale::destroy() {
|
||||
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
|
||||
void Locale::loadFromFile(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
|
||||
if (file_path.empty()) {
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
// No se permite cambio de idioma en caliente.
|
||||
class Locale {
|
||||
public:
|
||||
static void init(const std::string& file_path); // Crea e inicializa el singleton
|
||||
static void destroy(); // Destruye el singleton
|
||||
static auto get() -> Locale*; // Devuelve 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 auto get() -> Locale*; // Devuelve el singleton
|
||||
|
||||
// Devuelve la traducción de la clave dada.
|
||||
// Si la clave no existe, devuelve la propia clave como fallback.
|
||||
@@ -19,6 +20,7 @@ class Locale {
|
||||
private:
|
||||
Locale() = default;
|
||||
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
|
||||
|
||||
static Locale* instance;
|
||||
|
||||
118
source/core/rendering/palette_manager.cpp
Normal file
118
source/core/rendering/palette_manager.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#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/defaults.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 lower_name = toLower(name + ".pal");
|
||||
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||
if (toLower(palettes_[i]) == lower_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(), ::tolower);
|
||||
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(), ::tolower);
|
||||
return name;
|
||||
}
|
||||
|
||||
auto PaletteManager::getPrettyName() const -> std::string {
|
||||
std::string name = getCurrentName();
|
||||
std::ranges::replace(name, '-', ' ');
|
||||
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 lower_name = toLower(name + ".pal");
|
||||
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||
if (toLower(getFileName(palettes_[i])) == lower_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Fallback: buscar la paleta por defecto
|
||||
const std::string default_name = toLower(std::string(Defaults::Video::PALETTE_NAME) + ".pal");
|
||||
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||
if (toLower(getFileName(palettes_[i])) == default_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PaletteManager::processPathList() {
|
||||
for (auto& palette : palettes_) {
|
||||
palette = getFileName(palette);
|
||||
}
|
||||
}
|
||||
38
source/core/rendering/palette_manager.hpp
Normal file
38
source/core/rendering/palette_manager.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#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 (minúsculas, sin .pal)
|
||||
[[nodiscard]] auto getCurrentName() const -> std::string; // Nombre de la paleta actual (minúsculas, sin .pal)
|
||||
[[nodiscard]] auto getPrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios
|
||||
|
||||
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
|
||||
Screen::Screen()
|
||||
: palettes_(Resource::List::get()->getListByType(Resource::List::Type::PALETTE)) {
|
||||
Screen::Screen() {
|
||||
// Arranca SDL VIDEO, crea la ventana y el renderizador
|
||||
initSDLVideo();
|
||||
if (Options::video.fullscreen) { SDL_HideCursor(); }
|
||||
@@ -55,7 +54,6 @@ Screen::Screen()
|
||||
|
||||
// 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};
|
||||
current_palette_ = findPalette(Options::video.palette);
|
||||
|
||||
// Define el color del borde para el modo de pantalla completa
|
||||
border_color_ = static_cast<Uint8>(PaletteColor::BLACK);
|
||||
@@ -76,19 +74,27 @@ Screen::Screen()
|
||||
}
|
||||
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
// Cargar la paleta una sola vez
|
||||
auto initial_palette = readPalFile(palettes_.at(current_palette_));
|
||||
|
||||
// Crea la surface donde se dibujan los graficos del juego
|
||||
// Crea las surfaces (PaletteManager aplicará la paleta inicial en su constructor)
|
||||
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));
|
||||
|
||||
// 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_->setPalette(initial_palette);
|
||||
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)
|
||||
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
|
||||
border_argb_color_ = border_pixel_buffer_[0];
|
||||
@@ -99,9 +105,6 @@ Screen::Screen()
|
||||
// Crea el objeto de texto para la pantalla de carga
|
||||
createText();
|
||||
|
||||
// Extrae el nombre de las paletas desde su ruta
|
||||
processPaletteList();
|
||||
|
||||
// Renderizar una vez la textura vacía para que tenga contenido válido
|
||||
// antes de inicializar los shaders (evita pantalla negra)
|
||||
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
|
||||
@@ -194,6 +197,21 @@ auto Screen::incWindowZoom() -> bool {
|
||||
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
|
||||
void Screen::setBorderColor(Uint8 color) {
|
||||
border_color_ = color;
|
||||
@@ -342,55 +360,10 @@ void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) {
|
||||
}
|
||||
|
||||
// Cambia la paleta
|
||||
void Screen::nextPalette() {
|
||||
++current_palette_;
|
||||
if (current_palette_ == static_cast<int>(palettes_.size())) {
|
||||
current_palette_ = 0;
|
||||
}
|
||||
|
||||
setPalete();
|
||||
}
|
||||
void Screen::nextPalette() { palette_manager_->next(); }
|
||||
|
||||
// Cambia la paleta
|
||||
void Screen::previousPalette() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
void Screen::previousPalette() { palette_manager_->previous(); }
|
||||
|
||||
// Copia la surface a la textura
|
||||
void Screen::surfaceToTexture() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
@@ -470,17 +443,12 @@ void Screen::renderOverlays() {
|
||||
if (Console::get() != nullptr) { Console::get()->render(); } // Console (encima)
|
||||
}
|
||||
|
||||
// Localiza la paleta dentro del vector de paletas
|
||||
auto Screen::findPalette(const std::string& name) -> size_t { // NOLINT(readability-convert-member-functions-to-static)
|
||||
std::string upper_name = toUpper(name + ".pal");
|
||||
// Cambia a una paleta por nombre (case-insensitive); devuelve false si no existe
|
||||
auto Screen::setPaletteByName(const std::string& name) -> bool { return palette_manager_->setByName(name); }
|
||||
|
||||
for (size_t i = 0; i < palettes_.size(); ++i) {
|
||||
if (toUpper(getFileName(palettes_[i])) == upper_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return static_cast<size_t>(0);
|
||||
}
|
||||
// Devuelve los nombres de paletas disponibles (minúsculas, sin extensión .pal)
|
||||
auto Screen::getPaletteNames() const -> std::vector<std::string> { return palette_manager_->getNames(); }
|
||||
auto Screen::getPalettePrettyName() const -> std::string { return palette_manager_->getPrettyName(); }
|
||||
|
||||
// Limpia la game_surface_
|
||||
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
#include <utility> // Para std::pair
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType, ShaderBackend
|
||||
#include "utils/utils.hpp" // Para Color
|
||||
#include "core/rendering/palette_manager.hpp" // Para PaletteManager
|
||||
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType, ShaderBackend
|
||||
#include "utils/utils.hpp" // Para Color
|
||||
class Surface;
|
||||
class Text;
|
||||
|
||||
@@ -35,14 +36,15 @@ class Screen {
|
||||
void update(float delta_time); // Actualiza la lógica de la clase
|
||||
|
||||
// Video y ventana
|
||||
void setVideoMode(bool mode); // Establece el modo de video
|
||||
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
|
||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||
auto decWindowZoom() -> bool; // Reduce el tamaño de la ventana
|
||||
auto incWindowZoom() -> bool; // Aumenta el tamaño de la ventana
|
||||
void show(); // Muestra la ventana
|
||||
void hide(); // Oculta la ventana
|
||||
void setVideoMode(bool mode); // Establece el modo de video
|
||||
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
|
||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||
auto decWindowZoom() -> bool; // Reduce 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 hide(); // Oculta la ventana
|
||||
|
||||
// Borde
|
||||
void setBorderColor(Uint8 color); // Cambia el color del borde
|
||||
@@ -52,17 +54,19 @@ class Screen {
|
||||
void toggleBorder(); // Cambia entre borde visible y no visible
|
||||
|
||||
// Paletas y PostFX
|
||||
void nextPalette(); // Cambia a la siguiente paleta
|
||||
void previousPalette(); // Cambia a la paleta anterior
|
||||
void setPalete(); // Establece la paleta actual
|
||||
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
||||
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
||||
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
||||
void reloadCrtPi(); // Recarga el shader CrtPi del preset actual sin toggle
|
||||
void setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
|
||||
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
||||
void setActiveShader(Rendering::ShaderType type); // Cambia el shader de post-procesado activo
|
||||
void nextShader(); // Cicla al siguiente shader disponible (para futura UI)
|
||||
void nextPalette(); // Cambia a la siguiente paleta
|
||||
void previousPalette(); // Cambia a la paleta anterior
|
||||
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 (minúsculas, sin .pal)
|
||||
[[nodiscard]] auto getPalettePrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios
|
||||
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
|
||||
void toggleSupersampling(); // Activa/desactiva el supersampling global
|
||||
void reloadPostFX(); // Recarga el shader del preset actual sin toggle
|
||||
void reloadCrtPi(); // Recarga el shader CrtPi del preset actual sin toggle
|
||||
void setLinearUpscale(bool linear); // Upscale NEAREST (false) o LINEAR (true) en el paso SS
|
||||
void setDownscaleAlgo(int algo); // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
||||
void setActiveShader(Rendering::ShaderType type); // Cambia el shader de post-procesado activo
|
||||
void nextShader(); // Cicla al siguiente shader disponible (para futura UI)
|
||||
|
||||
// Surfaces y notificaciones
|
||||
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces
|
||||
@@ -79,6 +83,7 @@ class Screen {
|
||||
[[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
|
||||
[[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; }
|
||||
[[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; }
|
||||
[[nodiscard]] auto getMaxZoom() const -> int;
|
||||
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int>;
|
||||
|
||||
private:
|
||||
@@ -116,20 +121,18 @@ class Screen {
|
||||
static Screen* screen;
|
||||
|
||||
// Métodos privados
|
||||
void renderNotifications() const; // Dibuja las notificaciones
|
||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||
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 textureToRenderer(); // Copia la textura al renderizador
|
||||
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 applyCurrentPostFXPreset(); // Aplica los parámetros del preset PostFX actual al backend
|
||||
void applyCurrentCrtPiPreset(); // Aplica los parámetros del preset CrtPi actual al backend
|
||||
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
|
||||
void createText(); // Crea el objeto de texto
|
||||
void renderNotifications() const; // Dibuja las notificaciones
|
||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||
void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador
|
||||
void surfaceToTexture(); // Copia la surface a la textura
|
||||
void textureToRenderer(); // Copia la textura al renderizador
|
||||
void renderOverlays(); // Renderiza todos los overlays
|
||||
void initShaders(); // Inicializa los shaders
|
||||
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 getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
|
||||
void createText(); // Crea el objeto de texto
|
||||
|
||||
// Constructor y destructor
|
||||
Screen();
|
||||
@@ -163,9 +166,8 @@ class Screen {
|
||||
SDL_FRect game_surface_dstrect_; // Coordenadas donde se dibuja la textura del juego
|
||||
|
||||
// Paletas y colores
|
||||
Uint8 border_color_{0}; // Color del borde
|
||||
std::vector<std::string> palettes_; // Listado de ficheros de paleta disponibles
|
||||
Uint8 current_palette_{0}; // Índice para el vector de paletas
|
||||
Uint8 border_color_{0}; // Color del borde
|
||||
std::unique_ptr<PaletteManager> palette_manager_; // Gestor de paletas de color
|
||||
|
||||
// Estado y configuración
|
||||
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
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
static const uint8_t kupscale_frag_spv[] = {
|
||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
|
||||
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
|
||||
0x00, 0x00, 0x00, 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
|
||||
};
|
||||
0x03,
|
||||
0x02,
|
||||
0x23,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x14,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x4c,
|
||||
0x53,
|
||||
0x4c,
|
||||
0x2e,
|
||||
0x73,
|
||||
0x74,
|
||||
0x64,
|
||||
0x2e,
|
||||
0x34,
|
||||
0x35,
|
||||
0x30,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
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;
|
||||
|
||||
@@ -175,12 +175,12 @@ void Surface::fillRect(const SDL_FRect* rect, Uint8 color) { // NOLINT(readabil
|
||||
float x_end = std::min(rect->x + rect->w, surface_data_->width);
|
||||
float y_end = std::min(rect->y + rect->h, surface_data_->height);
|
||||
|
||||
// Recorrer cada píxel dentro del rectángulo directamente
|
||||
for (int y = y_start; y < y_end; ++y) {
|
||||
for (int x = x_start; x < x_end; ++x) {
|
||||
const int INDEX = x + (y * surface_data_->width);
|
||||
surface_data_->data.get()[INDEX] = color;
|
||||
}
|
||||
// Rellenar fila a fila con memset (memoria contigua por fila)
|
||||
Uint8* data_ptr = surface_data_->data.get();
|
||||
const int surf_width = static_cast<int>(surface_data_->width);
|
||||
const int row_width = static_cast<int>(x_end) - static_cast<int>(x_start);
|
||||
for (int y = static_cast<int>(y_start); y < static_cast<int>(y_end); ++y) {
|
||||
std::memset(data_ptr + (y * surf_width) + static_cast<int>(x_start), color, row_width);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,16 +192,12 @@ void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) { // NOLINT(re
|
||||
float x_end = std::min(rect->x + rect->w, surface_data_->width);
|
||||
float y_end = std::min(rect->y + rect->h, surface_data_->height);
|
||||
|
||||
// Dibujar bordes horizontales
|
||||
for (int x = x_start; x < x_end; ++x) {
|
||||
// Borde superior
|
||||
const int TOP_INDEX = x + (y_start * surface_data_->width);
|
||||
surface_data_->data.get()[TOP_INDEX] = color;
|
||||
|
||||
// Borde inferior
|
||||
const int BOTTOM_INDEX = x + ((y_end - 1) * surface_data_->width);
|
||||
surface_data_->data.get()[BOTTOM_INDEX] = color;
|
||||
}
|
||||
// Dibujar bordes horizontales con memset (líneas contiguas en memoria)
|
||||
Uint8* data_ptr = surface_data_->data.get();
|
||||
const int surf_width = static_cast<int>(surface_data_->width);
|
||||
const int row_width = static_cast<int>(x_end) - static_cast<int>(x_start);
|
||||
std::memset(data_ptr + (static_cast<int>(y_start) * surf_width) + static_cast<int>(x_start), color, row_width);
|
||||
std::memset(data_ptr + ((static_cast<int>(y_end) - 1) * surf_width) + static_cast<int>(x_start), color, row_width);
|
||||
|
||||
// Dibujar bordes verticales
|
||||
for (int y = y_start; y < y_end; ++y) {
|
||||
@@ -261,6 +257,8 @@ void Surface::render(float dx, float dy, float sx, float sy, float w, float h) {
|
||||
w = std::min(w, surface_data->width - dx);
|
||||
h = std::min(h, surface_data->height - dy);
|
||||
|
||||
const Uint8* src_ptr = surface_data_->data.get();
|
||||
Uint8* dst_ptr = surface_data->data.get();
|
||||
for (int iy = 0; iy < h; ++iy) {
|
||||
for (int ix = 0; ix < w; ++ix) {
|
||||
// Verificar que las coordenadas de destino están dentro de los límites
|
||||
@@ -269,9 +267,9 @@ void Surface::render(float dx, float dy, float sx, float sy, float w, float h) {
|
||||
int src_x = sx + ix;
|
||||
int src_y = sy + iy;
|
||||
|
||||
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
|
||||
Uint8 color = src_ptr[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
|
||||
if (color != static_cast<Uint8>(transparent_color_)) {
|
||||
surface_data->data.get()[static_cast<size_t>(dest_x + (dest_y * surface_data->width))] = sub_palette_[color];
|
||||
dst_ptr[static_cast<size_t>(dest_x + (dest_y * surface_data->width))] = sub_palette_[color];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,6 +297,8 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { //
|
||||
h = std::min(h, surface_data_dest->height - y);
|
||||
|
||||
// Renderiza píxel por píxel aplicando el flip si es necesario
|
||||
const Uint8* src_ptr = surface_data_->data.get();
|
||||
Uint8* dst_ptr = surface_data_dest->data.get();
|
||||
for (int iy = 0; iy < h; ++iy) {
|
||||
for (int ix = 0; ix < w; ++ix) {
|
||||
// Coordenadas de origen
|
||||
@@ -312,9 +312,9 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { //
|
||||
// Verificar que las coordenadas de destino están dentro de los límites
|
||||
if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) {
|
||||
// Copia el píxel si no es transparente
|
||||
Uint8 color = surface_data_->data.get()[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
|
||||
Uint8 color = src_ptr[static_cast<size_t>(src_x + (src_y * surface_data_->width))];
|
||||
if (color != static_cast<Uint8>(transparent_color_)) {
|
||||
surface_data_dest->data[dest_x + (dest_y * surface_data_dest->width)] = sub_palette_[color];
|
||||
dst_ptr[static_cast<size_t>(dest_x + (dest_y * surface_data_dest->width))] = sub_palette_[color];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -567,13 +567,16 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) { //
|
||||
// Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware)
|
||||
int row_stride = pitch / sizeof(Uint32);
|
||||
|
||||
for (int y = 0; y < surface_data_->height; ++y) {
|
||||
for (int x = 0; x < surface_data_->width; ++x) {
|
||||
// Calcular la posición correcta en la textura teniendo en cuenta el stride
|
||||
int texture_index = (y * row_stride) + x;
|
||||
int surface_index = (y * surface_data_->width) + x;
|
||||
|
||||
pixels[texture_index] = palette_[surface_data_->data.get()[surface_index]];
|
||||
// Cachear punteros fuera del bucle para permitir autovectorización SIMD
|
||||
const Uint8* src = surface_data_->data.get();
|
||||
const Uint32* pal = palette_.data();
|
||||
const int width = surface_data_->width;
|
||||
const int height = surface_data_->height;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
const Uint8* src_row = src + (y * width);
|
||||
Uint32* dst_row = pixels + (y * row_stride);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_row[x] = pal[src_row[x]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,12 +616,16 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FR
|
||||
|
||||
int row_stride = pitch / sizeof(Uint32);
|
||||
|
||||
for (int y = 0; y < surface_data_->height; ++y) {
|
||||
for (int x = 0; x < surface_data_->width; ++x) {
|
||||
int texture_index = (y * row_stride) + x;
|
||||
int surface_index = (y * surface_data_->width) + x;
|
||||
|
||||
pixels[texture_index] = palette_[surface_data_->data.get()[surface_index]];
|
||||
// Cachear punteros fuera del bucle para permitir autovectorización SIMD
|
||||
const Uint8* src = surface_data_->data.get();
|
||||
const Uint32* pal = palette_.data();
|
||||
const int width = surface_data_->width;
|
||||
const int height = surface_data_->height;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
const Uint8* src_row = src + (y * width);
|
||||
Uint32* dst_row = pixels + (y * row_stride);
|
||||
for (int x = 0; x < width; ++x) {
|
||||
dst_row[x] = pal[src_row[x]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,8 +189,13 @@ Director::Director() {
|
||||
|
||||
// Inicializa el sistema de localización (antes de Cheevos que usa textos traducidos)
|
||||
#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
|
||||
Locale::init(Resource::List::get()->get(Options::language + ".yaml")); // NOLINT(readability-static-accessed-through-instance)
|
||||
#endif
|
||||
|
||||
@@ -880,6 +880,10 @@ auto Player::handleLandingFromAir(float displacement, const SDL_FRect& projectio
|
||||
|
||||
// No hay colisión
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ class Player {
|
||||
[[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 setIgnoreInput(bool value) { ignore_input_ = value; } // Ignora inputs del jugador (física sigue activa)
|
||||
[[nodiscard]] auto getIgnoreInput() const -> bool { return ignore_input_; }
|
||||
|
||||
#ifdef _DEBUG
|
||||
// --- Funciones de debug ---
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/input/input_types.hpp" // Para BUTTON_TO_STRING, STRING_TO_BUTTON
|
||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||
#include "game/defaults.hpp" // Para GameDefaults::VERSION
|
||||
#include "utils/utils.hpp" // Para toLower
|
||||
|
||||
namespace Options {
|
||||
|
||||
@@ -177,24 +178,6 @@ namespace Options {
|
||||
{"LEFT_STICK_LEFT", 200},
|
||||
{"LEFT_STICK_RIGHT", 201}};
|
||||
|
||||
// Lista de paletas válidas
|
||||
const std::vector<std::string> VALID_PALETTES = {
|
||||
"black-and-white",
|
||||
"green-phosphor",
|
||||
"island-joy-16",
|
||||
"lost-century",
|
||||
"na16",
|
||||
"orange-screen",
|
||||
"pico-8",
|
||||
"ruzx-spectrum",
|
||||
"ruzx-spectrum-revision-2",
|
||||
"steam-lords",
|
||||
"sweetie-16",
|
||||
"sweetie-16_bona",
|
||||
"zx-spectrum",
|
||||
"zx-spectrum-adjusted",
|
||||
"zxarne-5-2"};
|
||||
|
||||
// Funciones helper de conversión
|
||||
auto filterToString(Screen::Filter filter) -> std::string {
|
||||
auto it = FILTER_TO_STRING.find(filter);
|
||||
@@ -257,12 +240,6 @@ namespace Options {
|
||||
}
|
||||
}
|
||||
|
||||
auto isValidPalette(const std::string& palette) -> bool {
|
||||
std::string lower_palette = palette;
|
||||
std::ranges::transform(lower_palette, lower_palette.begin(), ::tolower);
|
||||
return std::ranges::any_of(VALID_PALETTES, [&lower_palette](const auto& valid) { return valid == lower_palette; });
|
||||
}
|
||||
|
||||
// --- Funciones helper para loadFromFile() ---
|
||||
|
||||
// Carga configuración de ventana desde YAML
|
||||
@@ -309,12 +286,12 @@ namespace Options {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: carga el campo palette con validación extra
|
||||
// Helper: carga el campo palette; PaletteManager hará el fallback si el nombre no existe
|
||||
void loadPaletteFromYaml(const fkyaml::node& vid) {
|
||||
if (!vid.contains("palette")) { return; }
|
||||
try {
|
||||
auto palette_str = vid["palette"].get_value<std::string>();
|
||||
video.palette = isValidPalette(palette_str) ? palette_str : Defaults::Video::PALETTE_NAME;
|
||||
video.palette = toLower(palette_str);
|
||||
} catch (...) {
|
||||
video.palette = Defaults::Video::PALETTE_NAME;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ Game::Game(Mode mode)
|
||||
|
||||
GameControl::refresh_player_color = [this]() -> void { player_->setColor(); };
|
||||
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 {
|
||||
Options::game.player_skin = skin_num;
|
||||
player_->setSkin(skin_num);
|
||||
@@ -882,9 +883,11 @@ void Game::checkEndGameCheevos() { // NOLINT(readability-convert-member-functio
|
||||
|
||||
// Inicializa al jugador
|
||||
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";
|
||||
const Player::Data PLAYER{.spawn_data = spawn_point, .animations_path = player_animations, .room = std::move(room)};
|
||||
player_ = std::make_shared<Player>(PLAYER);
|
||||
if (IGNORE_INPUT) { player_->setIgnoreInput(true); }
|
||||
}
|
||||
|
||||
// Crea la textura para poner el nombre de la habitación
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cctype> // Para toupper
|
||||
#include <functional> // Para function
|
||||
#include <iostream> // Para std::cout
|
||||
#include <sstream> // Para std::istringstream
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
@@ -27,9 +28,16 @@
|
||||
|
||||
// ── 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 {
|
||||
std::string_view keyword;
|
||||
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
|
||||
@@ -69,7 +77,7 @@ static auto parseTokens(const std::string& input) -> std::vector<std::string> {
|
||||
(toggle_fn); \
|
||||
return label " OFF"; \
|
||||
} \
|
||||
return "Usage: " label " [ON|OFF]"; \
|
||||
return "usage: " label " [on|off]"; \
|
||||
}
|
||||
|
||||
// Texto de ayuda común para HELP y ?
|
||||
@@ -128,6 +136,13 @@ static void printHelp() {
|
||||
#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
|
||||
static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
// 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);
|
||||
return "Upscale: Linear";
|
||||
}
|
||||
return "Usage: SS UPSCALE [NEAREST|LINEAR]";
|
||||
return "usage: ss upscale [nearest|linear]";
|
||||
}
|
||||
if (!args.empty() && args[0] == "DOWNSCALE") {
|
||||
if (args.size() == 1) {
|
||||
@@ -164,7 +179,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (args[1] == "BILINEAR") { algo = 0; }
|
||||
if (args[1] == "LANCZOS2") { algo = 1; }
|
||||
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) {
|
||||
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();
|
||||
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)
|
||||
{.keyword = "SHADER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
if (args.empty()) {
|
||||
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 (Options::video.postfx) { return "Shader already ON"; }
|
||||
@@ -206,11 +226,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
}
|
||||
if (args[0] == "POSTFX") {
|
||||
Screen::get()->setActiveShader(Rendering::ShaderType::POSTFX);
|
||||
return "Shader: POSTFX";
|
||||
return "Shader: PostFX";
|
||||
}
|
||||
if (args[0] == "CRTPI") {
|
||||
Screen::get()->setActiveShader(Rendering::ShaderType::CRTPI);
|
||||
return "Shader: CRTPI";
|
||||
return "Shader: CrtPi";
|
||||
}
|
||||
if (args[0] == "NEXT") {
|
||||
// SHADER NEXT PRESET → cicla presets del shader activo
|
||||
@@ -232,16 +252,20 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
return "PostFX preset: " +
|
||||
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();
|
||||
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)
|
||||
{.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
|
||||
{.keyword = "FULLSCREEN", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -268,12 +292,13 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Screen::get()->toggleVideoMode();
|
||||
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)
|
||||
{.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 (!Screen::get()->incWindowZoom()) { return "Max zoom reached"; }
|
||||
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"; }
|
||||
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)
|
||||
{.keyword = "INTSCALE", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
const bool ON = args.empty() ? !Options::video.integer_scale
|
||||
: (args[0] == "ON");
|
||||
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) {
|
||||
return std::string("IntScale already ") + (ON ? "ON" : "OFF");
|
||||
@@ -298,10 +335,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Screen::get()->toggleIntegerScale();
|
||||
Screen::get()->setVideoMode(Options::video.fullscreen);
|
||||
return std::string("IntScale ") + (Options::video.integer_scale ? "ON" : "OFF");
|
||||
}},
|
||||
},
|
||||
.completions = {{"INTSCALE", {"ON", "OFF"}}}},
|
||||
|
||||
// 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)
|
||||
{.keyword = "DRIVER", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -357,20 +395,29 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Options::video.gpu_preferred_driver = driver_lower;
|
||||
Options::saveToFile();
|
||||
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 {
|
||||
if (args.empty()) { return "Usage: PALETTE [NEXT|PREV]"; }
|
||||
const auto palName = []() -> std::string {
|
||||
return Screen::get()->getPalettePrettyName();
|
||||
};
|
||||
if (args.empty()) { return "usage: palette [next|prev|<name>]"; }
|
||||
if (args[0] == "NEXT") {
|
||||
Screen::get()->nextPalette();
|
||||
return "Palette: " + Options::video.palette;
|
||||
return "Palette: " + palName();
|
||||
}
|
||||
if (args[0] == "PREV") {
|
||||
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
|
||||
@@ -389,15 +436,16 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
GameControl::toggle_debug_mode();
|
||||
return "Debug mode OFF";
|
||||
}
|
||||
if (!args.empty()) { return "Usage: DEBUG [ON|OFF]"; }
|
||||
if (!args.empty()) { return "usage: debug [on|off]"; }
|
||||
GameControl::toggle_debug_mode();
|
||||
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
|
||||
{.keyword = "ROOM", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
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;
|
||||
if (args[0] == "NEXT" || args[0] == "PREV") {
|
||||
if (!GameControl::get_current_room) { return "Game not initialized"; }
|
||||
@@ -409,7 +457,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
} else {
|
||||
try {
|
||||
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"; }
|
||||
char buf[16];
|
||||
@@ -418,7 +466,8 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
return std::string("Room: ") + buf;
|
||||
}
|
||||
return std::string("Room not found: ") + buf;
|
||||
}},
|
||||
},
|
||||
.completions = {{"ROOM", {"NEXT", "PREV"}}}},
|
||||
|
||||
#endif
|
||||
|
||||
@@ -433,31 +482,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)
|
||||
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
|
||||
if (args.empty() || args[0] != "INFO") { return "Usage: SHOW [INFO]"; }
|
||||
if (args.empty() || args[0] != "INFO") { return "usage: show [info]"; }
|
||||
#endif
|
||||
if (RenderInfo::get()->isActive()) { return "Info overlay already ON"; }
|
||||
RenderInfo::get()->toggle();
|
||||
return "Info overlay ON";
|
||||
}},
|
||||
},
|
||||
.completions = {
|
||||
#ifdef _DEBUG
|
||||
{"SHOW", {"INFO", "NOTIFICATION", "CHEEVO"}},
|
||||
#else
|
||||
{"SHOW", {"INFO"}},
|
||||
#endif
|
||||
}},
|
||||
|
||||
// HIDE INFO — disponible en Release
|
||||
{.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"; }
|
||||
RenderInfo::get()->toggle();
|
||||
return "Info overlay OFF";
|
||||
}},
|
||||
},
|
||||
.completions = {{"HIDE", {"INFO"}}}},
|
||||
|
||||
// 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 {
|
||||
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]
|
||||
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;
|
||||
using State = Options::Cheat::State;
|
||||
const std::vector<std::string> REST(args.begin() + 2, args.end());
|
||||
@@ -470,7 +527,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (cheat == State::DISABLED) { return "Infinite lives already OFF"; }
|
||||
cheat = State::DISABLED;
|
||||
} else {
|
||||
return "Usage: CHEAT INFINITE LIVES [ON|OFF]";
|
||||
return "usage: cheat infinite lives [on|off]";
|
||||
}
|
||||
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
||||
return std::string("Infinite lives ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
||||
@@ -489,7 +546,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (cheat == State::DISABLED) { return "Invincibility already OFF"; }
|
||||
cheat = State::DISABLED;
|
||||
} else {
|
||||
return "Usage: CHEAT INVINCIBILITY [ON|OFF]";
|
||||
return "usage: cheat invincibility [on|off]";
|
||||
}
|
||||
if (GameControl::refresh_player_color) { GameControl::refresh_player_color(); }
|
||||
return std::string("Invincibility ") + (cheat == State::ENABLED ? "ON" : "OFF");
|
||||
@@ -497,7 +554,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
|
||||
// CHEAT OPEN THE JAIL
|
||||
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"; }
|
||||
Options::cheats.jail_is_open = Options::Cheat::State::ENABLED;
|
||||
return "Jail opened";
|
||||
@@ -505,14 +562,25 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
|
||||
// CHEAT CLOSE THE JAIL
|
||||
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"; }
|
||||
Options::cheats.jail_is_open = Options::Cheat::State::DISABLED;
|
||||
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 INITIAL [ROOM|POS] — Guarda habitación/posición actual como inicio (solo _DEBUG, GAME)
|
||||
@@ -525,7 +593,7 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
try {
|
||||
num = std::stoi(args[2]);
|
||||
} 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"; }
|
||||
GameControl::change_player_skin(num);
|
||||
return "Player skin: " + std::to_string(num);
|
||||
@@ -538,27 +606,29 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (args.size() >= 3) {
|
||||
if (args[2] == "GAME") {
|
||||
target = SceneManager::Scene::GAME;
|
||||
name = "GAME";
|
||||
name = "game";
|
||||
} else if (args[2] == "LOGO") {
|
||||
target = SceneManager::Scene::LOGO;
|
||||
name = "LOGO";
|
||||
name = "logo";
|
||||
} else if (args[2] == "LOADING") {
|
||||
target = SceneManager::Scene::LOADING_SCREEN;
|
||||
name = "LOADING";
|
||||
name = "loading";
|
||||
} else if (args[2] == "TITLE") {
|
||||
target = SceneManager::Scene::TITLE;
|
||||
name = "TITLE";
|
||||
name = "title";
|
||||
} else if (args[2] == "CREDITS") {
|
||||
target = SceneManager::Scene::CREDITS;
|
||||
name = "CREDITS";
|
||||
name = "credits";
|
||||
} else if (args[2] == "ENDING") {
|
||||
target = SceneManager::Scene::ENDING;
|
||||
name = "ENDING";
|
||||
name = "ending";
|
||||
} else if (args[2] == "ENDING2") {
|
||||
target = SceneManager::Scene::ENDING2;
|
||||
name = "ENDING2";
|
||||
name = "ending2";
|
||||
} 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);
|
||||
@@ -570,23 +640,23 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
|
||||
// SET ITEMS <0-200> — Fija el contador de items recogidos
|
||||
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;
|
||||
try {
|
||||
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 (!GameControl::set_items) { return "Game not initialized"; }
|
||||
GameControl::set_items(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_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"; }
|
||||
|
||||
std::string result;
|
||||
@@ -597,15 +667,26 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
}
|
||||
return result;
|
||||
#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
|
||||
}},
|
||||
|
||||
#ifdef _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 {
|
||||
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)
|
||||
if (args[0] == "RESTART") {
|
||||
@@ -633,14 +714,17 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (args[0] == "ENDING") { return GO_TO(SceneManager::Scene::ENDING, "Ending"); }
|
||||
if (args[0] == "ENDING2") { return GO_TO(SceneManager::Scene::ENDING2, "Ending 2"); }
|
||||
return "Unknown scene: " + args[0];
|
||||
}},
|
||||
},
|
||||
.completions = {{"SCENE", {"LOGO", "LOADING", "TITLE", "CREDITS", "GAME", "ENDING", "ENDING2", "RESTART"}}}},
|
||||
#endif
|
||||
|
||||
// RESTART — Reiniciar desde el principio (equivale a SCENE LOGO)
|
||||
{.keyword = "RESTART", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||
SceneManager::current = SceneManager::Scene::LOGO;
|
||||
Audio::get()->stopMusic();
|
||||
return "Restarting...";
|
||||
}},
|
||||
},
|
||||
.instant = true},
|
||||
|
||||
// KIOSK [ON|OFF PLEASE|PLEASE] — Modo kiosko
|
||||
{.keyword = "KIOSK", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -659,8 +743,9 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
if (!Options::video.fullscreen) { Screen::get()->toggleVideoMode(); }
|
||||
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)
|
||||
{.keyword = "AUDIO", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -688,10 +773,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Options::audio.volume = static_cast<float>(VAL) / 100.0F;
|
||||
Audio::get()->enable(Options::audio.enabled);
|
||||
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
|
||||
{.keyword = "MUSIC", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -723,10 +809,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Audio::get()->setMusicVolume(Options::audio.music.volume);
|
||||
}
|
||||
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
|
||||
{.keyword = "SOUND", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -758,10 +845,11 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
Audio::get()->setSoundVolume(Options::audio.sound.volume);
|
||||
}
|
||||
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)
|
||||
{.keyword = "EXIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
@@ -770,14 +858,16 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
}
|
||||
SceneManager::current = SceneManager::Scene::QUIT;
|
||||
return "Quitting...";
|
||||
}},
|
||||
},
|
||||
.instant = true},
|
||||
{.keyword = "QUIT", .execute = [](const std::vector<std::string>& args) -> std::string {
|
||||
if (Options::kiosk.enabled && (args.empty() || args[0] != "PLEASE")) {
|
||||
return "Not allowed in kiosk mode";
|
||||
}
|
||||
SceneManager::current = SceneManager::Scene::QUIT;
|
||||
return "Quitting...";
|
||||
}},
|
||||
},
|
||||
.instant = true},
|
||||
|
||||
// SIZE — Devuelve el tamaño actual de la ventana en píxeles
|
||||
{.keyword = "SIZE", .execute = [](const std::vector<std::string>&) -> std::string {
|
||||
@@ -787,17 +877,77 @@ static const std::vector<ConsoleCommand> COMMANDS = {
|
||||
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 {
|
||||
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 {
|
||||
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]
|
||||
@@ -822,12 +972,18 @@ auto Console::get() -> Console* {
|
||||
// Constructor
|
||||
Console::Console(const std::string& font_name)
|
||||
: text_(Resource::Cache::get()->getText(font_name)) {
|
||||
const int TEXT_SIZE = 6;
|
||||
const int PADDING_IN_V = TEXT_SIZE / 2;
|
||||
height_ = static_cast<float>((TEXT_SIZE * 2) + (PADDING_IN_V * 2));
|
||||
msg_lines_ = {std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION)};
|
||||
height_ = calcTargetHeight(static_cast<int>(msg_lines_.size()));
|
||||
target_height_ = 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();
|
||||
}
|
||||
@@ -849,9 +1005,9 @@ void Console::buildSurface() {
|
||||
// Redibuja el texto dinámico sobre la surface (fondo + borde + líneas)
|
||||
void Console::redrawText() {
|
||||
const float WIDTH = Options::game.width;
|
||||
const int TEXT_SIZE = 6;
|
||||
const int PADDING_IN_H = TEXT_SIZE;
|
||||
const int PADDING_IN_V = TEXT_SIZE / 2;
|
||||
constexpr int TEXT_SIZE = 6;
|
||||
constexpr int PADDING_IN_H = TEXT_SIZE;
|
||||
constexpr int PADDING_IN_V = TEXT_SIZE / 2;
|
||||
|
||||
auto previous_renderer = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(surface_);
|
||||
@@ -861,13 +1017,21 @@ void Console::redrawText() {
|
||||
SDL_FRect rect = {.x = 0, .y = 0, .w = WIDTH, .h = height_};
|
||||
surface_->drawRectBorder(&rect, BORDER_COLOR);
|
||||
|
||||
// Línea 1: mensajes
|
||||
text_->writeColored(PADDING_IN_H, PADDING_IN_V, msg_line_, MSG_COLOR);
|
||||
// Líneas de mensaje con efecto typewriter (solo muestra los primeros typewriter_chars_)
|
||||
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 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);
|
||||
}
|
||||
@@ -888,6 +1052,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
|
||||
redrawText();
|
||||
|
||||
@@ -905,6 +1107,9 @@ void Console::update(float delta_time) {
|
||||
if (y_ <= -height_) {
|
||||
y_ = -height_;
|
||||
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;
|
||||
}
|
||||
@@ -914,6 +1119,7 @@ void Console::update(float delta_time) {
|
||||
|
||||
SDL_FRect rect = {.x = 0, .y = y_, .w = Options::game.width, .h = height_};
|
||||
sprite_->setPosition(rect);
|
||||
sprite_->setClip({.x = 0.0F, .y = 0.0F, .w = Options::game.width, .h = height_});
|
||||
}
|
||||
|
||||
// Renderiza la consola
|
||||
@@ -928,21 +1134,36 @@ void Console::render() {
|
||||
void Console::toggle() {
|
||||
switch (status_) {
|
||||
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;
|
||||
input_line_.clear();
|
||||
cursor_timer_ = 0.0F;
|
||||
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());
|
||||
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); }
|
||||
break;
|
||||
case Status::ACTIVE:
|
||||
// Al cerrar: mantener el texto visible hasta que esté completamente oculta
|
||||
status_ = Status::VANISHING;
|
||||
msg_line_ = std::string(CONSOLE_NAME) + " " + std::string(CONSOLE_VERSION);
|
||||
target_height_ = height_; // No animar durante VANISHING
|
||||
history_index_ = -1;
|
||||
saved_input_.clear();
|
||||
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); }
|
||||
break;
|
||||
default:
|
||||
@@ -956,15 +1177,19 @@ void Console::handleEvent(const SDL_Event& event) {
|
||||
if (status_ != Status::ACTIVE) { return; }
|
||||
|
||||
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) {
|
||||
input_line_ += event.text.text;
|
||||
}
|
||||
tab_matches_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type == SDL_EVENT_KEY_DOWN) {
|
||||
switch (event.key.scancode) {
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
tab_matches_.clear();
|
||||
if (!input_line_.empty()) { input_line_.pop_back(); }
|
||||
break;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
@@ -973,6 +1198,7 @@ void Console::handleEvent(const SDL_Event& event) {
|
||||
break;
|
||||
case SDL_SCANCODE_UP:
|
||||
// Navegar hacia atrás en el historial
|
||||
tab_matches_.clear();
|
||||
if (history_index_ < static_cast<int>(history_.size()) - 1) {
|
||||
if (history_index_ == -1) { saved_input_ = input_line_; }
|
||||
++history_index_;
|
||||
@@ -981,6 +1207,7 @@ void Console::handleEvent(const SDL_Event& event) {
|
||||
break;
|
||||
case SDL_SCANCODE_DOWN:
|
||||
// Navegar hacia el presente en el historial
|
||||
tab_matches_.clear();
|
||||
if (history_index_ >= 0) {
|
||||
--history_index_;
|
||||
input_line_ = (history_index_ == -1)
|
||||
@@ -988,6 +1215,56 @@ void Console::handleEvent(const SDL_Event& event) {
|
||||
: history_[static_cast<size_t>(history_index_)];
|
||||
}
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@@ -1009,25 +1286,44 @@ void Console::processCommand() {
|
||||
if (!TOKENS.empty()) {
|
||||
const std::string& cmd = TOKENS[0];
|
||||
const std::vector<std::string> ARGS(TOKENS.begin() + 1, TOKENS.end());
|
||||
std::string result;
|
||||
bool found = false;
|
||||
bool instant = false;
|
||||
for (const auto& command : COMMANDS) {
|
||||
if (command.keyword == cmd) {
|
||||
msg_line_ = command.execute(ARGS);
|
||||
result = command.execute(ARGS);
|
||||
instant = command.instant;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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();
|
||||
history_index_ = -1;
|
||||
saved_input_.clear();
|
||||
tab_matches_.clear();
|
||||
cursor_timer_ = 0.0F;
|
||||
cursor_visible_ = true;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <deque> // Para deque (historial)
|
||||
#include <functional> // Para function
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <deque> // Para deque (historial)
|
||||
#include <functional> // Para function
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <unordered_map> // Para unordered_map (tab_completions_)
|
||||
#include <vector> // Para vector
|
||||
|
||||
class Surface;
|
||||
class Sprite;
|
||||
@@ -44,15 +46,16 @@ class Console {
|
||||
static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK
|
||||
static constexpr Uint8 BORDER_COLOR = 9; // PaletteColor::BRIGHT_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
|
||||
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_HISTORY_SIZE = 20;
|
||||
static constexpr float CURSOR_ON_TIME = 0.5F;
|
||||
static constexpr float CURSOR_OFF_TIME = 0.3F;
|
||||
static constexpr float TYPEWRITER_CHAR_DELAY = 0.01F; // segundos entre letra y letra
|
||||
|
||||
// [SINGLETON]
|
||||
static Console* console;
|
||||
@@ -62,9 +65,10 @@ class Console {
|
||||
~Console() = default;
|
||||
|
||||
// Métodos privados
|
||||
void buildSurface(); // Crea la Surface con el aspecto visual
|
||||
void redrawText(); // Redibuja el texto dinámico (msg + input + cursor)
|
||||
void processCommand(); // Procesa el comando introducido por el usuario
|
||||
void buildSurface(); // Crea la Surface con el aspecto visual
|
||||
void redrawText(); // Redibuja el texto dinámico (msg + input + cursor)
|
||||
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
|
||||
std::shared_ptr<Text> text_;
|
||||
@@ -77,13 +81,26 @@ class Console {
|
||||
float height_{0.0F}; // Altura del panel
|
||||
|
||||
// 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_;
|
||||
float cursor_timer_{0.0F};
|
||||
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)
|
||||
std::deque<std::string> history_;
|
||||
int history_index_{-1}; // -1 = en la entrada actual (presente)
|
||||
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 {
|
||||
constexpr const char* WINDOW_CAPTION = "© 2022 JailDoctor's Dilemma — 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
|
||||
|
||||
// Tamaño de bloque
|
||||
|
||||
125
tools/sort_palette/sort_palette.py
Normal file
125
tools/sort_palette/sort_palette.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import sys
|
||||
import math
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
# ------------------------------
|
||||
# Utilidades de color
|
||||
# ------------------------------
|
||||
|
||||
def luminosidad(rgb):
|
||||
r, g, b = rgb
|
||||
return 0.2126*r + 0.7152*g + 0.0722*b
|
||||
|
||||
def distancia_rgb(c1, c2):
|
||||
return math.sqrt(
|
||||
(c1[0] - c2[0])**2 +
|
||||
(c1[1] - c2[1])**2 +
|
||||
(c1[2] - c2[2])**2
|
||||
)
|
||||
|
||||
# Paleta ZX Spectrum
|
||||
PALETA_SPECTRUM = [
|
||||
(0, 0, 0),
|
||||
(0, 0, 0),
|
||||
(0, 0, 216),
|
||||
(0, 0, 255),
|
||||
(216, 0, 0),
|
||||
(255, 0, 0),
|
||||
(216, 0, 216),
|
||||
(255, 0, 255),
|
||||
(0, 216, 0),
|
||||
(0, 255, 0),
|
||||
(0, 216, 216),
|
||||
(0, 255, 255),
|
||||
(216, 216, 0),
|
||||
(255, 255, 0),
|
||||
(216, 216, 216),
|
||||
(255, 255, 255),
|
||||
]
|
||||
|
||||
# ------------------------------
|
||||
# Lectura / escritura JASC-PAL
|
||||
# ------------------------------
|
||||
|
||||
def leer_paleta_jasc(ruta):
|
||||
with open(ruta, "r") as f:
|
||||
lineas = [l.strip() for l in f.readlines()]
|
||||
|
||||
if lineas[0] != "JASC-PAL":
|
||||
raise ValueError("El fichero no es un JASC-PAL válido")
|
||||
|
||||
num_colores = int(lineas[2])
|
||||
colores = []
|
||||
|
||||
for i in range(num_colores):
|
||||
r, g, b = map(int, lineas[3 + i].split())
|
||||
colores.append((r, g, b))
|
||||
|
||||
return colores
|
||||
|
||||
def guardar_paleta_jasc(ruta, colores):
|
||||
with open(ruta, "w") as f:
|
||||
f.write("JASC-PAL\n")
|
||||
f.write("0100\n")
|
||||
f.write(f"{len(colores)}\n")
|
||||
for r, g, b in colores:
|
||||
f.write(f"{r} {g} {b}\n")
|
||||
|
||||
# ------------------------------
|
||||
# Métodos de ordenación
|
||||
# ------------------------------
|
||||
|
||||
def ordenar_por_luminosidad(colores):
|
||||
return sorted(colores, key=luminosidad)
|
||||
|
||||
def ordenar_por_similitud_spectrum(colores):
|
||||
colores_disponibles = colores.copy()
|
||||
resultado = []
|
||||
|
||||
for ref in PALETA_SPECTRUM:
|
||||
mejor = min(colores_disponibles, key=lambda c: distancia_rgb(c, ref))
|
||||
resultado.append(mejor)
|
||||
colores_disponibles.remove(mejor)
|
||||
|
||||
return resultado
|
||||
|
||||
# ------------------------------
|
||||
# Main
|
||||
# ------------------------------
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Uso: python ordenar_paleta.py entrada.pal salida.pal [luminosidad|spectrum]")
|
||||
return
|
||||
|
||||
entrada = sys.argv[1]
|
||||
salida = sys.argv[2]
|
||||
modo = sys.argv[3].lower()
|
||||
|
||||
colores = leer_paleta_jasc(entrada)
|
||||
|
||||
if modo == "luminosidad":
|
||||
colores_ordenados = ordenar_por_luminosidad(colores)
|
||||
elif modo == "spectrum":
|
||||
colores_ordenados = ordenar_por_similitud_spectrum(colores)
|
||||
else:
|
||||
print(f"Modo desconocido: {modo}")
|
||||
print("Modos disponibles: luminosidad, spectrum")
|
||||
return
|
||||
|
||||
# Si salida == entrada, sobrescribimos de forma segura
|
||||
if entrada == salida:
|
||||
# Guardamos primero en un temporal para evitar corrupción si algo falla
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
temp_path = tmp.name
|
||||
guardar_paleta_jasc(temp_path, colores_ordenados)
|
||||
os.replace(temp_path, entrada)
|
||||
print(f"Paleta sobrescrita ({modo}) en {entrada}")
|
||||
else:
|
||||
guardar_paleta_jasc(salida, colores_ordenados)
|
||||
print(f"Paleta ordenada ({modo}) guardada en {salida}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user