Compare commits

..

108 Commits

Author SHA1 Message Date
405f2248ec actualitzat changelog 2026-04-02 08:57:24 +02:00
93b1cd80b7 corregit make release per a macos 2026-04-02 08:55:36 +02:00
b53bf87730 arreglos en makefile 2026-04-02 08:35:36 +02:00
015a9cc4e1 - restaurades les paletes amb la ordenacio original
- afegida opció de reordenar les paletes automaticament per luminositat o paregut a la paleta d'spectrum
2026-04-02 07:59:30 +02:00
3bd13b72cd afegit .vscode a git 2026-04-01 23:06:24 +02:00
c94adf39af reestructurat els comandos de consola 2026-04-01 22:46:02 +02:00
950eeffb07 el indicador de trucos ja no es el color del jugador sino que es veu al marcador 2026-04-01 22:12:52 +02:00
b37b62ef1e - pots canviar el color del jugador desde la consola (persistent)
- cokmprova que el color no siga el mateix que el del fono (canvia a default)
- eliminades animacions sobrants del jugador
- canviada la logica del marcador pero a mostrar la animació de les vides del jugador
- posibilitat d'utilitzar skins d'enemics en el jugador
- canvi en calent de la skin en el marcador (abans soles en el constructir)
2026-04-01 21:31:25 +02:00
0c8aa5fe50 fix: entrar i eixir al mode debug manté l'estat previ del jugador 2026-04-01 20:17:31 +02:00
fe520dd341 - Es pot posar shader preset directament per nom desde la consola
- shader preset i palette ja autocompleten amb la llista de noms
2026-04-01 20:08:55 +02:00
ec9a9aff81 Console ara llig els comandos desde un fitxer extern 2026-04-01 19:31:09 +02:00
f9c1c4843d reestructurat el apartat de video de config.yaml 2026-04-01 18:57:32 +02:00
a804ad1368 - posibilitat de desactivar la aceleració hardware desde el fitxer de configuració de manera mes intuitiva
- si no hi ha aceleració ja no va cap tecla ni comando relacionat amb els shaders
2026-04-01 18:24:22 +02:00
c689507982 - afegides noves paletes
- ordenades les paletes que tenien els color mal ubicats
- eliminades responsabilitats a Options sobre les paletes
- "pretty" name per a les paletes (canvia els "-" per " ")
- nova tool/ en python per a reordenar paletes
2026-03-31 20:02:18 +02:00
417643018f optimitzacions en Surface 2026-03-31 14:56:39 +02:00
2ed7316948 afegit changelog.md 2026-03-31 07:56:09 +02:00
2228153d59 corregit makefile 2026-03-31 07:29:29 +02:00
7315032ff2 fix: si entraves a GAME amb la consola oberta, el jugador no tenia els inputs deshabilitats 2026-03-31 07:28:03 +02:00
3fc6795593 decrementada la responsabilitat d'Screen i afegit PaletteManager 2026-03-31 07:14:58 +02:00
16924cf503 screen: opcio d'establir el nivell de zoom directament
console: opcio d'establir el zoom directament
2026-03-30 23:42:30 +02:00
705a9fc7cd corregit el case en algunes respostes de console 2026-03-30 23:33:58 +02:00
b164c11ba7 console crea la tabla tab_completions automaticament 2026-03-30 23:27:38 +02:00
1817d00881 screen torna llista de paletes i permet canviar a una paleta pel nom
console 2.1 por canviar de paleta pel nom
2026-03-30 23:03:21 +02:00
8dcf473f31 autocompletar amb armadura de lagarto 2026-03-30 22:50:56 +02:00
8f191f02fa afegit autocompletar a la consola. si la de raimon en te, la meua no anava a ser menos 2026-03-30 22:26:53 +02:00
1077c13fd0 amb la consola oberta el jugador te els inputs deshabilitats. si moria amb la consola oberta, recuperava els inputs i es movia mentre escrius comandos en la consola 2026-03-30 20:48:21 +02:00
d5a4caa86e al fer restart, si estava sonant la musica del attract mode, la musica no parava al anar al logo 2026-03-30 20:22:14 +02:00
f3bad9f4ed afegida guarda per a que en debug el jugador no caiga infinitament si ix de la pantalla 2026-03-30 20:19:07 +02:00
32f22c99db mil arreglos cosmetics a console 2.0 2026-03-30 19:56:31 +02:00
cd14ae22c5 separacio de linies automatica en console 2026-03-30 19:35:43 +02:00
1fdc29e9d2 afegit typewriter effect a console 2026-03-30 19:16:57 +02:00
0a740a5be2 new: treballant en Console 2.0 2026-03-30 19:11:24 +02:00
9e1b2b8960 fix: make macos_release 2026-03-30 18:09:53 +02:00
ed3724193e fix: no es llegien els fitxers de Locale desde resources.pack 2026-03-30 17:59:59 +02:00
98f01a6dde corregit makefile per a windows 2026-03-30 09:41:28 +02:00
e13a28f69a - faltaven varios sources al makefile
- help de la consola organitzat
- modificats alguns comandos i comentaris de la consola
- habilitat render_info en release
2026-03-29 23:15:12 +02:00
301e0145fb la consola ja no pausa al jugador 2026-03-29 18:24:47 +02:00
3b233f0e12 eliminat Options::console 2026-03-29 18:14:51 +02:00
fd9be2066d reorganitzats els comandos de consola 2026-03-29 18:08:33 +02:00
7551115912 renderInfo amb animacio 2026-03-29 17:47:48 +02:00
b986778bb4 fix: dos logs de consola amb el format incorrecte 2026-03-29 17:34:42 +02:00
fd2e2f1014 eliminat el soport per a arguments 2026-03-29 17:32:06 +02:00
77b844065e canvi d'skin en la consola 2026-03-29 17:21:05 +02:00
2fe79de1d8 arreglos en Console i SS 2026-03-29 14:24:20 +02:00
d6ecadfd3a fix: vsync off no anava en Wayland 2026-03-29 14:13:02 +02:00
ad467847b9 corregit compile_spirv.cmake y la system_folder per a shaders 2026-03-29 13:45:29 +02:00
1bb2142d19 fix: corregida logica per obrir i entrar a la jail. ja no mira el nom de la habitacio sino el numero 2026-03-29 12:58:32 +02:00
f5a82229fe afegits cheats a la consola 2026-03-29 12:42:51 +02:00
145bab037f - eliminats accents en titols d'habitacions
- corregits fitxers .fnt (falta aseprite)
- corregint font_gent.py
- revisades algunes traduccions
2026-03-29 09:48:58 +02:00
754ad2de49 comandos i tecles per a manejar el nou disseny de shaders 2026-03-29 09:09:22 +02:00
a9b7c3f025 treballant en poder incloure diferents shaders 2026-03-29 08:23:42 +02:00
4db92d423c cos: afegida duracio final el logo 2026-03-29 07:45:28 +02:00
6717c1e307 fix: bug mileanri que deixava al jugador atascat en algunes rampes en certes condicions 2026-03-28 22:22:32 +01:00
9282d661aa millorada una mica la classe Debug en quant a mostrar info 2026-03-28 21:58:54 +01:00
a21f530dd4 corregit el caracter de coret que s'havia perdut 2026-03-28 20:53:33 +01:00
7483bf63c8 establir la posicio i habitacio inicial de debug desde la consola 2026-03-28 20:36:56 +01:00
268763f162 clase Debug ara carrega la posicio i habitacio inicial desde un fitxer 2026-03-28 20:15:44 +01:00
71c7b8e553 mes comandos per a Console 2026-03-28 14:14:33 +01:00
854a5f04b2 les tecles de funcio ja tornena a funcionar amb la consola oberta 2026-03-28 14:03:41 +01:00
ed21a47f92 augmentat el limit de caracters en la consola 2026-03-28 13:53:07 +01:00
637df23bc7 mes comandos en Console 2026-03-28 13:37:46 +01:00
ea421b4e17 fix: en TITLE, la consola no bloquejava la pulsacio del 1 al 4 i entrava a les opcions 2026-03-28 13:31:20 +01:00
3a5ff06dab - afegits comandos de GameControl per activar trucos o canviar de habitacio
- modificat el comando debug, ja no activa el overlay sino el mode debug
- nou alias show info i hide info
2026-03-28 13:27:33 +01:00
dfb0d2134f ajustat "al gust" el hud (consola, render info i notifier) 2026-03-28 13:04:03 +01:00
b459e2106f clang format 2026-03-28 12:50:00 +01:00
065f66d40e nova clase renderInfo
afegit control de offset a les notificacions
2026-03-28 12:49:38 +01:00
f15658a767 afegit soport per a configurar el audio (en config i en console) 2026-03-28 11:08:01 +01:00
21c8d1e8ca eliminat renderInfo de ResourceCache::init 2026-03-28 10:49:39 +01:00
a06eb8c8e9 fix: #include <iomanip> en Screen per a std::setprecision() 2026-03-28 10:08:08 +01:00
6b73a76d31 canvis en renderInfo
acabant de pulir el calcul actual del zoom en non integer scale
2026-03-28 02:01:51 +01:00
348a76090b permet escollir driver de gpu o no escollir-ne cap 2026-03-28 01:14:41 +01:00
02c1bf647e modificats defaults per a kiosk mode 2026-03-28 00:42:27 +01:00
a7f0a18e6d canvis de upscale i downscale en consola 2026-03-28 00:37:52 +01:00
8355c266a6 afegits comandos y restriccions en la consola per al modo kiosko 2026-03-28 00:26:29 +01:00
7bad27d686 canvi i reinici d'escene en la consola 2026-03-28 00:02:14 +01:00
d39622c7e2 afegits els comandos de les tecles de funció 2026-03-27 23:32:44 +01:00
4910d201f9 primer comando implementat en la consola 2026-03-27 23:19:47 +01:00
e85800c5ed ja es pot escriure en la consola 2026-03-27 22:54:47 +01:00
f25ee18329 treballant en la consola 2026-03-27 22:24:55 +01:00
3712f0c8d9 implementat lanzcos en el supersampling 2026-03-27 21:59:14 +01:00
c063488e8e optimitzat textureToRenderer() 2026-03-27 20:34:45 +01:00
deb0a8677f mostra el render device en info_debug 2026-03-27 10:18:41 +01:00
c5a7c9e70d optimitzant textureToRenderer() 2026-03-27 09:46:25 +01:00
92453a6104 llevats colorins del debug 2026-03-26 08:49:49 +01:00
7aff3e2109 afegit overlay de debug 2026-03-26 08:47:24 +01:00
8d213e7b3e afinant els shaders 2026-03-26 07:46:11 +01:00
c6d409c303 corregides scanlines per a treballar amb subpixels per proporció 2026-03-25 22:31:34 +01:00
6914f7df93 supersampling ara aplica al tamany de la finestra, no a la textura base 2026-03-25 22:00:10 +01:00
1dbfff2c17 fix: el supersampling es feia per cpu en lloc de per gpu 2026-03-25 21:33:06 +01:00
3493636954 correccions en les traduccions 2026-03-25 20:51:52 +01:00
8ff1073e4a corregides les scanlines per a paletes amb fondo blanc 2026-03-25 18:31:36 +01:00
6497e26202 reordenades i renombrades les classes sprite 2026-03-25 18:01:33 +01:00
e0e37204d7 eliminada la classe Texture 2026-03-25 17:54:23 +01:00
6595b28790 clang-format
clang-tidy (macos)
2026-03-23 07:26:21 +01:00
0ddb6c85e1 afegit un poc de chroma al preset crt 2026-03-22 23:00:39 +01:00
f84007902e afegit flicker a postfx 2026-03-22 22:38:18 +01:00
49ae2ae41f per defecte el joc eixirà ara en valencià 2026-03-22 22:07:12 +01:00
c701421a8f corregits offsets en smb2.fnt 2026-03-22 22:04:23 +01:00
1ecb427106 supersampling implementat 2026-03-22 21:55:18 +01:00
c87779cc09 imlementant supersampling 2026-03-22 21:24:20 +01:00
24594fa89a deixant postfx al gust 2026-03-22 20:54:02 +01:00
030779794e F12 toggle de showFPS en mode debug 2026-03-22 19:59:48 +01:00
495c23a3d2 fix: en la migracio de la marquesina a la nova versio de Text s'havia posat per error un kerning superior al que havia
opt: millores en la getió de la marquesina per optimitzar rendiment
2026-03-22 19:43:35 +01:00
911ee7a13e modificada la paleta d'aseprite.gif per consistencia 2026-03-22 19:22:56 +01:00
b876ccbb09 afegit fallback a la font_gen
afegida deteccio de caracters no definits a font_gen
2026-03-22 19:19:00 +01:00
94684e8758 ferramenta de text pot importar gifs
ferramenta de text accepta separació entre quadricules de lletres
2026-03-22 19:06:01 +01:00
0c116665bc treballant en el generador de .fnt 2026-03-22 18:40:51 +01:00
d0ed49d192 revisada i actualitzada la classe Text per a donar suport a utf-8 2026-03-22 12:47:32 +01:00
178 changed files with 27578 additions and 5521 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
.cache/ .cache/
.vscode/
*data/config/config.yaml *data/config/config.yaml
*stats.txt *stats.txt
*.DS_Store *.DS_Store

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json"
}

170
CHANGELOG.md Normal file
View File

@@ -0,0 +1,170 @@
# Changelog
All notable changes to JailDoctor's Dilemma are documented here.
---
## [v1.12] - 2026-04-02
### Novedades
- **Color del jugador configurable:** se puede cambiar desde la consola (persistente), con comprobación automática para evitar que coincida con el color de fondo
- **Skins de enemigos para el jugador:** posibilidad de usar skins de enemigos en el jugador, con cambio en caliente en el marcador
- **Indicador de trucos mejorado:** ya no usa el color del jugador, ahora se muestra en el marcador
- **Shader presets por nombre:** se puede establecer un shader preset directamente por nombre desde la consola, con autocompletado
- **Comandos externos en consola:** la consola lee los comandos desde un fichero externo
- **Gestión de paletas mejorada:**
- Nuevas paletas añadidas
- Restaurado el orden original de las paletas
- Opción de reordenar paletas automáticamente por luminosidad o parecido a la paleta de Spectrum
- Nombres "pretty" para las paletas (cambia los `-` por ` `)
- Eliminadas responsabilidades de `Options` sobre las paletas
- Nueva herramienta en Python para reordenar paletas
- **Aceleración hardware configurable:** posibilidad de desactivar la aceleración hardware desde el fichero de configuración; si no hay aceleración, se deshabilitan teclas y comandos de shaders
- **Autocompletado mejorado:** shader preset y palette autocompletan con la lista de nombres
- Reestructuración de comandos de consola
- Reestructuración del apartado de vídeo en `config.yaml`
- Optimizaciones en `Surface`
### Correcciones
- Fix: entrar y salir del modo debug mantiene el estado previo del jugador
- Corregido Makefile: migración completa a cmake, detección automática de SO para release
---
## [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/).*

View File

@@ -34,31 +34,32 @@ set(APP_SOURCES
# Core - Input # Core - Input
source/core/input/global_inputs.cpp source/core/input/global_inputs.cpp
source/core/input/input.cpp
source/core/input/input_types.cpp source/core/input/input_types.cpp
source/core/input/input.cpp
source/core/input/mouse.cpp source/core/input/mouse.cpp
# Core - Rendering # Core - Rendering
source/core/rendering/gif.cpp source/core/rendering/gif.cpp
source/core/rendering/palette_manager.cpp
source/core/rendering/pixel_reveal.cpp source/core/rendering/pixel_reveal.cpp
source/core/rendering/render_info.cpp
source/core/rendering/screen.cpp source/core/rendering/screen.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/surface.cpp
source/core/rendering/surface_animated_sprite.cpp
source/core/rendering/surface_dissolve_sprite.cpp
source/core/rendering/surface_moving_sprite.cpp
source/core/rendering/surface_sprite.cpp
source/core/rendering/text.cpp source/core/rendering/text.cpp
source/core/rendering/texture.cpp
# Core - Locale # Core - Locale
source/core/locale/locale.cpp source/core/locale/locale.cpp
# Core - Resources # Core - Resources
source/core/resources/resource_list.cpp
source/core/resources/resource_cache.cpp source/core/resources/resource_cache.cpp
source/core/resources/resource_pack.cpp
source/core/resources/resource_loader.cpp
source/core/resources/resource_helper.cpp source/core/resources/resource_helper.cpp
source/core/resources/resource_list.cpp
source/core/resources/resource_loader.cpp
source/core/resources/resource_pack.cpp
# Core - System # Core - System
source/core/system/director.cpp source/core/system/director.cpp
@@ -78,9 +79,9 @@ set(APP_SOURCES
source/game/gameplay/enemy_manager.cpp source/game/gameplay/enemy_manager.cpp
source/game/gameplay/item_manager.cpp source/game/gameplay/item_manager.cpp
source/game/gameplay/item_tracker.cpp source/game/gameplay/item_tracker.cpp
source/game/gameplay/room.cpp
source/game/gameplay/room_loader.cpp source/game/gameplay/room_loader.cpp
source/game/gameplay/room_tracker.cpp source/game/gameplay/room_tracker.cpp
source/game/gameplay/room.cpp
source/game/gameplay/scoreboard.cpp source/game/gameplay/scoreboard.cpp
source/game/gameplay/stats.cpp source/game/gameplay/stats.cpp
source/game/gameplay/tilemap_renderer.cpp source/game/gameplay/tilemap_renderer.cpp
@@ -89,13 +90,15 @@ set(APP_SOURCES
source/game/scenes/credits.cpp source/game/scenes/credits.cpp
source/game/scenes/ending.cpp source/game/scenes/ending.cpp
source/game/scenes/ending2.cpp source/game/scenes/ending2.cpp
source/game/scenes/game.cpp
source/game/scenes/game_over.cpp source/game/scenes/game_over.cpp
source/game/scenes/game.cpp
source/game/scenes/loading_screen.cpp source/game/scenes/loading_screen.cpp
source/game/scenes/logo.cpp source/game/scenes/logo.cpp
source/game/scenes/title.cpp source/game/scenes/title.cpp
# Game - UI # Game - UI
source/game/ui/console.cpp
source/game/ui/console_commands.cpp
source/game/ui/notifier.cpp source/game/ui/notifier.cpp
# Utils # Utils
@@ -124,32 +127,62 @@ message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
if(NOT APPLE) if(NOT APPLE)
find_program(GLSLC_EXE NAMES glslc) find_program(GLSLC_EXE NAMES glslc)
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert") set(SHADERS_DIR "${CMAKE_SOURCE_DIR}/data/shaders")
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag") set(HEADERS_DIR "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu")
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_vert_spv.h")
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_frag_spv.h") set(SHADER_POSTFX_VERT_SRC "${SHADERS_DIR}/postfx.vert")
set(SHADER_POSTFX_FRAG_SRC "${SHADERS_DIR}/postfx.frag")
set(SHADER_UPSCALE_FRAG_SRC "${SHADERS_DIR}/upscale.frag")
set(SHADER_DOWNSCALE_FRAG_SRC "${SHADERS_DIR}/downscale.frag")
set(SHADER_CRTPI_FRAG_SRC "${SHADERS_DIR}/crtpi_frag.glsl")
set(SHADER_POSTFX_VERT_H "${HEADERS_DIR}/postfx_vert_spv.h")
set(SHADER_POSTFX_FRAG_H "${HEADERS_DIR}/postfx_frag_spv.h")
set(SHADER_UPSCALE_FRAG_H "${HEADERS_DIR}/upscale_frag_spv.h")
set(SHADER_DOWNSCALE_FRAG_H "${HEADERS_DIR}/downscale_frag_spv.h")
set(SHADER_CRTPI_FRAG_H "${HEADERS_DIR}/crtpi_frag_spv.h")
set(ALL_SHADER_HEADERS
"${SHADER_POSTFX_VERT_H}"
"${SHADER_POSTFX_FRAG_H}"
"${SHADER_UPSCALE_FRAG_H}"
"${SHADER_DOWNSCALE_FRAG_H}"
"${SHADER_CRTPI_FRAG_H}"
)
set(ALL_SHADER_SOURCES
"${SHADER_POSTFX_VERT_SRC}"
"${SHADER_POSTFX_FRAG_SRC}"
"${SHADER_UPSCALE_FRAG_SRC}"
"${SHADER_DOWNSCALE_FRAG_SRC}"
"${SHADER_CRTPI_FRAG_SRC}"
)
if(GLSLC_EXE) if(GLSLC_EXE)
add_custom_command( add_custom_command(
OUTPUT "${SHADER_VERT_H}" "${SHADER_FRAG_H}" OUTPUT ${ALL_SHADER_HEADERS}
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh" COMMAND ${CMAKE_COMMAND}
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" -D GLSLC=${GLSLC_EXE}
-D SHADERS_DIR=${SHADERS_DIR}
-D HEADERS_DIR=${HEADERS_DIR}
-P ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
DEPENDS ${ALL_SHADER_SOURCES}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Compilando shaders SPIR-V..." COMMENT "Compilando shaders SPIR-V..."
) )
add_custom_target(shaders DEPENDS "${SHADER_VERT_H}" "${SHADER_FRAG_H}") add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
message(STATUS "glslc encontrado: shaders se compilarán automáticamente") message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
else() else()
if(NOT EXISTS "${SHADER_VERT_H}" OR NOT EXISTS "${SHADER_FRAG_H}") foreach(HDR ${ALL_SHADER_HEADERS})
message(FATAL_ERROR if(NOT EXISTS "${HDR}")
"glslc no encontrado y headers SPIR-V no existen.\n" message(FATAL_ERROR
" Instala glslc: sudo apt install glslang-tools (Linux)\n" "glslc no encontrado y header SPIR-V no existe: ${HDR}\n"
" choco install vulkan-sdk (Windows)\n" " Instala glslc: sudo apt install glslang-tools (Linux)\n"
" O genera los headers manualmente: tools/shaders/compile_spirv.sh" " choco install vulkan-sdk (Windows)\n"
) " O genera los headers manualmente: tools/shaders/compile_spirv.sh"
else() )
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados") endif()
endif() endforeach()
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
endif() endif()
else() else()
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)") message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")
@@ -192,7 +225,17 @@ if(WIN32)
elseif(APPLE) elseif(APPLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD) target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated) target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
set(CMAKE_OSX_ARCHITECTURES "arm64") if(NOT CMAKE_OSX_ARCHITECTURES)
set(CMAKE_OSX_ARCHITECTURES "arm64")
endif()
if(MACOS_BUNDLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUNDLE)
target_link_options(${PROJECT_NAME} PRIVATE
-framework SDL3
-F ${CMAKE_SOURCE_DIR}/release/macos/frameworks/SDL3.xcframework/macos-arm64_x86_64
-rpath @executable_path/../Frameworks/
)
endif()
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD) target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
endif() endif()

311
Makefile
View File

@@ -18,25 +18,18 @@ RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME)
RESOURCE_FILE := release/windows/jdd.res RESOURCE_FILE := release/windows/jdd.res
# ============================================================================== # ==============================================================================
# PACKING TOOL # TOOLS
# ==============================================================================
ifeq ($(OS),Windows_NT)
PACK_TOOL := $(DIR_TOOLS)pack_resources/pack_resources.exe
PACK_CXX := $(CXX)
else
PACK_TOOL := $(DIR_TOOLS)pack_resources/pack_resources
PACK_CXX := $(CXX)
endif
PACK_SOURCES := $(DIR_TOOLS)pack_resources/pack_resources.cpp source/core/resources/resource_pack.cpp
PACK_INCLUDES := -Isource
# ==============================================================================
# SHADERS
# ============================================================================== # ==============================================================================
DIR_PACK_TOOL := $(DIR_TOOLS)pack_resources
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
SHADER_VERT_H := $(DIR_ROOT)source/core/rendering/sdl3gpu/postfx_vert_spv.h SHADER_CMAKE := $(DIR_ROOT)tools/shaders/compile_spirv.cmake
SHADER_FRAG_H := $(DIR_ROOT)source/core/rendering/sdl3gpu/postfx_frag_spv.h SHADERS_DIR := $(DIR_ROOT)data/shaders
GLSLC := $(shell command -v glslc 2>/dev/null) HEADERS_DIR := $(DIR_ROOT)source/core/rendering/sdl3gpu
ifeq ($(OS),Windows_NT)
GLSLC := $(shell where glslc 2>NUL)
else
GLSLC := $(shell command -v glslc 2>/dev/null)
endif
# ============================================================================== # ==============================================================================
# VERSION (extracted from defines.hpp) # VERSION (extracted from defines.hpp)
@@ -47,6 +40,14 @@ else
VERSION := v$(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/') VERSION := v$(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
endif endif
# ==============================================================================
# SHELL (Windows usa cmd.exe para que las recetas con powershell funcionen igual
# desde cualquier terminal: PowerShell, cmd o git-bash)
# ==============================================================================
ifeq ($(OS),Windows_NT)
SHELL := cmd.exe
endif
# ============================================================================== # ==============================================================================
# WINDOWS-SPECIFIC VARIABLES # WINDOWS-SPECIFIC VARIABLES
# ============================================================================== # ==============================================================================
@@ -67,213 +68,116 @@ MACOS_APPLE_SILICON_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-macos-apple
LINUX_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-linux.tar.gz LINUX_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-linux.tar.gz
# ============================================================================== # ==============================================================================
# SOURCE FILES # PLATAFORMA
# ============================================================================== # ==============================================================================
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/surface_sprite.cpp \
source/core/rendering/surface_animated_sprite.cpp \
source/core/rendering/surface_moving_sprite.cpp \
source/core/rendering/text.cpp \
source/core/rendering/texture.cpp \
source/core/rendering/gif.cpp \
source/core/rendering/pixel_reveal.cpp \
source/core/rendering/surface_dissolve_sprite.cpp \
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp \
source/core/resources/resource_list.cpp \
source/core/resources/resource_cache.cpp \
source/core/resources/resource_helper.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/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/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_tracker.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/scenes/ending.cpp \
source/game/scenes/ending2.cpp \
source/game/scenes/credits.cpp \
source/game/ui/notifier.cpp \
source/utils/utils.cpp \
source/utils/delta_timer.cpp
# All sources combined
ALL_SOURCES := $(APP_SOURCES)
# ==============================================================================
# INCLUDES
# ==============================================================================
INCLUDES := -Isource
# ==============================================================================
# COMPILER FLAGS (OS-specific)
# ==============================================================================
CPP_STANDARD := c++20
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
FixPath = $(subst /,\\,$1) FixPath = $(subst /,\\,$1)
CXXFLAGS := -std=$(CPP_STANDARD) -Wall -Os -ffunction-sections -fdata-sections \
-Wl,--gc-sections -static-libstdc++ -static-libgcc \
-Wl,-subsystem,windows -DWINDOWS_BUILD
CXXFLAGS_DEBUG := -std=$(CPP_STANDARD) -Wall -g -D_DEBUG -DWINDOWS_BUILD
LDFLAGS := -lmingw32 -lws2_32 -lSDL3
RM := del /Q RM := del /Q
MKDIR := mkdir MKDIR := mkdir
else else
FixPath = $1 FixPath = $1
CXXFLAGS := -std=$(CPP_STANDARD) -Wall -Os -ffunction-sections -fdata-sections
CXXFLAGS_DEBUG := -std=$(CPP_STANDARD) -Wall -g -D_DEBUG
LDFLAGS := -lSDL3
RMFILE := rm -f RMFILE := rm -f
RMDIR := rm -rdf RMDIR := rm -rdf
MKDIR := mkdir -p MKDIR := mkdir -p
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
CXXFLAGS += -DLINUX_BUILD
endif
ifeq ($(UNAME_S),Darwin)
CXXFLAGS += -DMACOS_BUILD
CXXFLAGS_DEBUG += -DMACOS_BUILD
# Configurar arquitectura (por defecto arm64)
CXXFLAGS += -arch arm64
CXXFLAGS_DEBUG += -arch arm64
endif
endif endif
# ============================================================================== # ==============================================================================
# REGLAS PARA COMPILACIÓN DE SHADERS # COMPILACIÓN CON CMAKE
# ==============================================================================
all:
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
@cmake --build build
debug:
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
@cmake --build build
# ==============================================================================
# RELEASE AUTOMÁTICO (detecta SO)
# ==============================================================================
release:
ifeq ($(OS),Windows_NT)
@"$(MAKE)" windows_release
else
ifeq ($(UNAME_S),Darwin)
@$(MAKE) macos_release
else
@$(MAKE) linux_release
endif
endif
# ==============================================================================
# REGLAS PARA COMPILACIÓN DE SHADERS (multiplataforma via cmake)
# ============================================================================== # ==============================================================================
compile_shaders: compile_shaders:
ifdef GLSLC ifdef GLSLC
@echo "Compilando shaders SPIR-V..." @cmake -D GLSLC=$(GLSLC) -D SHADERS_DIR=$(SHADERS_DIR) -D HEADERS_DIR=$(HEADERS_DIR) -P $(SHADER_CMAKE)
@$(SHADER_SCRIPT)
@echo "✓ Shaders compilados"
else else
@if [ -f "$(SHADER_VERT_H)" ] && [ -f "$(SHADER_FRAG_H)" ]; then \ @echo "glslc no encontrado - asegurate de que los headers SPIR-V precompilados existen"
echo "⚠ glslc no encontrado - usando headers SPIR-V precompilados"; \
else \
echo "ERROR: glslc no encontrado y headers SPIR-V no existen."; \
echo " Instala glslc: sudo apt install glslang-tools"; \
echo " O ejecuta manualmente: tools/shaders/compile_spirv.sh"; \
exit 1; \
fi
endif endif
# ============================================================================== # ==============================================================================
# REGLAS PARA HERRAMIENTA DE EMPAQUETADO Y RESOURCES.PACK # REGLAS PARA HERRAMIENTA DE EMPAQUETADO Y RESOURCES.PACK
# ============================================================================== # ==============================================================================
$(PACK_TOOL): FORCE pack_tool:
@echo "Compilando herramienta de empaquetado..." @$(MAKE) -C $(DIR_PACK_TOOL)
$(PACK_CXX) -std=$(CPP_STANDARD) -Wall -Os $(PACK_INCLUDES) $(PACK_SOURCES) -o $(PACK_TOOL)
@echo "✓ Herramienta de empaquetado lista: $(PACK_TOOL)"
pack_tool: $(PACK_TOOL) resources.pack: pack_tool
@$(MAKE) -C $(DIR_PACK_TOOL) pack
resources.pack: $(PACK_TOOL)
@echo "Generando resources.pack desde directorio data/..."
$(PACK_TOOL) data resources.pack
@echo "✓ resources.pack generado exitosamente"
# ============================================================================== # ==============================================================================
# COMPILACIÓN PARA WINDOWS # COMPILACIÓN PARA WINDOWS (RELEASE)
# ============================================================================== # ==============================================================================
windows:
@echo off
@echo Generando version.h...
@powershell -Command "$$GIT_HASH = (git rev-parse --short=7 HEAD 2>$$null); if (-not $$GIT_HASH) { $$GIT_HASH = 'unknown' }; (Get-Content source/version.h.in) -replace '@GIT_HASH@', $$GIT_HASH | Set-Content source/version.h"
@echo Compilando para Windows con nombre: "$(WIN_TARGET_FILE).exe"
windres release/windows/jdd.rc -O coff -o $(RESOURCE_FILE)
g++ $(ALL_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_TARGET_FILE).exe"
strip -s -R .comment -R .gnu.version "$(WIN_TARGET_FILE).exe" --strip-unneeded
windows_release: windows_release:
@$(MAKE) compile_shaders
@$(MAKE) resources.pack
@echo off @echo off
@echo Creando release para Windows - Version: $(VERSION) @echo Creando release para Windows - Version: $(VERSION)
# Generate version.h from version.h.in # Compila con cmake (genera shaders, resources.pack y ejecutable)
@echo "Generando version.h..." @cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
@powershell -Command "$$GIT_HASH = (git rev-parse --short=7 HEAD 2>$$null); if (-not $$GIT_HASH) { $$GIT_HASH = 'unknown' }; (Get-Content source/version.h.in) -replace '@GIT_HASH@', $$GIT_HASH | Set-Content source/version.h" @cmake --build build
# Crea carpeta de distribución y carpeta temporal 'RELEASE_FOLDER' # Crea carpeta de distribución y carpeta temporal 'RELEASE_FOLDER'
powershell if (-not (Test-Path "$(DIST_DIR)")) {New-Item "$(DIST_DIR)" -ItemType Directory} @powershell -Command "if (-not (Test-Path '$(DIST_DIR)')) {New-Item '$(DIST_DIR)' -ItemType Directory}"
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force} @powershell -Command "if (Test-Path '$(RELEASE_FOLDER)') {Remove-Item '$(RELEASE_FOLDER)' -Recurse -Force}"
powershell if (-not (Test-Path "$(RELEASE_FOLDER)")) {New-Item "$(RELEASE_FOLDER)" -ItemType Directory} @powershell -Command "if (-not (Test-Path '$(RELEASE_FOLDER)')) {New-Item '$(RELEASE_FOLDER)' -ItemType Directory}"
# Copia el archivo 'resources.pack' # Copia ficheros
powershell Copy-Item -Path "resources.pack" -Destination "$(RELEASE_FOLDER)" @powershell -Command "Copy-Item -Path 'resources.pack' -Destination '$(RELEASE_FOLDER)'"
@powershell -Command "Copy-Item 'LICENSE' -Destination '$(RELEASE_FOLDER)'"
# Copia los ficheros que están en la raíz del proyecto @powershell -Command "Copy-Item 'README.md' -Destination '$(RELEASE_FOLDER)'"
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)" @powershell -Command "Copy-Item 'gamecontrollerdb.txt' -Destination '$(RELEASE_FOLDER)'"
powershell Copy-Item "README.md" -Destination "$(RELEASE_FOLDER)" @powershell -Command "Copy-Item 'release\windows\dll\*.dll' -Destination '$(RELEASE_FOLDER)'"
powershell Copy-Item "gamecontrollerdb.txt" -Destination "$(RELEASE_FOLDER)" @powershell -Command "Copy-Item -Path '$(TARGET_FILE)' -Destination '\"$(WIN_RELEASE_FILE).exe\"'"
powershell Copy-Item "release\windows\dll\*.dll" -Destination "$(RELEASE_FOLDER)"
# Compila (con icono)
windres release/windows/jdd.rc -O coff -o $(RESOURCE_FILE)
g++ $(ALL_SOURCES) $(RESOURCE_FILE) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_RELEASE_FILE).exe"
strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
# Crea el fichero .zip # Crea el fichero .zip
powershell if (Test-Path "$(WINDOWS_RELEASE)") {Remove-Item "$(WINDOWS_RELEASE)"} @powershell -Command "if (Test-Path '$(WINDOWS_RELEASE)') {Remove-Item '$(WINDOWS_RELEASE)'}"
powershell Compress-Archive -Path "$(RELEASE_FOLDER)"/* -DestinationPath "$(WINDOWS_RELEASE)" @powershell -Command "Compress-Archive -Path '$(RELEASE_FOLDER)/*' -DestinationPath '$(WINDOWS_RELEASE)'"
@echo Release creado: $(WINDOWS_RELEASE) @echo Release creado: $(WINDOWS_RELEASE)
# Elimina la carpeta temporal 'RELEASE_FOLDER' # Elimina la carpeta temporal 'RELEASE_FOLDER'
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force} @powershell -Command "if (Test-Path '$(RELEASE_FOLDER)') {Remove-Item '$(RELEASE_FOLDER)' -Recurse -Force}"
# ============================================================================== # ==============================================================================
# COMPILACIÓN PARA MACOS # COMPILACIÓN PARA MACOS (RELEASE)
# ============================================================================== # ==============================================================================
macos:
@GIT_HASH=$$(git rev-parse --short=7 HEAD 2>/dev/null || echo "unknown"); \
sed "s/@GIT_HASH@/$$GIT_HASH/g" source/version.h.in > source/version.h
@echo "Compilando para macOS: $(TARGET_NAME)"
clang++ $(ALL_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
macos_release: macos_release:
@$(MAKE) compile_shaders
@$(MAKE) resources.pack
@echo "Creando release para macOS - Version: $(VERSION)" @echo "Creando release para macOS - Version: $(VERSION)"
# Verificar e instalar create-dmg si es necesario # Verificar e instalar create-dmg si es necesario
@which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg) @which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg)
# Generate version.h from version.h.in # Compila la versión para procesadores Intel con cmake (genera shaders y resources.pack)
@echo "Generando version.h..." @cmake -S . -B build/intel -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DMACOS_BUNDLE=ON
@GIT_HASH=$$(git rev-parse --short=7 HEAD 2>/dev/null || echo "unknown"); \ @cmake --build build/intel
sed "s/@GIT_HASH@/$$GIT_HASH/g" source/version.h.in > source/version.h
# Elimina datos de compilaciones anteriores # Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
$(RMFILE) tmp.dmg $(RMFILE) tmp.dmg
$(RMFILE) "$(DIST_DIR)"/rw.* $(RMFILE) "$(DIST_DIR)"/rw.*
$(RMFILE) "$(MACOS_INTEL_RELEASE)"
$(RMFILE) "$(MACOS_APPLE_SILICON_RELEASE)"
# Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macOS # Crea la carpeta temporal para hacer el trabajo y las carpetas obligatorias para crear una app de macOS
$(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks" $(MKDIR) "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
@@ -295,8 +199,8 @@ macos_release:
sed -i '' '/<key>CFBundleShortVersionString<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist"; \ sed -i '' '/<key>CFBundleShortVersionString<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist"; \
sed -i '' '/<key>CFBundleVersion<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist" sed -i '' '/<key>CFBundleVersion<\/key>/{n;s|<string>.*</string>|<string>'"$$RAW_VERSION"'</string>|;}' "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Info.plist"
# Compila la versión para procesadores Intel # Copia el ejecutable Intel al bundle
clang++ $(ALL_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DRELEASE_BUILD -std=$(CPP_STANDARD) -Wall -Os -framework SDL3 -F release/macos/frameworks/SDL3.xcframework/macos-arm64_x86_64 -ffunction-sections -fdata-sections -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.15 cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"
# Firma la aplicación # Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app" codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
@@ -318,8 +222,10 @@ macos_release:
"$(RELEASE_FOLDER)" || true "$(RELEASE_FOLDER)" || true
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)" @echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
# Compila la versión para procesadores Apple Silicon # Compila la versión para procesadores Apple Silicon con cmake
clang++ $(ALL_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DRELEASE_BUILD -std=$(CPP_STANDARD) -Wall -Os -framework SDL3 -F release/macos/frameworks/SDL3.xcframework/macos-arm64_x86_64 -ffunction-sections -fdata-sections -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11 @cmake -S . -B build/arm -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DMACOS_BUNDLE=ON
@cmake --build build/arm
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"
# Firma la aplicación # Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app" codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
@@ -343,27 +249,19 @@ macos_release:
# Elimina las carpetas temporales # Elimina las carpetas temporales
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
$(RMDIR) build/intel
$(RMDIR) build/arm
$(RMFILE) "$(DIST_DIR)"/rw.* $(RMFILE) "$(DIST_DIR)"/rw.*
# ============================================================================== # ==============================================================================
# COMPILACIÓN PARA LINUX # COMPILACIÓN PARA LINUX (RELEASE)
# ============================================================================== # ==============================================================================
linux:
@GIT_HASH=$$(git rev-parse --short=7 HEAD 2>/dev/null || echo "unknown"); \
sed "s/@GIT_HASH@/$$GIT_HASH/g" source/version.h.in > source/version.h
@echo "Compilando para Linux: $(TARGET_NAME)"
g++ $(ALL_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
strip -s -R .comment -R .gnu.version "$(TARGET_FILE)" --strip-unneeded
linux_release: linux_release:
@$(MAKE) compile_shaders
@$(MAKE) resources.pack
@echo "Creando release para Linux - Version: $(VERSION)" @echo "Creando release para Linux - Version: $(VERSION)"
# Generate version.h from version.h.in # Compila con cmake (genera shaders, resources.pack y ejecutable)
@echo "Generando version.h..." @cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
@GIT_HASH=$$(git rev-parse --short=7 HEAD 2>/dev/null || echo "unknown"); \ @cmake --build build
sed "s/@GIT_HASH@/$$GIT_HASH/g" source/version.h.in > source/version.h
# Elimina carpeta temporal previa y la recrea (crea dist/ si no existe) # Elimina carpeta temporal previa y la recrea (crea dist/ si no existe)
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -374,9 +272,7 @@ linux_release:
cp LICENSE "$(RELEASE_FOLDER)" cp LICENSE "$(RELEASE_FOLDER)"
cp README.md "$(RELEASE_FOLDER)" cp README.md "$(RELEASE_FOLDER)"
cp gamecontrollerdb.txt "$(RELEASE_FOLDER)" cp gamecontrollerdb.txt "$(RELEASE_FOLDER)"
cp "$(TARGET_FILE)" "$(RELEASE_FILE)"
# Compila
g++ $(ALL_SOURCES) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE)"
strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded
# Empaqueta ficheros # Empaqueta ficheros
@@ -398,17 +294,24 @@ show_version:
help: help:
@echo "Makefile para JailDoctor's Dilemma" @echo "Makefile para JailDoctor's Dilemma"
@echo "Comandos disponibles:" @echo "Comandos disponibles:"
@echo " windows - Compilar para Windows" @echo ""
@echo " windows_release - Crear release completo para Windows" @echo " Compilacion:"
@echo " linux - Compilar para Linux" @echo " make - Compilar con cmake (Release)"
@echo " linux_release - Crear release completo para Linux" @echo " make debug - Compilar con cmake (Debug)"
@echo " macos - Compilar para macOS" @echo ""
@echo " macos_release - Crear release completo para macOS" @echo " Release:"
@echo " pack_tool - Compilar herramienta de empaquetado" @echo " make release - Crear release (detecta SO automaticamente)"
@echo " resources.pack - Generar pack de recursos desde data/" @echo " make windows_release - Crear release para Windows"
@echo " show_version - Mostrar version actual ($(VERSION))" @echo " make linux_release - Crear release para Linux"
@echo " help - Mostrar esta ayuda" @echo " make macos_release - Crear release para macOS"
@echo ""
@echo " Herramientas:"
@echo " make compile_shaders - Compilar shaders SPIR-V"
@echo " make pack_tool - Compilar herramienta de empaquetado"
@echo " make resources.pack - Generar pack de recursos desde data/"
@echo ""
@echo " Otros:"
@echo " make show_version - Mostrar version actual ($(VERSION))"
@echo " make help - Mostrar esta ayuda"
FORCE: .PHONY: all debug release windows_release macos_release linux_release compile_shaders pack_tool resources.pack show_version help
.PHONY: windows windows_release macos macos_release linux linux_release compile_shaders pack_tool resources.pack show_version help

View File

@@ -7,23 +7,23 @@ assets:
- type: BITMAP - type: BITMAP
path: ${PREFIX}/data/font/smb2.gif path: ${PREFIX}/data/font/smb2.gif
- type: FONT - type: FONT
path: ${PREFIX}/data/font/smb2.txt path: ${PREFIX}/data/font/smb2.fnt
- type: BITMAP - type: BITMAP
path: ${PREFIX}/data/font/aseprite.gif path: ${PREFIX}/data/font/aseprite.gif
- type: FONT - type: FONT
path: ${PREFIX}/data/font/aseprite.txt path: ${PREFIX}/data/font/aseprite.fnt
- type: BITMAP - type: BITMAP
path: ${PREFIX}/data/font/gauntlet.gif path: ${PREFIX}/data/font/gauntlet.gif
- type: FONT - type: FONT
path: ${PREFIX}/data/font/gauntlet.txt path: ${PREFIX}/data/font/gauntlet.fnt
- type: BITMAP - type: BITMAP
path: ${PREFIX}/data/font/subatomic.gif path: ${PREFIX}/data/font/subatomic.gif
- type: FONT - type: FONT
path: ${PREFIX}/data/font/subatomic.txt path: ${PREFIX}/data/font/subatomic.fnt
- type: BITMAP - type: BITMAP
path: ${PREFIX}/data/font/8bithud.gif path: ${PREFIX}/data/font/8bithud.gif
- type: FONT - type: FONT
path: ${PREFIX}/data/font/8bithud.txt path: ${PREFIX}/data/font/8bithud.fnt
# PALETTES # PALETTES
palettes: palettes:
@@ -32,7 +32,7 @@ assets:
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/zx-spectrum-adjusted.pal path: ${PREFIX}/data/palette/zx-spectrum-adjusted.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/zxarne-5-2.pal path: ${PREFIX}/data/palette/zxarne-5.2.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/black-and-white.pal path: ${PREFIX}/data/palette/black-and-white.pal
- type: PALETTE - type: PALETTE
@@ -46,15 +46,33 @@ assets:
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/pico-8.pal path: ${PREFIX}/data/palette/pico-8.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/sweetie-16.pal path: ${PREFIX}/data/palette/sweetie.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/island-joy-16.pal path: ${PREFIX}/data/palette/island-joy.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/lost-century.pal path: ${PREFIX}/data/palette/lost-century.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/na16.pal path: ${PREFIX}/data/palette/na.pal
- type: PALETTE - type: PALETTE
path: ${PREFIX}/data/palette/steam-lords.pal 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
locale: locale:
@@ -74,6 +92,10 @@ assets:
path: ${SYSTEM_FOLDER}/config.yaml path: ${SYSTEM_FOLDER}/config.yaml
required: false required: false
absolute: true absolute: true
- type: DATA
path: ${SYSTEM_FOLDER}/debug.yaml
required: false
absolute: true
- type: DATA - type: DATA
path: ${SYSTEM_FOLDER}/stats_buffer.csv path: ${SYSTEM_FOLDER}/stats_buffer.csv
required: false required: false
@@ -87,9 +109,18 @@ assets:
required: false required: false
absolute: true absolute: true
- type: DATA - type: DATA
path: ${SYSTEM_FOLDER}/postfx.yaml path: ${SYSTEM_FOLDER}/shaders/postfx.yaml
required: false required: false
absolute: true absolute: true
- type: DATA
path: ${SYSTEM_FOLDER}/shaders/crtpi.yaml
required: false
absolute: true
# CONSOLE
console:
- type: DATA
path: ${PREFIX}/data/console/commands.yaml
# ROOMS # ROOMS
rooms: rooms:

232
data/console/commands.yaml Normal file
View File

@@ -0,0 +1,232 @@
# JailDoctor's Dilemma - Console Commands
# Metadata for the in-game console command system.
# Execution logic stays in C++; this file defines metadata only.
#
# Fields:
# keyword - Command name (uppercase)
# handler - C++ handler function identifier
# description - Short description for help output
# usage - Full usage string for terminal help
# instant - (optional) Skip typewriter effect (default: false)
# hidden - (optional) Hide from TAB completion (default: false)
# debug_only - (optional) Only available in debug builds (default: false)
# help_hidden - (optional) Don't show in help output (default: false)
# dynamic_completions - (optional) Completions generated at runtime (default: false)
# completions - (optional) Static TAB completion tree
# debug_extras - (optional) Overrides applied in debug builds
categories:
- name: VIDEO
commands:
- keyword: SS
handler: cmd_ss
description: Supersampling
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]
- keyword: SHADER
handler: cmd_shader
description: "Toggle/select shader (F4)"
usage: "SHADER [ON|OFF|NEXT|POSTFX|CRTPI|PRESET [NEXT|PREV|<name>]]"
completions:
SHADER: [ON, OFF, NEXT, POSTFX, CRTPI, PRESET]
dynamic_completions: true
- keyword: BORDER
handler: cmd_border
description: "Decorative border (B)"
usage: "BORDER [ON|OFF]"
completions:
BORDER: [ON, OFF]
- keyword: FULLSCREEN
handler: cmd_fullscreen
description: "Fullscreen mode (F3)"
usage: "FULLSCREEN [ON|OFF]"
completions:
FULLSCREEN: [ON, OFF]
- keyword: ZOOM
handler: cmd_zoom
description: "Window zoom (F1/F2)"
usage: "ZOOM [UP|DOWN|<1-N>]"
completions:
ZOOM: [UP, DOWN]
- keyword: INTSCALE
handler: cmd_intscale
description: "Integer scaling (F7)"
usage: "INTSCALE [ON|OFF]"
completions:
INTSCALE: [ON, OFF]
- keyword: VSYNC
handler: cmd_vsync
description: "Vertical sync"
usage: "VSYNC [ON|OFF]"
completions:
VSYNC: [ON, OFF]
- keyword: DRIVER
handler: cmd_driver
description: "GPU driver (restart to apply)"
usage: "DRIVER [LIST|AUTO|NONE|<name>]"
completions:
DRIVER: [LIST, AUTO, NONE]
- keyword: PALETTE
handler: cmd_palette
description: "Color palette (F5/F6)"
usage: "PALETTE [NEXT|PREV|SORT [ORIGINAL|LUMINANCE|SPECTRUM]|DEFAULT|<name>]"
completions:
PALETTE SORT: [ORIGINAL, LUMINANCE, SPECTRUM]
dynamic_completions: true
- name: AUDIO
commands:
- keyword: AUDIO
handler: cmd_audio
description: Audio master
usage: "AUDIO [ON|OFF|VOL <0-100>]"
completions:
AUDIO: [ON, OFF, VOL]
- keyword: MUSIC
handler: cmd_music
description: Music volume
usage: "MUSIC [ON|OFF|VOL <0-100>]"
completions:
MUSIC: [ON, OFF, VOL]
- keyword: SOUND
handler: cmd_sound
description: Sound volume
usage: "SOUND [ON|OFF|VOL <0-100>]"
completions:
SOUND: [ON, OFF, VOL]
- name: GAME
commands:
- keyword: PLAYER
handler: cmd_player
description: "Player skin and color"
usage: "PLAYER SKIN <name> | PLAYER COLOR <0-15>|DEFAULT"
completions:
PLAYER: [SKIN, COLOR]
PLAYER SKIN: [DEFAULT, ABAD, BATMAN, CHIP, CONGO, JEANNINE, MUMMY, UPV_STUDENT]
PLAYER COLOR: [DEFAULT]
- keyword: RESTART
handler: cmd_restart
description: Restart from the beginning
usage: RESTART
instant: true
- keyword: KIOSK
handler: cmd_kiosk
description: Enable kiosk mode
usage: "KIOSK [ON]"
completions:
KIOSK: [ON]
- keyword: EXIT
handler: cmd_exit
description: Quit application
usage: EXIT
instant: true
- keyword: QUIT
handler: cmd_quit
description: Quit application
usage: QUIT
instant: true
help_hidden: true
- name: INFO
commands:
- keyword: SHOW
handler: cmd_show
description: Show info overlay
usage: "SHOW [INFO]"
completions:
SHOW: [INFO]
debug_extras:
description: "Show overlay/test notification"
usage: "SHOW [INFO|NOTIFICATION|CHEEVO]"
completions:
SHOW: [INFO, NOTIFICATION, CHEEVO]
- keyword: HIDE
handler: cmd_hide
description: Hide info overlay
usage: "HIDE [INFO]"
completions:
HIDE: [INFO]
- keyword: SIZE
handler: cmd_size
description: Window size in pixels
usage: SIZE
- keyword: HELP
handler: cmd_help
description: "Show this help"
usage: "HELP / ?"
- keyword: "?"
handler: cmd_help
help_hidden: true
- name: DEBUG
debug_only: true
commands:
- keyword: DEBUG
handler: cmd_debug
description: "Debug mode and start options (F12)"
usage: "DEBUG [MODE [ON|OFF]|START [HERE|ROOM|POS|SCENE <name>]]"
completions:
DEBUG: [MODE, START]
DEBUG MODE: [ON, OFF]
DEBUG START: [HERE, ROOM, POS, SCENE]
DEBUG START SCENE: [LOGO, LOADING, TITLE, CREDITS, GAME, ENDING, ENDING2]
- keyword: ITEMS
handler: cmd_items
description: "Set item count (GAME only)"
usage: "ITEMS <0-200>"
- keyword: ROOM
handler: cmd_room
description: "Change to room number (GAME only)"
usage: "ROOM <1-60>|NEXT|PREV"
completions:
ROOM: [NEXT, PREV]
- keyword: SCENE
handler: cmd_scene
description: Change scene
usage: "SCENE [LOGO|LOADING|TITLE|CREDITS|GAME|ENDING|ENDING2|RESTART]"
completions:
SCENE: [LOGO, LOADING, TITLE, CREDITS, GAME, ENDING, ENDING2, RESTART]
- name: CHEATS
commands:
- keyword: CHEAT
handler: cmd_cheat
description: "Game cheats (GAME only)"
usage: "CHEAT [INFINITE LIVES|INVINCIBILITY|OPEN THE JAIL|CLOSE THE JAIL]"
hidden: true
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]
debug_extras:
hidden: false

132
data/font/8bithud.fnt Normal file
View File

@@ -0,0 +1,132 @@
# Font: 8bithud — generado desde 8-bit-hud.ttf size 5
# Generado con tools/font_gen/font_gen.py
box_width 8
box_height 8
columns 15
# codepoint_decimal ancho_visual
32 3 # U+0020
33 2 # !
34 5 # "
35 6 # #
36 6 # $
37 6 # %
38 6 # &
39 2 # '
40 3 # (
41 3 # )
42 4 # *
43 3 # +
44 2 # ,
45 3 # -
46 2 # .
47 4 # /
48 6 # 0
49 3 # 1
50 6 # 2
51 6 # 3
52 6 # 4
53 6 # 5
54 6 # 6
55 6 # 7
56 6 # 8
57 6 # 9
58 2 # :
59 2 # ;
60 4 # <
61 3 # =
62 4 # >
63 6 # ?
64 8 # @
65 6 # A
66 6 # B
67 6 # C
68 6 # D
69 6 # E
70 6 # F
71 6 # G
72 6 # H
73 6 # I
74 6 # J
75 6 # K
76 6 # L
77 6 # M
78 6 # N
79 6 # O
80 6 # P
81 6 # Q
82 6 # R
83 6 # S
84 6 # T
85 6 # U
86 5 # V
87 6 # W
88 6 # X
89 6 # Y
90 6 # Z
91 3 # [
92 4 # \
93 3 # ]
94 4 # ^
95 6 # _
96 2 # `
97 5 # a
98 5 # b
99 5 # c
100 5 # d
101 5 # e
102 5 # f
103 5 # g
104 5 # h
105 4 # i
106 5 # j
107 5 # k
108 5 # l
109 6 # m
110 5 # n
111 5 # o
112 5 # p
113 5 # q
114 5 # r
115 5 # s
116 4 # t
117 5 # u
118 5 # v
119 6 # w
120 4 # x
121 4 # y
122 5 # z
123 4 # {
124 1 # |
125 4 # }
126 4 # ~
192 6 # À
193 6 # Á
200 6 # È
201 6 # É
204 6 # Ì
205 6 # Í
210 6 # Ò
211 6 # Ó
219 6 # Ù
218 6 # Ú
209 6 # Ñ
199 6 # Ç
224 5 # à
225 5 # á
232 5 # è
233 5 # é
236 4 # ì
237 4 # í
242 5 # ò
243 5 # ó
249 5 # ù
250 5 # ú
241 5 # ñ
231 5 # ç
161 2 # ¡
191 6 # ¿
171 4 # «
187 4 # »
183 2 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 837 B

View File

@@ -1,194 +0,0 @@
# box width
8
# box height
8
# 32 espacio ( )
2
# 33 !
2
# 34 "
5
# 35 #
6
# 36 $
6
# 37 %
6
# 38 &
6
# 39 '
2
# 40 (
3
# 41 )
3
# 42 *
4
# 43 +
3
# 44 ,
2
# 45 -
3
# 46 .
2
# 47 /
4
# 48 0
6
# 49 1
6
# 50 2
6
# 51 3
6
# 52 4
6
# 53 5
6
# 54 6
6
# 55 7
6
# 56 8
6
# 57 9
6
# 58 :
2
# 59 ;
2
# 60 <
4
# 61 =
3
# 62 >
4
# 63 ?
6
# 64 @
8
# 65 A
6
# 66 B
6
# 67 C
6
# 68 D
6
# 69 E
6
# 70 F
6
# 71 G
6
# 72 H
6
# 73 I
6
# 74 J
6
# 75 K
6
# 76 L
6
# 77 M
6
# 78 N
6
# 79 O
6
# 80 P
6
# 81 Q
6
# 82 R
6
# 83 S
6
# 84 T
6
# 85 U
6
# 86 V
5
# 87 W
6
# 88 X
6
# 89 Y
6
# 90 Z
6
# 91 [
3
# 92 \
5
# 93 ]
3
# 94 ^
4
# 95 _
6
# 96 `
2
# 97 a
5
# 98 b
5
# 99 c
5
# 100 d
5
# 101 e
5
# 102 f
5
# 103 g
5
# 104 h
5
# 105 i
4
# 106 j
5
# 107 k
5
# 108 l
5
# 109 m
6
# 110 n
5
# 111 o
5
# 112 p
5
# 113 q
5
# 114 r
5
# 115 s
5
# 116 t
4
# 117 u
5
# 118 v
5
# 119 w
6
# 120 x
4
# 121 y
4
# 122 z
5
# 123 {
3
# 124 |
2
# 125 }
3
# 126 ~
3

134
data/font/aseprite.fnt Normal file
View File

@@ -0,0 +1,134 @@
# Font: aseprite — generado desde aseprite_font.gif
# Generado con tools/font_gen/font_gen.py
box_width 10
box_height 7
columns 16
cell_spacing 1
row_spacing 4
# codepoint_decimal ancho_visual
32 3 # U+0020
33 1 # !
34 3 # "
35 5 # #
36 4 # $
37 5 # %
38 5 # &
39 2 # '
40 2 # (
41 2 # )
42 5 # *
43 5 # +
44 2 # ,
45 3 # -
46 1 # .
47 3 # /
48 4 # 0
49 2 # 1
50 4 # 2
51 4 # 3
52 4 # 4
53 4 # 5
54 4 # 6
55 4 # 7
56 4 # 8
57 4 # 9
58 1 # :
59 2 # ;
60 3 # <
61 4 # =
62 3 # >
63 4 # ?
64 8 # @
65 4 # A
66 4 # B
67 4 # C
68 4 # D
69 4 # E
70 4 # F
71 4 # G
72 4 # H
73 1 # I
74 2 # J
75 4 # K
76 4 # L
77 5 # M
78 4 # N
79 5 # O
80 4 # P
81 5 # Q
82 4 # R
83 4 # S
84 5 # T
85 4 # U
86 5 # V
87 7 # W
88 5 # X
89 5 # Y
90 4 # Z
91 2 # [
92 3 # \
93 2 # ]
94 5 # ^
95 5 # _
96 3 # `
97 4 # a
98 4 # b
99 4 # c
100 4 # d
101 4 # e
102 2 # f
103 4 # g
104 4 # h
105 1 # i
106 2 # j
107 4 # k
108 1 # l
109 7 # m
110 4 # n
111 4 # o
112 4 # p
113 4 # q
114 3 # r
115 3 # s
116 2 # t
117 4 # u
118 4 # v
119 5 # w
120 5 # x
121 4 # y
122 4 # z
123 3 # {
124 1 # |
125 3 # }
126 4 # ~
192 5 # À
193 5 # Á
200 5 # È
201 5 # É
205 5 # Í
207 5 # Ï
210 5 # Ò
211 5 # Ó
218 5 # Ú
220 5 # Ü
209 5 # Ñ
199 5 # Ç
224 5 # à
225 5 # á
232 5 # è
233 5 # é
237 5 # í
239 5 # ï
242 5 # ò
243 5 # ó
250 5 # ú
252 5 # ü
241 5 # ñ
231 5 # ç
161 5 # ¡
191 5 # ¿
171 5 # «
187 5 # »
183 5 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,194 +0,0 @@
# box width
8
# box height
8
# 32 espacio ( )
3
# 33 !
1
# 34 "
3
# 35 #
3
# 36 $
4
# 37 %
5
# 38 &
5
# 39 '
2
# 40 (
2
# 41 )
2
# 42 *
5
# 43 +
5
# 44 ,
3
# 45 -
3
# 46 .
1
# 47 /
4
# 48 0
4
# 49 1
2
# 50 2
4
# 51 3
4
# 52 4
4
# 53 5
4
# 54 6
4
# 55 7
4
# 56 8
4
# 57 9
4
# 58 :
1
# 59 ;
1
# 60 <
3
# 61 =
4
# 62 >
4
# 63 ?
4
# 64 @
7
# 65 A
4
# 66 B
4
# 67 C
4
# 68 D
4
# 69 E
4
# 70 F
4
# 71 G
4
# 72 H
4
# 73 I
2
# 74 J
2
# 75 K
4
# 76 L
4
# 77 M
5
# 78 N
4
# 79 O
5
# 80 P
4
# 81 Q
5
# 82 R
4
# 83 S
4
# 84 T
5
# 85 U
4
# 86 V
5
# 87 W
7
# 88 X
5
# 89 Y
5
# 90 Z
4
# 91 [
2
# 92 \
3
# 93 ]
2
# 94 ^
5
# 95 _
5
# 96 `
3
# 97 a
4
# 98 b
4
# 99 c
4
# 100 d
4
# 101 e
4
# 102 f
2
# 103 g
4
# 104 h
4
# 105 i
1
# 106 j
2
# 107 k
4
# 108 l
1
# 109 m
7
# 110 n
4
# 111 o
4
# 112 p
4
# 113 q
4
# 114 r
3
# 115 s
3
# 116 t
2
# 117 u
4
# 118 v
4
# 119 w
5
# 120 x
5
# 121 y
4
# 122 z
4
# 123 {
3
# 124 |
3
# 125 }
3
# 126 ~
5

128
data/font/gauntlet.fnt Normal file
View File

@@ -0,0 +1,128 @@
# Font: gauntlet — generado desde Gauntlet.ttf size 7
# Generado con tools/font_gen/font_gen.py
box_width 8
box_height 8
columns 15
# codepoint_decimal ancho_visual
32 3 # U+0020
33 2 # !
34 5 # "
35 6 # #
36 6 # $
37 7 # %
38 7 # &
39 2 # '
40 4 # (
41 4 # )
42 6 # *
43 8 # +
44 2 # ,
45 7 # -
46 2 # .
47 7 # /
48 7 # 0
49 6 # 1
50 6 # 2
51 6 # 3
52 7 # 4
53 6 # 5
54 6 # 6
55 6 # 7
56 6 # 8
57 6 # 9
58 2 # :
59 3 # ;
60 5 # <
61 6 # =
62 5 # >
63 6 # ?
64 6 # @
65 6 # A
66 7 # B
67 7 # C
68 7 # D
69 7 # E
70 7 # F
71 7 # G
72 6 # H
73 6 # I
74 7 # J
75 7 # K
76 7 # L
77 7 # M
78 7 # N
79 7 # O
80 7 # P
81 7 # Q
82 7 # R
83 6 # S
84 6 # T
85 6 # U
86 6 # V
87 7 # W
88 7 # X
89 6 # Y
90 7 # Z
91 8 # [
92 3 # \
93 7 # ]
94 7 # ^
95 8 # _
97 6 # a
98 7 # b
99 7 # c
100 7 # d
101 7 # e
102 7 # f
103 7 # g
104 6 # h
105 6 # i
106 7 # j
107 7 # k
108 7 # l
109 7 # m
110 7 # n
111 7 # o
112 7 # p
113 7 # q
114 7 # r
115 6 # s
116 6 # t
117 6 # u
118 6 # v
119 7 # w
120 7 # x
121 6 # y
122 7 # z
126 6 # ~
192 6 # À
193 6 # Á
200 7 # È
201 7 # É
204 6 # Ì
205 6 # Í
210 7 # Ò
211 7 # Ó
217 6 # Ù
218 6 # Ú
209 7 # Ñ
199 7 # Ç
224 6 # à
225 6 # á
232 7 # è
233 7 # é
236 6 # ì
237 6 # í
242 7 # ò
243 7 # ó
249 6 # ù
250 6 # ú
241 7 # ñ
231 7 # ç
161 2 # ¡
191 6 # ¿
171 5 # «
187 5 # »
183 2 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -1,194 +0,0 @@
# box width
8
# box height
8
# 32 espacio ( )
6
# 33 !
2
# 34 "
5
# 35 #
6
# 36 $
6
# 37 %
7
# 38 &
7
# 39 '
2
# 40 (
4
# 41 )
4
# 42 *
6
# 43 +
8
# 44 ,
2
# 45 -
7
# 46 .
2
# 47 /
7
# 48 0
7
# 49 1
6
# 50 2
6
# 51 3
6
# 52 4
7
# 53 5
6
# 54 6
6
# 55 7
6
# 56 8
6
# 57 9
6
# 58 :
2
# 59 ;
2
# 60 <
5
# 61 =
6
# 62 >
5
# 63 ?
6
# 64 @
6
# 65 A
6
# 66 B
7
# 67 C
7
# 68 D
7
# 69 E
7
# 70 F
7
# 71 G
7
# 72 H
6
# 73 I
6
# 74 J
7
# 75 K
7
# 76 L
7
# 77 M
7
# 78 N
7
# 79 O
7
# 80 P
7
# 81 Q
7
# 82 R
7
# 83 S
6
# 84 T
6
# 85 U
6
# 86 V
6
# 87 W
7
# 88 X
7
# 89 Y
6
# 90 Z
7
# 91 [
8
# 92 \
3
# 93 ]
7
# 94 ^
7
# 95 _
8
# 96 `
0
# 97 a
6
# 98 b
7
# 99 c
7
# 100 d
7
# 101 e
7
# 102 f
7
# 103 g
7
# 104 h
6
# 105 i
6
# 106 j
7
# 107 k
7
# 108 l
7
# 109 m
7
# 110 n
7
# 111 o
7
# 112 p
7
# 113 q
7
# 114 r
7
# 115 s
6
# 116 t
6
# 117 u
6
# 118 v
6
# 119 w
7
# 120 x
7
# 121 y
6
# 122 z
7
# 123 {
0
# 124 |
0
# 125 }
0
# 126 ~
0

133
data/font/smb2.fnt Normal file
View File

@@ -0,0 +1,133 @@
# Font: smb2 — generado desde Super Mario Bros. 2.ttf size 8
# Generado con tools/font_gen/font_gen.py
box_width 8
box_height 8
columns 15
# codepoint_decimal ancho_visual
32 7 # U+0020
33 7 # !
34 7 # "
35 7 # #
36 7 # $
37 7 # %
38 7 # &
39 7 # '
40 7 # (
41 7 # )
42 7 # *
43 7 # +
44 7 # ,
45 7 # -
46 7 # .
47 7 # /
48 7 # 0
49 7 # 1
50 7 # 2
51 7 # 3
52 7 # 4
53 7 # 5
54 7 # 6
55 7 # 7
56 7 # 8
57 7 # 9
58 7 # :
59 7 # ;
60 7 # <
61 7 # =
62 7 # >
63 7 # ?
64 7 # @
65 7 # A
66 7 # B
67 7 # C
68 7 # D
69 7 # E
70 7 # F
71 7 # G
72 7 # H
73 7 # I
74 7 # J
75 7 # K
76 7 # L
77 7 # M
78 7 # N
79 7 # O
80 7 # P
81 7 # Q
82 7 # R
83 7 # S
84 7 # T
85 7 # U
86 7 # V
87 7 # W
88 7 # X
89 7 # Y
90 7 # Z
91 7 # [
92 7 # \
93 7 # ]
94 7 # ^
95 7 # _
96 7 # `
97 7 # a
98 7 # b
99 7 # c
100 7 # d
101 7 # e
102 7 # f
103 7 # g
104 7 # h
105 7 # i
106 7 # j
107 7 # k
108 7 # l
109 7 # m
110 7 # n
111 7 # o
112 7 # p
113 7 # q
114 7 # r
115 7 # s
116 7 # t
117 7 # u
118 7 # v
119 7 # w
120 7 # x
121 7 # y
122 7 # z
123 7 # {
124 7 # |
125 7 # }
126 7 # ~
192 7 # À
193 7 # Á
200 7 # È
201 7 # É
204 7 # Ì
205 7 # Í
210 7 # Ò
211 7 # Ó
217 7 # Ù
218 7 # Ú
209 7 # Ñ
199 7 # Ç
224 7 # à
225 7 # á
232 7 # è
233 7 # é
236 7 # ì
237 7 # í
242 7 # ò
243 7 # ó
249 7 # ù
250 7 # ú
241 7 # ñ
231 7 # ç
161 7 # ¡
191 7 # ¿
171 7 # «
187 7 # »
183 7 # ·
228 7 # ä (corazón)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 1001 B

View File

@@ -1,194 +0,0 @@
# box width
8
# box height
8
# 32 espacio ( )
7
# 33 !
7
# 34 "
7
# 35 #
7
# 36 $
7
# 37 %
7
# 38 &
7
# 39 '
7
# 40 (
7
# 41 )
7
# 42 *
7
# 43 +
7
# 44 ,
7
# 45 -
7
# 46 .
7
# 47 /
7
# 48 0
7
# 49 1
7
# 50 2
7
# 51 3
7
# 52 4
7
# 53 5
7
# 54 6
7
# 55 7
7
# 56 8
7
# 57 9
7
# 58 :
7
# 59 ;
7
# 60 <
7
# 61 =
7
# 62 >
7
# 63 ?
7
# 64 @
7
# 65 A
7
# 66 B
7
# 67 C
7
# 68 D
7
# 69 E
7
# 70 F
7
# 71 G
7
# 72 H
7
# 73 I
7
# 74 J
7
# 75 K
7
# 76 L
7
# 77 M
7
# 78 N
7
# 79 O
7
# 80 P
7
# 81 Q
7
# 82 R
7
# 83 S
7
# 84 T
7
# 85 U
7
# 86 V
7
# 87 W
7
# 88 X
7
# 89 Y
7
# 90 Z
7
# 91 [
7
# 92 \
7
# 93 ]
7
# 94 ^
7
# 95 _
7
# 96 `
7
# 97 a
7
# 98 b
7
# 99 c
7
# 100 d
7
# 101 e
7
# 102 f
7
# 103 g
7
# 104 h
7
# 105 i
7
# 106 j
7
# 107 k
7
# 108 l
7
# 109 m
7
# 110 n
7
# 111 o
7
# 112 p
7
# 113 q
7
# 114 r
7
# 115 s
7
# 116 t
7
# 117 u
7
# 118 v
7
# 119 w
7
# 120 x
7
# 121 y
7
# 122 z
7
# 123 {
7
# 124 |
7
# 125 }
7
# 126 ~
7

132
data/font/subatomic.fnt Normal file
View File

@@ -0,0 +1,132 @@
# Font: subatomic — generado desde atomics.TTF size 6
# Generado con tools/font_gen/font_gen.py
box_width 7
box_height 7
columns 15
# codepoint_decimal ancho_visual
32 4 # U+0020
33 1 # !
34 3 # "
35 5 # #
36 5 # $
37 5 # %
38 6 # &
39 1 # '
40 2 # (
41 2 # )
42 5 # *
43 5 # +
44 1 # ,
45 5 # -
46 1 # .
47 5 # /
48 5 # 0
49 2 # 1
50 5 # 2
51 5 # 3
52 5 # 4
53 5 # 5
54 5 # 6
55 5 # 7
56 5 # 8
57 5 # 9
58 1 # :
59 1 # ;
60 3 # <
61 5 # =
62 3 # >
63 4 # ?
64 5 # @
65 5 # A
66 5 # B
67 5 # C
68 5 # D
69 4 # E
70 5 # F
71 5 # G
72 5 # H
73 1 # I
74 5 # J
75 5 # K
76 4 # L
77 5 # M
78 5 # N
79 5 # O
80 5 # P
81 5 # Q
82 5 # R
83 5 # S
84 5 # T
85 5 # U
86 5 # V
87 5 # W
88 5 # X
89 5 # Y
90 5 # Z
91 2 # [
92 5 # \
93 2 # ]
94 3 # ^
95 5 # _
96 2 # `
97 4 # a
98 4 # b
99 3 # c
100 4 # d
101 4 # e
102 3 # f
103 4 # g
104 4 # h
105 1 # i
106 2 # j
107 3 # k
108 1 # l
109 5 # m
110 4 # n
111 4 # o
112 4 # p
113 4 # q
114 3 # r
115 4 # s
116 2 # t
117 4 # u
118 4 # v
119 5 # w
120 3 # x
121 4 # y
122 4 # z
123 3 # {
124 1 # |
125 3 # }
126 4 # ~
192 5 # À
193 5 # Á
200 4 # È
201 4 # É
204 1 # Ì
205 1 # Í
210 5 # Ò
211 5 # Ó
217 5 # Ù
218 5 # Ú
209 5 # Ñ
199 5 # Ç
224 4 # à
225 4 # á
232 4 # è
233 4 # é
236 1 # ì
237 1 # í
242 4 # ò
243 4 # ó
249 4 # ù
250 4 # ú
241 4 # ñ
231 3 # ç
161 1 # ¡
191 4 # ¿
171 3 # «
187 3 # »
183 1 # ·

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 648 B

View File

@@ -1,194 +0,0 @@
# box width
7
# box height
7
# 32 espacio ( )
4
# 33 !
1
# 34 "
3
# 35 #
5
# 36 $
5
# 37 %
5
# 38 &
6
# 39 '
1
# 40 (
2
# 41 )
2
# 42 *
5
# 43 +
5
# 44 ,
1
# 45 -
5
# 46 .
1
# 47 /
5
# 48 0
5
# 49 1
2
# 50 2
5
# 51 3
5
# 52 4
5
# 53 5
5
# 54 6
5
# 55 7
5
# 56 8
5
# 57 9
5
# 58 :
1
# 59 ;
2
# 60 <
3
# 61 =
5
# 62 >
3
# 63 ?
4
# 64 @
5
# 65 A
5
# 66 B
5
# 67 C
5
# 68 D
5
# 69 E
4
# 70 F
5
# 71 G
5
# 72 H
5
# 73 I
1
# 74 J
5
# 75 K
5
# 76 L
2
# 77 M
5
# 78 N
5
# 79 O
5
# 80 P
5
# 81 Q
5
# 82 R
5
# 83 S
5
# 84 T
5
# 85 U
5
# 86 V
5
# 87 W
5
# 88 X
5
# 89 Y
5
# 90 Z
5
# 91 [
2
# 92 \
5
# 93 ]
2
# 94 ^
3
# 95 _
5
# 96 `
2
# 97 a
4
# 98 b
4
# 99 c
3
# 100 d
4
# 101 e
4
# 102 f
3
# 103 g
4
# 104 h
4
# 105 i
1
# 106 j
2
# 107 k
3
# 108 l
1
# 109 m
5
# 110 n
4
# 111 o
4
# 112 p
4
# 113 q
4
# 114 r
3
# 115 s
4
# 116 t
2
# 117 u
4
# 118 v
4
# 119 w
5
# 120 x
3
# 121 y
4
# 122 z
4
# 123 {
0
# 124 |
0
# 125 }
0
# 126 ~
0

View File

@@ -1,118 +1,122 @@
# JailDoctor's Dilemma - Catalan Locale # JailDoctor's Dilemma - Catalan Locale
# lang: ca # lang: ca
# Nota: s'utilitzen nomes caracters ASCII per compatibilitat amb la font del joc
title: title:
marquee: "EH JAILEROS!! ES EL 2022 I ENCARA HO PETEM COM EL 1998!!! HEU SENTIT? ELS JAILGAMES HAN TORNAT!! SIII HAN TORNAT!! MES DE 10 TITOLS A LA CUINA DEL JAILDOC!! AIXO ES MOLT, PERO QUIN SERA EL PRIMER? TAMBE HI HA UN NOU APARELL QUE US FARA VOLAR EL CAP AMB JAILGAMES A TOT ARREU: P.A.C.O. PERO ESPERA! QUE ES AQUELLA BELLESA QUE VEIG ALLA? OOOH AQUELLA PETITA MINIASCII ES PUR AMOR!! VULL LLEPAR CADA BYTE! OH MERDA! I NO OBLIDEU PORTAR AQUELLS VELLS I GRASSOS JAILGAMES DE MS-DOS A GITHUB PER MANTENIR-LOS VIUS!! QUIN SERA EL PROPER LLANCAMENT DEL JAILDOC? QUIN PROJECTE COBRARA VIDA?? OH NANOS NO HO SABEM PERO AQUI PODEU TROBAR LA RESPOSTA, NOMES HEU DE COMPLETAR EL DILEMA DEL JAILDOCTOR ... PODEU?" marquee: "EI JAILERS!! ESTEM EN 2022 I ENCARA HO PETEM COM EN 1998!! QUÉ, HO HEU SENTIT O NO? ELS JAILGAMES HAN TORNAT!! SÍ, COLLONS, HAN TORNAT!! MÉS DE 10 TÍTOLS QUE EL JAILDOC TÉ EN LA CUINA A FOC LENT!! MOLT LENT!! AIXÒ ÉS UNA BARBARITAT, PERÒ... QUIN EIXIRÀ PRIMER? I ATENCIÓ, QUE HI HA UN APARELLET NOU QUE VOS FARÀ VOLAR EL CAP: EL P.A.C.O.! PERÒ UN MOMENT... QUÈ ÉS AQUELLA COSETA QUE VE PER ALLÀ? OOOH, AQUELLA MINIASCII ÉS AMOR DEL BO!! LI PEGARIA UNA LLEPAETA A CADA BYTE! OSTRES! I NO VOS OBLIDEU DE PUJAR AQUELLS JAILGAMES VELLS I PANXUTS DE MS-DOS A GITHUB, QUE SI NO ES PERDRAN!! QUIN SERÀ EL PRÒXIM PROJECTE DE JAILDOC? SERÀ UN PROJECTE DE MERDA? AI MARE... NI IDEA, PERÒ A PODEU SABER-HO SI RESOLEU EL DILEMA DEL JAILDOCTOR... VOS ATREVIU O QUÈ? VAAAAA!!!"
menu: menu:
play: "1. JUGAR" play: "1. JUGAR"
keyboard: "2. REDEFINIR TECLAT" keyboard: "2. REDEFINIR TECLES"
joystick: "3. REDEFINIR MANDO" joystick: "3. REDEFINIR MANDO"
projects: "4. PROJECTES" projects: "4. PROJECTES"
keys: keys:
prompt0: "PREM TECLA PER ESQUERRA" prompt0: "PREM UNA TECLA PER A ESQUERRA"
prompt1: "PREM TECLA PER DRETA" prompt1: "PREM UNA TECLA PER A DRETA"
prompt2: "PREM TECLA PER SALTAR" prompt2: "PREM UNA TECLA PER A SALTAR"
defined: "TECLES DEFINIDES" defined: "TECLES DEFINIDES"
label0: "ESQUERRA: " label0: "ESQUERRA: "
label1: "DRETA: " label1: "DRETA: "
label2: "SALTAR: " label2: "SALTAR: "
invalid: "TECLA INVALIDA! PROVA UNA ALTRA" invalid: "TECLA INVÀLIDA! PROVA'N UNA ALTRA"
already_used: "TECLA JA USADA! PROVA UNA ALTRA" already_used: "TECLA JA USADA! PROVA'N UNA ALTRA"
buttons: buttons:
prompt0: "PREM BOTO PER ESQUERRA" prompt0: "PREM UN BOTÓ PER A ESQUERRA"
prompt1: "PREM BOTO PER DRETA" prompt1: "PREM UN BOTÓ PER A DRETA"
prompt2: "PREM BOTO PER SALTAR" prompt2: "PREM UN BOTÓ PER A SALTAR"
defined: "BOTONS DEFINITS" defined: "BOTONS DEFINITS"
already_used: "BOTO JA USAT! PROVA UN ALTRE" already_used: "BOTÓ JA USAT! PROVA'N UN ALTRE"
projects: "PROJECTES" projects: "PROJECTES"
game_over: game_over:
title: "G A M E O V E R" title: "G A M E O V E R"
items: "OBJECTES: " items: "OBJECTES: "
rooms: "SALES: " rooms: "SALES: "
worst_nightmare: "EL TEU PITJOR MALSON ES" worst_nightmare: "EL TEU PITJOR MALSON ÉS"
ending: ending:
t0: "FINALMENT HO VA ACONSEGUIR" t0: "FINALMENT HO VA ACONSEGUIR"
t1: "ARRIBAR A LA JAIL" t1: "ARRIBAR A LA JAIL"
t2: "AMB TOTS ELS SEUS PROJECTES" t2: "AMB TOTS ELS SEUS PROJECTES"
t3: "A PUNT PER SER ALLIBERATS" t3: "A PUNT D'ALLIBERAR-LOS"
t4: "ALLI ESTAVEN TOTS ELS JAILERS" t4: "ALLÍ ESTAVEN TOTS ELS JAILERS"
t5: "ESPERANT QUE ELS JAILGAMES" t5: "ESPERANT QUE ELS JAILGAMES"
t6: "FOSSIN ALLIBERATS" t6: "FOREN ALLIBERATS"
t7: "HI HAVIA FINS I TOT BARRULLS" t7: "HI HAVIA FINS I TOT BARRULLS"
t8: "I BEGGINERS ENTRE LA MULTITUD" t8: "I BEGGINERS ENTRE LA GENT"
t9: "BRY ESTAVA PLORANT..." t9: "BRY ESTAVA PLORANT..."
t10: "PERO DE SOBTE ALGUNA COSA" t10: "PERÒ DE SOBTE ALGUNA COSA"
t11: "VA ATREURE LA SEVA ATENCIO" t11: "LI VA CRIDAR L'ATENCIÓ"
t12: "UN MUNT DE FERALLA!" t12: "UN MUNT DE FERRALLA!"
t13: "PLE DE TRASTOS QUE NO FUNCIONEN!!" t13: "PLE DE TRASTOS QUE NI ANAVEN!!"
t14: "I ALESHORES," t14: "I ALESHORES,"
t15: "QUARANTA NOUS PROJECTES" t15: "QUARANTA PROJECTES NOUS"
t16: "VAN NEIXER..." t16: "VAN NÀIXER..."
ending2: ending2:
starring: "PROTAGONISTES" starring: "PROTAGONISTES"
jaildoctor: "JAILDOCTOR" jaildoctor: "JAILDOCTOR"
thank_you: "GRACIES" thank_you: "GRÀCIES"
for_playing: "PER JUGAR!" for_playing: "PER JUGAR!"
credits: credits:
instructions: "INSTRUCCIONS:" instructions: "INSTRUCCIONS:"
l0: "AJUDA A JAILDOC A RECUPERAR" l0: "AJUDA A JAILDOC A RECUPERAR"
l1: "ELS SEUS PROJECTES I ANAR A" l1: "ELS SEUS PROJECTES I ARRIBAR"
l2: "LA JAIL PER ACABAR-LOS" l2: "A LA JAIL PER ACABAR-LOS"
keys: "TECLES:" keys: "TECLES:"
keys_move: "CURSORS PER MOURE I SALTAR" keys_move: "CURSORS PER A MOURE I SALTAR"
f8: "F8 ACTIVAR/DESACTIVAR MUSICA" f8: "F8 ACTIVAR/DESACTIVAR MÚSICA"
f11: "F11 PAUSAR EL JOC" f11: "F11 PAUSAR EL JOC"
f1f2: "F1-F2 MIDA DE LA FINESTRA" f1f2: "F1-F2 MIDA DE LA FINESTRA"
f3: "F3 PANTALLA COMPLETA" f3: "F3 PANTALLA COMPLETA"
f9: "F9 VORA DE LA PANTALLA" f9: "F9 VORA DE LA PANTALLA"
author: "UN JOC DE JAILDESIGNER" author: "UN JOC DE JAILDESIGNER"
date: "FET A L'ESTIU/TARDOR DEL 2022" date: "FET A L'ESTIU/TARDOR DEL 2022"
love: "M'ENCANTEN ELS JAILGAMES! " love: "I LOVE JAILGAMES! "
achievements: achievements:
header: "ASSOLIMENT DESBLOQUEJAT!" header: "ASSOLIMENT DESBLOQUEJAT!"
c1: "COSES BRILLANTS" c1: "COSES BRILLANTS"
d1: "Obteniu el 25% dels objectes" d1: "Aconseguiu el 25% dels objectes"
c2: "A MEITAT DE CAMI" c2: "A MITJAN CAMÍ"
d2: "Obteniu el 50% dels objectes" d2: "Aconseguiu el 50% dels objectes"
c3: "QUASI HI SOM" c3: "QUASI HI SOM"
d3: "Obteniu el 75% dels objectes" d3: "Aconseguiu el 75% dels objectes"
c4: "EL COL LECCIONISTA" c4: "EL COL·LECCIONISTA"
d4: "Obteniu el 100% dels objectes" d4: "Aconseguiu el 100% dels objectes"
c5: "PASSEJANT PER AQUI" c5: "PASSEJANT PER A"
d5: "Visiteu 20 sales" d5: "Visiteu 20 sales"
c6: "M'HE PERDUT" c6: "M'HE PERDUT"
d6: "Visiteu 40 sales" d6: "Visiteu 40 sales"
c7: "M'AGRADA EXPLORAR" c7: "M'AGRADA EXPLORAR"
d7: "Visiteu totes les sales" d7: "Visiteu totes les sales"
c8: "JA ESTA?" c8: "JA ESTÀ?"
d8: "Completa el joc" d8: "Completeu el joc"
c9: "EM VA XUCLAR UN FORAT" c9: "UN FORAT EM VA ENGOLIR"
d9: "Completa el joc sense entrar a la preso" d9: "Completeu el joc sense entrar a la presó"
c10: "ELS MEUS PROJECTES" c10: "ELS MEUS PROJECTES"
d10: "Completa el joc amb tots els objectes" d10: "Completeu el joc amb tots els objectes"
c11: "M'AGRADEN ELS MEUS AMICS DE COLORS" c11: "M'AGRADEN ELS MEUS AMICS DE COLORS"
d11: "Completa el joc sense morir" d11: "Completeu el joc sense morir"
c12: "PROJECTES MALS FETS DE PRESSA" c12: "PROJECTES A CORRE-CUITA"
d12: "Completa el joc en menys de 30 minuts" d12: "Completeu el joc en menys de 30 minuts"
ui: ui:
press_again_menu: "PREM DE NOU PER TORNAR AL MENU" press_again_menu: "PREM DE NOU PER TORNAR AL MENÚ"
press_again_exit: "PREM DE NOU PER SORTIR" press_again_exit: "PREM DE NOU PER EIXIR"
border_enabled: "BORDE ACTIVAT" border_enabled: "VORA ACTIVADA"
border_disabled: "BORDE DESACTIVAT" border_disabled: "VORA DESACTIVADA"
fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA" fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA"
fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA" fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA"
window_zoom: "ZOOM FINESTRA x" window_zoom: "ZOOM FINESTRA x"
postfx_enabled: "POSTFX ACTIVAT" shaders_enabled: "SHADERS ACTIVATS"
postfx_disabled: "POSTFX DESACTIVAT" shaders_disabled: "SHADERS DESACTIVATS"
shader: "SHADER"
postfx: "POSTFX" postfx: "POSTFX"
crtpi: "CRTPI"
supersampling_enabled: "SUPERMOSTREIG ACTIVAT"
supersampling_disabled: "SUPERMOSTREIG DESACTIVAT"
palette: "PALETA" palette: "PALETA"
palette_sort: "ORDENACIÓ PALETA"
integer_scale_enabled: "ESCALAT SENCER ACTIVAT" integer_scale_enabled: "ESCALAT SENCER ACTIVAT"
integer_scale_disabled: "ESCALAT SENCER DESACTIVAT" integer_scale_disabled: "ESCALAT SENCER DESACTIVAT"
vsync_enabled: "V-SYNC ACTIVAT" vsync_enabled: "V-SYNC ACTIVAT"
@@ -122,10 +126,12 @@ scoreboard:
items: "TRESORS PILLATS " items: "TRESORS PILLATS "
time: " HORA " time: " HORA "
rooms: "SALES" rooms: "SALES"
cheat_infinite_lives: "vides inf"
cheat_invincibility: "inv"
game: game:
music_enabled: "MUSICA ACTIVADA" music_enabled: "MÚSICA ACTIVADA"
music_disabled: "MUSICA DESACTIVADA" music_disabled: "MÚSICA DESACTIVADA"
paused: "JOC EN PAUSA" paused: "JOC EN PAUSA"
running: "JOC EN MARXA" running: "JOC EN MARXA"
enabled: " ACTIVAT" enabled: " ACTIVAT"

View File

@@ -108,10 +108,15 @@ ui:
fullscreen_enabled: "FULLSCREEN ENABLED" fullscreen_enabled: "FULLSCREEN ENABLED"
fullscreen_disabled: "FULLSCREEN DISABLED" fullscreen_disabled: "FULLSCREEN DISABLED"
window_zoom: "WINDOW ZOOM x" window_zoom: "WINDOW ZOOM x"
postfx_enabled: "POSTFX ENABLED" shaders_enabled: "SHADERS ON"
postfx_disabled: "POSTFX DISABLED" shaders_disabled: "SHADERS OFF"
shader: "SHADER"
postfx: "POSTFX" postfx: "POSTFX"
crtpi: "CRTPI"
supersampling_enabled: "SUPERSAMPLING ON"
supersampling_disabled: "SUPERSAMPLING OFF"
palette: "PALETTE" palette: "PALETTE"
palette_sort: "PALETTE SORT"
integer_scale_enabled: "INTEGER SCALE ENABLED" integer_scale_enabled: "INTEGER SCALE ENABLED"
integer_scale_disabled: "INTEGER SCALE DISABLED" integer_scale_disabled: "INTEGER SCALE DISABLED"
vsync_enabled: "V-SYNC ENABLED" vsync_enabled: "V-SYNC ENABLED"
@@ -121,6 +126,8 @@ scoreboard:
items: "ITEMS COLLECTED " items: "ITEMS COLLECTED "
time: " TIME " time: " TIME "
rooms: "ROOMS" rooms: "ROOMS"
cheat_infinite_lives: "inf lives"
cheat_invincibility: "inv"
game: game:
music_enabled: "MUSIC ENABLED" music_enabled: "MUSIC ENABLED"

View 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

View 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

View File

@@ -0,0 +1,19 @@
JASC-PAL
0100
16
22 23 26
127 6 34
214 36 17
255 132 38
255 209 0
250 253 255
255 128 164
255 38 116
148 33 106
67 0 103
35 73 117
104 174 212
191 255 60
16 210 117
0 120 153
0 40 89

19
data/palette/darkseed.pal Normal file
View 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
View File

@@ -0,0 +1,19 @@
JASC-PAL
0100
16
226 217 228
108 154 154
82 103 93
55 64 59
243 200 147
229 152 125
203 94 92
114 51 76
192 165 169
191 125 133
128 77 83
64 48 56
124 143 178
76 82 116
46 51 77
31 32 37

View 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

View File

@@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 3 60
0 82 96
0 157 74
10 255 82
0 56 132
0 138 197
0 247 255
255 92 255
172 41 206
96 0 136
177 5 133
255 0 78
42 46 121
78 110 168
173 212 250
255 255 255

View File

@@ -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

View File

@@ -2,18 +2,18 @@ JASC-PAL
0100 0100
16 16
26 28 44 26 28 44
41 54 111
51 60 87
86 108 134
59 93 201
37 113 121
93 39 93 93 39 93
177 62 83 177 62 83
56 183 100
167 240 112
65 166 246
115 239 247
239 125 87 239 125 87
255 205 117 255 205 117
148 176 194 167 240 112
56 183 100
37 113 121
41 54 111
59 93 201
65 166 246
115 239 247
244 244 244 244 244 244
148 176 194
86 108 134
51 60 87

View 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

View File

@@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
50 1 50
84 1 103
118 50 118
50 50 171
35 103 239
152 186 220
253 253 253
1 186 152
1 137 84
254 239 69
119 70 2
186 118 84
254 1 69
239 152 152
254 205 205

View File

@@ -4,17 +4,7 @@ frameWidth: 8
frameHeight: 16 frameHeight: 16
animations: animations:
- name: stand - name: default
speed: 0.1333
loop: 0
frames: [0]
- name: walk
speed: 0.1333 speed: 0.1333
loop: 0 loop: 0
frames: [0, 1, 2, 3] frames: [0, 1, 2, 3]
- name: walk_menu
speed: 0.0
loop: 0
frames: [0, 1, 2, 3]

View File

@@ -4,17 +4,7 @@ frameWidth: 8
frameHeight: 16 frameHeight: 16
animations: animations:
- name: stand - name: default
speed: 0.1333
loop: 0
frames: [0]
- name: walk
speed: 0.1333 speed: 0.1333
loop: 0 loop: 0
frames: [0, 1, 2, 3, 4, 5, 6, 7] frames: [0, 1, 2, 3, 4, 5, 6, 7]
- name: walk_menu
speed: 0.0
loop: 0
frames: [0, 1, 2, 3, 4, 5, 6, 7]

View File

@@ -1,7 +1,7 @@
# BIG JUMP # BIG JUMP
room: room:
name_en: "BIG JUMP" name_en: "BIG JUMP"
name_ca: "GRAN SALT" name_ca: "EL GRAN BOT"
bgColor: black bgColor: black
border: red border: red
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# WELCOME TO MY ABBEY # WELCOME TO MY ABBEY
room: room:
name_en: "WELCOME TO MY ABBEY" name_en: "WELCOME TO MY ABBEY"
name_ca: "BENVINGUT A LA MEVA ABADIA" name_ca: "BENVINGUT A LA MEUA ABADIA"
bgColor: blue bgColor: blue
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# TREE TOP # TREE TOP
room: room:
name_en: "TREE TOP" name_en: "TREE TOP"
name_ca: "AMUNT DE L'ARBRE" name_ca: "DALT DE L'ARBRE"
bgColor: bright_black bgColor: bright_black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# LAZY ROOM # LAZY ROOM
room: room:
name_en: "LAZY ROOM" name_en: "LAZY ROOM"
name_ca: "SALA DE LA PEREA" name_ca: "LA SALA GOSSA"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# KILLING SPREE # KILLING SPREE
room: room:
name_en: "KILLING SPREE" name_en: "KILLING SPREE"
name_ca: "MATANCA INDISCRIMINADA" name_ca: "MATANÇA INDISCRIMINADA"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# NOW THIS IS THE BATCAVE! # NOW THIS IS THE BATCAVE!
room: room:
name_en: "NOW THIS IS THE BATCAVE!" name_en: "NOW THIS IS THE BATCAVE!"
name_ca: "AQUESTA SI QUE ES LA BATCOVA!" name_ca: "ESTA SI QUE ES LA BATCOVA!"
bgColor: black bgColor: black
border: black border: black
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# THE FRIDGE # THE FRIDGE
room: room:
name_en: "THE FRIDGE" name_en: "THE FRIDGE"
name_ca: "LA NEVERA" name_ca: "EL FRIGO"
bgColor: blue bgColor: blue
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# I DID NOT COPY THIS ONE # I DID NOT COPY THIS ONE
room: room:
name_en: "I DID NOT COPY THIS ONE" name_en: "I DID NOT COPY THIS ONE"
name_ca: "ESTA NO LA HE COPIADA, NO" name_ca: "ESTA NO LA HE COPIADA"
bgColor: black bgColor: black
border: magenta border: magenta
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# THIS CAN'T BE THE BATCAVE # THIS CAN'T BE THE BATCAVE
room: room:
name_en: "THIS CAN'T BE THE BATCAVE" name_en: "THIS CAN'T BE THE BATCAVE"
name_ca: "AQUESTA NO POT SER LA BATCOVA" name_ca: "ESTA NO POT SER LA BATCOVA"
bgColor: black bgColor: black
border: cyan border: cyan
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# ENTER PAKU SIMBEL # ENTER PAKU SIMBEL
room: room:
name_en: "ENTER PAKU SIMBEL" name_en: "ENTER PAKU SIMBEL"
name_ca: "ENTRANT A PAKU SIMBEL" name_ca: "ACCEDINT A PAKU SIMBEL"
bgColor: bright_black bgColor: bright_black
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# } WE ALL LOVE JAILGAMES } # } WE ALL LOVE JAILGAMES }
room: room:
name_en: "} WE ALL LOVE JAILGAMES }" name_en: "ä WE ALL LOVE JAILGAMES ä"
name_ca: "} AMOR PELS JAILGAMES }" name_ca: "ä AMOR PELS JAILGAMES ä"
bgColor: black bgColor: black
border: bright_black border: bright_black
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# PREVENT THE CRISIS # PREVENT THE CRISIS
room: room:
name_en: "PREVENT THE CRISIS" name_en: "PREVENT THE CRISIS"
name_ca: "PREVEU LA CRISI" name_ca: "EVITA LA CRISI"
bgColor: black bgColor: black
border: bright_magenta border: bright_magenta
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# AROUND WITH ME # AROUND WITH ME
room: room:
name_en: "AROUND WITH ME" name_en: "AROUND WITH ME"
name_ca: "VOLTA AMB MI" name_ca: "AROUNDA AMB MI"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# SANDWITCH AND COUNTER # SANDWITCH AND COUNTER
room: room:
name_en: "SANDWITCH AND COUNTER" name_en: "SANDWITCH AND COUNTER"
name_ca: "SANDVITX I COUNTER S." name_ca: "SANDVITX I COUNTER STRIKE"
bgColor: black bgColor: black
border: cyan border: cyan
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# FEEL THE HEAT # FEEL THE HEAT
room: room:
name_en: "FEEL THE HEAT" name_en: "FEEL THE HEAT"
name_ca: "NOTA LA CALOR" name_ca: "NOTA EL CALORET"
bgColor: bright_black bgColor: bright_black
border: bright_yellow border: bright_yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# WE NEED A ROBOT # WE NEED A ROBOT
room: room:
name_en: "WE NEED A ROBOT" name_en: "WE NEED A JAILROBOT"
name_ca: "NECESSITEM UN ROBOT" name_ca: "NECESSITEM UN JAILROBOT"
bgColor: black bgColor: black
border: red border: red
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# STORED JAILGAMES # STORED JAILGAMES
room: room:
name_en: "STORED JAILGAMES" name_en: "STORED JAILGAMES"
name_ca: "JAILGAMES EMMAGATZEMATS" name_ca: "EL MAGATZEM DE JAILGAMES"
bgColor: black bgColor: black
border: blue border: blue
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# P.A.C.O. WORKSHOP # P.A.C.O. WORKSHOP
room: room:
name_en: "P.A.C.O. WORKSHOP" name_en: "P.A.C.O. WORKSHOP"
name_ca: "TALLER DE P.A.C.O." name_ca: "EL TALLER DE P.A.C.O."
bgColor: black bgColor: black
border: yellow border: yellow
tileSetFile: standard.gif tileSetFile: standard.gif

View File

@@ -1,7 +1,7 @@
# CHIRPING # CHIRPING
room: room:
name_en: "CHIRPING DEVELOPMENT" name_en: "CHIRPING"
name_ca: "DESENVOLUPANT CHIRPING" name_ca: "CHIRPING"
bgColor: black bgColor: black
border: magenta border: magenta
tileSetFile: standard.gif tileSetFile: standard.gif

157
data/shaders/crtpi.glsl Normal file
View File

@@ -0,0 +1,157 @@
#version 330 core
// Configuración
#define SCANLINES
#define MULTISAMPLE
#define GAMMA
//#define FAKE_GAMMA
//#define CURVATURE
//#define SHARPER
#define MASK_TYPE 2
#define CURVATURE_X 0.05
#define CURVATURE_Y 0.1
#define MASK_BRIGHTNESS 0.80
#define SCANLINE_WEIGHT 6.0
#define SCANLINE_GAP_BRIGHTNESS 0.12
#define BLOOM_FACTOR 3.5
#define INPUT_GAMMA 2.4
#define OUTPUT_GAMMA 2.2
// Inputs desde vertex shader
in vec2 vTexCoord;
in float vFilterWidth;
#if defined(CURVATURE)
in vec2 vScreenScale;
#endif
// Output
out vec4 FragColor;
// Uniforms
uniform sampler2D Texture;
uniform vec2 TextureSize;
#if defined(CURVATURE)
vec2 Distort(vec2 coord)
{
vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y);
vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION);
coord *= vScreenScale;
coord -= vec2(0.5);
float rsq = coord.x * coord.x + coord.y * coord.y;
coord += coord * (CURVATURE_DISTORTION * rsq);
coord *= barrelScale;
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
coord = vec2(-1.0);
else
{
coord += vec2(0.5);
coord /= vScreenScale;
}
return coord;
}
#endif
float CalcScanLineWeight(float dist)
{
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
}
float CalcScanLine(float dy)
{
float scanLineWeight = CalcScanLineWeight(dy);
#if defined(MULTISAMPLE)
scanLineWeight += CalcScanLineWeight(dy - vFilterWidth);
scanLineWeight += CalcScanLineWeight(dy + vFilterWidth);
scanLineWeight *= 0.3333333;
#endif
return scanLineWeight;
}
void main()
{
#if defined(CURVATURE)
vec2 texcoord = Distort(vTexCoord);
if (texcoord.x < 0.0) {
FragColor = vec4(0.0);
return;
}
#else
vec2 texcoord = vTexCoord;
#endif
vec2 texcoordInPixels = texcoord * TextureSize;
#if defined(SHARPER)
vec2 tempCoord = floor(texcoordInPixels) + 0.5;
vec2 coord = tempCoord / TextureSize;
vec2 deltas = texcoordInPixels - tempCoord;
float scanLineWeight = CalcScanLine(deltas.y);
vec2 signs = sign(deltas);
deltas.x *= 2.0;
deltas = deltas * deltas;
deltas.y = deltas.y * deltas.y;
deltas.x *= 0.5;
deltas.y *= 8.0;
deltas /= TextureSize;
deltas *= signs;
vec2 tc = coord + deltas;
#else
float tempY = floor(texcoordInPixels.y) + 0.5;
float yCoord = tempY / TextureSize.y;
float dy = texcoordInPixels.y - tempY;
float scanLineWeight = CalcScanLine(dy);
float signY = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0;
dy /= TextureSize.y;
dy *= signY;
vec2 tc = vec2(texcoord.x, yCoord + dy);
#endif
vec3 colour = texture(Texture, tc).rgb;
#if defined(SCANLINES)
#if defined(GAMMA)
#if defined(FAKE_GAMMA)
colour = colour * colour;
#else
colour = pow(colour, vec3(INPUT_GAMMA));
#endif
#endif
scanLineWeight *= BLOOM_FACTOR;
colour *= scanLineWeight;
#if defined(GAMMA)
#if defined(FAKE_GAMMA)
colour = sqrt(colour);
#else
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
#endif
#endif
#endif
#if MASK_TYPE == 0
FragColor = vec4(colour, 1.0);
#elif MASK_TYPE == 1
float whichMask = fract(gl_FragCoord.x * 0.5);
vec3 mask;
if (whichMask < 0.5)
mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS);
else
mask = vec3(1.0, MASK_BRIGHTNESS, 1.0);
FragColor = vec4(colour * mask, 1.0);
#elif MASK_TYPE == 2
float whichMask = fract(gl_FragCoord.x * 0.3333333);
vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS);
if (whichMask < 0.3333333)
mask.x = 1.0;
else if (whichMask < 0.6666666)
mask.y = 1.0;
else
mask.z = 1.0;
FragColor = vec4(colour * mask, 1.0);
#endif
}

48
data/shaders/crtpi.vert Normal file
View File

@@ -0,0 +1,48 @@
#version 330 core
// Configuración
#define SCANLINES
#define MULTISAMPLE
#define GAMMA
//#define FAKE_GAMMA
//#define CURVATURE
//#define SHARPER
#define MASK_TYPE 2
#define CURVATURE_X 0.05
#define CURVATURE_Y 0.1
#define MASK_BRIGHTNESS 0.80
#define SCANLINE_WEIGHT 6.0
#define SCANLINE_GAP_BRIGHTNESS 0.12
#define BLOOM_FACTOR 3.5
#define INPUT_GAMMA 2.4
#define OUTPUT_GAMMA 2.2
// Inputs (desde VAO)
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
// Outputs al fragment shader
out vec2 vTexCoord;
out float vFilterWidth;
#if defined(CURVATURE)
out vec2 vScreenScale;
#endif
// Uniforms
uniform vec2 TextureSize;
void main()
{
#if defined(CURVATURE)
vScreenScale = vec2(1.0, 1.0);
#endif
// Calcula filterWidth dinámicamente basándose en la altura de la textura
vFilterWidth = (768.0 / TextureSize.y) / 3.0;
// Pasar coordenadas de textura (invertir Y para SDL)
vTexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y) * 1.0001;
// Posición del vértice (ya en espacio de clip [-1, 1])
gl_Position = vec4(aPosition, 0.0, 1.0);
}

View File

@@ -0,0 +1,152 @@
#version 450
// Vulkan GLSL fragment shader — CRT-Pi PostFX
// Algoritmo de scanlines continuas con pesos gaussianos, bloom y máscara de fósforo.
// Basado en el shader CRT-Pi original (GLSL 3.3), portado a GLSL 4.50 con parámetros uniformes.
//
// Compile: glslc -fshader-stage=frag --target-env=vulkan1.0 crtpi_frag.glsl -o crtpi_frag.spv
// xxd -i crtpi_frag.spv > ../../source/core/rendering/sdl3gpu/crtpi_frag_spv.h
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D Texture;
layout(set = 3, binding = 0) uniform CrtPiBlock {
// vec4 #0
float scanline_weight; // Ajuste gaussiano de scanlines (default 6.0)
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
float bloom_factor; // Factor de brillo en zonas iluminadas (default 3.5)
float input_gamma; // Gamma de entrada — linealización (default 2.4)
// vec4 #1
float output_gamma; // Gamma de salida — codificación (default 2.2)
float mask_brightness; // Brillo sub-píxeles de la máscara (default 0.80)
float curvature_x; // Distorsión barrel eje X (default 0.05)
float curvature_y; // Distorsión barrel eje Y (default 0.10)
// vec4 #2
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
int enable_scanlines; // 0 = off, 1 = on
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico de scanlines)
int enable_gamma; // 0 = off, 1 = on
// vec4 #3
int enable_curvature; // 0 = off, 1 = on
int enable_sharper; // 0 = off, 1 = on
float texture_width; // Ancho del canvas lógico en píxeles
float texture_height; // Alto del canvas lógico en píxeles
} u;
// Distorsión barrel CRT
vec2 distort(vec2 coord, vec2 screen_scale) {
vec2 curvature = vec2(u.curvature_x, u.curvature_y);
vec2 barrel_scale = 1.0 - (0.23 * curvature);
coord *= screen_scale;
coord -= vec2(0.5);
float rsq = coord.x * coord.x + coord.y * coord.y;
coord += coord * (curvature * rsq);
coord *= barrel_scale;
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) {
return vec2(-1.0); // fuera de pantalla
}
coord += vec2(0.5);
coord /= screen_scale;
return coord;
}
float calcScanLineWeight(float dist) {
return max(1.0 - dist * dist * u.scanline_weight, u.scanline_gap_brightness);
}
float calcScanLine(float dy, float filter_width) {
float weight = calcScanLineWeight(dy);
if (u.enable_multisample != 0) {
weight += calcScanLineWeight(dy - filter_width);
weight += calcScanLineWeight(dy + filter_width);
weight *= 0.3333333;
}
return weight;
}
void main() {
vec2 tex_size = vec2(u.texture_width, u.texture_height);
// filterWidth: equivalente al original (768.0 / TextureSize.y) / 3.0
float filter_width = (768.0 / u.texture_height) / 3.0;
vec2 texcoord = v_uv;
// Curvatura barrel opcional
if (u.enable_curvature != 0) {
texcoord = distort(texcoord, vec2(1.0, 1.0));
if (texcoord.x < 0.0) {
out_color = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
}
vec2 texcoord_in_pixels = texcoord * tex_size;
vec2 tc;
float scan_line_weight;
if (u.enable_sharper != 0) {
// Modo SHARPER: filtrado bicúbico-like con subpixel sharpen
vec2 temp_coord = floor(texcoord_in_pixels) + 0.5;
tc = temp_coord / tex_size;
vec2 deltas = texcoord_in_pixels - temp_coord;
scan_line_weight = calcScanLine(deltas.y, filter_width);
vec2 signs = sign(deltas);
deltas.x *= 2.0;
deltas = deltas * deltas;
deltas.y = deltas.y * deltas.y;
deltas.x *= 0.5;
deltas.y *= 8.0;
deltas /= tex_size;
deltas *= signs;
tc = tc + deltas;
} else {
// Modo estándar
float temp_y = floor(texcoord_in_pixels.y) + 0.5;
float y_coord = temp_y / tex_size.y;
float dy = texcoord_in_pixels.y - temp_y;
scan_line_weight = calcScanLine(dy, filter_width);
float sign_y = sign(dy);
dy = dy * dy;
dy = dy * dy;
dy *= 8.0;
dy /= tex_size.y;
dy *= sign_y;
tc = vec2(texcoord.x, y_coord + dy);
}
vec3 colour = texture(Texture, tc).rgb;
if (u.enable_scanlines != 0) {
if (u.enable_gamma != 0) {
colour = pow(colour, vec3(u.input_gamma));
}
colour *= scan_line_weight * u.bloom_factor;
if (u.enable_gamma != 0) {
colour = pow(colour, vec3(1.0 / u.output_gamma));
}
}
// Máscara de fósforo
if (u.mask_type == 1) {
float which_mask = fract(gl_FragCoord.x * 0.5);
vec3 mask = (which_mask < 0.5)
? vec3(u.mask_brightness, 1.0, u.mask_brightness)
: vec3(1.0, u.mask_brightness, 1.0);
colour *= mask;
} else if (u.mask_type == 2) {
float which_mask = fract(gl_FragCoord.x * 0.3333333);
vec3 mask = vec3(u.mask_brightness);
if (which_mask < 0.3333333)
mask.x = 1.0;
else if (which_mask < 0.6666666)
mask.y = 1.0;
else
mask.z = 1.0;
colour *= mask;
}
out_color = vec4(colour, 1.0);
}

BIN
data/shaders/crtpi_frag.spv Normal file

Binary file not shown.

View File

@@ -0,0 +1,48 @@
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D source;
layout(set = 3, binding = 0) uniform DownscaleUniforms {
int algorithm; // 0 = Lanczos2 (ventana 2, ±2 taps), 1 = Lanczos3 (ventana 3, ±3 taps)
float pad0;
float pad1;
float pad2;
} u;
// Kernel Lanczos normalizado: sinc(t) * sinc(t/a) para |t| < a, 0 fuera.
float lanczos(float t, float a) {
t = abs(t);
if (t < 0.0001) { return 1.0; }
if (t >= a) { return 0.0; }
const float PI = 3.14159265358979;
float pt = PI * t;
return (a * sin(pt) * sin(pt / a)) / (pt * pt);
}
void main() {
vec2 src_size = vec2(textureSize(source, 0));
// Posición en coordenadas de texel (centros de texel en N+0.5)
vec2 p = v_uv * src_size;
vec2 p_floor = floor(p);
float a = (u.algorithm == 0) ? 2.0 : 3.0;
int win = int(a);
vec4 color = vec4(0.0);
float weight_sum = 0.0;
for (int j = -win; j <= win; j++) {
for (int i = -win; i <= win; i++) {
// Centro del texel (i,j) relativo a p_floor
vec2 tap_center = p_floor + vec2(float(i), float(j)) + 0.5;
vec2 offset = tap_center - p;
float w = lanczos(offset.x, a) * lanczos(offset.y, a);
color += texture(source, tap_center / src_size) * w;
weight_sum += w;
}
}
out_color = (weight_sum > 0.0) ? (color / weight_sum) : vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@@ -22,6 +22,10 @@ layout(set = 3, binding = 0) uniform PostFXUniforms {
float gamma_strength; float gamma_strength;
float curvature; float curvature;
float bleeding; float bleeding;
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
float time; // seconds since SDL init
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — 48 bytes total (3 × 16)
} u; } u;
// YCbCr helpers for NTSC bleeding // YCbCr helpers for NTSC bleeding
@@ -64,23 +68,25 @@ void main() {
// Muestra base // Muestra base
vec3 base = texture(scene, uv).rgb; vec3 base = texture(scene, uv).rgb;
// Sangrado NTSC — difuminado horizontal de crominancia // Sangrado NTSC — difuminado horizontal de crominancia.
// step = 1 pixel lógico de juego en UV (corrige SS: textureSize.x = game_w * oversample).
vec3 colour; vec3 colour;
if (u.bleeding > 0.0) { if (u.bleeding > 0.0) {
float tw = float(textureSize(scene, 0).x); float tw = float(textureSize(scene, 0).x);
float step = u.oversample / tw; // 1 pixel lógico en UV
vec3 ycc = rgb_to_ycc(base); vec3 ycc = rgb_to_ycc(base);
vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0/tw, 0.0)).rgb); vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0*step, 0.0)).rgb);
vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0/tw, 0.0)).rgb); vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0*step, 0.0)).rgb);
vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0/tw, 0.0)).rgb); vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0*step, 0.0)).rgb);
vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + vec2(2.0/tw, 0.0)).rgb); vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + vec2(2.0*step, 0.0)).rgb);
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0; ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
colour = mix(base, ycc_to_rgb(ycc), u.bleeding); colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
} else { } else {
colour = base; colour = base;
} }
// Aberración cromática // Aberración cromática (drift animado con time para efecto NTSC real)
float ca = u.chroma_strength * 0.005; float ca = u.chroma_strength * 0.005 * (1.0 + 0.15 * sin(u.time * 7.3));
colour.r = texture(scene, uv + vec2(ca, 0.0)).r; colour.r = texture(scene, uv + vec2(ca, 0.0)).r;
colour.b = texture(scene, uv - vec2(ca, 0.0)).b; colour.b = texture(scene, uv - vec2(ca, 0.0)).b;
@@ -90,14 +96,24 @@ void main() {
colour = mix(colour, lin, u.gamma_strength); colour = mix(colour, lin, u.gamma_strength);
} }
// Scanlines // Scanlines — proporción 2/3 brillantes + 1/3 oscuras por fila lógica.
float texHeight = float(textureSize(scene, 0).y); // Casos especiales: 1 subfila → sin efecto; 2 subfilas → 1+1 (50/50).
float scaleY = u.screen_height / texHeight; // Constantes ajustables:
float screenY = uv.y * u.screen_height; const float SCAN_DARK_RATIO = 0.333; // fracción de subfilas oscuras (ps >= 3)
float posInRow = mod(screenY, scaleY); const float SCAN_DARK_FLOOR = 0.42; // multiplicador de brillo de subfilas oscuras
float scanLineDY = posInRow / scaleY - 0.5; if (u.scanline_strength > 0.0) {
float scan = max(1.0 - scanLineDY * scanLineDY * 6.0, 0.12) * 3.5; float ps = max(1.0, round(u.pixel_scale));
colour *= mix(1.0, scan, u.scanline_strength); float frac_in_row = fract(uv.y * u.screen_height);
float row_pos = floor(frac_in_row * ps);
// bright_rows: cuántas subfilas son brillantes
// ps==1 → ps (todo brillante → is_dark nunca se activa)
// ps==2 → 1 brillante + 1 oscura
// ps>=3 → floor(ps * (1 - DARK_RATIO)) brillantes
float bright_rows = (ps < 2.0) ? ps : ((ps < 3.0) ? 1.0 : floor(ps * (1.0 - SCAN_DARK_RATIO)));
float is_dark = step(bright_rows, row_pos);
float scan = mix(1.0, SCAN_DARK_FLOOR, is_dark);
colour *= mix(1.0, scan, u.scanline_strength);
}
if (u.gamma_strength > 0.0) { if (u.gamma_strength > 0.0) {
vec3 enc = pow(colour, vec3(1.0 / 2.2)); vec3 enc = pow(colour, vec3(1.0 / 2.2));
@@ -109,7 +125,8 @@ void main() {
float vignette = 1.0 - dot(d, d) * u.vignette_strength; float vignette = 1.0 - dot(d, d) * u.vignette_strength;
colour *= clamp(vignette, 0.0, 1.0); colour *= clamp(vignette, 0.0, 1.0);
// Máscara de fósforo RGB // Máscara de fósforo RGB — después de scanlines (orden original):
// filas brillantes saturadas → máscara invisible, filas oscuras → RGB visible.
if (u.mask_strength > 0.0) { if (u.mask_strength > 0.0) {
float whichMask = fract(gl_FragCoord.x * 0.3333333); float whichMask = fract(gl_FragCoord.x * 0.3333333);
vec3 mask = vec3(0.80); vec3 mask = vec3(0.80);
@@ -122,5 +139,11 @@ void main() {
colour = mix(colour, colour * mask, u.mask_strength); colour = mix(colour, colour * mask, u.mask_strength);
} }
// Parpadeo de fósforo CRT (~50 Hz)
if (u.flicker > 0.0) {
float flicker_wave = sin(u.time * 100.0) * 0.5 + 0.5;
colour *= 1.0 - u.flicker * 0.04 * flicker_wave;
}
out_color = vec4(colour, 1.0); out_color = vec4(colour, 1.0);
} }

Binary file not shown.

15
data/shaders/upscale.frag Normal file
View File

@@ -0,0 +1,15 @@
#version 450
// Vulkan GLSL fragment shader — Nearest-neighbour upscale pass
// Used as the first render pass when supersampling is active.
// Compile: glslc upscale.frag -o upscale.frag.spv
// xxd -i upscale.frag.spv > ../../source/core/rendering/sdl3gpu/upscale_frag_spv.h
layout(location = 0) in vec2 v_uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D scene;
void main() {
out_color = texture(scene, v_uv);
}

Binary file not shown.

View File

@@ -41,7 +41,7 @@ void Audio::update() {
} }
// Reproduce la música // Reproduce la música
void Audio::playMusic(const std::string& name, const int loop) { void Audio::playMusic(const std::string& name, const int loop) { // NOLINT(readability-convert-member-functions-to-static)
bool new_loop = (loop != 0); bool new_loop = (loop != 0);
// Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada // Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada
@@ -71,7 +71,7 @@ void Audio::playMusic(const std::string& name, const int loop) {
} }
// Pausa la música // Pausa la música
void Audio::pauseMusic() { void Audio::pauseMusic() { // NOLINT(readability-convert-member-functions-to-static)
if (music_enabled_ && music_.state == MusicState::PLAYING) { if (music_enabled_ && music_.state == MusicState::PLAYING) {
JA_PauseMusic(); JA_PauseMusic();
music_.state = MusicState::PAUSED; music_.state = MusicState::PAUSED;
@@ -79,7 +79,7 @@ void Audio::pauseMusic() {
} }
// Continua la música pausada // Continua la música pausada
void Audio::resumeMusic() { void Audio::resumeMusic() { // NOLINT(readability-convert-member-functions-to-static)
if (music_enabled_ && music_.state == MusicState::PAUSED) { if (music_enabled_ && music_.state == MusicState::PAUSED) {
JA_ResumeMusic(); JA_ResumeMusic();
music_.state = MusicState::PLAYING; music_.state = MusicState::PLAYING;
@@ -87,7 +87,7 @@ void Audio::resumeMusic() {
} }
// Detiene la música // Detiene la música
void Audio::stopMusic() { void Audio::stopMusic() { // NOLINT(readability-make-member-function-const)
if (music_enabled_) { if (music_enabled_) {
JA_StopMusic(); JA_StopMusic();
music_.state = MusicState::STOPPED; music_.state = MusicState::STOPPED;
@@ -177,6 +177,18 @@ void Audio::initSDLAudio() {
JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2);
enable(Options::audio.enabled); enable(Options::audio.enabled);
// Aplicar estado de música y sonido guardado en las opciones.
// enable() ya aplica los volúmenes, pero no toca music_enabled_/sound_enabled_.
// Si alguno está desactivado, hay que forzar el volumen a 0 en el backend.
if (!Options::audio.music.enabled) {
setMusicVolume(0.0F); // music_enabled_=true aún → llega a JA
enableMusic(false);
}
if (!Options::audio.sound.enabled) {
setSoundVolume(0.0F); // sound_enabled_=true aún → llega a JA
enableSound(false);
}
std::cout << "\n** AUDIO SYSTEM **\n"; std::cout << "\n** AUDIO SYSTEM **\n";
std::cout << "Audio system initialized successfully\n"; std::cout << "Audio system initialized successfully\n";
} }

View File

@@ -5,13 +5,15 @@
#include <string> // Para allocator, operator+, char_traits, string #include <string> // Para allocator, operator+, char_traits, string
#include <vector> // Para vector #include <vector> // Para vector
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
#include "core/locale/locale.hpp" // Para Locale #include "core/locale/locale.hpp" // Para Locale
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/render_info.hpp" // Para RenderInfo
#include "game/options.hpp" // Para Options, options, OptionsVideo, Section #include "core/rendering/screen.hpp" // Para Screen
#include "game/scene_manager.hpp" // Para SceneManager #include "game/options.hpp" // Para Options, options, OptionsVideo, Section
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText #include "game/scene_manager.hpp" // Para SceneManager
#include "utils/utils.hpp" // Para stringInVector #include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier, NotificationText
#include "utils/utils.hpp" // Para stringInVector
namespace GlobalInputs { namespace GlobalInputs {
@@ -24,7 +26,7 @@ namespace GlobalInputs {
if (stringInVector(Notifier::get()->getCodes(), CODE)) { if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::TITLE; SceneManager::current = SceneManager::Scene::TITLE;
} else { } else {
Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE); Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE); // NOLINT(readability-static-accessed-through-instance)
} }
return; return;
} }
@@ -44,7 +46,7 @@ namespace GlobalInputs {
if (stringInVector(Notifier::get()->getCodes(), CODE)) { if (stringInVector(Notifier::get()->getCodes(), CODE)) {
SceneManager::current = SceneManager::Scene::QUIT; SceneManager::current = SceneManager::Scene::QUIT;
} else { } else {
Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE); Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE); // NOLINT(readability-static-accessed-through-instance)
} }
} }
@@ -68,61 +70,79 @@ namespace GlobalInputs {
void handleToggleBorder() { void handleToggleBorder() {
Screen::get()->toggleBorder(); Screen::get()->toggleBorder();
Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleVideoMode() { void handleToggleVideoMode() {
Screen::get()->toggleVideoMode(); Screen::get()->toggleVideoMode();
Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")}); Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleDecWindowZoom() { void handleDecWindowZoom() {
if (Screen::get()->decWindowZoom()) { if (Screen::get()->decWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); // NOLINT(readability-static-accessed-through-instance)
} }
} }
void handleIncWindowZoom() { void handleIncWindowZoom() {
if (Screen::get()->incWindowZoom()) { if (Screen::get()->incWindowZoom()) {
Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); // NOLINT(readability-static-accessed-through-instance)
} }
} }
void handleTogglePostFX() { void handleToggleShaders() {
Screen::get()->togglePostFX(); Screen::get()->toggleShaders();
Notifier::get()->show({Locale::get()->get(Options::video.postfx ? "ui.postfx_enabled" : "ui.postfx_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.shader.enabled ? "ui.shaders_enabled" : "ui.shaders_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleNextPostFXPreset() { void handleNextShaderPreset() {
if (!Options::postfx_presets.empty()) { if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size()); if (!Options::crtpi_presets.empty()) {
Screen::get()->reloadPostFX(); Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)].name}); Screen::get()->reloadCrtPi();
Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name)}); // NOLINT(readability-static-accessed-through-instance)
}
} else {
if (!Options::postfx_presets.empty()) {
Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Screen::get()->reloadPostFX();
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name)}); // NOLINT(readability-static-accessed-through-instance)
}
} }
} }
void handleNextShader() {
Screen::get()->nextShader();
Notifier::get()->show({Locale::get()->get("ui.shader") + " " + // NOLINT(readability-static-accessed-through-instance)
(Options::video.shader.current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX")});
}
void handleNextPalette() { void handleNextPalette() {
Screen::get()->nextPalette(); Screen::get()->nextPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance)
} }
void handlePreviousPalette() { void handlePreviousPalette() {
Screen::get()->previousPalette(); Screen::get()->previousPalette();
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance)
}
void handleNextPaletteSortMode() {
Screen::get()->nextPaletteSortMode();
Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleIntegerScale() { void handleToggleIntegerScale() {
Screen::get()->toggleIntegerScale(); Screen::get()->toggleIntegerScale();
Screen::get()->setVideoMode(Options::video.fullscreen); Screen::get()->setVideoMode(Options::video.fullscreen);
Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleToggleVSync() { void handleToggleVSync() {
Screen::get()->toggleVSync(); Screen::get()->toggleVSync();
Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")}); Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")}); // NOLINT(readability-static-accessed-through-instance)
} }
// Detecta qué acción global ha sido presionada (si alguna) // Detecta qué acción global ha sido presionada (si alguna)
auto getPressedAction() -> InputAction { auto getPressedAction() -> InputAction {
if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
@@ -145,17 +165,25 @@ namespace GlobalInputs {
return InputAction::WINDOW_INC_ZOOM; return InputAction::WINDOW_INC_ZOOM;
} }
} }
if (Input::get()->checkAction(InputAction::TOGGLE_POSTFX, Input::DO_NOT_ALLOW_REPEAT)) { if (Screen::get()->isHardwareAccelerated()) {
if (Options::video.postfx && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) { if (Input::get()->checkAction(InputAction::TOGGLE_SHADER, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::NEXT_POSTFX_PRESET; if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::TOGGLE_SUPERSAMPLING; // Ctrl+F4
}
if (Options::video.shader.enabled && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
return InputAction::NEXT_SHADER_PRESET; // Shift+F4
}
return InputAction::TOGGLE_SHADER; // F4
} }
return InputAction::TOGGLE_POSTFX;
} }
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::NEXT_PALETTE; if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::PREVIOUS_PALETTE; // Ctrl+F5
}
return InputAction::NEXT_PALETTE; // F5
} }
if (Input::get()->checkAction(InputAction::PREVIOUS_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::NEXT_PALETTE_SORT, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::PREVIOUS_PALETTE; return InputAction::NEXT_PALETTE_SORT; // F6
} }
if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_INTEGER_SCALE; return InputAction::TOGGLE_INTEGER_SCALE;
@@ -163,11 +191,11 @@ namespace GlobalInputs {
if (Input::get()->checkAction(InputAction::TOGGLE_VSYNC, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::TOGGLE_VSYNC, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_VSYNC; return InputAction::TOGGLE_VSYNC;
} }
if (Input::get()->checkAction(InputAction::TOGGLE_DEBUG, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::TOGGLE_INFO, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_DEBUG; return InputAction::TOGGLE_INFO;
} }
if (Input::get()->checkAction(InputAction::SHOW_DEBUG_INFO, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::SHOW_DEBUG_INFO; return InputAction::TOGGLE_CONSOLE;
} }
return InputAction::NONE; return InputAction::NONE;
} }
@@ -178,19 +206,35 @@ namespace GlobalInputs {
// Comprueba los inputs que se pueden introducir en cualquier sección del juego // Comprueba los inputs que se pueden introducir en cualquier sección del juego
void handle() { void handle() {
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q) const bool CONSOLE_ACTIVE = Console::get() != nullptr && Console::get()->isActive();
if (Options::kiosk.enabled) {
SDL_Keymod mod = SDL_GetModState(); if (CONSOLE_ACTIVE) {
const bool* ks = SDL_GetKeyboardState(nullptr); // TAB/ESC cierran la consola en lugar de ejecutar sus acciones normales
if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) { if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) ||
SceneManager::current = SceneManager::Scene::QUIT; Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
Console::get()->toggle();
return; return;
} }
} else {
// Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
if (Options::kiosk.enabled) {
SDL_Keymod mod = SDL_GetModState();
const bool* ks = SDL_GetKeyboardState(nullptr);
if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) {
SceneManager::current = SceneManager::Scene::QUIT;
return;
}
}
} }
// Detectar qué acción global está siendo presionada // Detectar qué acción global está siendo presionada
InputAction action = getPressedAction(); InputAction action = getPressedAction();
// Con consola activa, ACCEPT (saltar sección) y EXIT están bloqueados
if (CONSOLE_ACTIVE && (action == InputAction::ACCEPT || action == InputAction::EXIT)) {
return;
}
// Ejecutar el handler correspondiente usando switch statement // Ejecutar el handler correspondiente usando switch statement
switch (action) { switch (action) {
case InputAction::EXIT: case InputAction::EXIT:
@@ -217,12 +261,16 @@ namespace GlobalInputs {
handleIncWindowZoom(); handleIncWindowZoom();
break; break;
case InputAction::TOGGLE_POSTFX: case InputAction::TOGGLE_SHADER:
handleTogglePostFX(); handleToggleShaders();
break; break;
case InputAction::NEXT_POSTFX_PRESET: case InputAction::NEXT_SHADER_PRESET:
handleNextPostFXPreset(); handleNextShaderPreset();
break;
case InputAction::TOGGLE_SUPERSAMPLING:
handleNextShader();
break; break;
case InputAction::NEXT_PALETTE: case InputAction::NEXT_PALETTE:
@@ -233,6 +281,10 @@ namespace GlobalInputs {
handlePreviousPalette(); handlePreviousPalette();
break; break;
case InputAction::NEXT_PALETTE_SORT:
handleNextPaletteSortMode();
break;
case InputAction::TOGGLE_INTEGER_SCALE: case InputAction::TOGGLE_INTEGER_SCALE:
handleToggleIntegerScale(); handleToggleIntegerScale();
break; break;
@@ -241,6 +293,14 @@ namespace GlobalInputs {
handleToggleVSync(); handleToggleVSync();
break; break;
case InputAction::TOGGLE_CONSOLE:
if (Console::get() != nullptr) { Console::get()->toggle(); }
break;
case InputAction::TOGGLE_INFO:
if (RenderInfo::get() != nullptr) { RenderInfo::get()->toggle(); }
break;
case InputAction::NONE: case InputAction::NONE:
default: default:
// No se presionó ninguna acción global // No se presionó ninguna acción global

View File

@@ -14,7 +14,7 @@
Input* Input::instance = nullptr; Input* Input::instance = nullptr;
// Inicializa la instancia única del singleton // Inicializa la instancia única del singleton
void Input::init(const std::string& game_controller_db_path) { void Input::init(const std::string& game_controller_db_path) { // NOLINT(readability-convert-member-functions-to-static)
Input::instance = new Input(game_controller_db_path); Input::instance = new Input(game_controller_db_path);
} }
@@ -43,15 +43,16 @@ Input::Input(std::string game_controller_db_path)
{Action::WINDOW_DEC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F1}}, {Action::WINDOW_DEC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F1}},
{Action::WINDOW_INC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F2}}, {Action::WINDOW_INC_ZOOM, KeyState{.scancode = SDL_SCANCODE_F2}},
{Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}}, {Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}},
{Action::TOGGLE_POSTFX, KeyState{.scancode = SDL_SCANCODE_F4}}, {Action::TOGGLE_SHADER, KeyState{.scancode = SDL_SCANCODE_F4}},
{Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}}, {Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}},
{Action::PREVIOUS_PALETTE, KeyState{.scancode = SDL_SCANCODE_F6}}, {Action::NEXT_PALETTE_SORT, KeyState{.scancode = SDL_SCANCODE_F6}},
{Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}}, {Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}},
{Action::TOGGLE_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}}, {Action::TOGGLE_IN_GAME_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}},
{Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}}, {Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}},
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}}, {Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}}, {Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
{Action::TOGGLE_DEBUG, KeyState{.scancode = SDL_SCANCODE_F12}}}; {Action::TOGGLE_INFO, KeyState{.scancode = SDL_SCANCODE_F12}},
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_GRAVE}}};
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
} }
@@ -69,7 +70,7 @@ void Input::applyKeyboardBindingsFromOptions() {
} }
// Aplica configuración de botones del gamepad desde Options al primer gamepad conectado // Aplica configuración de botones del gamepad desde Options al primer gamepad conectado
void Input::applyGamepadBindingsFromOptions() { void Input::applyGamepadBindingsFromOptions() { // NOLINT(readability-convert-member-functions-to-static)
// Si no hay gamepads conectados, no hay nada que hacer // Si no hay gamepads conectados, no hay nada que hacer
if (gamepads_.empty()) { if (gamepads_.empty()) {
return; return;
@@ -90,21 +91,21 @@ void Input::applyGamepadBindingsFromOptions() {
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action, SDL_GamepadButton button) { void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action, SDL_GamepadButton button) { // NOLINT(readability-convert-member-functions-to-static)
if (gamepad != nullptr) { if (gamepad != nullptr) {
gamepad->bindings[action].button = button; gamepad->bindings[action].button = button;
} }
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action_target, Action action_source) { void Input::bindGameControllerButton(const std::shared_ptr<Gamepad>& gamepad, Action action_target, Action action_source) { // NOLINT(readability-convert-member-functions-to-static)
if (gamepad != nullptr) { if (gamepad != nullptr) {
gamepad->bindings[action_target].button = gamepad->bindings[action_source].button; gamepad->bindings[action_target].button = gamepad->bindings[action_source].button;
} }
} }
// Comprueba si alguna acción está activa // Comprueba si alguna acción está activa
auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { // NOLINT(readability-convert-member-functions-to-static)
bool success_keyboard = false; bool success_keyboard = false;
bool success_controller = false; bool success_controller = false;
@@ -142,7 +143,7 @@ auto Input::checkAction(Action action, bool repeat, bool check_keyboard, const s
} }
// Comprueba si hay almenos una acción activa // Comprueba si hay almenos una acción activa
auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& gamepad) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Obtenemos el número total de acciones posibles para iterar sobre ellas. // Obtenemos el número total de acciones posibles para iterar sobre ellas.
// --- Comprobación del Teclado --- // --- Comprobación del Teclado ---
@@ -179,7 +180,7 @@ auto Input::checkAnyInput(bool check_keyboard, const std::shared_ptr<Gamepad>& g
} }
// Comprueba si hay algún botón pulsado // Comprueba si hay algún botón pulsado
auto Input::checkAnyButton(bool repeat) -> bool { auto Input::checkAnyButton(bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Solo comprueba los botones definidos previamente // Solo comprueba los botones definidos previamente
for (auto bi : BUTTON_INPUTS) { for (auto bi : BUTTON_INPUTS) {
// Comprueba el teclado // Comprueba el teclado
@@ -219,7 +220,7 @@ auto Input::getControllerNames() const -> std::vector<std::string> {
auto Input::getNumGamepads() const -> int { return gamepads_.size(); } auto Input::getNumGamepads() const -> int { return gamepads_.size(); }
// Obtiene el gamepad a partir de un event.id // Obtiene el gamepad a partir de un event.id
auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& gamepad : gamepads_) { for (const auto& gamepad : gamepads_) {
if (gamepad->instance_id == id) { if (gamepad->instance_id == id) {
return gamepad; return gamepad;
@@ -228,7 +229,7 @@ auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepa
return nullptr; return nullptr;
} }
auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
for (const auto& gamepad : gamepads_) { for (const auto& gamepad : gamepads_) {
if (gamepad && gamepad->name == name) { if (gamepad && gamepad->name == name) {
return gamepad; return gamepad;
@@ -238,12 +239,12 @@ auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<I
} }
// Obtiene el SDL_GamepadButton asignado a un action // Obtiene el SDL_GamepadButton asignado a un action
auto Input::getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton { auto Input::getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton { // NOLINT(readability-convert-member-functions-to-static)
return static_cast<SDL_GamepadButton>(gamepad->bindings[action].button); return static_cast<SDL_GamepadButton>(gamepad->bindings[action].button);
} }
// Comprueba el eje del mando // Comprueba el eje del mando
auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Obtener el binding configurado para esta acción // Obtener el binding configurado para esta acción
auto& binding = gamepad->bindings[action]; auto& binding = gamepad->bindings[action];
@@ -286,7 +287,7 @@ auto Input::checkAxisInput(Action action, const std::shared_ptr<Gamepad>& gamepa
} }
// Comprueba los triggers del mando como botones digitales // Comprueba los triggers del mando como botones digitales
auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gamepad, bool repeat) -> bool { // NOLINT(readability-convert-member-functions-to-static)
// Solo manejamos botones específicos que pueden ser triggers // Solo manejamos botones específicos que pueden ser triggers
if (gamepad->bindings[action].button != static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID)) { if (gamepad->bindings[action].button != static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID)) {
// Solo procesamos L2 y R2 como triggers // Solo procesamos L2 y R2 como triggers
@@ -333,13 +334,13 @@ auto Input::checkTriggerInput(Action action, const std::shared_ptr<Gamepad>& gam
return false; return false;
} }
void Input::addGamepadMappingsFromFile() { void Input::addGamepadMappingsFromFile() { // NOLINT(readability-convert-member-functions-to-static)
if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) { if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) {
std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << '\n'; std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << '\n';
} }
} }
void Input::discoverGamepads() { void Input::discoverGamepads() { // NOLINT(readability-convert-member-functions-to-static)
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
handleEvent(event); // Comprueba mandos conectados handleEvent(event); // Comprueba mandos conectados
@@ -375,7 +376,7 @@ void Input::resetInputStates() {
} }
} }
void Input::update() { void Input::update() { // NOLINT(readability-convert-member-functions-to-static)
// --- TECLADO --- // --- TECLADO ---
const bool* key_states = SDL_GetKeyboardState(nullptr); const bool* key_states = SDL_GetKeyboardState(nullptr);
@@ -399,7 +400,7 @@ void Input::update() {
} }
} }
auto Input::handleEvent(const SDL_Event& event) -> std::string { auto Input::handleEvent(const SDL_Event& event) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
switch (event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
return addGamepad(event.gdevice.which); return addGamepad(event.gdevice.which);
@@ -409,7 +410,7 @@ auto Input::handleEvent(const SDL_Event& event) -> std::string {
return {}; return {};
} }
auto Input::addGamepad(int device_index) -> std::string { auto Input::addGamepad(int device_index) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
SDL_Gamepad* pad = SDL_OpenGamepad(device_index); SDL_Gamepad* pad = SDL_OpenGamepad(device_index);
if (pad == nullptr) { if (pad == nullptr) {
std::cerr << "Error al abrir el gamepad: " << SDL_GetError() << '\n'; std::cerr << "Error al abrir el gamepad: " << SDL_GetError() << '\n';
@@ -423,8 +424,8 @@ auto Input::addGamepad(int device_index) -> std::string {
return name + " CONNECTED"; return name + " CONNECTED";
} }
auto Input::removeGamepad(SDL_JoystickID id) -> std::string { auto Input::removeGamepad(SDL_JoystickID id) -> std::string { // NOLINT(readability-convert-member-functions-to-static)
auto it = std::ranges::find_if(gamepads_, [id](const std::shared_ptr<Gamepad>& gamepad) { auto it = std::ranges::find_if(gamepads_, [id](const std::shared_ptr<Gamepad>& gamepad) -> bool {
return gamepad->instance_id == id; return gamepad->instance_id == id;
}); });
@@ -438,7 +439,7 @@ auto Input::removeGamepad(SDL_JoystickID id) -> std::string {
return {}; return {};
} }
void Input::printConnectedGamepads() const { void Input::printConnectedGamepads() const { // NOLINT(readability-convert-member-functions-to-static)
if (gamepads_.empty()) { if (gamepads_.empty()) {
std::cout << "No hay gamepads conectados." << '\n'; std::cout << "No hay gamepads conectados." << '\n';
return; return;
@@ -452,7 +453,7 @@ void Input::printConnectedGamepads() const {
} }
} }
auto Input::findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Input::Gamepad> { auto Input::findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Input::Gamepad> { // NOLINT(readability-convert-member-functions-to-static)
// Si no hay gamepads disponibles, devolver gamepad por defecto // Si no hay gamepads disponibles, devolver gamepad por defecto
if (gamepads_.empty()) { if (gamepads_.empty()) {
return nullptr; return nullptr;

View File

@@ -101,12 +101,12 @@ class Input {
// --- Gestión de gamepads --- // --- Gestión de gamepads ---
[[nodiscard]] auto gameControllerFound() const -> bool; [[nodiscard]] auto gameControllerFound() const -> bool;
[[nodiscard]] auto getNumGamepads() const -> int; [[nodiscard]] auto getNumGamepads() const -> int;
auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>; [[nodiscard]] auto getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Gamepad>;
auto getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad>; [[nodiscard]] auto getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad>;
auto getGamepads() const -> const Gamepads& { return gamepads_; } [[nodiscard]] auto getGamepads() const -> const Gamepads& { return gamepads_; }
auto findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Gamepad>; auto findAvailableGamepadByName(const std::string& gamepad_name) -> std::shared_ptr<Gamepad>;
static auto getControllerName(const std::shared_ptr<Gamepad>& gamepad) -> std::string; static auto getControllerName(const std::shared_ptr<Gamepad>& gamepad) -> std::string;
auto getControllerNames() const -> std::vector<std::string>; [[nodiscard]] auto getControllerNames() const -> std::vector<std::string>;
[[nodiscard]] static auto getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton; [[nodiscard]] static auto getControllerBinding(const std::shared_ptr<Gamepad>& gamepad, Action action) -> SDL_GamepadButton;
void printConnectedGamepads() const; void printConnectedGamepads() const;

View File

@@ -17,13 +17,13 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
{InputAction::TOGGLE_VSYNC, "TOGGLE_VSYNC"}, {InputAction::TOGGLE_VSYNC, "TOGGLE_VSYNC"},
{InputAction::TOGGLE_INTEGER_SCALE, "TOGGLE_INTEGER_SCALE"}, {InputAction::TOGGLE_INTEGER_SCALE, "TOGGLE_INTEGER_SCALE"},
{InputAction::TOGGLE_BORDER, "TOGGLE_BORDER"}, {InputAction::TOGGLE_BORDER, "TOGGLE_BORDER"},
{InputAction::TOGGLE_MUSIC, "TOGGLE_MUSIC"}, {InputAction::TOGGLE_IN_GAME_MUSIC, "TOGGLE_MUSIC"},
{InputAction::NEXT_PALETTE, "NEXT_PALETTE"}, {InputAction::NEXT_PALETTE, "NEXT_PALETTE"},
{InputAction::PREVIOUS_PALETTE, "PREVIOUS_PALETTE"}, {InputAction::PREVIOUS_PALETTE, "PREVIOUS_PALETTE"},
{InputAction::TOGGLE_POSTFX, "TOGGLE_POSTFX"}, {InputAction::NEXT_PALETTE_SORT, "NEXT_PALETTE_SORT"},
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"}, {InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"},
{InputAction::SHOW_DEBUG_INFO, "SHOW_DEBUG_INFO"}, {InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"},
{InputAction::TOGGLE_DEBUG, "TOGGLE_DEBUG"}, {InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"},
{InputAction::NONE, "NONE"}}; {InputAction::NONE, "NONE"}};
const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = { const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
@@ -40,13 +40,13 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
{"TOGGLE_VSYNC", InputAction::TOGGLE_VSYNC}, {"TOGGLE_VSYNC", InputAction::TOGGLE_VSYNC},
{"TOGGLE_INTEGER_SCALE", InputAction::TOGGLE_INTEGER_SCALE}, {"TOGGLE_INTEGER_SCALE", InputAction::TOGGLE_INTEGER_SCALE},
{"TOGGLE_BORDER", InputAction::TOGGLE_BORDER}, {"TOGGLE_BORDER", InputAction::TOGGLE_BORDER},
{"TOGGLE_MUSIC", InputAction::TOGGLE_MUSIC}, {"TOGGLE_MUSIC", InputAction::TOGGLE_IN_GAME_MUSIC},
{"NEXT_PALETTE", InputAction::NEXT_PALETTE}, {"NEXT_PALETTE", InputAction::NEXT_PALETTE},
{"PREVIOUS_PALETTE", InputAction::PREVIOUS_PALETTE}, {"PREVIOUS_PALETTE", InputAction::PREVIOUS_PALETTE},
{"TOGGLE_POSTFX", InputAction::TOGGLE_POSTFX}, {"NEXT_PALETTE_SORT", InputAction::NEXT_PALETTE_SORT},
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET}, {"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER},
{"SHOW_DEBUG_INFO", InputAction::SHOW_DEBUG_INFO}, {"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET},
{"TOGGLE_DEBUG", InputAction::TOGGLE_DEBUG}, {"TOGGLE_DEBUG", InputAction::TOGGLE_INFO},
{"NONE", InputAction::NONE}}; {"NONE", InputAction::NONE}};
const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = { const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = {

View File

@@ -24,14 +24,16 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
TOGGLE_FULLSCREEN, TOGGLE_FULLSCREEN,
TOGGLE_VSYNC, TOGGLE_VSYNC,
TOGGLE_INTEGER_SCALE, TOGGLE_INTEGER_SCALE,
TOGGLE_POSTFX, TOGGLE_SHADER,
NEXT_POSTFX_PRESET, NEXT_SHADER_PRESET,
TOGGLE_SUPERSAMPLING,
TOGGLE_BORDER, TOGGLE_BORDER,
TOGGLE_MUSIC, TOGGLE_IN_GAME_MUSIC,
NEXT_PALETTE, NEXT_PALETTE,
PREVIOUS_PALETTE, PREVIOUS_PALETTE,
SHOW_DEBUG_INFO, NEXT_PALETTE_SORT,
TOGGLE_DEBUG, TOGGLE_INFO,
TOGGLE_CONSOLE,
// Input obligatorio // Input obligatorio
NONE, NONE,

View File

@@ -2,20 +2,26 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream>
#include <string> #include <string>
#include "external/fkyaml_node.hpp" // Para fkyaml::node #include "external/fkyaml_node.hpp" // Para fkyaml::node
#include "game/options.hpp" // Para Options::console
// [SINGLETON] // [SINGLETON]
Locale* Locale::instance = nullptr; Locale* Locale::instance = nullptr;
// [SINGLETON] Crea el objeto con esta función estática // [SINGLETON] Crea el objeto con esta función estática
void Locale::init(const std::string& file_path) { void Locale::init(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
Locale::instance = new Locale(); Locale::instance = new Locale();
Locale::instance->loadFromFile(file_path); Locale::instance->loadFromFile(file_path);
} }
// [SINGLETON] Crea el objeto desde contenido en memoria (para release con pack)
void Locale::initFromContent(const std::string& content) { // NOLINT(readability-convert-member-functions-to-static)
Locale::instance = new Locale();
Locale::instance->loadFromContent(content);
}
// [SINGLETON] Destruye el objeto con esta función estática // [SINGLETON] Destruye el objeto con esta función estática
void Locale::destroy() { void Locale::destroy() {
delete Locale::instance; delete Locale::instance;
@@ -28,20 +34,18 @@ auto Locale::get() -> Locale* {
} }
// Devuelve la traducción de la clave o la clave como fallback // Devuelve la traducción de la clave o la clave como fallback
auto Locale::get(const std::string& key) const -> std::string { auto Locale::get(const std::string& key) const -> std::string { // NOLINT(readability-convert-member-functions-to-static)
auto it = strings_.find(key); auto it = strings_.find(key);
if (it != strings_.end()) { if (it != strings_.end()) {
return it->second; return it->second;
} }
if (Options::console) { std::cerr << "Locale: clave no encontrada: " << key << '\n';
std::cerr << "Locale: clave no encontrada: " << key << '\n';
}
return key; return key;
} }
// Aplana un nodo YAML de forma recursiva: {a: {b: "val"}} -> {"a.b" -> "val"} // Aplana un nodo YAML de forma recursiva: {a: {b: "val"}} -> {"a.b" -> "val"}
void Locale::flatten(const void* node_ptr, const std::string& prefix) { void Locale::flatten(const void* node_ptr, const std::string& prefix) { // NOLINT(readability-convert-member-functions-to-static)
const auto& node = *static_cast<const fkyaml::node*>(node_ptr); const auto& node = *static_cast<const fkyaml::node*>(node_ptr);
for (auto itr = node.begin(); itr != node.end(); ++itr) { for (auto itr = node.begin(); itr != node.end(); ++itr) {
@@ -58,20 +62,34 @@ void Locale::flatten(const void* node_ptr, const std::string& prefix) {
} }
} }
// Carga las traducciones desde contenido YAML en memoria
void Locale::loadFromContent(const std::string& content) { // NOLINT(readability-convert-member-functions-to-static)
if (content.empty()) {
std::cerr << "Locale: contenido vacío, sin traducciones cargadas\n";
return;
}
try {
std::istringstream stream(content);
auto yaml = fkyaml::node::deserialize(stream);
flatten(&yaml, "");
std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde pack\n";
} catch (const fkyaml::exception& e) {
std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n';
}
}
// Carga las traducciones desde el fichero YAML indicado // Carga las traducciones desde el fichero YAML indicado
void Locale::loadFromFile(const std::string& file_path) { void Locale::loadFromFile(const std::string& file_path) { // NOLINT(readability-convert-member-functions-to-static)
if (file_path.empty()) { if (file_path.empty()) {
if (Options::console) { std::cerr << "Locale: ruta de fichero vacía, sin traducciones cargadas\n";
std::cerr << "Locale: ruta de fichero vacía, sin traducciones cargadas\n";
}
return; return;
} }
std::ifstream file(file_path); std::ifstream file(file_path);
if (!file.is_open()) { if (!file.is_open()) {
if (Options::console) { std::cerr << "Locale: no se puede abrir " << file_path << '\n';
std::cerr << "Locale: no se puede abrir " << file_path << '\n';
}
return; return;
} }
@@ -79,12 +97,8 @@ void Locale::loadFromFile(const std::string& file_path) {
auto yaml = fkyaml::node::deserialize(file); auto yaml = fkyaml::node::deserialize(file);
flatten(&yaml, ""); flatten(&yaml, "");
if (Options::console) { std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde " << file_path << '\n';
std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde " << file_path << '\n';
}
} catch (const fkyaml::exception& e) { } catch (const fkyaml::exception& e) {
if (Options::console) { std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n';
std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n';
}
} }
} }

View File

@@ -8,9 +8,10 @@
// No se permite cambio de idioma en caliente. // No se permite cambio de idioma en caliente.
class Locale { class Locale {
public: public:
static void init(const std::string& file_path); // Crea e inicializa el singleton static void init(const std::string& file_path); // Crea e inicializa el singleton
static void destroy(); // Destruye el singleton static void initFromContent(const std::string& content); // Crea e inicializa desde contenido en memoria (pack)
static auto get() -> Locale*; // Devuelve el singleton static void destroy(); // Destruye el singleton
static auto get() -> Locale*; // Devuelve el singleton
// Devuelve la traducción de la clave dada. // Devuelve la traducción de la clave dada.
// Si la clave no existe, devuelve la propia clave como fallback. // Si la clave no existe, devuelve la propia clave como fallback.
@@ -19,6 +20,7 @@ class Locale {
private: private:
Locale() = default; Locale() = default;
void loadFromFile(const std::string& file_path); void loadFromFile(const std::string& file_path);
void loadFromContent(const std::string& content);
void flatten(const void* node_ptr, const std::string& prefix); // Aplana nodos YAML anidados void flatten(const void* node_ptr, const std::string& prefix); // Aplana nodos YAML anidados
static Locale* instance; static Locale* instance;

View File

@@ -15,7 +15,7 @@ namespace GIF {
} }
// Inicializa el diccionario LZW con los valores iniciales // Inicializa el diccionario LZW con los valores iniciales
inline void initializeDictionary(std::vector<DictionaryEntry>& dictionary, int code_length, int& dictionary_ind) { inline void initializeDictionary(std::vector<DictionaryEntry>& dictionary, int code_length, int& dictionary_ind) { // NOLINT(readability-identifier-naming)
int size = 1 << code_length; int size = 1 << code_length;
dictionary.resize(1 << (code_length + 1)); dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < size; dictionary_ind++) { for (dictionary_ind = 0; dictionary_ind < size; dictionary_ind++) {
@@ -55,7 +55,7 @@ namespace GIF {
} }
// Agrega una nueva entrada al diccionario // Agrega una nueva entrada al diccionario
inline void addDictionaryEntry(std::vector<DictionaryEntry>& dictionary, int& dictionary_ind, int& code_length, int prev, int code) { inline void addDictionaryEntry(std::vector<DictionaryEntry>& dictionary, int& dictionary_ind, int& code_length, int prev, int code) { // NOLINT(readability-identifier-naming)
uint8_t first_byte; uint8_t first_byte;
if (code == dictionary_ind) { if (code == dictionary_ind) {
first_byte = findFirstByte(dictionary, prev); first_byte = findFirstByte(dictionary, prev);
@@ -90,7 +90,7 @@ namespace GIF {
return match_len; return match_len;
} }
void Gif::decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out) { void Gif::decompress(int code_length, const uint8_t* input, int input_length, uint8_t* out) { // NOLINT(readability-convert-member-functions-to-static)
// Verifica que el code_length tenga un rango razonable. // Verifica que el code_length tenga un rango razonable.
if (code_length < 2 || code_length > 12) { if (code_length < 2 || code_length > 12) {
throw std::runtime_error("Invalid LZW code length"); throw std::runtime_error("Invalid LZW code length");
@@ -146,7 +146,7 @@ namespace GIF {
} }
} }
auto Gif::readSubBlocks(const uint8_t*& buffer) -> std::vector<uint8_t> { auto Gif::readSubBlocks(const uint8_t*& buffer) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<uint8_t> data; std::vector<uint8_t> data;
uint8_t block_size = *buffer; uint8_t block_size = *buffer;
buffer++; buffer++;
@@ -159,7 +159,7 @@ namespace GIF {
return data; return data;
} }
auto Gif::processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits) -> std::vector<uint8_t> { auto Gif::processImageDescriptor(const uint8_t*& buffer, const std::vector<RGB>& gct, int resolution_bits) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
ImageDescriptor image_descriptor; ImageDescriptor image_descriptor;
// Lee 9 bytes para el image descriptor. // Lee 9 bytes para el image descriptor.
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor)); readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
@@ -175,7 +175,7 @@ namespace GIF {
return uncompressed_data; return uncompressed_data;
} }
auto Gif::loadPalette(const uint8_t* buffer) -> std::vector<uint32_t> { auto Gif::loadPalette(const uint8_t* buffer) -> std::vector<uint32_t> { // NOLINT(readability-convert-member-functions-to-static)
uint8_t header[6]; uint8_t header[6];
std::memcpy(header, buffer, 6); std::memcpy(header, buffer, 6);
buffer += 6; buffer += 6;
@@ -186,7 +186,7 @@ namespace GIF {
std::vector<uint32_t> global_color_table; std::vector<uint32_t> global_color_table;
if ((screen_descriptor.fields & 0x80) != 0) { if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); int global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1);
global_color_table.resize(global_color_table_size); global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) { for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0]; uint8_t r = buffer[0];
@@ -199,7 +199,7 @@ namespace GIF {
return global_color_table; return global_color_table;
} }
auto Gif::processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h) -> std::vector<uint8_t> { auto Gif::processGifStream(const uint8_t* buffer, uint16_t& w, uint16_t& h) -> std::vector<uint8_t> { // NOLINT(readability-convert-member-functions-to-static)
// Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a") // Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a")
uint8_t header[6]; uint8_t header[6];
std::memcpy(header, buffer, 6); std::memcpy(header, buffer, 6);
@@ -222,7 +222,7 @@ namespace GIF {
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table; std::vector<RGB> global_color_table;
if ((screen_descriptor.fields & 0x80) != 0) { if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); int global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1);
global_color_table.resize(global_color_table_size); global_color_table.resize(global_color_table_size);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
buffer += 3 * global_color_table_size; buffer += 3 * global_color_table_size;

View File

@@ -0,0 +1,276 @@
#include "core/rendering/palette_manager.hpp"
#include <algorithm>
#include <cctype>
#include <cmath>
#include <string>
#include <vector>
#include "core/rendering/surface.hpp"
#include "core/resources/resource_cache.hpp"
#include "game/defaults.hpp"
#include "game/options.hpp"
#include "utils/utils.hpp"
// ── Conversión string ↔ PaletteSortMode ──────────────────────────────────────
auto sortModeFromString(const std::string& str) -> PaletteSortMode {
const std::string lower = toLower(str);
if (lower == "luminance") { return PaletteSortMode::LUMINANCE; }
if (lower == "spectrum") { return PaletteSortMode::SPECTRUM; }
return PaletteSortMode::ORIGINAL;
}
auto sortModeToString(PaletteSortMode mode) -> std::string {
switch (mode) {
case PaletteSortMode::LUMINANCE:
return "luminance";
case PaletteSortMode::SPECTRUM:
return "spectrum";
default:
return "original";
}
}
// ── Paleta de referencia ZX Spectrum (16 colores ARGB) ───────────────────────
namespace {
// Helpers para extraer componentes RGB de un color ARGB (0xAARRGGBB)
constexpr auto redOf(Uint32 c) -> int { return static_cast<int>((c >> 16) & 0xFF); }
constexpr auto greenOf(Uint32 c) -> int { return static_cast<int>((c >> 8) & 0xFF); }
constexpr auto blueOf(Uint32 c) -> int { return static_cast<int>(c & 0xFF); }
constexpr auto makeARGB(int r, int g, int b) -> Uint32 {
return (0xFFU << 24) | (static_cast<Uint32>(r) << 16) | (static_cast<Uint32>(g) << 8) | static_cast<Uint32>(b);
}
// Paleta ZX Spectrum de referencia (misma que en tools/sort_palette/sort_palette.py)
constexpr std::array<Uint32, 16> SPECTRUM_REFERENCE = {
makeARGB(0, 0, 0),
makeARGB(0, 0, 0),
makeARGB(0, 0, 216),
makeARGB(0, 0, 255),
makeARGB(216, 0, 0),
makeARGB(255, 0, 0),
makeARGB(216, 0, 216),
makeARGB(255, 0, 255),
makeARGB(0, 216, 0),
makeARGB(0, 255, 0),
makeARGB(0, 216, 216),
makeARGB(0, 255, 255),
makeARGB(216, 216, 0),
makeARGB(255, 255, 0),
makeARGB(216, 216, 216),
makeARGB(255, 255, 255),
};
// Luminancia percibida (ITU-R BT.709)
auto luminance(Uint32 color) -> double {
return 0.2126 * redOf(color) + 0.7152 * greenOf(color) + 0.0722 * blueOf(color);
}
// Distancia euclídea al cuadrado en espacio RGB (no necesita sqrt para comparar)
auto rgbDistanceSq(Uint32 a, Uint32 b) -> int {
const int dr = redOf(a) - redOf(b);
const int dg = greenOf(a) - greenOf(b);
const int db = blueOf(a) - blueOf(b);
return dr * dr + dg * dg + db * db;
}
// Cuenta los colores activos en la paleta (los que tienen alpha != 0)
auto countActiveColors(const Palette& palette) -> size_t {
size_t count = 0;
for (const auto& c : palette) {
if (c == 0) { break; }
++count;
}
return count;
}
// Ordenar por luminancia
auto sortByLuminance(const Palette& palette) -> Palette {
const size_t n = countActiveColors(palette);
std::vector<Uint32> colors(palette.begin(), palette.begin() + static_cast<ptrdiff_t>(n));
std::sort(colors.begin(), colors.end(), [](Uint32 a, Uint32 b) {
return luminance(a) < luminance(b);
});
Palette result{};
result.fill(0);
std::copy(colors.begin(), colors.end(), result.begin());
return result;
}
// Ordenar por similitud con la paleta ZX Spectrum (greedy matching)
auto sortBySpectrum(const Palette& palette) -> Palette {
const size_t n = countActiveColors(palette);
std::vector<Uint32> available(palette.begin(), palette.begin() + static_cast<ptrdiff_t>(n));
std::vector<Uint32> result;
result.reserve(n);
// Para cada color de referencia del Spectrum, buscar el más cercano disponible
const size_t refs = std::min(n, SPECTRUM_REFERENCE.size());
for (size_t i = 0; i < refs && !available.empty(); ++i) {
const Uint32 ref = SPECTRUM_REFERENCE[i];
auto best = std::min_element(available.begin(), available.end(), [ref](Uint32 a, Uint32 b) {
return rgbDistanceSq(a, ref) < rgbDistanceSq(b, ref);
});
result.push_back(*best);
available.erase(best);
}
// Si quedan colores sin asignar, añadirlos al final
for (const auto& c : available) {
result.push_back(c);
}
Palette out{};
out.fill(0);
std::copy(result.begin(), result.end(), out.begin());
return out;
}
} // namespace
// ── PaletteManager ───────────────────────────────────────────────────────────
PaletteManager::PaletteManager(
std::vector<std::string> raw_paths,
const std::string& initial_name,
PaletteSortMode initial_sort_mode,
std::shared_ptr<Surface> game_surface,
std::shared_ptr<Surface> border_surface,
OnChangeCallback on_change)
: palettes_(std::move(raw_paths)),
sort_mode_(initial_sort_mode),
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 = sortPalette(readPalFile(palettes_.at(current_)), sort_mode_);
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::nextSortMode() {
sort_mode_ = static_cast<PaletteSortMode>((static_cast<int>(sort_mode_) + 1) % static_cast<int>(PaletteSortMode::COUNT));
Options::video.palette_sort = sortModeToString(sort_mode_);
apply();
}
void PaletteManager::setSortMode(PaletteSortMode mode) {
sort_mode_ = mode;
Options::video.palette_sort = sortModeToString(sort_mode_);
apply();
}
auto PaletteManager::getSortMode() const -> PaletteSortMode {
return sort_mode_;
}
auto PaletteManager::getSortModeName() const -> std::string {
return sortModeToString(sort_mode_);
}
void PaletteManager::apply() {
Palette raw = Resource::Cache::get()->getPalette(palettes_.at(current_));
Palette sorted = sortPalette(raw, sort_mode_);
game_surface_->loadPalette(sorted);
border_surface_->loadPalette(sorted);
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);
}
}
auto PaletteManager::sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette {
switch (mode) {
case PaletteSortMode::LUMINANCE:
return sortByLuminance(palette);
case PaletteSortMode::SPECTRUM:
return sortBySpectrum(palette);
default:
return palette;
}
}

View File

@@ -0,0 +1,64 @@
#pragma once
#include <SDL3/SDL.h>
#include <array>
#include <functional>
#include <memory>
#include <string>
#include <vector>
// Alias de paleta (igual que en surface.hpp; evita incluir todo el header)
using Palette = std::array<Uint32, 256>;
class Surface;
// Modo de ordenación de paletas
enum class PaletteSortMode : int {
ORIGINAL = 0, // Paleta tal cual viene del fichero
LUMINANCE = 1, // Ordenada por luminancia percibida
SPECTRUM = 2, // Reordenada para imitar la paleta ZX Spectrum
COUNT = 3
};
// Conversión string ↔ PaletteSortMode
auto sortModeFromString(const std::string& str) -> PaletteSortMode;
auto sortModeToString(PaletteSortMode mode) -> std::string;
class PaletteManager {
public:
using OnChangeCallback = std::function<void()>;
PaletteManager(
std::vector<std::string> raw_paths,
const std::string& initial_name,
PaletteSortMode initial_sort_mode,
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
void nextSortMode(); // Cicla al siguiente modo de ordenación
void setSortMode(PaletteSortMode mode); // Establece un modo de ordenación concreto
[[nodiscard]] auto getSortMode() const -> PaletteSortMode; // Devuelve el modo de ordenación actual
[[nodiscard]] auto getSortModeName() const -> std::string; // Nombre del modo actual ("ORIGINAL", etc.)
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
static auto sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette; // Reordena una paleta según el modo
std::vector<std::string> palettes_;
size_t current_{0};
PaletteSortMode sort_mode_{PaletteSortMode::ORIGINAL};
std::shared_ptr<Surface> game_surface_;
std::shared_ptr<Surface> border_surface_;
OnChangeCallback on_change_;
};

View File

@@ -65,11 +65,8 @@ PixelReveal::PixelReveal(int width, int height, float pixels_per_second, float s
} }
} }
// Destructor
PixelReveal::~PixelReveal() = default;
// Actualiza el estado del revelado // Actualiza el estado del revelado
void PixelReveal::update(float time_active) { void PixelReveal::update(float time_active) { // NOLINT(readability-make-member-function-const)
// En modo normal revela (pone transparente); en modo inverso cubre (pone negro) // En modo normal revela (pone transparente); en modo inverso cubre (pone negro)
const auto PIXEL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::BLACK) : static_cast<Uint8>(PaletteColor::TRANSPARENT); const auto PIXEL_COLOR = reverse_ ? static_cast<Uint8>(PaletteColor::BLACK) : static_cast<Uint8>(PaletteColor::TRANSPARENT);
@@ -106,5 +103,5 @@ void PixelReveal::render(int dst_x, int dst_y) const {
// Indica si el revelado ha completado todas las filas // Indica si el revelado ha completado todas las filas
auto PixelReveal::isComplete() const -> bool { auto PixelReveal::isComplete() const -> bool {
return std::ranges::all_of(row_step_, [this](int s) { return s >= num_steps_; }); return std::ranges::all_of(row_step_, [this](int s) -> bool { return s >= num_steps_; });
} }

View File

@@ -16,8 +16,7 @@ class PixelReveal {
// Constructor // Constructor
PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4, bool reverse = false, RevealMode mode = RevealMode::RANDOM); PixelReveal(int width, int height, float pixels_per_second, float step_duration, int num_steps = 4, bool reverse = false, RevealMode mode = RevealMode::RANDOM);
// Destructor definido en el .cpp para que unique_ptr<Surface> funcione con forward declaration ~PixelReveal() = default;
~PixelReveal();
// Actualiza el estado del revelado según el tiempo transcurrido // Actualiza el estado del revelado según el tiempo transcurrido
void update(float time_active); void update(float time_active);

View File

@@ -0,0 +1,159 @@
#include "core/rendering/render_info.hpp"
#include <SDL3/SDL.h>
#include <algorithm> // Para transform
#include <cmath> // Para round, floor
#include <iomanip> // Para setprecision
#include <sstream> // Para ostringstream
#include <string> // Para string
#include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/surface.hpp" // Para Surface
#include "core/rendering/text.hpp" // Para Text
#include "game/options.hpp" // Para Options
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier
#include "utils/utils.hpp" // Para prettyName
// [SINGLETON]
RenderInfo* RenderInfo::render_info = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática
void RenderInfo::init() {
RenderInfo::render_info = new RenderInfo();
}
// [SINGLETON] Destruiremos el objeto con esta función estática
void RenderInfo::destroy() {
delete RenderInfo::render_info;
RenderInfo::render_info = nullptr;
}
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
auto RenderInfo::get() -> RenderInfo* {
return RenderInfo::render_info;
}
// Constructor: en DEBUG se activa inmediatamente (notifica a Notifier del offset)
RenderInfo::RenderInfo() {
#ifdef _DEBUG
toggle();
#endif
}
// Actualiza la animación de entrada/salida del overlay
void RenderInfo::update(float delta_time) {
switch (status_) {
case Status::RISING:
y_ += SLIDE_SPEED * delta_time;
if (y_ >= 0.0F) {
y_ = 0.0F;
status_ = Status::ACTIVE;
}
break;
case Status::VANISHING:
y_ -= SLIDE_SPEED * delta_time;
if (y_ <= static_cast<float>(-HEIGHT)) {
y_ = static_cast<float>(-HEIGHT);
status_ = Status::HIDDEN;
}
break;
default:
break;
}
}
// Renderiza el overlay de información por pantalla
void RenderInfo::render() const {
if (status_ == Status::HIDDEN) { return; }
// FPS
std::string line = std::to_string(Screen::get()->getLastFPS()) + " fps";
// Driver GPU
const auto& driver = Screen::get()->getGPUDriver();
line += " | " + (driver.empty() ? std::string("sdl") : driver);
// Zoom calculado (alto físico / alto lógico), con coma decimal y sin ceros innecesarios
const float ROUNDED = std::round(Screen::get()->getZoomFactor() * 100.0F) / 100.0F;
std::string zoom_str;
if (ROUNDED == std::floor(ROUNDED)) {
zoom_str = std::to_string(static_cast<int>(ROUNDED));
} else {
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << ROUNDED;
zoom_str = oss.str();
if (zoom_str.back() == '0') { zoom_str.pop_back(); }
std::replace(zoom_str.begin(), zoom_str.end(), '.', ',');
}
line += " | " + zoom_str + "x";
// PostFX: muestra shader + preset y supersampling, o nada si está desactivado
if (Options::video.shader.enabled) {
const bool IS_CRTPI = (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI);
const std::string SHADER_NAME = IS_CRTPI ? "crtpi" : "postfx";
std::string preset_name = "-";
if (IS_CRTPI) {
if (!Options::crtpi_presets.empty()) {
preset_name = prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name);
}
} else {
if (!Options::postfx_presets.empty()) {
preset_name = prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name);
}
}
const bool SHOW_SS = Options::video.supersampling.enabled && !IS_CRTPI;
line += " | " + SHADER_NAME + " " + preset_name + (SHOW_SS ? " (ss)" : "");
}
// Todo en lowercase
std::transform(line.begin(), line.end(), line.begin(), [](unsigned char c) { return std::tolower(c); });
// Constantes visuales (igual que Console)
static constexpr Uint8 BG_COLOR = 0; // PaletteColor::BLACK
static constexpr Uint8 MSG_COLOR = 9; // PaletteColor::BRIGHT_GREEN
static constexpr int TEXT_SIZE = 6;
static constexpr int PADDING_V = TEXT_SIZE / 2 - 1;
// Fuente: preferir la de la consola si está disponible
auto text_obj = (Console::get() != nullptr) ? Console::get()->getText() : Screen::get()->getText();
// Posición Y: debajo de la consola + offset animado propio
const int CONSOLE_Y = (Console::get() != nullptr) ? Console::get()->getVisibleHeight() : 0;
const int Y = CONSOLE_Y + static_cast<int>(y_);
// Rectángulo de fondo: ancho completo, alto ajustado al texto
const SDL_FRect RECT = {
.x = 0.0F,
.y = static_cast<float>(Y),
.w = Options::game.width,
.h = static_cast<float>(TEXT_SIZE + (PADDING_V * 2))};
auto game_surface = Screen::get()->getGameSurface();
game_surface->fillRect(&RECT, BG_COLOR);
// game_surface->drawRectBorder(&RECT, BORDER_COLOR);
text_obj->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG,
static_cast<int>(Options::game.width / 2),
Y + PADDING_V,
line,
1,
MSG_COLOR);
}
// Activa o desactiva el overlay y notifica a Notifier del cambio de offset
void RenderInfo::toggle() {
switch (status_) {
case Status::HIDDEN:
status_ = Status::RISING;
Screen::get()->updateZoomFactor();
if (Notifier::get() != nullptr) { Notifier::get()->addYOffset(HEIGHT); }
break;
case Status::ACTIVE:
status_ = Status::VANISHING;
if (Notifier::get() != nullptr) { Notifier::get()->removeYOffset(HEIGHT); }
break;
default:
break;
}
}

View File

@@ -0,0 +1,37 @@
#pragma once
class RenderInfo {
public:
// Singleton
static void init();
static void destroy();
static auto get() -> RenderInfo*;
// Métodos principales
void update(float delta_time);
void render() const;
void toggle();
// Consultas
[[nodiscard]] auto isActive() const -> bool { return status_ != Status::HIDDEN; }
// Altura fija del overlay (TEXT_SIZE(6) + PADDING_V(2) * 2)
static constexpr int HEIGHT = 10;
static constexpr float SLIDE_SPEED = 120.0F;
private:
enum class Status { HIDDEN,
RISING,
ACTIVE,
VANISHING };
// Singleton
static RenderInfo* render_info;
// Constructor y destructor privados [SINGLETON]
RenderInfo();
~RenderInfo() = default;
Status status_{Status::HIDDEN};
float y_{static_cast<float>(-HEIGHT)};
};

View File

@@ -4,12 +4,15 @@
#include <algorithm> // Para max, min, transform #include <algorithm> // Para max, min, transform
#include <cctype> // Para toupper #include <cctype> // Para toupper
#include <cmath> // Para round, floor
#include <cstring> // Para memcpy
#include <fstream> // Para basic_ostream, operator<<, endl, basic_... #include <fstream> // Para basic_ostream, operator<<, endl, basic_...
#include <iostream> // Para cerr #include <iostream> // Para cerr
#include <iterator> // Para istreambuf_iterator, operator== #include <iterator> // Para istreambuf_iterator, operator==
#include <string> // Para char_traits, string, operator+, operator== #include <string> // Para char_traits, string, operator+, operator==
#include "core/input/mouse.hpp" // Para updateCursorVisibility #include "core/input/mouse.hpp" // Para updateCursorVisibility
#include "core/rendering/render_info.hpp" // Para RenderInfo
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader #include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
#include "core/rendering/surface.hpp" // Para Surface, readPalFile #include "core/rendering/surface.hpp" // Para Surface, readPalFile
#include "core/rendering/text.hpp" // Para Text #include "core/rendering/text.hpp" // Para Text
@@ -17,6 +20,7 @@
#include "core/resources/resource_helper.hpp" // Para ResourceHelper #include "core/resources/resource_helper.hpp" // Para ResourceHelper
#include "core/resources/resource_list.hpp" // Para Asset, AssetType #include "core/resources/resource_list.hpp" // Para Asset, AssetType
#include "game/options.hpp" // Para Options, options, OptionsVideo, Border #include "game/options.hpp" // Para Options, options, OptionsVideo, Border
#include "game/ui/console.hpp" // Para Console
#include "game/ui/notifier.hpp" // Para Notifier #include "game/ui/notifier.hpp" // Para Notifier
// [SINGLETON] // [SINGLETON]
@@ -38,18 +42,18 @@ auto Screen::get() -> Screen* {
} }
// Constructor // Constructor
Screen::Screen() Screen::Screen() {
: palettes_(Resource::List::get()->getListByType(Resource::List::Type::PALETTE)) {
// Arranca SDL VIDEO, crea la ventana y el renderizador // Arranca SDL VIDEO, crea la ventana y el renderizador
initSDLVideo(); initSDLVideo();
if (Options::video.fullscreen) { if (Options::video.fullscreen) { SDL_HideCursor(); }
SDL_HideCursor();
} // Calcular tamaños y hacer .resize() de los buffers de píxeles
adjustWindowSize();
adjustRenderLogicalSize();
updateZoomFactor();
// Ajusta los tamaños // Ajusta los tamaños
game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height}; game_surface_dstrect_ = {.x = Options::video.border.width, .y = Options::video.border.height, .w = Options::game.width, .h = Options::game.height};
// adjustWindowSize();
current_palette_ = findPalette(Options::video.palette);
// Define el color del borde para el modo de pantalla completa // Define el color del borde para el modo de pantalla completa
border_color_ = static_cast<Uint8>(PaletteColor::BLACK); border_color_ = static_cast<Uint8>(PaletteColor::BLACK);
@@ -58,9 +62,7 @@ Screen::Screen()
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width, Options::game.height); game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width, Options::game.height);
if (game_texture_ == nullptr) { if (game_texture_ == nullptr) {
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (Options::console) { std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
}
} }
SDL_SetTextureScaleMode(game_texture_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(game_texture_, SDL_SCALEMODE_NEAREST);
@@ -68,34 +70,42 @@ Screen::Screen()
border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2)); border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2));
if (border_texture_ == nullptr) { if (border_texture_ == nullptr) {
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (Options::console) { std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << '\n';
}
} }
SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(border_texture_, SDL_SCALEMODE_NEAREST);
// Cargar la paleta una sola vez // Crea las surfaces (PaletteManager aplicará la paleta inicial en su constructor)
auto initial_palette = readPalFile(palettes_.at(current_palette_));
// Crea la surface donde se dibujan los graficos del juego
game_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height); game_surface_ = std::make_shared<Surface>(Options::game.width, Options::game.height);
game_surface_->setPalette(initial_palette);
game_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK)); game_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
// Crea la surface para el borde de colores
border_surface_ = std::make_shared<Surface>(Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2)); border_surface_ = std::make_shared<Surface>(Options::game.width + (Options::video.border.width * 2), Options::game.height + (Options::video.border.height * 2));
border_surface_->setPalette(initial_palette);
border_surface_->clear(border_color_); border_surface_->clear(border_color_);
// Crea el gestor de paletas; aplica la paleta inicial a ambas surfaces
palette_manager_ = std::make_unique<PaletteManager>(
Resource::List::get()->getListByType(Resource::List::Type::PALETTE),
Options::video.palette,
sortModeFromString(Options::video.palette_sort),
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];
// Establece la surface que actuará como renderer para recibir las llamadas a render() // Establece la surface que actuará como renderer para recibir las llamadas a render()
renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_); renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
// Crea el objeto de texto para la pantalla de carga // Crea el objeto de texto para la pantalla de carga
createText(); createText();
// Extrae el nombre de las paletas desde su ruta
processPaletteList();
// Renderizar una vez la textura vacía para que tenga contenido válido // Renderizar una vez la textura vacía para que tenga contenido válido
// antes de inicializar los shaders (evita pantalla negra) // antes de inicializar los shaders (evita pantalla negra)
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr); SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
@@ -144,8 +154,10 @@ void Screen::setVideoMode(bool mode) {
// Configura el modo de pantalla y ajusta la ventana // Configura el modo de pantalla y ajusta la ventana
SDL_SetWindowFullscreen(window_, Options::video.fullscreen); SDL_SetWindowFullscreen(window_, Options::video.fullscreen);
SDL_SyncWindow(window_);
adjustWindowSize(); adjustWindowSize();
adjustRenderLogicalSize(); adjustRenderLogicalSize();
updateZoomFactor();
} }
// Camibia entre pantalla completa y ventana // Camibia entre pantalla completa y ventana
@@ -186,10 +198,30 @@ auto Screen::incWindowZoom() -> bool {
return false; return false;
} }
// Establece el zoom directamente; false si fuera del rango [1, max_zoom] o en pantalla completa
auto Screen::setWindowZoom(int zoom) -> bool {
if (Options::video.fullscreen) { return false; }
if (zoom < 1 || zoom > Options::window.max_zoom) { return false; }
if (zoom == Options::window.zoom) { return false; }
Options::window.zoom = zoom;
setVideoMode(Options::video.fullscreen);
return true;
}
// Devuelve el zoom máximo permitido según la pantalla actual
auto Screen::getMaxZoom() const -> int {
return Options::window.max_zoom;
}
// Cambia el color del borde // Cambia el color del borde
void Screen::setBorderColor(Uint8 color) { void Screen::setBorderColor(Uint8 color) {
border_color_ = color; border_color_ = color;
border_surface_->clear(border_color_); border_surface_->clear(border_color_);
// Actualizar caché ARGB del borde sólido (ocurre una vez por habitación, no cada frame)
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
border_argb_color_ = border_pixel_buffer_[0];
border_is_solid_ = true;
} }
// Cambia entre borde visible y no visible // Cambia entre borde visible y no visible
@@ -206,14 +238,21 @@ void Screen::renderNotifications() const {
} }
} }
// Cambia el estado del PostFX // Activa/desactiva todos los shaders respetando el shader actualmente seleccionado
void Screen::togglePostFX() { void Screen::toggleShaders() {
Options::video.postfx = !Options::video.postfx; Options::video.shader.enabled = !Options::video.shader.enabled;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
if (Options::video.postfx) { if (Options::video.shader.enabled) {
applyCurrentPostFXPreset(); // Activar: usar el shader actualmente seleccionado
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
applyCurrentCrtPiPreset();
} else {
applyCurrentPostFXPreset();
}
} else { } else {
// Pass-through: efectos a 0, el shader copia la textura sin modificar // Desactivar: pass-through con POSTFX (pipeline sin efecto)
shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
shader_backend_->setPostFXParams(Rendering::PostFXParams{}); shader_backend_->setPostFXParams(Rendering::PostFXParams{});
} }
} else { } else {
@@ -224,18 +263,28 @@ void Screen::togglePostFX() {
// Recarga el shader del preset actual sin toggle // Recarga el shader del preset actual sin toggle
void Screen::reloadPostFX() { void Screen::reloadPostFX() {
if (Options::video.postfx && shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (Options::video.shader.enabled && shader_backend_ && shader_backend_->isHardwareAccelerated()) {
// El backend ya está activo: solo actualizar uniforms, sin recrear el pipeline // El backend ya está activo: solo actualizar uniforms, sin recrear el pipeline
applyCurrentPostFXPreset(); applyCurrentPostFXPreset();
} else if (Options::video.postfx) { } else if (Options::video.shader.enabled) {
initShaders(); initShaders();
} }
} }
// Recarga el shader CrtPi del preset actual sin toggle
void Screen::reloadCrtPi() {
if (!shader_backend_) { return; }
applyCurrentCrtPiPreset();
}
// Actualiza la lógica de la clase (versión nueva con delta_time para escenas migradas) // Actualiza la lógica de la clase (versión nueva con delta_time para escenas migradas)
void Screen::update(float delta_time) { void Screen::update(float delta_time) {
fps_.calculate(SDL_GetTicks()); fps_.calculate(SDL_GetTicks());
Notifier::get()->update(delta_time); Notifier::get()->update(delta_time);
if (Console::get() != nullptr) {
Console::get()->update(delta_time);
}
if (RenderInfo::get() != nullptr) { RenderInfo::get()->update(delta_time); }
Mouse::updateCursorVisibility(); Mouse::updateCursorVisibility();
} }
@@ -244,21 +293,41 @@ void Screen::adjustWindowSize() {
window_width_ = Options::game.width + (Options::video.border.enabled ? Options::video.border.width * 2 : 0); window_width_ = Options::game.width + (Options::video.border.enabled ? Options::video.border.width * 2 : 0);
window_height_ = Options::game.height + (Options::video.border.enabled ? Options::video.border.height * 2 : 0); window_height_ = Options::game.height + (Options::video.border.enabled ? Options::video.border.height * 2 : 0);
// Establece el nuevo tamaño // Reservamos memoria una sola vez.
// Si el buffer es más pequeño que la superficie, crash asegurado.
border_pixel_buffer_.resize(static_cast<size_t>(window_width_ * window_height_));
game_pixel_buffer_.resize(static_cast<size_t>(Options::game.width * Options::game.height));
// border_pixel_buffer_ es el buffer que se sube a la GPU (tamaño total ventana).
if (Options::video.border.enabled) {
border_pixel_buffer_.resize(static_cast<size_t>(window_width_ * window_height_));
}
// Lógica de centrado y redimensionado de ventana SDL
if (static_cast<int>(Options::video.fullscreen) == 0) { if (static_cast<int>(Options::video.fullscreen) == 0) {
int old_width; int old_w, old_h;
int old_height; SDL_GetWindowSize(window_, &old_w, &old_h);
SDL_GetWindowSize(window_, &old_width, &old_height); int old_x, old_y;
SDL_GetWindowPosition(window_, &old_x, &old_y);
int old_pos_x; const int new_w = window_width_ * Options::window.zoom;
int old_pos_y; const int new_h = window_height_ * Options::window.zoom;
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y); const int NEW_X = old_x + ((old_w - new_w) / 2);
const int NEW_Y = old_y + ((old_h - new_h) / 2);
const int NEW_POS_X = old_pos_x + ((old_width - (window_width_ * Options::window.zoom)) / 2); SDL_SetWindowSize(window_, new_w, new_h);
const int NEW_POS_Y = old_pos_y + ((old_height - (window_height_ * Options::window.zoom)) / 2);
SDL_SetWindowSize(window_, window_width_ * Options::window.zoom, window_height_ * Options::window.zoom); // En Wayland, SDL_SetWindowPosition es ignorado por el compositor (limitación de
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS), std::max(NEW_POS_Y, 0)); // protocolo: el compositor controla la posición de ventanas toplevel). Solo se
// aplica en X11/Windows/macOS donde el posicionado funciona correctamente.
// SDL_SyncWindow garantiza que el resize esté completado antes de reposicionar
// (evita el race condition en X11).
SDL_SyncWindow(window_);
const char* driver = SDL_GetCurrentVideoDriver();
const bool is_wayland = (driver != nullptr && SDL_strcmp(driver, "wayland") == 0);
if (!is_wayland) {
SDL_SetWindowPosition(window_, std::max(NEW_X, WINDOWS_DECORATIONS), std::max(NEW_Y, 0));
}
} }
} }
@@ -267,58 +336,38 @@ void Screen::adjustRenderLogicalSize() {
SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); SDL_SetRenderLogicalPresentation(renderer_, window_width_, window_height_, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX);
} }
// Recalcula y almacena el factor de zoom. Llamar solo cuando SDL ya ha estabilizado el estado de la ventana.
// En ventana: Options::window.zoom (siempre entero).
// En fullscreen: mínimo de las escalas en ambos ejes; floor si integer scale está activo.
void Screen::updateZoomFactor() {
if (!Options::video.fullscreen) {
zoom_factor_ = static_cast<float>(Options::window.zoom);
return;
}
if (window_width_ == 0 || window_height_ == 0) {
zoom_factor_ = 1.0F;
return;
}
int pw{0}, ph{0};
SDL_GetRenderOutputSize(renderer_, &pw, &ph);
const float SCALE = std::min(static_cast<float>(pw) / static_cast<float>(window_width_),
static_cast<float>(ph) / static_cast<float>(window_height_));
zoom_factor_ = Options::video.integer_scale ? std::floor(SCALE) : SCALE;
}
// Establece el renderizador para las surfaces // Establece el renderizador para las surfaces
void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) { void Screen::setRendererSurface(const std::shared_ptr<Surface>& surface) {
(surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_); (surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
} }
// Cambia la paleta // Cambia la paleta
void Screen::nextPalette() { void Screen::nextPalette() { palette_manager_->next(); }
++current_palette_;
if (current_palette_ == static_cast<int>(palettes_.size())) {
current_palette_ = 0;
}
setPalete();
}
// Cambia la paleta // Cambia la paleta
void Screen::previousPalette() { void Screen::previousPalette() { palette_manager_->previous(); }
if (current_palette_ > 0) {
--current_palette_;
} else {
current_palette_ = static_cast<Uint8>(palettes_.size() - 1);
}
setPalete();
}
// Establece la paleta
void Screen::setPalete() {
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);
}
// Extrae los nombres de las paletas
void Screen::processPaletteList() {
for (auto& palette : palettes_) {
palette = getFileName(palette);
}
}
// Copia la surface a la textura // Copia la surface a la textura
void Screen::surfaceToTexture() { void Screen::surfaceToTexture() { // NOLINT(readability-convert-member-functions-to-static)
if (Options::video.border.enabled) { if (Options::video.border.enabled) {
border_surface_->copyToTexture(renderer_, border_texture_); border_surface_->copyToTexture(renderer_, border_texture_);
game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_); game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_);
@@ -329,83 +378,81 @@ void Screen::surfaceToTexture() {
// Copia la textura al renderizador (o hace el present GPU) // Copia la textura al renderizador (o hace el present GPU)
void Screen::textureToRenderer() { void Screen::textureToRenderer() {
SDL_Texture* texture_to_render = Options::video.border.enabled ? border_texture_ : game_texture_;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
// ---- SDL3 GPU path: convertir Surface → ARGB → upload → PostFX/pass-through → present ---- const int GAME_W = Options::game.width;
if (Options::video.border.enabled) { const int GAME_H = Options::game.height;
// El border_surface_ solo tiene el color de borde; hay que componer encima el game_surface_
const int BORDER_W = static_cast<int>(border_surface_->getWidth());
const int BORDER_H = static_cast<int>(border_surface_->getHeight());
pixel_buffer_.resize(static_cast<size_t>(BORDER_W * BORDER_H));
border_surface_->toARGBBuffer(pixel_buffer_.data());
// Compositar game_surface_ en la posición correcta dentro del buffer if (Options::video.border.enabled) {
const int GAME_W = static_cast<int>(game_surface_->getWidth()); const int BORDER_W = window_width_;
const int GAME_H = static_cast<int>(game_surface_->getHeight()); const int BORDER_H = window_height_;
const int OFF_X = static_cast<int>(game_surface_dstrect_.x); const int OFF_X = static_cast<int>(game_surface_dstrect_.x);
const int OFF_Y = static_cast<int>(game_surface_dstrect_.y); const int OFF_Y = static_cast<int>(game_surface_dstrect_.y);
std::vector<Uint32> game_pixels(static_cast<size_t>(GAME_W * GAME_H));
game_surface_->toARGBBuffer(game_pixels.data()); if (border_is_solid_) {
for (int y = 0; y < GAME_H; ++y) { // Path A: borde sólido (gameplay normal)
for (int x = 0; x < GAME_W; ++x) { // Rellena solo el marco con el color cacheado — sin lookups de paleta.
pixel_buffer_[static_cast<size_t>(((OFF_Y + y) * BORDER_W) + (OFF_X + x))] = game_pixels[static_cast<size_t>((y * GAME_W) + x)]; // El área central (juego) se deja sin tocar; el overlay la sobreescribe igualmente.
// Franjas superior e inferior (ancho completo)
std::fill_n(border_pixel_buffer_.data(), OFF_Y * BORDER_W, border_argb_color_);
std::fill_n(&border_pixel_buffer_[(OFF_Y + GAME_H) * BORDER_W],
(BORDER_H - OFF_Y - GAME_H) * BORDER_W,
border_argb_color_);
// Columnas laterales en las filas del área de juego
for (int y = OFF_Y; y < OFF_Y + GAME_H; ++y) {
std::fill_n(&border_pixel_buffer_[y * BORDER_W], OFF_X, border_argb_color_);
std::fill_n(&border_pixel_buffer_[y * BORDER_W + OFF_X + GAME_W],
BORDER_W - OFF_X - GAME_W,
border_argb_color_);
} }
} else {
// Path B: borde dinámico (escena de carga — bandas de colores animadas)
// Conversión completa: la escena modifica border_surface_ cada frame
border_surface_->toARGBBuffer(border_pixel_buffer_.data());
} }
shader_backend_->uploadPixels(pixel_buffer_.data(), BORDER_W, BORDER_H);
// Overlay del juego sobre el centro del buffer (ambos paths)
game_surface_->toARGBBuffer(game_pixel_buffer_.data());
for (int y = 0; y < GAME_H; ++y) {
const Uint32* src = &game_pixel_buffer_[y * GAME_W];
Uint32* dst = &border_pixel_buffer_[(OFF_Y + y) * BORDER_W + OFF_X];
std::memcpy(dst, src, GAME_W * sizeof(Uint32));
}
shader_backend_->uploadPixels(border_pixel_buffer_.data(), BORDER_W, BORDER_H);
} else { } else {
const int GAME_W = static_cast<int>(game_surface_->getWidth()); // Caso sin borde: subida directa simplificada
const int GAME_H = static_cast<int>(game_surface_->getHeight()); game_surface_->toARGBBuffer(game_pixel_buffer_.data());
pixel_buffer_.resize(static_cast<size_t>(GAME_W * GAME_H)); shader_backend_->uploadPixels(game_pixel_buffer_.data(), GAME_W, GAME_H);
game_surface_->toARGBBuffer(pixel_buffer_.data());
shader_backend_->uploadPixels(pixel_buffer_.data(), GAME_W, GAME_H);
} }
shader_backend_->render(); shader_backend_->render();
} else { } else {
// ---- SDL_Renderer path (fallback / no-shader) ---- // Fallback SDL_Renderer (mantiene tu lógica de texturas SDL)
SDL_Texture* tex = Options::video.border.enabled ? border_texture_ : game_texture_;
SDL_SetRenderTarget(renderer_, nullptr); SDL_SetRenderTarget(renderer_, nullptr);
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer_); SDL_RenderClear(renderer_);
SDL_RenderTexture(renderer_, texture_to_render, nullptr, nullptr); SDL_RenderTexture(renderer_, tex, nullptr, nullptr);
SDL_RenderPresent(renderer_); SDL_RenderPresent(renderer_);
} }
} }
// Renderiza todos los overlays // Renderiza todos los overlays (orden: último dibujado queda encima)
void Screen::renderOverlays() { void Screen::renderOverlays() {
renderNotifications(); renderNotifications(); // Notifier (abajo)
#ifdef _DEBUG if (RenderInfo::get() != nullptr) { RenderInfo::get()->render(); } // RenderInfo (medio)
renderInfo(); if (Console::get() != nullptr) { Console::get()->render(); } // Console (encima)
#endif
} }
// Localiza la paleta dentro del vector de paletas // Cambia a una paleta por nombre (case-insensitive); devuelve false si no existe
auto Screen::findPalette(const std::string& name) -> size_t { auto Screen::setPaletteByName(const std::string& name) -> bool { return palette_manager_->setByName(name); }
std::string upper_name = toUpper(name + ".pal");
for (size_t i = 0; i < palettes_.size(); ++i) { // Devuelve los nombres de paletas disponibles (minúsculas, sin extensión .pal)
if (toUpper(getFileName(palettes_[i])) == upper_name) { auto Screen::getPaletteNames() const -> std::vector<std::string> { return palette_manager_->getNames(); }
return i; auto Screen::getPalettePrettyName() const -> std::string { return palette_manager_->getPrettyName(); }
} void Screen::nextPaletteSortMode() { palette_manager_->nextSortMode(); }
} void Screen::setPaletteSortMode(PaletteSortMode mode) { palette_manager_->setSortMode(mode); }
return static_cast<size_t>(0); auto Screen::getPaletteSortModeName() const -> std::string { return palette_manager_->getSortModeName(); }
}
// Muestra información por pantalla
void Screen::renderInfo() const {
if (show_fps_ && (Resource::Cache::get() != nullptr)) {
auto text = Resource::Cache::get()->getText("smb2");
auto color = static_cast<Uint8>(PaletteColor::YELLOW);
auto shadow = static_cast<Uint8>(PaletteColor::BLACK);
// FPS con sombra
const std::string FPS_TEXT = std::to_string(fps_.last_value) + " FPS";
const int FPS_X = Options::game.width - text->length(FPS_TEXT) - 1;
text->writeColored(FPS_X + 1, 1, FPS_TEXT, shadow);
text->writeColored(FPS_X, 0, FPS_TEXT, color);
}
}
// Limpia la game_surface_ // Limpia la game_surface_
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); } void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
@@ -428,9 +475,6 @@ void Screen::hide() { SDL_HideWindow(window_); }
// Establece la visibilidad de las notificaciones // Establece la visibilidad de las notificaciones
void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = value; } void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = value; }
// Activa / desactiva el contador de FPS
void Screen::toggleFPS() { show_fps_ = !show_fps_; }
// Alterna entre activar y desactivar el escalado entero // Alterna entre activar y desactivar el escalado entero
void Screen::toggleIntegerScale() { void Screen::toggleIntegerScale() {
Options::video.integer_scale = !Options::video.integer_scale; Options::video.integer_scale = !Options::video.integer_scale;
@@ -438,6 +482,7 @@ void Screen::toggleIntegerScale() {
if (shader_backend_) { if (shader_backend_) {
shader_backend_->setScaleMode(Options::video.integer_scale); shader_backend_->setScaleMode(Options::video.integer_scale);
} }
updateZoomFactor();
} }
// Alterna entre activar y desactivar el V-Sync // Alterna entre activar y desactivar el V-Sync
@@ -452,22 +497,107 @@ void Screen::toggleVSync() {
// Getters // Getters
auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; } auto Screen::getRenderer() -> SDL_Renderer* { return renderer_; }
auto Screen::getRendererSurface() -> std::shared_ptr<Surface> { return (*renderer_surface_); } auto Screen::getRendererSurface() -> std::shared_ptr<Surface> { return (*renderer_surface_); }
auto Screen::getBorderSurface() -> std::shared_ptr<Surface> { return border_surface_; } auto Screen::getGameSurface() -> std::shared_ptr<Surface> { return game_surface_; }
auto Screen::getBorderSurface() -> std::shared_ptr<Surface> {
border_is_solid_ = false; // Modificación externa → modo borde dinámico
return border_surface_;
}
auto loadData(const std::string& filepath) -> std::vector<uint8_t> { auto loadData(const std::string& filepath) -> std::vector<uint8_t> {
// Load using ResourceHelper (supports both filesystem and pack) // Load using ResourceHelper (supports both filesystem and pack)
return Resource::Helper::loadFile(filepath); return Resource::Helper::loadFile(filepath);
} }
void Screen::setLinearUpscale(bool linear) {
Options::video.supersampling.linear_upscale = linear;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
shader_backend_->setLinearUpscale(linear);
}
}
void Screen::setDownscaleAlgo(int algo) {
Options::video.supersampling.downscale_algo = algo;
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
shader_backend_->setDownscaleAlgo(algo);
}
}
auto Screen::getSsTextureSize() const -> std::pair<int, int> {
if (!shader_backend_) { return {0, 0}; }
return shader_backend_->getSsTextureSize();
}
// Activa/desactiva el supersampling global (Ctrl+F4)
void Screen::toggleSupersampling() {
Options::video.supersampling.enabled = !Options::video.supersampling.enabled;
if (Options::video.shader.enabled && shader_backend_ && shader_backend_->isHardwareAccelerated()) {
applyCurrentPostFXPreset();
}
}
// Aplica los parámetros del preset actual al backend de shaders // Aplica los parámetros del preset actual al backend de shaders
void Screen::applyCurrentPostFXPreset() { void Screen::applyCurrentPostFXPreset() { // NOLINT(readability-convert-member-functions-to-static)
if (shader_backend_ && !Options::postfx_presets.empty()) { if (shader_backend_ && !Options::postfx_presets.empty()) {
const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)]; const auto& p = Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)];
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding}; // Supersampling es un toggle global (Options::video.supersampling.enabled), no por preset.
// setOversample primero: puede recrear texturas antes de que setPostFXParams
// decida si hornear scanlines en CPU o aplicarlas en GPU.
shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
Rendering::PostFXParams params{.vignette = p.vignette, .scanlines = p.scanlines, .chroma = p.chroma, .mask = p.mask, .gamma = p.gamma, .curvature = p.curvature, .bleeding = p.bleeding, .flicker = p.flicker};
shader_backend_->setPostFXParams(params); shader_backend_->setPostFXParams(params);
} }
} }
// Aplica los parámetros del preset CrtPi actual al backend de shaders
void Screen::applyCurrentCrtPiPreset() { // NOLINT(readability-convert-member-functions-to-static)
if (shader_backend_ && !Options::crtpi_presets.empty()) {
const auto& p = Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)];
Rendering::CrtPiParams params{
.scanline_weight = p.scanline_weight,
.scanline_gap_brightness = p.scanline_gap_brightness,
.bloom_factor = p.bloom_factor,
.input_gamma = p.input_gamma,
.output_gamma = p.output_gamma,
.mask_brightness = p.mask_brightness,
.curvature_x = p.curvature_x,
.curvature_y = p.curvature_y,
.mask_type = p.mask_type,
.enable_scanlines = p.enable_scanlines,
.enable_multisample = p.enable_multisample,
.enable_gamma = p.enable_gamma,
.enable_curvature = p.enable_curvature,
.enable_sharper = p.enable_sharper,
};
shader_backend_->setCrtPiParams(params);
}
}
// Cambia el shader de post-procesado activo y aplica el preset correspondiente
void Screen::setActiveShader(Rendering::ShaderType type) {
Options::video.shader.current_shader = type;
if (!shader_backend_) { return; }
if (!Options::video.shader.enabled) {
// Shaders desactivados: guardar preferencia pero mantener pass-through
shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
shader_backend_->setPostFXParams(Rendering::PostFXParams{});
return;
}
shader_backend_->setActiveShader(type);
if (type == Rendering::ShaderType::CRTPI) {
applyCurrentCrtPiPreset();
} else {
applyCurrentPostFXPreset();
}
}
// Cicla al siguiente shader disponible (preparado para futura UI)
void Screen::nextShader() {
const Rendering::ShaderType NEXT = (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX)
? Rendering::ShaderType::CRTPI
: Rendering::ShaderType::POSTFX;
setActiveShader(NEXT);
}
// Inicializa los shaders // Inicializa los shaders
// El device GPU se crea siempre (independientemente de postfx) para evitar // El device GPU se crea siempre (independientemente de postfx) para evitar
// conflictos SDL_Renderer/SDL_GPU al hacer toggle F4 en Windows/Vulkan. // conflictos SDL_Renderer/SDL_GPU al hacer toggle F4 en Windows/Vulkan.
@@ -476,23 +606,34 @@ void Screen::initShaders() {
if (!shader_backend_) { if (!shader_backend_) {
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>(); shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
const std::string fallback_driver = "none";
shader_backend_->setPreferredDriver(Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : fallback_driver);
} }
shader_backend_->init(window_, tex, "", ""); shader_backend_->init(window_, tex, "", "");
gpu_driver_ = shader_backend_->getDriverName();
// Propagar flags de vsync e integer scale al backend GPU // Propagar flags de vsync, integer scale, upscale y downscale al backend GPU
shader_backend_->setVSync(Options::video.vertical_sync); shader_backend_->setVSync(Options::video.vertical_sync);
shader_backend_->setScaleMode(Options::video.integer_scale); shader_backend_->setScaleMode(Options::video.integer_scale);
shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
shader_backend_->setDownscaleAlgo(Options::video.supersampling.downscale_algo);
if (Options::video.postfx) { if (Options::video.shader.enabled) {
applyCurrentPostFXPreset(); applyCurrentPostFXPreset();
} else { } else {
// Pass-through: todos los efectos a 0, el shader solo copia la textura // Pass-through: todos los efectos a 0, el shader solo copia la textura
shader_backend_->setPostFXParams(Rendering::PostFXParams{}); shader_backend_->setPostFXParams(Rendering::PostFXParams{});
} }
// Restaurar el shader activo guardado en config (y sus parámetros CrtPi si aplica)
shader_backend_->setActiveShader(Options::video.shader.current_shader);
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
applyCurrentCrtPiPreset();
}
} }
// Obtiene información sobre la pantalla // Obtiene información sobre la pantalla
void Screen::getDisplayInfo() { void Screen::getDisplayInfo() { // NOLINT(readability-convert-member-functions-to-static)
std::cout << "\n** VIDEO SYSTEM **\n"; std::cout << "\n** VIDEO SYSTEM **\n";
int num_displays = 0; int num_displays = 0;
@@ -597,10 +738,10 @@ auto Screen::initSDLVideo() -> bool {
} }
// Crea el objeto de texto // Crea el objeto de texto
void Screen::createText() { void Screen::createText() { // NOLINT(readability-convert-member-functions-to-static)
// Carga la surface de la fuente directamente del archivo // Carga la surface de la fuente directamente del archivo
auto surface = std::make_shared<Surface>(Resource::List::get()->get("aseprite.gif")); auto surface = std::make_shared<Surface>(Resource::List::get()->get("aseprite.gif"));
// Crea el objeto de texto (el constructor de Text carga el archivo text_file internamente) // Crea el objeto de texto (el constructor de Text carga el archivo text_file internamente)
text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.txt")); text_ = std::make_shared<Text>(surface, Resource::List::get()->get("aseprite.fnt"));
} }

View File

@@ -6,14 +6,14 @@
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <memory> // Para shared_ptr, __shared_ptr_access #include <memory> // Para shared_ptr, __shared_ptr_access
#include <string> // Para string #include <string> // Para string
#include <utility> // Para std::pair
#include <vector> // Para vector #include <vector> // Para vector
#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 Surface;
class Text; class Text;
namespace Rendering {
class ShaderBackend;
}
class Screen { class Screen {
public: public:
@@ -36,14 +36,15 @@ class Screen {
void update(float delta_time); // Actualiza la lógica de la clase void update(float delta_time); // Actualiza la lógica de la clase
// Video y ventana // Video y ventana
void setVideoMode(bool mode); // Establece el modo de video void setVideoMode(bool mode); // Establece el modo de video
void toggleVideoMode(); // Cambia entre pantalla completa y ventana void toggleVideoMode(); // Cambia entre pantalla completa y ventana
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
auto decWindowZoom() -> bool; // Reduce el tamaño de la ventana auto decWindowZoom() -> bool; // Reduce el tamaño de la ventana
auto incWindowZoom() -> bool; // Aumenta el tamaño de la ventana auto incWindowZoom() -> bool; // Aumenta el tamaño de la ventana
void show(); // Muestra la ventana auto setWindowZoom(int zoom) -> bool; // Establece zoom directo; false si fuera de [1, max_zoom]
void hide(); // Oculta la ventana void show(); // Muestra la ventana
void hide(); // Oculta la ventana
// Borde // Borde
void setBorderColor(Uint8 color); // Cambia el color del borde void setBorderColor(Uint8 color); // Cambia el color del borde
@@ -53,23 +54,41 @@ class Screen {
void toggleBorder(); // Cambia entre borde visible y no visible void toggleBorder(); // Cambia entre borde visible y no visible
// Paletas y PostFX // Paletas y PostFX
void nextPalette(); // Cambia a la siguiente paleta void nextPalette(); // Cambia a la siguiente paleta
void previousPalette(); // Cambia a la paleta anterior void previousPalette(); // Cambia a la paleta anterior
void setPalete(); // Establece la paleta actual auto setPaletteByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe
void togglePostFX(); // Cambia el estado del PostFX [[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>; // Nombres disponibles (minúsculas, sin .pal)
void reloadPostFX(); // Recarga el shader del preset actual sin toggle [[nodiscard]] auto getPalettePrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios
void nextPaletteSortMode(); // Cicla al siguiente modo de ordenación de paleta
void setPaletteSortMode(PaletteSortMode mode); // Establece modo de ordenación concreto
[[nodiscard]] auto getPaletteSortModeName() const -> std::string; // Nombre del modo de ordenación 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)
// Surfaces y notificaciones // Surfaces y notificaciones
void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr); // Establece el renderizador para las surfaces
void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones
void toggleFPS(); // Activa o desactiva el contador de FPS void updateZoomFactor(); // Recalcula y almacena el factor de zoom real
// Getters // Getters
auto getRenderer() -> SDL_Renderer*; auto getRenderer() -> SDL_Renderer*;
auto getRendererSurface() -> std::shared_ptr<Surface>; auto getRendererSurface() -> std::shared_ptr<Surface>;
auto getBorderSurface() -> std::shared_ptr<Surface>; auto getBorderSurface() -> std::shared_ptr<Surface>;
auto getGameSurface() -> std::shared_ptr<Surface>;
[[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; } [[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }
[[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; } [[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; }
[[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
[[nodiscard]] auto isHardwareAccelerated() const -> bool { return shader_backend_ && shader_backend_->isHardwareAccelerated(); }
[[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: private:
// Estructuras // Estructuras
@@ -106,20 +125,18 @@ class Screen {
static Screen* screen; static Screen* screen;
// Métodos privados // Métodos privados
void renderNotifications() const; // Dibuja las notificaciones void renderNotifications() const; // Dibuja las notificaciones
void adjustWindowSize(); // Calcula el tamaño de la ventana void adjustWindowSize(); // Calcula el tamaño de la ventana
void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador void adjustRenderLogicalSize(); // Ajusta el tamaño lógico del renderizador
void processPaletteList(); // Extrae los nombres de las paletas void surfaceToTexture(); // Copia la surface a la textura
void surfaceToTexture(); // Copia la surface a la textura void textureToRenderer(); // Copia la textura al renderizador
void textureToRenderer(); // Copia la textura al renderizador void renderOverlays(); // Renderiza todos los overlays
void renderOverlays(); // Renderiza todos los overlays void initShaders(); // Inicializa los shaders
auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas void applyCurrentPostFXPreset(); // Aplica los parámetros del preset PostFX actual al backend
void initShaders(); // Inicializa los shaders void applyCurrentCrtPiPreset(); // Aplica los parámetros del preset CrtPi actual al backend
void applyCurrentPostFXPreset(); // Aplica los parámetros del preset actual al backend void getDisplayInfo(); // Obtiene información sobre la pantalla
void renderInfo() const; // Muestra información por pantalla auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
void getDisplayInfo(); // Obtiene información sobre la pantalla void createText(); // Crea el objeto de texto
auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana
void createText(); // Crea el objeto de texto
// Constructor y destructor // Constructor y destructor
Screen(); Screen();
@@ -138,15 +155,23 @@ class Screen {
std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend de shaders (OpenGL/Metal/Vulkan) std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend de shaders (OpenGL/Metal/Vulkan)
std::shared_ptr<Text> text_; // Objeto para escribir texto std::shared_ptr<Text> text_; // Objeto para escribir texto
// Buffers persistentes para evitar .resize() cada frame
std::vector<Uint32> game_pixel_buffer_; // Textura de juego
std::vector<Uint32> border_pixel_buffer_; // Textura de borde (composición final borde+juego)
// Caché del borde sólido (gameplay normal)
bool border_is_solid_{true}; // true = borde de color sólido; false = borde dinámico (carga)
Uint32 border_argb_color_{0}; // Color ARGB pre-convertido del borde sólido
// Configuración de ventana y pantalla // Configuración de ventana y pantalla
int window_width_{0}; // Ancho de la pantalla o ventana int window_width_{0}; // Ancho de la pantalla o ventana
int window_height_{0}; // Alto de la pantalla o ventana int window_height_{0}; // Alto de la pantalla o ventana
float zoom_factor_{1.0f}; // Factor de zoom calculado (alto físico / alto lógico)
SDL_FRect game_surface_dstrect_; // Coordenadas donde se dibuja la textura del juego SDL_FRect game_surface_dstrect_; // Coordenadas donde se dibuja la textura del juego
// Paletas y colores // Paletas y colores
Uint8 border_color_{0}; // Color del borde Uint8 border_color_{0}; // Color del borde
std::vector<std::string> palettes_; // Listado de ficheros de paleta disponibles std::unique_ptr<PaletteManager> palette_manager_; // Gestor de paletas de color
Uint8 current_palette_{0}; // Índice para el vector de paletas
// Estado y configuración // Estado y configuración
bool notifications_enabled_{false}; // Indica si se muestran las notificaciones bool notifications_enabled_{false}; // Indica si se muestran las notificaciones
@@ -154,12 +179,6 @@ class Screen {
DisplayMonitor display_monitor_; // Información de la pantalla DisplayMonitor display_monitor_; // Información de la pantalla
// Shaders // Shaders
std::string info_resolution_; // Texto con la información de la pantalla std::string info_resolution_; // Texto con la información de la pantalla
std::vector<Uint32> pixel_buffer_; // Buffer intermedio para SDL3GPU path (surface → ARGB) std::string gpu_driver_; // Nombre del driver GPU (SDL3GPU), capturado en initShaders()
#ifdef _DEBUG
bool show_fps_{true}; // Indica si ha de mostrar el contador de FPS
#else
bool show_fps_{false}; // Indica si ha de mostrar el contador de FPS
#endif
}; };

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

File diff suppressed because it is too large Load Diff

View File

@@ -7,16 +7,55 @@
// PostFX uniforms pushed to fragment stage each frame. // PostFX uniforms pushed to fragment stage each frame.
// Must match the MSL struct and GLSL uniform block layout. // Must match the MSL struct and GLSL uniform block layout.
// 8 floats = 32 bytes — meets Metal/Vulkan 16-byte alignment requirement. // 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
struct PostFXUniforms { struct PostFXUniforms {
float vignette_strength; // 0 = none, ~0.8 = subtle float vignette_strength; // 0 = none, ~0.8 = subtle
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
float scanline_strength; // 0 = off, 1 = full float scanline_strength; // 0 = off, 1 = full
float screen_height; // logical height in pixels (for resolution-independent scanlines) float screen_height; // logical height in pixels (used by bleeding effect)
float mask_strength; // 0 = off, 1 = full phosphor dot mask float mask_strength; // 0 = off, 1 = full phosphor dot mask
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
float curvature; // 0 = flat, 1 = max barrel distortion float curvature; // 0 = flat, 1 = max barrel distortion
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
};
// CrtPi uniforms pushed to fragment stage each frame.
// Must match the MSL struct and GLSL uniform block layout.
// 14 fields (8 floats + 6 ints) + 2 floats (texture size) = 16 fields = 64 bytes — 4 × 16-byte alignment.
struct CrtPiUniforms {
// vec4 #0
float scanline_weight; // Ajuste gaussiano (default 6.0)
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
float bloom_factor; // Factor brillo zonas iluminadas (default 3.5)
float input_gamma; // Gamma de entrada (default 2.4)
// vec4 #1
float output_gamma; // Gamma de salida (default 2.2)
float mask_brightness; // Brillo sub-píxeles máscara (default 0.80)
float curvature_x; // Distorsión barrel X (default 0.05)
float curvature_y; // Distorsión barrel Y (default 0.10)
// vec4 #2
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
int enable_scanlines; // 0 = off, 1 = on
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico)
int enable_gamma; // 0 = off, 1 = on
// vec4 #3
int enable_curvature; // 0 = off, 1 = on
int enable_sharper; // 0 = off, 1 = on
float texture_width; // Ancho del canvas en píxeles (inyectado en render)
float texture_height; // Alto del canvas en píxeles (inyectado en render)
};
// Downscale uniforms pushed to the Lanczos downscale fragment stage.
// 1 int + 3 floats = 16 bytes — meets Metal/Vulkan alignment.
struct DownscaleUniforms {
int algorithm; // 0 = Lanczos2 (ventana 2), 1 = Lanczos3 (ventana 3)
float pad0;
float pad1;
float pad2;
}; };
namespace Rendering { namespace Rendering {
@@ -43,6 +82,10 @@ namespace Rendering {
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; } [[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
[[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; }
// Establece el driver GPU preferido (vacío = auto). Debe llamarse antes de init().
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
// Sube píxeles ARGB8888 desde CPU; llamado antes de render() // Sube píxeles ARGB8888 desde CPU; llamado antes de render()
void uploadPixels(const Uint32* pixels, int width, int height) override; void uploadPixels(const Uint32* pixels, int width, int height) override;
@@ -56,6 +99,27 @@ namespace Rendering {
// Activa/desactiva escalado entero (integer scale) // Activa/desactiva escalado entero (integer scale)
void setScaleMode(bool integer_scale) override; void setScaleMode(bool integer_scale) override;
// Establece factor de supersampling (1 = off, 3 = 3×SS)
void setOversample(int factor) override;
// Activa/desactiva interpolación LINEAR en el upscale (false = NEAREST)
void setLinearUpscale(bool linear) override;
// Selecciona algoritmo de downscale: 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
void setDownscaleAlgo(int algo) override;
// Devuelve las dimensiones de la textura de supersampling (0,0 si SS desactivado)
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
// Selecciona el shader de post-procesado activo (POSTFX o CRTPI)
void setActiveShader(ShaderType type) override;
// Actualiza los parámetros del shader CRT-Pi
void setCrtPiParams(const CrtPiParams& p) override;
// Devuelve el shader activo
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
private: private:
static auto createShaderMSL(SDL_GPUDevice* device, static auto createShaderMSL(SDL_GPUDevice* device,
const char* msl_source, const char* msl_source,
@@ -73,21 +137,42 @@ namespace Rendering {
Uint32 num_uniform_buffers) -> SDL_GPUShader*; Uint32 num_uniform_buffers) -> SDL_GPUShader*;
auto createPipeline() -> bool; auto createPipeline() -> bool;
auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi
auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_
auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado
static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3)
// Devuelve el mejor present mode disponible: IMMEDIATE > MAILBOX > VSYNC
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
SDL_Window* window_ = nullptr; SDL_Window* window_ = nullptr;
SDL_GPUDevice* device_ = nullptr; SDL_GPUDevice* device_ = nullptr;
SDL_GPUGraphicsPipeline* pipeline_ = nullptr; SDL_GPUGraphicsPipeline* pipeline_ = nullptr; // PostFX pass (→ swapchain o → postfx_texture_)
SDL_GPUTexture* scene_texture_ = nullptr; SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr; // CrtPi pass (→ swapchain directo, sin SS)
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr; // PostFX → postfx_texture_ (B8G8R8A8, solo con Lanczos)
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr; // Upscale pass (solo con SS)
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr; // Lanczos downscale (solo con SS + algo > 0)
SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del juego (game_width_ × game_height_)
SDL_GPUTexture* scaled_texture_ = nullptr; // Upscale target (game×factor), solo con SS
SDL_GPUTexture* postfx_texture_ = nullptr; // PostFX output a resolución escalada, solo con Lanczos
SDL_GPUTransferBuffer* upload_buffer_ = nullptr; SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
SDL_GPUSampler* sampler_ = nullptr; SDL_GPUSampler* sampler_ = nullptr; // NEAREST
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F}; PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F};
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
ShaderType active_shader_ = ShaderType::POSTFX; // Shader de post-procesado activo
int tex_width_ = 0; int game_width_ = 0; // Dimensiones originales del canvas
int tex_height_ = 0; int game_height_ = 0;
int ss_factor_ = 0; // Factor SS activo (3, 6, 9...) o 0 si SS desactivado
int oversample_ = 1; // SS on/off (1 = off, >1 = on)
int downscale_algo_ = 1; // 0 = bilinear legacy, 1 = Lanczos2, 2 = Lanczos3
std::string driver_name_;
std::string preferred_driver_; // Driver preferido; vacío = auto (SDL elige)
bool is_initialized_ = false; bool is_initialized_ = false;
bool vsync_ = true; bool vsync_ = true;
bool integer_scale_ = false; bool integer_scale_ = false;
bool linear_upscale_ = false; // Upscale NEAREST (false) o LINEAR (true)
}; };
} // namespace Rendering } // namespace Rendering

View File

@@ -0,0 +1,633 @@
#pragma once
#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};
static const size_t kupscale_frag_spv_size = 628;

View File

@@ -3,9 +3,14 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <string> #include <string>
#include <utility>
namespace Rendering { namespace Rendering {
/** @brief Identificador del shader de post-procesado activo */
enum class ShaderType { POSTFX,
CRTPI };
/** /**
* @brief Parámetros de intensidad de los efectos PostFX * @brief Parámetros de intensidad de los efectos PostFX
* Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp * Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp
@@ -18,6 +23,28 @@ namespace Rendering {
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full) float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
float curvature = 0.0F; // Curvatura barrel CRT float curvature = 0.0F; // Curvatura barrel CRT
float bleeding = 0.0F; // Sangrado de color NTSC float bleeding = 0.0F; // Sangrado de color NTSC
float flicker = 0.0F; // Parpadeo de fósforo CRT ~50 Hz
};
/**
* @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
* Diferente al PostFX: usa pesos gaussianos por distancia subpixel y bloom.
*/
struct CrtPiParams {
float scanline_weight{6.0F}; // Ajuste gaussiano (mayor = scanlines más estrechas)
float scanline_gap_brightness{0.12F}; // Brillo mínimo en las ranuras entre scanlines
float bloom_factor{3.5F}; // Factor de brillo para zonas iluminadas
float input_gamma{2.4F}; // Gamma de entrada (linealización)
float output_gamma{2.2F}; // Gamma de salida (codificación)
float mask_brightness{0.80F}; // Sub-píxeles tenues en la máscara de fósforo
float curvature_x{0.05F}; // Distorsión barrel eje X
float curvature_y{0.10F}; // Distorsión barrel eje Y
int mask_type{2}; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
bool enable_scanlines{true}; // Activar efecto de scanlines
bool enable_multisample{true}; // Antialiasing analítico de scanlines
bool enable_gamma{true}; // Corrección gamma
bool enable_curvature{false}; // Distorsión barrel CRT
bool enable_sharper{false}; // Submuestreo más nítido (modo SHARPER)
}; };
/** /**
@@ -82,11 +109,67 @@ namespace Rendering {
*/ */
virtual void setScaleMode(bool /*integer_scale*/) {} virtual void setScaleMode(bool /*integer_scale*/) {}
/**
* @brief Establece el factor de supersampling (1 = off, 3 = 3× SS)
* Con factor > 1, la textura GPU se crea a game×factor resolución y
* las scanlines se hornean en CPU (uploadPixels). El sampler usa LINEAR.
*/
virtual void setOversample(int /*factor*/) {}
/**
* @brief Activa/desactiva interpolación LINEAR en el paso de upscale (SS).
* Por defecto NEAREST (false). Solo tiene efecto con supersampling activo.
*/
virtual void setLinearUpscale(bool /*linear*/) {}
[[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }
/**
* @brief Selecciona el algoritmo de downscale tras el PostFX (SS activo).
* 0 = bilinear legacy (comportamiento actual, sin textura intermedia),
* 1 = Lanczos2 (ventana 2, ~25 muestras), 2 = Lanczos3 (ventana 3, ~49 muestras).
*/
virtual void setDownscaleAlgo(int /*algo*/) {}
[[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }
/**
* @brief Devuelve las dimensiones de la textura de supersampling.
* @return Par (ancho, alto) en píxeles; (0, 0) si SS está desactivado.
*/
[[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }
/** /**
* @brief Verifica si el backend está usando aceleración por hardware * @brief Verifica si el backend está usando aceleración por hardware
* @return true si usa aceleración (OpenGL/Metal/Vulkan) * @return true si usa aceleración (OpenGL/Metal/Vulkan)
*/ */
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0; [[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
/**
* @brief Nombre del driver GPU activo (p.ej. "vulkan", "metal", "direct3d12")
* @return Cadena vacía si no disponible
*/
[[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }
/**
* @brief Establece el driver GPU preferido antes de init().
* Vacío = selección automática de SDL. Implementado en SDL3GPUShader.
*/
virtual void setPreferredDriver(const std::string& /*driver*/) {}
/**
* @brief Selecciona el shader de post-procesado activo (POSTFX o CRTPI).
* Debe llamarse antes de render(). No recrea pipelines.
*/
virtual void setActiveShader(ShaderType /*type*/) {}
/**
* @brief Establece los parámetros del shader CRT-Pi.
*/
virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}
/**
* @brief Devuelve el shader de post-procesado activo.
*/
[[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }
}; };
} // namespace Rendering } // namespace Rendering

View File

@@ -1,4 +1,4 @@
#include "core/rendering/surface_animated_sprite.hpp" #include "core/rendering/sprite/animated_sprite.hpp"
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <fstream> // Para basic_ostream, basic_istream, operator<<, basic... #include <fstream> // Para basic_ostream, basic_istream, operator<<, basic...
@@ -16,7 +16,7 @@
// Helper: Convierte un nodo YAML de frames (array) a vector de SDL_FRect // Helper: Convierte un nodo YAML de frames (array) a vector de SDL_FRect
auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<SDL_FRect> { auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<SDL_FRect> {
std::vector<SDL_FRect> frames; std::vector<SDL_FRect> frames;
SDL_FRect rect = {0.0F, 0.0F, frame_width, frame_height}; SDL_FRect rect = {.x = 0.0F, .y = 0.0F, .w = frame_width, .h = frame_height};
for (const auto& frame_index_node : frames_node) { for (const auto& frame_index_node : frames_node) {
const int NUM_TILE = frame_index_node.get_value<int>(); const int NUM_TILE = frame_index_node.get_value<int>();
@@ -31,7 +31,7 @@ auto convertYAMLFramesToRects(const fkyaml::node& frames_node, float frame_width
} }
// Carga las animaciones desde un fichero YAML // Carga las animaciones desde un fichero YAML
auto SurfaceAnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData> { auto AnimatedSprite::loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData> { // NOLINT(readability-convert-member-functions-to-static)
std::vector<AnimationData> animations; std::vector<AnimationData> animations;
// Extract filename for logging // Extract filename for logging
@@ -124,7 +124,7 @@ auto SurfaceAnimatedSprite::loadAnimationsFromYAML(const std::string& file_path,
} }
// Constructor con bytes YAML del cache (parsing lazy) // Constructor con bytes YAML del cache (parsing lazy)
SurfaceAnimatedSprite::SurfaceAnimatedSprite(const AnimationResource& cached_data) { AnimatedSprite::AnimatedSprite(const AnimationResource& cached_data) {
// Parsear YAML desde los bytes cargados en cache // Parsear YAML desde los bytes cargados en cache
std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end()); std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end());
@@ -215,8 +215,8 @@ SurfaceAnimatedSprite::SurfaceAnimatedSprite(const AnimationResource& cached_dat
} }
// Constructor per a subclasses amb surface directa (sense YAML) // Constructor per a subclasses amb surface directa (sense YAML)
SurfaceAnimatedSprite::SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) AnimatedSprite::AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceMovingSprite(std::move(surface), pos) { : MovingSprite(std::move(surface), pos) {
// animations_ queda buit (protegit per el guard de animate()) // animations_ queda buit (protegit per el guard de animate())
if (surface_) { if (surface_) {
clip_ = {.x = 0, .y = 0, .w = surface_->getWidth(), .h = surface_->getHeight()}; clip_ = {.x = 0, .y = 0, .w = surface_->getWidth(), .h = surface_->getHeight()};
@@ -224,7 +224,7 @@ SurfaceAnimatedSprite::SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, S
} }
// Obtiene el indice de la animación a partir del nombre // Obtiene el indice de la animación a partir del nombre
auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int { auto AnimatedSprite::getIndex(const std::string& name) -> int { // NOLINT(readability-convert-member-functions-to-static)
auto index = -1; auto index = -1;
for (const auto& a : animations_) { for (const auto& a : animations_) {
@@ -238,7 +238,7 @@ auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int {
} }
// Calcula el frame correspondiente a la animación (time-based) // Calcula el frame correspondiente a la animación (time-based)
void SurfaceAnimatedSprite::animate(float delta_time) { void AnimatedSprite::animate(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
if (animations_.empty()) { return; } if (animations_.empty()) { return; }
if (animations_[current_animation_].speed <= 0.0F) { if (animations_[current_animation_].speed <= 0.0F) {
return; return;
@@ -288,12 +288,12 @@ void SurfaceAnimatedSprite::animate(float delta_time) {
} }
// Comprueba si ha terminado la animación // Comprueba si ha terminado la animación
auto SurfaceAnimatedSprite::animationIsCompleted() -> bool { auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed; return animations_[current_animation_].completed;
} }
// Establece la animacion actual // Establece la animacion actual
void SurfaceAnimatedSprite::setCurrentAnimation(const std::string& name) { void AnimatedSprite::setCurrentAnimation(const std::string& name) {
const auto NEW_ANIMATION = getIndex(name); const auto NEW_ANIMATION = getIndex(name);
if (current_animation_ != NEW_ANIMATION) { if (current_animation_ != NEW_ANIMATION) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
@@ -305,7 +305,7 @@ void SurfaceAnimatedSprite::setCurrentAnimation(const std::string& name) {
} }
// Establece la animacion actual // Establece la animacion actual
void SurfaceAnimatedSprite::setCurrentAnimation(int index) { void AnimatedSprite::setCurrentAnimation(int index) {
const auto NEW_ANIMATION = index; const auto NEW_ANIMATION = index;
if (current_animation_ != NEW_ANIMATION) { if (current_animation_ != NEW_ANIMATION) {
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;
@@ -317,20 +317,20 @@ void SurfaceAnimatedSprite::setCurrentAnimation(int index) {
} }
// Actualiza las variables del objeto (time-based) // Actualiza las variables del objeto (time-based)
void SurfaceAnimatedSprite::update(float delta_time) { void AnimatedSprite::update(float delta_time) {
animate(delta_time); animate(delta_time);
SurfaceMovingSprite::update(delta_time); MovingSprite::update(delta_time);
} }
// Reinicia la animación // Reinicia la animación
void SurfaceAnimatedSprite::resetAnimation() { void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].accumulated_time = 0.0F; animations_[current_animation_].accumulated_time = 0.0F;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} }
// Establece el frame actual de la animación // Establece el frame actual de la animación
void SurfaceAnimatedSprite::setCurrentAnimationFrame(int num) { void AnimatedSprite::setCurrentAnimationFrame(int num) {
// Descarta valores fuera de rango // Descarta valores fuera de rango
if (num < 0 || num >= static_cast<int>(animations_[current_animation_].frames.size())) { if (num < 0 || num >= static_cast<int>(animations_[current_animation_].frames.size())) {
num = 0; num = 0;

View File

@@ -7,12 +7,12 @@
#include <utility> #include <utility>
#include <vector> // Para vector #include <vector> // Para vector
#include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite #include "core/rendering/sprite/moving_sprite.hpp" // Para SMovingSprite
#include "core/resources/resource_types.hpp" // Para AnimationResource #include "core/resources/resource_types.hpp" // Para AnimationResource
class Surface; class Surface;
class SurfaceAnimatedSprite : public SurfaceMovingSprite { class AnimatedSprite : public MovingSprite {
public: public:
using Animations = std::vector<std::string>; // Tipo para lista de animaciones using Animations = std::vector<std::string>; // Tipo para lista de animaciones
@@ -31,9 +31,9 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite {
static auto loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData>; // Carga las animaciones desde fichero YAML static auto loadAnimationsFromYAML(const std::string& file_path, std::shared_ptr<Surface>& surface, float& frame_width, float& frame_height) -> std::vector<AnimationData>; // Carga las animaciones desde fichero YAML
// Constructores // Constructores
explicit SurfaceAnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache explicit AnimatedSprite(const AnimationResource& cached_data); // Constructor con datos pre-cargados del cache
~SurfaceAnimatedSprite() override = default; // Destructor ~AnimatedSprite() override = default; // Destructor
void update(float delta_time) override; // Actualiza las variables del objeto (time-based) void update(float delta_time) override; // Actualiza las variables del objeto (time-based)
@@ -50,7 +50,7 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite {
protected: protected:
// Constructor per a ús de subclasses que gestionen la surface directament (sense YAML) // Constructor per a ús de subclasses que gestionen la surface directament (sense YAML)
SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); AnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
// Métodos protegidos // Métodos protegidos
void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based) void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based)

View File

@@ -1,4 +1,4 @@
#include "core/rendering/surface_dissolve_sprite.hpp" #include "core/rendering/sprite/dissolve_sprite.hpp"
#include <algorithm> // Para min #include <algorithm> // Para min
#include <cstdint> // Para uint32_t #include <cstdint> // Para uint32_t
@@ -15,7 +15,7 @@ static auto pixelRank(int col, int row) -> float {
} }
// Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori) // Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori)
auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float { auto DissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float {
const float RANDOM = pixelRank(col, row); const float RANDOM = pixelRank(col, row);
if (dir == DissolveDirection::NONE || frame_h <= 0) { if (dir == DissolveDirection::NONE || frame_h <= 0) {
return RANDOM; return RANDOM;
@@ -32,8 +32,8 @@ auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h, Diss
} }
// Constructor per a surface directa (sense AnimationResource) // Constructor per a surface directa (sense AnimationResource)
SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) DissolveSprite::DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceAnimatedSprite(std::move(surface), pos) { : AnimatedSprite(std::move(surface), pos) {
if (surface_) { if (surface_) {
const int W = static_cast<int>(surface_->getWidth()); const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight()); const int H = static_cast<int>(surface_->getHeight());
@@ -44,8 +44,8 @@ SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr<Surface> surface, S
} }
// Constructor // Constructor
SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data) DissolveSprite::DissolveSprite(const AnimationResource& data)
: SurfaceAnimatedSprite(data) { : AnimatedSprite(data) {
if (surface_) { if (surface_) {
const int W = static_cast<int>(surface_->getWidth()); const int W = static_cast<int>(surface_->getWidth());
const int H = static_cast<int>(surface_->getHeight()); const int H = static_cast<int>(surface_->getHeight());
@@ -57,7 +57,7 @@ SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data)
} }
// Reconstrueix la surface_display_ filtrant píxels per progress_ // Reconstrueix la surface_display_ filtrant píxels per progress_
void SurfaceDissolveSprite::rebuildDisplaySurface() { void DissolveSprite::rebuildDisplaySurface() {
if (!surface_ || !surface_display_) { if (!surface_ || !surface_display_) {
return; return;
} }
@@ -109,9 +109,9 @@ void SurfaceDissolveSprite::rebuildDisplaySurface() {
} }
// Actualitza animació, moviment i transició temporal // Actualitza animació, moviment i transició temporal
void SurfaceDissolveSprite::update(float delta_time) { void DissolveSprite::update(float delta_time) {
const SDL_FRect OLD_CLIP = clip_; const SDL_FRect OLD_CLIP = clip_;
SurfaceAnimatedSprite::update(delta_time); AnimatedSprite::update(delta_time);
// Detecta canvi de frame d'animació // Detecta canvi de frame d'animació
if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y || if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y ||
@@ -136,22 +136,22 @@ void SurfaceDissolveSprite::update(float delta_time) {
} }
// Renderitza: usa surface_display_ (amb color replace) si disponible // Renderitza: usa surface_display_ (amb color replace) si disponible
void SurfaceDissolveSprite::render() { void DissolveSprite::render() {
if (!surface_display_) { if (!surface_display_) {
SurfaceAnimatedSprite::render(); AnimatedSprite::render();
return; return;
} }
surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_); surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
} }
// Estableix el progrés manualment // Estableix el progrés manualment
void SurfaceDissolveSprite::setProgress(float progress) { void DissolveSprite::setProgress(float progress) {
progress_ = std::min(std::max(progress, 0.0F), 1.0F); progress_ = std::min(std::max(progress, 0.0F), 1.0F);
needs_rebuild_ = true; needs_rebuild_ = true;
} }
// Inicia dissolució temporal (visible → invisible) // Inicia dissolució temporal (visible → invisible)
void SurfaceDissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) { void DissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) {
direction_ = dir; direction_ = dir;
transition_mode_ = TransitionMode::DISSOLVING; transition_mode_ = TransitionMode::DISSOLVING;
transition_duration_ = duration_ms; transition_duration_ = duration_ms;
@@ -161,7 +161,7 @@ void SurfaceDissolveSprite::startDissolve(float duration_ms, DissolveDirection d
} }
// Inicia generació temporal (invisible → visible) // Inicia generació temporal (invisible → visible)
void SurfaceDissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) { void DissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) {
direction_ = dir; direction_ = dir;
transition_mode_ = TransitionMode::GENERATING; transition_mode_ = TransitionMode::GENERATING;
transition_duration_ = duration_ms; transition_duration_ = duration_ms;
@@ -171,17 +171,17 @@ void SurfaceDissolveSprite::startGenerate(float duration_ms, DissolveDirection d
} }
// Atura la transició temporal // Atura la transició temporal
void SurfaceDissolveSprite::stopTransition() { void DissolveSprite::stopTransition() {
transition_mode_ = TransitionMode::NONE; transition_mode_ = TransitionMode::NONE;
} }
// Retorna si la transició ha acabat // Retorna si la transició ha acabat
auto SurfaceDissolveSprite::isTransitionDone() const -> bool { auto DissolveSprite::isTransitionDone() const -> bool {
return transition_mode_ == TransitionMode::NONE; return transition_mode_ == TransitionMode::NONE;
} }
// Configura substitució de color per a la reconstrucció // Configura substitució de color per a la reconstrucció
void SurfaceDissolveSprite::setColorReplace(Uint8 source, Uint8 target) { void DissolveSprite::setColorReplace(Uint8 source, Uint8 target) {
source_color_ = source; source_color_ = source;
target_color_ = target; target_color_ = target;
needs_rebuild_ = true; needs_rebuild_ = true;

View File

@@ -4,7 +4,7 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include "core/rendering/surface_animated_sprite.hpp" // Para SurfaceAnimatedSprite #include "core/rendering/sprite/animated_sprite.hpp" // Para SurfaceAnimatedSprite
class Surface; class Surface;
@@ -15,11 +15,11 @@ enum class DissolveDirection { NONE,
// Sprite que pot dissoldre's o generar-se de forma aleatòria en X mil·lisegons. // Sprite que pot dissoldre's o generar-se de forma aleatòria en X mil·lisegons.
// progress_ va de 0.0 (totalment visible) a 1.0 (totalment invisible). // progress_ va de 0.0 (totalment visible) a 1.0 (totalment invisible).
class SurfaceDissolveSprite : public SurfaceAnimatedSprite { class DissolveSprite : public AnimatedSprite {
public: public:
explicit SurfaceDissolveSprite(const AnimationResource& data); explicit DissolveSprite(const AnimationResource& data);
SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
~SurfaceDissolveSprite() override = default; ~DissolveSprite() override = default;
void update(float delta_time) override; void update(float delta_time) override;
void render() override; void render() override;
@@ -52,7 +52,7 @@ class SurfaceDissolveSprite : public SurfaceAnimatedSprite {
TransitionMode transition_mode_{TransitionMode::NONE}; TransitionMode transition_mode_{TransitionMode::NONE};
float transition_duration_{0.0F}; float transition_duration_{0.0F};
float transition_elapsed_{0.0F}; float transition_elapsed_{0.0F};
SDL_FRect prev_clip_{0, 0, 0, 0}; SDL_FRect prev_clip_{.x = 0, .y = 0, .w = 0, .h = 0};
bool needs_rebuild_{false}; bool needs_rebuild_{false};
Uint8 source_color_{255}; // 255 = transparent = sense replace per defecte Uint8 source_color_{255}; // 255 = transparent = sense replace per defecte
Uint8 target_color_{0}; Uint8 target_color_{0};

View File

@@ -1,28 +1,28 @@
#include "core/rendering/surface_moving_sprite.hpp" #include "core/rendering/sprite/moving_sprite.hpp"
#include <utility> #include <utility>
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
// Constructor // Constructor
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip)
: SurfaceSprite(std::move(surface), pos), : Sprite(std::move(surface), pos),
x_(pos.x), x_(pos.x),
y_(pos.y), y_(pos.y),
flip_(flip) { SurfaceSprite::pos_ = pos; } flip_(flip) { Sprite::pos_ = pos; }
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
: SurfaceSprite(std::move(surface), pos), : Sprite(std::move(surface), pos),
x_(pos.x), x_(pos.x),
y_(pos.y) { SurfaceSprite::pos_ = pos; } y_(pos.y) { Sprite::pos_ = pos; }
SurfaceMovingSprite::SurfaceMovingSprite() { SurfaceSprite::clear(); } MovingSprite::MovingSprite() { Sprite::clear(); }
SurfaceMovingSprite::SurfaceMovingSprite(std::shared_ptr<Surface> surface) MovingSprite::MovingSprite(std::shared_ptr<Surface> surface)
: SurfaceSprite(std::move(surface)) { SurfaceSprite::clear(); } : Sprite(std::move(surface)) { Sprite::clear(); }
// Reinicia todas las variables // Reinicia todas las variables
void SurfaceMovingSprite::clear() { void MovingSprite::clear() {
// Resetea posición // Resetea posición
x_ = 0.0F; x_ = 0.0F;
y_ = 0.0F; y_ = 0.0F;
@@ -38,13 +38,13 @@ void SurfaceMovingSprite::clear() {
// Resetea flip // Resetea flip
flip_ = SDL_FLIP_NONE; flip_ = SDL_FLIP_NONE;
SurfaceSprite::clear(); Sprite::clear();
} }
// Mueve el sprite (time-based) // Mueve el sprite (time-based)
// Nota: vx_, vy_ ahora se interpretan como pixels/segundo // Nota: vx_, vy_ ahora se interpretan como pixels/segundo
// Nota: ax_, ay_ ahora se interpretan como pixels/segundo² // Nota: ax_, ay_ ahora se interpretan como pixels/segundo²
void SurfaceMovingSprite::move(float delta_time) { void MovingSprite::move(float delta_time) {
// Aplica aceleración a velocidad (time-based) // Aplica aceleración a velocidad (time-based)
vx_ += ax_ * delta_time; vx_ += ax_ * delta_time;
vy_ += ay_ * delta_time; vy_ += ay_ * delta_time;
@@ -59,22 +59,22 @@ void SurfaceMovingSprite::move(float delta_time) {
} }
// Actualiza las variables internas del objeto (time-based) // Actualiza las variables internas del objeto (time-based)
void SurfaceMovingSprite::update(float delta_time) { void MovingSprite::update(float delta_time) {
move(delta_time); move(delta_time);
} }
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceMovingSprite::render() { void MovingSprite::render() {
surface_->render(pos_.x, pos_.y, &clip_, flip_); surface_->render(pos_.x, pos_.y, &clip_, flip_);
} }
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceMovingSprite::render(Uint8 source_color, Uint8 target_color) { void MovingSprite::render(Uint8 source_color, Uint8 target_color) {
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_, flip_); surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_, flip_);
} }
// Establece la posición y_ el tamaño del objeto // Establece la posición y_ el tamaño del objeto
void SurfaceMovingSprite::setPos(SDL_FRect rect) { void MovingSprite::setPos(SDL_FRect rect) {
x_ = rect.x; x_ = rect.x;
y_ = rect.y; y_ = rect.y;
@@ -82,7 +82,7 @@ void SurfaceMovingSprite::setPos(SDL_FRect rect) {
} }
// Establece el valor de las variables // Establece el valor de las variables
void SurfaceMovingSprite::setPos(float x, float y) { void MovingSprite::setPos(float x, float y) {
x_ = x; x_ = x;
y_ = y; y_ = y;
@@ -91,13 +91,13 @@ void SurfaceMovingSprite::setPos(float x, float y) {
} }
// Establece el valor de la variable // Establece el valor de la variable
void SurfaceMovingSprite::setPosX(float value) { void MovingSprite::setPosX(float value) {
x_ = value; x_ = value;
pos_.x = static_cast<int>(x_); pos_.x = static_cast<int>(x_);
} }
// Establece el valor de la variable // Establece el valor de la variable
void SurfaceMovingSprite::setPosY(float value) { void MovingSprite::setPosY(float value) {
y_ = value; y_ = value;
pos_.y = static_cast<int>(y_); pos_.y = static_cast<int>(y_);
} }

View File

@@ -4,18 +4,18 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include "core/rendering/surface_sprite.hpp" // Para SSprite #include "core/rendering/sprite/sprite.hpp" // Para SSprite
class Surface; // lines 8-8 class Surface; // lines 8-8
// Clase SMovingSprite. Añade movimiento y flip al sprite // Clase SMovingSprite. Añade movimiento y flip al sprite
class SurfaceMovingSprite : public SurfaceSprite { class MovingSprite : public Sprite {
public: public:
// Constructores // Constructores
SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip); MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip);
SurfaceMovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos); MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
explicit SurfaceMovingSprite(); explicit MovingSprite();
explicit SurfaceMovingSprite(std::shared_ptr<Surface> surface); explicit MovingSprite(std::shared_ptr<Surface> surface);
~SurfaceMovingSprite() override = default; ~MovingSprite() override = default;
// Actualización y renderizado // Actualización y renderizado
void update(float delta_time) override; // Actualiza variables internas (time-based) void update(float delta_time) override; // Actualiza variables internas (time-based)

View File

@@ -1,37 +1,37 @@
#include "core/rendering/surface_sprite.hpp" #include "core/rendering/sprite/sprite.hpp"
#include <utility> #include <utility>
#include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface.hpp" // Para Surface
// Constructor // Constructor
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h) Sprite::Sprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_{x, y, w, h}, pos_{.x = x, .y = y, .w = w, .h = h},
clip_{0.0F, 0.0F, pos_.w, pos_.h} {} clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface, SDL_FRect rect) Sprite::Sprite(std::shared_ptr<Surface> surface, SDL_FRect rect)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_(rect), pos_(rect),
clip_{0.0F, 0.0F, pos_.w, pos_.h} {} clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
SurfaceSprite::SurfaceSprite() = default; Sprite::Sprite() = default;
SurfaceSprite::SurfaceSprite(std::shared_ptr<Surface> surface) Sprite::Sprite(std::shared_ptr<Surface> surface)
: surface_(std::move(surface)), : surface_(std::move(surface)),
pos_{0.0F, 0.0F, surface_->getWidth(), surface_->getHeight()}, pos_{0.0F, 0.0F, surface_->getWidth(), surface_->getHeight()},
clip_(pos_) {} clip_(pos_) {}
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void SurfaceSprite::render() { void Sprite::render() {
surface_->render(pos_.x, pos_.y, &clip_); surface_->render(pos_.x, pos_.y, &clip_);
} }
void SurfaceSprite::render(Uint8 source_color, Uint8 target_color) { void Sprite::render(Uint8 source_color, Uint8 target_color) {
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_); surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_);
} }
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height) { void Sprite::renderWithVerticalFade(int fade_h, int canvas_height) {
surface_->renderWithVerticalFade( surface_->renderWithVerticalFade(
static_cast<int>(pos_.x), static_cast<int>(pos_.x),
static_cast<int>(pos_.y), static_cast<int>(pos_.y),
@@ -40,7 +40,7 @@ void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height) {
&clip_); &clip_);
} }
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color) { void Sprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color) {
surface_->renderWithVerticalFade( surface_->renderWithVerticalFade(
static_cast<int>(pos_.x), static_cast<int>(pos_.x),
static_cast<int>(pos_.y), static_cast<int>(pos_.y),
@@ -52,25 +52,25 @@ void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height, Uint8
} }
// Establece la posición del objeto // Establece la posición del objeto
void SurfaceSprite::setPosition(float x, float y) { void Sprite::setPosition(float x, float y) {
pos_.x = x; pos_.x = x;
pos_.y = y; pos_.y = y;
} }
// Establece la posición del objeto // Establece la posición del objeto
void SurfaceSprite::setPosition(SDL_FPoint p) { void Sprite::setPosition(SDL_FPoint p) {
pos_.x = p.x; pos_.x = p.x;
pos_.y = p.y; pos_.y = p.y;
} }
// Reinicia las variables a cero // Reinicia las variables a cero
void SurfaceSprite::clear() { void Sprite::clear() {
pos_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; pos_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
clip_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; clip_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
} }
// Actualiza el estado del sprite (time-based) // Actualiza el estado del sprite (time-based)
void SurfaceSprite::update(float delta_time) { void Sprite::update(float delta_time) {
// Base implementation does nothing (static sprites) // Base implementation does nothing (static sprites)
(void)delta_time; // Evita warning de parámetro no usado (void)delta_time; // Evita warning de parámetro no usado
} }

View File

@@ -7,16 +7,16 @@
class Surface; // lines 5-5 class Surface; // lines 5-5
// Clase SurfaceSprite // Clase SurfaceSprite
class SurfaceSprite { class Sprite {
public: public:
// Constructores // Constructores
SurfaceSprite(std::shared_ptr<Surface>, float x, float y, float w, float h); Sprite(std::shared_ptr<Surface>, float x, float y, float w, float h);
SurfaceSprite(std::shared_ptr<Surface>, SDL_FRect rect); Sprite(std::shared_ptr<Surface>, SDL_FRect rect);
SurfaceSprite(); Sprite();
explicit SurfaceSprite(std::shared_ptr<Surface>); explicit Sprite(std::shared_ptr<Surface>);
// Destructor // Destructor
virtual ~SurfaceSprite() = default; virtual ~Sprite() = default;
// Actualización y renderizado // Actualización y renderizado
virtual void update(float delta_time); // Actualiza el estado del sprite (time-based) virtual void update(float delta_time); // Actualiza el estado del sprite (time-based)
@@ -51,12 +51,12 @@ class SurfaceSprite {
// Modificación de clip y surface // Modificación de clip y surface
void setClip(SDL_FRect rect) { clip_ = rect; } void setClip(SDL_FRect rect) { clip_ = rect; }
void setClip(float x, float y, float w, float h) { clip_ = SDL_FRect{x, y, w, h}; } void setClip(float x, float y, float w, float h) { clip_ = SDL_FRect{.x = x, .y = y, .w = w, .h = h}; }
void setSurface(std::shared_ptr<Surface> surface) { surface_ = std::move(surface); } void setSurface(std::shared_ptr<Surface> surface) { surface_ = std::move(surface); }
protected: protected:
// Variables miembro // Variables miembro
std::shared_ptr<Surface> surface_{nullptr}; // Surface donde estan todos los dibujos del sprite std::shared_ptr<Surface> surface_{nullptr}; // Surface donde estan todos los dibujos del sprite
SDL_FRect pos_{0.0F, 0.0F, 0.0F, 0.0F}; // Posición y tamaño donde dibujar el sprite SDL_FRect pos_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; // Posición y tamaño donde dibujar el sprite
SDL_FRect clip_{0.0F, 0.0F, 0.0F, 0.0F}; // Rectangulo de origen de la surface que se dibujará en pantalla SDL_FRect clip_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F}; // Rectangulo de origen de la surface que se dibujará en pantalla
}; };

Some files were not shown because too many files have changed in this diff Show More