Compare commits
13 Commits
3c2a5c9b37
...
2026-04-03
| Author | SHA1 | Date | |
|---|---|---|---|
| 8538a1047f | |||
| e150097edc | |||
| 5f0d1f9577 | |||
| 6d8d02f0e4 | |||
| 5f7fb8625d | |||
| ce2fcefd71 | |||
| 6f31751d42 | |||
| 43de2c0b35 | |||
| 1ca9d0c01b | |||
| 90d5e6c3cc | |||
| a653dad7b0 | |||
| cf1f97a84f | |||
| 93fe17c3b2 |
200
CHANGELOG.md
Normal file
200
CHANGELOG.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
Historial de canvis i novetats de Coffee Crisis Arcade Edition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-04-03
|
||||||
|
|
||||||
|
- **Nova intro cinematogràfica**: les tarjetes s'llancen des dels costats de la pantalla amb zoom, rotació i rebot, simulant tirar cartes sobre una mesa. Les anteriors ixen despedides girant quan arriba la següent. Sombra amb efecte de perspectiva 2D→3D.
|
||||||
|
- **Efectes sonors i visuals en la intro**: shake de pantalla i sons configurables a cada impacte de tarjeta.
|
||||||
|
- **Migració a SDL3 GPU API**: postfx i crtpi migrats a SDL3GPU (Vulkan/Metal/D3D12).
|
||||||
|
- **Migració de configuració a YAML**: eliminat el format antic, ara tot en YAML.
|
||||||
|
- **Afegides opcions al Service Menu**.
|
||||||
|
- **HUD de FPS retocat**, presets per defecte ajustats, finestra a 2x i shader off per defecte.
|
||||||
|
- **Corregit bug d'input**: revertit un canvi que causava bucle infinit en F3 (pantalla completa) i F12 (service menu) en totes les escenes excepte Game.
|
||||||
|
- Neteja de codi: eliminades referencies a OpenGL, fitxers GLSL sobrants, normalitzada la carpeta release i el caption de la finestra.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-10-25
|
||||||
|
|
||||||
|
- **Migració a delta_time pur** en credits, instructions i hiscore_table. Eliminat un bug que feia que els credits no acabaren mai si no passaves a mà.
|
||||||
|
- **Corregida deformació subpixel** de textures en instructions i hiscore_table.
|
||||||
|
- **Detecció de fitxers de puntuació corruptes**.
|
||||||
|
- **Efecte de pulsos** afegit al scoreboard.
|
||||||
|
- Al posar nom, el carrusel apareix directament en el caràcter d'acabar si ja havies posat nom abans.
|
||||||
|
- Integrat jail_audio en la càrrega de resources.pack.
|
||||||
|
- Pasaeta de linter en múltiples fitxers.
|
||||||
|
- Nou icon per al joc.
|
||||||
|
- Corregida la versió release de macOS per a funcionar correctament amb resources.pack.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-08-21
|
||||||
|
|
||||||
|
- **Integració amb resources.pack**: textures, animacions, textos, dades de la demo i jail_audio integrats amb ResourceHelper.
|
||||||
|
- Actualitzat Makefile per a Windows, Linux i macOS.
|
||||||
|
- Neteja de temporals al acabar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-08-17
|
||||||
|
|
||||||
|
- **Afegit fade RANDOM_SQUARE2** amb timings canviats a mil·lisegons.
|
||||||
|
- **Outline parametritzat** per als textos dels items.
|
||||||
|
- **Colors de camiseta parametritzats** per defecte i quan pillen café.
|
||||||
|
- Creat `defaults.h` amb els valors per defecte de Param.
|
||||||
|
- Afegit `param_red.txt` amb guardes en setParams.
|
||||||
|
- Fix: items que es quedaven engantxats a la part de dalt.
|
||||||
|
- Fix: en el modo demo, assignava cafés al jugador que no jugava.
|
||||||
|
- Fix: bug en l'estat pre del fade.
|
||||||
|
- Fix: globos apareixien un frame mal situats al crear-se des d'un pare.
|
||||||
|
- Afegit suport per a mapejar botons tipus trigger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-08-10
|
||||||
|
|
||||||
|
- **Service Menu complet**: animació d'apertura/tancament, callback per a posar pausa en el joc, refresc visual al canviar mandos.
|
||||||
|
- **Mandos en calent**: es poden connectar i desconnectar mandos durant el joc, amb notificació visual.
|
||||||
|
- **PauseManager** afegit al joc.
|
||||||
|
- **Càrrega de recursos on_demand**.
|
||||||
|
- Afegit `shutdown.h` i `system_utils.h`.
|
||||||
|
- Fix: el nom apareixia duplicat en la tabla de records.
|
||||||
|
- Fix: Game no es desregistrava de ServiceMenu al destruir-se.
|
||||||
|
- Precàrrega de textures del jugador amb variants de paleta.
|
||||||
|
- Actualitzats frameworks per a macOS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-03-25
|
||||||
|
|
||||||
|
- **Nova secció Intro** amb escenes seqüencials, animacions de tarjetes i text narratiu.
|
||||||
|
- **Shaders respecten l'escalat sencer** i SDL_RenderSetLogicalSize en pantalla completa.
|
||||||
|
- **Tecla per canviar l'integer scale** (F-key).
|
||||||
|
- Afegit intro03.png i intro04.png.
|
||||||
|
- Renomenat InputType a InputActions.
|
||||||
|
- Actualitzat gamecontrollerdb.txt amb mappings de la recreativa.
|
||||||
|
- Fix: al fer reset des de Game, en Intro no sonava la música.
|
||||||
|
- Fix: al acabar la partida i vore els records, torna al títol.
|
||||||
|
- Fix: amb l'àudio mutat, el fade per al soroll de boles el tornava a deixar activat.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-02-07
|
||||||
|
|
||||||
|
- **EnterName millorat**: si has omplit tots els slots, apretar una volta mes fixa el nom.
|
||||||
|
- **Control de repetició per als eixos del joystick**.
|
||||||
|
- **La tabla de puntuació** mostra amb altre color la puntuació acabada d'afegir i les aconseguides amb 1CC.
|
||||||
|
- Nova font per a la intro.
|
||||||
|
- Afegit efecte d'eixida a les instruccions.
|
||||||
|
- Afegit disparador per a l'aparició de l'enemic nou.
|
||||||
|
- Duplicada la font 04b_25 per a versió gris i versió negra.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-01-05
|
||||||
|
|
||||||
|
- **Optimitzat el circuit de render** en pantalla.
|
||||||
|
- **Atenuat de pantalla restaurat**: Fade feia dos SDL_SetRenderDrawBlendMode sense restaurar.
|
||||||
|
- Fix: es podia polsar per a jugar mentre feia el fade cap a la demo.
|
||||||
|
- Fix: error en la seqüència final de retrocedir en el temps.
|
||||||
|
- Calibrats els polsos al gust.
|
||||||
|
- Afegida una lluna i un sol al fondo.
|
||||||
|
- La powerball ja no es pot destruir fins que no ha fet un rebot.
|
||||||
|
- Modificada la cadència de foc sense autofire.
|
||||||
|
- Afegit botó per a activar o desactivar el ratolí.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-12-31
|
||||||
|
|
||||||
|
- **Enemic nou** complet: gràfics, comportament, àudio i veus.
|
||||||
|
- **Fade out sincronitzat** de vídeo i àudio en el títol i el joc.
|
||||||
|
- **Roidets de col·lisió** per als globos en certs moments.
|
||||||
|
- La finestra ja es pot fer tan gran com permeta la pantalla (zoom dinàmic).
|
||||||
|
- Afegides veus al jugador i efectes de so al rebotar quan mor.
|
||||||
|
- Afegit delay opcional al flash de Screen.
|
||||||
|
- Afegit botó per activar o desactivar l'autofire.
|
||||||
|
- Fix: mode demo desactivava els sons permanentment.
|
||||||
|
- Actualitzat jail_audio.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-12-05
|
||||||
|
|
||||||
|
- **Secció Credits acabada** a 320x240 (i per extensió, a qualsevol resolució).
|
||||||
|
- **Zoom afegit a la classe Sprite** i al subtítol ARCADE EDITION.
|
||||||
|
- Duplicats fitxers de shaders per a resolucions verticals de 256 i 240.
|
||||||
|
- Afegit globalInputs::update() a totes les seccions.
|
||||||
|
- Fix: faltava corregir el flash de destroyAllBalloons().
|
||||||
|
- Fix: si saltes el logo, talla el so a meitat sonar.
|
||||||
|
- Canvi d'idioma amb una tecla (i reinicia).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-11-27
|
||||||
|
|
||||||
|
- **Secció Credits**: disseny, música, globos amb play_area definida, opció de canviar la paleta al text.
|
||||||
|
- Afegides traduccions dels credits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-11-20
|
||||||
|
|
||||||
|
- **Nova animació de mort del personatge**: rebots, llengua fora, ulls en X, gràfics de caure derrotat per al segon jugador.
|
||||||
|
- **Powerball redissenyada**: nous gràfics, nou comportament, ja no mata directament.
|
||||||
|
- **Globos fills** ja no ixen centrats al pare (evita apilar-se).
|
||||||
|
- Arreglos en el nom al obtindre la màxima puntuació.
|
||||||
|
- Acabat BalloonManager.
|
||||||
|
- CMakeLists.txt crea l'executable en l'arrel del projecte.
|
||||||
|
- Nova font de text gran amb el doble de definició.
|
||||||
|
- Fix: paleta verda del primer jugador ajustada a l'original.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-11-03
|
||||||
|
|
||||||
|
- **Teclat com a control independent**: ja pot jugar un jugador amb teclat i altre amb mando, o assignar el teclat a qualsevol jugador.
|
||||||
|
- **Implementat el final del joc** i l'Attract Mode.
|
||||||
|
- **Nou motor per a textos en pantalla** (game_text amb textures precarregades).
|
||||||
|
- **Noves animacions** per a deixar de disparar.
|
||||||
|
- Al redefinir botons, ja no pots repetir botó.
|
||||||
|
- Fix: l'animació de morir s'actualitzava dos voltes per frame.
|
||||||
|
- Fix: l'efecte de flash tenia un valor massa xicotet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-10-28
|
||||||
|
|
||||||
|
- **Classe PathSprite completada**: el game_text gasta PathSprites en lloc de SmartSprites.
|
||||||
|
- **Time stopper redissenyat**.
|
||||||
|
- La partida sempre comença igual (createTwoBigBalloons).
|
||||||
|
- Revisades les classes Balloon i Bullet.
|
||||||
|
- Millorada l'aparició dels game_text.
|
||||||
|
- Fix: la paleta dels jugadors no s'iniciava correctament.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-10-20
|
||||||
|
|
||||||
|
- **Classe Resource creada**: precàrrega de tots els recursos (textures, música, sons, animacions).
|
||||||
|
- **Paletes de color** per a textures GIF amb shared_ptr.
|
||||||
|
- Precàrrega i assignació de paletes.
|
||||||
|
- Implementat comptador per a posar el nom al acabar la partida.
|
||||||
|
- Classe Notifier independitzada de Screen amb codis identificadors.
|
||||||
|
- Afegit codi per a apagar el sistema al eixir del joc.
|
||||||
|
- Fix: globos verds tenien setters mal assignats i velocitat incorrecta.
|
||||||
|
- Fix: no guardar el fitxer de puntuacions en el mode demo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024-10-14
|
||||||
|
|
||||||
|
- **Versió inicial**: clon del repositori de Coffee Crisis, adaptat per a Arcade Edition.
|
||||||
|
- Pasaeta de include-what-you-use i cppcheck.
|
||||||
|
- Estandarització de noms segons convencions (CamelCase, camelBack, snake_case).
|
||||||
|
- Herències de les classes Sprite corregides.
|
||||||
|
- Canvi a C++ modern amb smart pointers per a la càrrega de surfaces des de GIF.
|
||||||
|
- Eliminats últims defines i passats a enum class.
|
||||||
101
CLAUDE.md
Normal file
101
CLAUDE.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Coffee Crisis Arcade Edition is a 2-player cooperative arcade shooter built with C++20 and SDL3. Players defend coffee against giant balloons. The game targets Windows, Linux, macOS (Intel/Apple Silicon), Raspberry Pi, and Anbernic handhelds.
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
The project uses both CMake and a top-level Makefile. The Makefile is the primary build interface.
|
||||||
|
|
||||||
|
### CMake (generates compile_commands.json for IDE/linter support)
|
||||||
|
```bash
|
||||||
|
cmake -B build -DCMAKE_BUILD_TYPE=Debug # configure
|
||||||
|
cmake --build build # build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Makefile (delegates to CMake)
|
||||||
|
```bash
|
||||||
|
make # build Release via cmake
|
||||||
|
make debug # build Debug via cmake
|
||||||
|
make release # create release package (auto-detects OS)
|
||||||
|
make linux_release # release tar.gz with resources.pack
|
||||||
|
make windows_release # release zip for Windows
|
||||||
|
make macos_release # release dmg for macOS (Intel + Apple Silicon)
|
||||||
|
make raspi_release # release tar.gz for Raspberry Pi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tools & Resources
|
||||||
|
```bash
|
||||||
|
make pack_tool # compile resource packer
|
||||||
|
make resources.pack # pack data/ into resources.pack (required for release builds)
|
||||||
|
make spirv # compile GLSL shaders to SPIR-V headers
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
```bash
|
||||||
|
make format # run clang-format on all sources (or: cmake --build build --target format)
|
||||||
|
make format-check # check formatting without modifying
|
||||||
|
make tidy # run clang-tidy static analysis (cmake --build build --target tidy)
|
||||||
|
make tidy-fix # run clang-tidy with auto-fix
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Singletons (core systems)
|
||||||
|
- **Director** (`source/director.hpp`) — Application state machine, orchestrates scene transitions (Logo → Intro → Title → Game → Credits/HiScore → Title)
|
||||||
|
- **Screen** (`source/screen.hpp`) — Window management, SDL3 GPU rendering pipeline, post-processing effects
|
||||||
|
- **Resource** (`source/resource.hpp`) — Asset loading/caching with PRELOAD and LAZY_LOAD modes, reads from `resources.pack`
|
||||||
|
- **Audio** (`source/audio.hpp`) — Music and SFX management
|
||||||
|
- **Input** (`source/input.hpp`) — Keyboard and gamepad input handling
|
||||||
|
|
||||||
|
### Scenes (source/sections/)
|
||||||
|
Each scene is a self-contained class with update/render lifecycle. Scene flow is managed by Director.
|
||||||
|
|
||||||
|
### Entity Managers
|
||||||
|
- `BalloonManager` / `BulletManager` — Object pool-based entity management
|
||||||
|
- `Player` — Two-player support (player 1: keyboard, player 2: gamepad)
|
||||||
|
|
||||||
|
### Rendering Pipeline
|
||||||
|
- SDL3 GPU API (Vulkan/Metal/D3D12 backends)
|
||||||
|
- SPIR-V shaders compiled offline from GLSL (`data/shaders/`) via `glslc`
|
||||||
|
- Compiled shader headers embedded in `source/rendering/sdl3gpu/postfx_*_spv.h`
|
||||||
|
- macOS uses Metal (no SPIR-V compilation needed)
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
- Game parameters: `config/param_320x240.txt`, `config/param_320x256.txt`
|
||||||
|
- Asset manifest: `config/assets.txt`
|
||||||
|
- Balloon formations: `config/formations.txt`
|
||||||
|
- Level definitions: `config/stages.txt`
|
||||||
|
- Gamepad mappings: `config/gamecontrollerdb.txt`
|
||||||
|
|
||||||
|
### External Libraries (header-only/vendored in source/external/)
|
||||||
|
- nlohmann/json, fkyaml (YAML), stb_image, stb_vorbis, jail_audio
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
Enforced via `.clang-format` (Google-based) and `.clang-tidy`:
|
||||||
|
|
||||||
|
- **Naming conventions**: Classes/structs `CamelCase`, methods/functions `camelBack`, variables/params `snake_case`, private/protected members `snake_case_` (trailing underscore), constants/constexpr `UPPER_CASE`, namespaces `CamelCase`, enum values `UPPER_CASE`
|
||||||
|
- 4-space indentation, no column limit, braces attach to statement
|
||||||
|
- clang-tidy treats all warnings as errors
|
||||||
|
|
||||||
|
## Conditional Compilation Defines
|
||||||
|
|
||||||
|
| Define | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `WINDOWS_BUILD` / `LINUX_BUILD` / `MACOS_BUILD` | Platform selection |
|
||||||
|
| `DEBUG` / `VERBOSE` | Debug output |
|
||||||
|
| `RELEASE_BUILD` | Release-specific code paths |
|
||||||
|
| `RECORDING` | Demo recording mode |
|
||||||
|
| `NO_SHADERS` | Disable shader pipeline (Anbernic) |
|
||||||
|
| `NO_AUDIO` | Build without audio |
|
||||||
|
| `ARCADE` | Arcade-specific mode |
|
||||||
|
| `MACOS_BUNDLE` | macOS .app bundle paths |
|
||||||
|
| `ANBERNIC` | Anbernic handheld build |
|
||||||
|
|
||||||
|
## Language
|
||||||
|
|
||||||
|
Code comments are in Spanish/Catalan. Game UI supports multiple languages via JSON files in `data/lang/`.
|
||||||
@@ -81,6 +81,7 @@ set(APP_SOURCES
|
|||||||
# --- Sprites y Gráficos ---
|
# --- Sprites y Gráficos ---
|
||||||
source/animated_sprite.cpp
|
source/animated_sprite.cpp
|
||||||
source/background.cpp
|
source/background.cpp
|
||||||
|
source/card_sprite.cpp
|
||||||
source/fade.cpp
|
source/fade.cpp
|
||||||
source/moving_sprite.cpp
|
source/moving_sprite.cpp
|
||||||
source/path_sprite.cpp
|
source/path_sprite.cpp
|
||||||
@@ -125,30 +126,41 @@ if(NOT APPLE)
|
|||||||
|
|
||||||
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||||
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag")
|
set(SHADER_FRAG_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.frag")
|
||||||
|
set(SHADER_CRTPI_SRC "${CMAKE_SOURCE_DIR}/data/shaders/crtpi_frag.glsl")
|
||||||
|
set(SHADER_UPSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/upscale.frag")
|
||||||
|
set(SHADER_DOWNSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/downscale.frag")
|
||||||
|
|
||||||
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_vert_spv.h")
|
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_vert_spv.h")
|
||||||
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_frag_spv.h")
|
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_frag_spv.h")
|
||||||
|
set(SHADER_CRTPI_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/crtpi_frag_spv.h")
|
||||||
|
set(SHADER_UPSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/upscale_frag_spv.h")
|
||||||
|
set(SHADER_DOWNSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/downscale_frag_spv.h")
|
||||||
|
|
||||||
|
set(ALL_SHADER_SOURCES "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" "${SHADER_CRTPI_SRC}" "${SHADER_UPSCALE_SRC}" "${SHADER_DOWNSCALE_SRC}")
|
||||||
|
set(ALL_SHADER_HEADERS "${SHADER_VERT_H}" "${SHADER_FRAG_H}" "${SHADER_CRTPI_H}" "${SHADER_UPSCALE_H}" "${SHADER_DOWNSCALE_H}")
|
||||||
|
|
||||||
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_SOURCE_DIR}/tools/shaders/compile_spirv.sh"
|
||||||
DEPENDS "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}"
|
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(_h IN LISTS ALL_SHADER_HEADERS)
|
||||||
|
if(NOT EXISTS "${_h}")
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"glslc no encontrado y headers SPIR-V no existen.\n"
|
"glslc no encontrado y header SPIR-V no existe: ${_h}\n"
|
||||||
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
||||||
" choco install vulkan-sdk (Windows)\n"
|
" choco install vulkan-sdk (Windows)\n"
|
||||||
" O genera los headers manualmente: tools/shaders/compile_spirv.sh"
|
" 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 +204,17 @@ if(WIN32)
|
|||||||
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32)
|
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
|
||||||
|
if(NOT CMAKE_OSX_ARCHITECTURES)
|
||||||
set(CMAKE_OSX_ARCHITECTURES "arm64")
|
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()
|
||||||
|
|||||||
415
Makefile
415
Makefile
@@ -1,233 +1,168 @@
|
|||||||
# Directorios
|
# ==============================================================================
|
||||||
|
# DIRECTORIES
|
||||||
|
# ==============================================================================
|
||||||
DIR_ROOT := $(dir $(abspath $(MAKEFILE_LIST)))
|
DIR_ROOT := $(dir $(abspath $(MAKEFILE_LIST)))
|
||||||
DIR_SOURCES := $(addsuffix /, $(DIR_ROOT)source)
|
|
||||||
DIR_BIN := $(addsuffix /, $(DIR_ROOT))
|
|
||||||
DIR_BUILD := $(addsuffix /, $(DIR_ROOT)build)
|
|
||||||
DIR_TOOLS := $(addsuffix /, $(DIR_ROOT)tools)
|
DIR_TOOLS := $(addsuffix /, $(DIR_ROOT)tools)
|
||||||
|
|
||||||
# Variables
|
# ==============================================================================
|
||||||
|
# TARGET NAMES
|
||||||
|
# ==============================================================================
|
||||||
TARGET_NAME := coffee_crisis_arcade_edition
|
TARGET_NAME := coffee_crisis_arcade_edition
|
||||||
TARGET_FILE := $(DIR_BIN)$(TARGET_NAME)
|
TARGET_FILE := $(DIR_ROOT)$(TARGET_NAME)
|
||||||
APP_NAME := Coffee Crisis Arcade Edition
|
APP_NAME := Coffee Crisis Arcade Edition
|
||||||
DIST_DIR := dist
|
DIST_DIR := dist
|
||||||
RELEASE_FOLDER := dist/_tmp
|
RELEASE_FOLDER := dist/_tmp
|
||||||
RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME)
|
RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME)
|
||||||
RESOURCE_FILE := release/windows/coffee.res
|
RESOURCE_FILE := release/windows/coffee.res
|
||||||
|
|
||||||
# Variables para herramienta de empaquetado
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
# TOOLS
|
||||||
PACK_TOOL := $(DIR_TOOLS)pack_resources/pack_resources.exe
|
# ==============================================================================
|
||||||
PACK_CXX := $(CXX)
|
DIR_PACK_TOOL := $(DIR_TOOLS)pack_resources
|
||||||
else
|
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
|
||||||
PACK_TOOL := $(DIR_TOOLS)pack_resources/pack_resources
|
|
||||||
PACK_CXX := $(CXX)
|
|
||||||
endif
|
|
||||||
PACK_SOURCES := $(DIR_TOOLS)pack_resources/pack_resources.cpp $(DIR_SOURCES)resource_pack.cpp
|
|
||||||
PACK_INCLUDES := -I$(DIR_ROOT) -I$(DIR_BUILD)
|
|
||||||
|
|
||||||
# Versión automática basada en la fecha actual (específica por SO)
|
# ==============================================================================
|
||||||
|
# VERSION (fecha actual)
|
||||||
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
VERSION := $(shell powershell -Command "Get-Date -Format 'yyyy-MM-dd'")
|
VERSION := $(shell powershell -Command "Get-Date -Format 'yyyy-MM-dd'")
|
||||||
else
|
else
|
||||||
VERSION := $(shell date +%Y-%m-%d)
|
VERSION := $(shell date +%Y-%m-%d)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Variables específicas para Windows (usando APP_NAME)
|
# ==============================================================================
|
||||||
|
# SHELL (Windows usa cmd.exe)
|
||||||
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
WIN_TARGET_FILE := $(DIR_BIN)$(APP_NAME)
|
SHELL := cmd.exe
|
||||||
|
endif
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# WINDOWS-SPECIFIC VARIABLES
|
||||||
|
# ==============================================================================
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
WIN_TARGET_FILE := $(DIR_ROOT)$(APP_NAME)
|
||||||
WIN_RELEASE_FILE := $(RELEASE_FOLDER)/$(APP_NAME)
|
WIN_RELEASE_FILE := $(RELEASE_FOLDER)/$(APP_NAME)
|
||||||
else
|
else
|
||||||
WIN_TARGET_FILE := $(TARGET_FILE)
|
WIN_TARGET_FILE := $(TARGET_FILE)
|
||||||
WIN_RELEASE_FILE := $(RELEASE_FILE)
|
WIN_RELEASE_FILE := $(RELEASE_FILE)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Nombres para los ficheros de lanzamiento
|
# ==============================================================================
|
||||||
|
# RELEASE NAMES
|
||||||
|
# ==============================================================================
|
||||||
WINDOWS_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
WINDOWS_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-win32-x64.zip
|
||||||
MACOS_INTEL_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-macos-intel.dmg
|
MACOS_INTEL_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-macos-intel.dmg
|
||||||
MACOS_APPLE_SILICON_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-macos-apple-silicon.dmg
|
MACOS_APPLE_SILICON_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-macos-apple-silicon.dmg
|
||||||
LINUX_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-linux.tar.gz
|
LINUX_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-linux.tar.gz
|
||||||
RASPI_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-raspberry.tar.gz
|
RASPI_RELEASE := $(DIST_DIR)/$(TARGET_NAME)-$(VERSION)-raspberry.tar.gz
|
||||||
|
|
||||||
# Lista completa de archivos fuente (basada en CMakeLists.txt)
|
# ==============================================================================
|
||||||
APP_SOURCES := \
|
# PLATAFORMA
|
||||||
source/asset.cpp \
|
# ==============================================================================
|
||||||
source/audio.cpp \
|
|
||||||
source/director.cpp \
|
|
||||||
source/global_events.cpp \
|
|
||||||
source/global_inputs.cpp \
|
|
||||||
source/input.cpp \
|
|
||||||
source/lang.cpp \
|
|
||||||
source/main.cpp \
|
|
||||||
source/param.cpp \
|
|
||||||
source/resource.cpp \
|
|
||||||
source/resource_helper.cpp \
|
|
||||||
source/resource_loader.cpp \
|
|
||||||
source/resource_pack.cpp \
|
|
||||||
source/screen.cpp \
|
|
||||||
source/text.cpp \
|
|
||||||
source/writer.cpp \
|
|
||||||
source/ui/menu_option.cpp \
|
|
||||||
source/ui/menu_renderer.cpp \
|
|
||||||
source/ui/notifier.cpp \
|
|
||||||
source/ui/service_menu.cpp \
|
|
||||||
source/ui/ui_message.cpp \
|
|
||||||
source/ui/window_message.cpp \
|
|
||||||
source/balloon_formations.cpp \
|
|
||||||
source/balloon_manager.cpp \
|
|
||||||
source/balloon.cpp \
|
|
||||||
source/bullet.cpp \
|
|
||||||
source/bullet_manager.cpp \
|
|
||||||
source/enter_name.cpp \
|
|
||||||
source/explosions.cpp \
|
|
||||||
source/game_logo.cpp \
|
|
||||||
source/item.cpp \
|
|
||||||
source/manage_hiscore_table.cpp \
|
|
||||||
source/player.cpp \
|
|
||||||
source/scoreboard.cpp \
|
|
||||||
source/tabe.cpp \
|
|
||||||
source/sections/credits.cpp \
|
|
||||||
source/sections/game.cpp \
|
|
||||||
source/sections/hiscore_table.cpp \
|
|
||||||
source/sections/instructions.cpp \
|
|
||||||
source/sections/intro.cpp \
|
|
||||||
source/sections/logo.cpp \
|
|
||||||
source/sections/title.cpp \
|
|
||||||
source/animated_sprite.cpp \
|
|
||||||
source/background.cpp \
|
|
||||||
source/fade.cpp \
|
|
||||||
source/moving_sprite.cpp \
|
|
||||||
source/path_sprite.cpp \
|
|
||||||
source/smart_sprite.cpp \
|
|
||||||
source/sprite.cpp \
|
|
||||||
source/texture.cpp \
|
|
||||||
source/tiled_bg.cpp \
|
|
||||||
source/color.cpp \
|
|
||||||
source/demo.cpp \
|
|
||||||
source/define_buttons.cpp \
|
|
||||||
source/difficulty.cpp \
|
|
||||||
source/input_types.cpp \
|
|
||||||
source/mouse.cpp \
|
|
||||||
source/options.cpp \
|
|
||||||
source/shutdown.cpp \
|
|
||||||
source/stage.cpp \
|
|
||||||
source/system_utils.cpp \
|
|
||||||
source/utils.cpp \
|
|
||||||
source/external/jail_audio.cpp \
|
|
||||||
source/external/gif.cpp \
|
|
||||||
source/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
|
||||||
|
|
||||||
# Includes
|
|
||||||
INCLUDES := -Isource -Isource/external -Isource/rendering -Isource/rendering/sdl3gpu -I$(DIR_BUILD)
|
|
||||||
|
|
||||||
# Variables según el sistema operativo
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
FixPath = $(subst /,\\,$1)
|
FixPath = $(subst /,\\,$1)
|
||||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows -DWINDOWS_BUILD
|
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -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=c++20 -Wall -Os -ffunction-sections -fdata-sections
|
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -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
|
endif
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# 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)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
CXXFLAGS += -DMACOS_BUILD
|
@$(MAKE) macos_release
|
||||||
CXXFLAGS_DEBUG += -DMACOS_BUILD
|
else
|
||||||
# Configurar arquitectura (por defecto arm64, como en CMake)
|
@$(MAKE) linux_release
|
||||||
CXXFLAGS += -arch arm64
|
|
||||||
CXXFLAGS_DEBUG += -arch arm64
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Reglas para herramienta de empaquetado y resources.pack
|
# ==============================================================================
|
||||||
$(PACK_TOOL): FORCE
|
# REGLAS PARA HERRAMIENTA DE EMPAQUETADO Y RESOURCES.PACK
|
||||||
@echo "Compilando herramienta de empaquetado..."
|
# ==============================================================================
|
||||||
$(PACK_CXX) -std=c++20 -Wall -Os $(PACK_INCLUDES) $(PACK_SOURCES) -o $(PACK_TOOL)
|
pack_tool:
|
||||||
@echo "✓ Herramienta de empaquetado lista: $(PACK_TOOL)"
|
@$(MAKE) -C $(DIR_PACK_TOOL)
|
||||||
|
|
||||||
pack_tool: $(PACK_TOOL)
|
resources.pack:
|
||||||
|
@$(MAKE) -C $(DIR_PACK_TOOL) pack
|
||||||
|
|
||||||
resources.pack: $(PACK_TOOL)
|
# ==============================================================================
|
||||||
@echo "Generando resources.pack desde directorio data/..."
|
# COMPILACIÓN DE SHADERS
|
||||||
$(PACK_TOOL) data resources.pack
|
# ==============================================================================
|
||||||
@echo "✓ resources.pack generado exitosamente"
|
spirv:
|
||||||
|
@echo "Compilando shaders SPIR-V..."
|
||||||
# Reglas para compilación
|
$(SHADER_SCRIPT)
|
||||||
windows:
|
|
||||||
@echo off
|
|
||||||
@echo Compilando para Windows con nombre: "$(APP_NAME).exe"
|
|
||||||
windres release/windows/coffee.rc -O coff -o $(RESOURCE_FILE)
|
|
||||||
$(CXX) $(APP_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_rec:
|
|
||||||
@echo off
|
|
||||||
@echo Compilando version de grabacion para Windows: "$(APP_NAME)_rec.exe"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRECORDING $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_rec.exe"
|
|
||||||
|
|
||||||
windows_debug:
|
|
||||||
@echo off
|
|
||||||
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_debug.exe"
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# COMPILACIÓN PARA WINDOWS (RELEASE)
|
||||||
|
# ==============================================================================
|
||||||
windows_release:
|
windows_release:
|
||||||
@$(MAKE) pack_tool
|
|
||||||
@$(MAKE) resources.pack
|
@$(MAKE) resources.pack
|
||||||
@echo off
|
@echo off
|
||||||
@echo Creando release para Windows - Version: $(VERSION)
|
@echo Creando release para Windows - Version: $(VERSION)
|
||||||
|
|
||||||
|
# Compila con cmake
|
||||||
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@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 la carpeta 'config' y el archivo 'resources.pack'
|
# Copia la carpeta 'config' y el archivo 'resources.pack'
|
||||||
powershell Copy-Item -Path "config" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
@powershell -Command "Copy-Item -Path 'config' -Destination '$(RELEASE_FOLDER)' -recurse -Force"
|
||||||
powershell Copy-Item -Path "resources.pack" -Destination "$(RELEASE_FOLDER)"
|
@powershell -Command "Copy-Item -Path 'resources.pack' -Destination '$(RELEASE_FOLDER)'"
|
||||||
|
|
||||||
# Copia los ficheros que estan en la raíz del proyecto
|
# Copia los ficheros que estan en la raíz del proyecto
|
||||||
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
@powershell -Command "Copy-Item 'LICENSE' -Destination '$(RELEASE_FOLDER)'"
|
||||||
powershell Copy-Item "README.md" -Destination "$(RELEASE_FOLDER)"
|
@powershell -Command "Copy-Item 'README.md' -Destination '$(RELEASE_FOLDER)'"
|
||||||
powershell Copy-Item "release\windows\dll\*.dll" -Destination "$(RELEASE_FOLDER)"
|
@powershell -Command "Copy-Item 'release\windows\dll\*.dll' -Destination '$(RELEASE_FOLDER)'"
|
||||||
|
@powershell -Command "Copy-Item -Path '$(TARGET_FILE)' -Destination '\"$(WIN_RELEASE_FILE).exe\"'"
|
||||||
# Compila
|
|
||||||
windres release/windows/coffee.rc -O coff -o $(RESOURCE_FILE)
|
|
||||||
$(CXX) $(APP_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}"
|
||||||
|
|
||||||
macos:
|
|
||||||
@echo "Compilando para macOS: $(TARGET_NAME)"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
|
|
||||||
|
|
||||||
macos_debug:
|
|
||||||
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# COMPILACIÓN PARA MACOS (RELEASE)
|
||||||
|
# ==============================================================================
|
||||||
macos_release:
|
macos_release:
|
||||||
@$(MAKE) pack_tool
|
|
||||||
@$(MAKE) resources.pack
|
@$(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)
|
||||||
|
|
||||||
|
# Compila la versión para procesadores Intel con cmake
|
||||||
|
@cmake -S . -B build/intel -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DMACOS_BUNDLE=ON
|
||||||
|
@cmake --build build/intel
|
||||||
|
|
||||||
# Elimina datos de compilaciones anteriores
|
# Elimina datos de compilaciones anteriores
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
$(RMFILE) tmp.dmg
|
$(RMFILE) tmp.dmg
|
||||||
@@ -250,9 +185,8 @@ macos_release:
|
|||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Compila la versión para procesadores Intel
|
# Copia el ejecutable Intel al bundle
|
||||||
ifdef ENABLE_MACOS_X86_64
|
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)"
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DMACOS_BUILD -DRELEASE_BUILD -std=c++20 -Wall -Os -Wno-deprecated -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
|
|
||||||
|
|
||||||
# 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"
|
||||||
@@ -273,10 +207,11 @@ ifdef ENABLE_MACOS_X86_64
|
|||||||
"$(MACOS_INTEL_RELEASE)" \
|
"$(MACOS_INTEL_RELEASE)" \
|
||||||
"$(RELEASE_FOLDER)" || true
|
"$(RELEASE_FOLDER)" || true
|
||||||
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
|
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
|
||||||
endif
|
|
||||||
|
|
||||||
# Compila la versión para procesadores Apple Silicon
|
# Compila la versión para procesadores Apple Silicon con cmake
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DMACOS_BUILD -DRELEASE_BUILD -DSDL_DISABLE_IMMINTRIN_H -std=c++20 -Wall -Os -Wno-deprecated -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"
|
||||||
@@ -300,22 +235,22 @@ endif
|
|||||||
|
|
||||||
# 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.*
|
||||||
|
|
||||||
linux:
|
# ==============================================================================
|
||||||
@echo "Compilando para Linux: $(TARGET_NAME)"
|
# COMPILACIÓN PARA LINUX (RELEASE)
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
|
# ==============================================================================
|
||||||
strip -s -R .comment -R .gnu.version "$(TARGET_FILE)" --strip-unneeded
|
|
||||||
|
|
||||||
linux_debug:
|
|
||||||
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
|
||||||
|
|
||||||
linux_release:
|
linux_release:
|
||||||
@$(MAKE) pack_tool
|
|
||||||
@$(MAKE) resources.pack
|
@$(MAKE) resources.pack
|
||||||
@echo "Creando release para Linux - Version: $(VERSION)"
|
@echo "Creando release para Linux - Version: $(VERSION)"
|
||||||
# Elimina carpetas previas y recrea (crea dist/ si no existe)
|
|
||||||
|
# Compila con cmake
|
||||||
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@cmake --build build
|
||||||
|
|
||||||
|
# Elimina carpeta temporal previa y la recrea (crea dist/ si no existe)
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -324,9 +259,7 @@ linux_release:
|
|||||||
cp resources.pack "$(RELEASE_FOLDER)"
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
cp "$(TARGET_FILE)" "$(RELEASE_FILE)"
|
||||||
# Compila
|
|
||||||
$(CXX) $(APP_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
|
||||||
@@ -337,10 +270,17 @@ linux_release:
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# COMPILACIÓN PARA LINUX (RELEASE CON INTEGRACIÓN DESKTOP)
|
||||||
|
# ==============================================================================
|
||||||
linux_release_desktop:
|
linux_release_desktop:
|
||||||
@$(MAKE) pack_tool
|
|
||||||
@$(MAKE) resources.pack
|
@$(MAKE) resources.pack
|
||||||
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
|
||||||
|
|
||||||
|
# Compila con cmake
|
||||||
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@cmake --build build
|
||||||
|
|
||||||
# Elimina carpetas previas y recrea (crea dist/ si no existe)
|
# Elimina carpetas previas y recrea (crea dist/ si no existe)
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -357,8 +297,8 @@ linux_release_desktop:
|
|||||||
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
|
|
||||||
# Compila el ejecutable
|
# Copia el ejecutable
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)"
|
cp "$(TARGET_FILE)" "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)"
|
||||||
strip -s -R .comment -R .gnu.version "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)" --strip-unneeded
|
strip -s -R .comment -R .gnu.version "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)" --strip-unneeded
|
||||||
|
|
||||||
# Crea el archivo .desktop
|
# Crea el archivo .desktop
|
||||||
@@ -432,19 +372,17 @@ linux_release_desktop:
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
raspi:
|
# ==============================================================================
|
||||||
@echo "Compilando para Raspberry Pi: $(TARGET_NAME)"
|
# COMPILACIÓN PARA RASPBERRY PI (RELEASE)
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(TARGET_FILE)
|
# ==============================================================================
|
||||||
strip -s -R .comment -R .gnu.version $(TARGET_FILE) --strip-unneeded
|
|
||||||
|
|
||||||
raspi_debug:
|
|
||||||
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
|
|
||||||
|
|
||||||
raspi_release:
|
raspi_release:
|
||||||
@$(MAKE) pack_tool
|
|
||||||
@$(MAKE) resources.pack
|
@$(MAKE) resources.pack
|
||||||
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
|
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
|
||||||
|
|
||||||
|
# Compila con cmake
|
||||||
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@cmake --build build
|
||||||
|
|
||||||
# Elimina carpetas previas y recrea (crea dist/ si no existe)
|
# Elimina carpetas previas y recrea (crea dist/ si no existe)
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
@@ -454,9 +392,7 @@ raspi_release:
|
|||||||
cp resources.pack "$(RELEASE_FOLDER)"
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
cp "$(TARGET_FILE)" "$(RELEASE_FILE)"
|
||||||
# Compila
|
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD -DVERBOSE $(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
|
||||||
@@ -467,59 +403,60 @@ raspi_release:
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
anbernic:
|
# ==============================================================================
|
||||||
@$(MAKE) pack_tool
|
# CODE QUALITY (delegados a cmake)
|
||||||
@$(MAKE) resources.pack
|
# ==============================================================================
|
||||||
@echo "Compilando para Anbernic: $(TARGET_NAME)"
|
format:
|
||||||
# Elimina carpetas previas
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
|
@cmake --build build --target format
|
||||||
|
|
||||||
# Crea la carpeta temporal para realizar el lanzamiento
|
format-check:
|
||||||
$(MKDIR) "$(RELEASE_FOLDER)"_anbernic
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@cmake --build build --target format-check
|
||||||
|
|
||||||
# Copia ficheros
|
tidy:
|
||||||
cp -R config "$(RELEASE_FOLDER)"_anbernic
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
cp resources.pack "$(RELEASE_FOLDER)"_anbernic
|
@cmake --build build --target tidy
|
||||||
|
|
||||||
# Compila
|
tidy-fix:
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
|
@cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
@cmake --build build --target tidy-fix
|
||||||
|
|
||||||
# Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake)
|
# ==============================================================================
|
||||||
no_audio:
|
# REGLAS ESPECIALES
|
||||||
@echo "Compilando sin audio: $(TARGET_NAME)_no_audio"
|
# ==============================================================================
|
||||||
$(CXX) $(filter-out source/external/jail_audio.cpp,$(APP_SOURCES)) $(INCLUDES) -DNO_AUDIO $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)_no_audio"
|
|
||||||
|
|
||||||
# Regla para mostrar la versión actual
|
|
||||||
show_version:
|
show_version:
|
||||||
@echo "Version actual: $(VERSION)"
|
@echo "Version actual: $(VERSION)"
|
||||||
|
|
||||||
# Regla de ayuda
|
|
||||||
help:
|
help:
|
||||||
@echo "Makefile para Coffee Crisis Arcade Edition"
|
@echo "Makefile para Coffee Crisis Arcade Edition"
|
||||||
@echo "Comandos disponibles:"
|
@echo "Comandos disponibles:"
|
||||||
@echo " windows - Compilar para Windows"
|
@echo ""
|
||||||
@echo " windows_debug - Compilar debug para Windows"
|
@echo " Compilacion:"
|
||||||
@echo " windows_release - Crear release completo para Windows"
|
@echo " make - Compilar con cmake (Release)"
|
||||||
@echo " linux - Compilar para Linux"
|
@echo " make debug - Compilar con cmake (Debug)"
|
||||||
@echo " linux_debug - Compilar debug para Linux"
|
@echo ""
|
||||||
@echo " linux_release - Crear release basico para Linux"
|
@echo " Release:"
|
||||||
@echo " linux_release_desktop - Crear release con integracion desktop para Linux"
|
@echo " make release - Crear release (detecta SO automaticamente)"
|
||||||
@echo " macos - Compilar para macOS"
|
@echo " make windows_release - Crear release para Windows"
|
||||||
@echo " macos_debug - Compilar debug para macOS"
|
@echo " make linux_release - Crear release basico para Linux"
|
||||||
@echo " macos_release - Crear release completo para macOS"
|
@echo " make linux_release_desktop - Crear release con integracion desktop para Linux"
|
||||||
@echo " raspi - Compilar para Raspberry Pi"
|
@echo " make macos_release - Crear release para macOS"
|
||||||
@echo " raspi_release - Crear release completo para Raspberry Pi"
|
@echo " make raspi_release - Crear release para Raspberry Pi"
|
||||||
@echo " anbernic - Compilar para Anbernic"
|
@echo ""
|
||||||
@echo " no_audio - Compilar sin sistema de audio"
|
@echo " Herramientas:"
|
||||||
@echo " pack_tool - Compilar herramienta de empaquetado"
|
@echo " make spirv - Compilar shaders SPIR-V"
|
||||||
@echo " resources.pack - Generar pack de recursos desde data/"
|
@echo " make pack_tool - Compilar herramienta de empaquetado"
|
||||||
@echo " show_version - Mostrar version actual ($(VERSION))"
|
@echo " make resources.pack - Generar pack de recursos desde data/"
|
||||||
@echo " help - Mostrar esta ayuda"
|
@echo ""
|
||||||
|
@echo " Calidad de codigo:"
|
||||||
|
@echo " make format - Formatear codigo con clang-format"
|
||||||
|
@echo " make format-check - Verificar formato sin modificar"
|
||||||
|
@echo " make tidy - Analisis estatico con clang-tidy"
|
||||||
|
@echo " make tidy-fix - Analisis estatico con auto-fix"
|
||||||
|
@echo ""
|
||||||
|
@echo " Otros:"
|
||||||
|
@echo " make show_version - Mostrar version actual ($(VERSION))"
|
||||||
|
@echo " make help - Mostrar esta ayuda"
|
||||||
|
|
||||||
spirv:
|
.PHONY: all debug release windows_release macos_release linux_release linux_release_desktop raspi_release pack_tool resources.pack spirv format format-check tidy tidy-fix show_version help
|
||||||
@echo "Compilando shaders SPIR-V..."
|
|
||||||
tools/shaders/compile_spirv.sh
|
|
||||||
|
|
||||||
.PHONY: windows windows_rec windows_debug windows_release macos macos_debug macos_release linux linux_debug linux_release linux_release_desktop raspi raspi_debug raspi_release anbernic no_audio show_version help pack_tool resources.pack spirv
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
DATA|${SYSTEM_FOLDER}/config.yaml|optional,absolute
|
DATA|${SYSTEM_FOLDER}/config.yaml|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/postfx.yaml|optional,absolute
|
DATA|${SYSTEM_FOLDER}/postfx.yaml|optional,absolute
|
||||||
|
DATA|${SYSTEM_FOLDER}/crtpi.yaml|optional,absolute
|
||||||
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
||||||
|
|
||||||
# Archivos de configuración del juego
|
# Archivos de configuración del juego
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ service_menu.window_message.text_safety_margin 15.0f # Margen de segu
|
|||||||
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
|
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
|
||||||
|
|
||||||
# --- INTRO ---
|
# --- INTRO ---
|
||||||
intro.bg_color 4664BD # Color de fondo de la intro
|
intro.bg_color 41526F # Color de fondo de la intro
|
||||||
intro.card_color CBDBFC # Color de las tarjetas en la intro
|
intro.card_color CBDBFC # Color de las tarjetas en la intro
|
||||||
intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro
|
intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro
|
||||||
intro.text_distance_from_bottom 48 # Posicion del texto
|
intro.text_distance_from_bottom 48 # Posicion del texto
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ service_menu.window_message.text_safety_margin 15.0f # Margen de segu
|
|||||||
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
|
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
|
||||||
|
|
||||||
# --- INTRO ---
|
# --- INTRO ---
|
||||||
intro.bg_color 4664BD # Color de fondo de la intro
|
intro.bg_color 41526F # Color de fondo de la intro
|
||||||
intro.card_color CBDBFC # Color de las tarjetas en la intro
|
intro.card_color CBDBFC # Color de las tarjetas en la intro
|
||||||
intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro
|
intro.shadow_color 00000080 # Color de la sombra de las tarjetas en la intro
|
||||||
intro.text_distance_from_bottom 48 # Posición del texto desde la parte inferior
|
intro.text_distance_from_bottom 48 # Posición del texto desde la parte inferior
|
||||||
|
|||||||
@@ -79,8 +79,9 @@
|
|||||||
"[SERVICE_MENU] SHUTDOWN": "Apagar el sistema",
|
"[SERVICE_MENU] SHUTDOWN": "Apagar el sistema",
|
||||||
"[SERVICE_MENU] FULLSCREEN": "Pantalla completa",
|
"[SERVICE_MENU] FULLSCREEN": "Pantalla completa",
|
||||||
"[SERVICE_MENU] WINDOW_SIZE": "Tamany de la finestra",
|
"[SERVICE_MENU] WINDOW_SIZE": "Tamany de la finestra",
|
||||||
"[SERVICE_MENU] POSTFX": "PostFX",
|
"[SERVICE_MENU] SHADER": "Shader",
|
||||||
"[SERVICE_MENU] POSTFX_PRESET": "Preset PostFX",
|
"[SERVICE_MENU] SHADER_DISABLED": "Desactivat",
|
||||||
|
"[SERVICE_MENU] SHADER_PRESET": "Preset",
|
||||||
"[SERVICE_MENU] SUPERSAMPLING": "Supermostreig",
|
"[SERVICE_MENU] SUPERSAMPLING": "Supermostreig",
|
||||||
"[SERVICE_MENU] VSYNC": "Sincronisme vertical",
|
"[SERVICE_MENU] VSYNC": "Sincronisme vertical",
|
||||||
"[SERVICE_MENU] INTEGER_SCALE": "Escalat sencer",
|
"[SERVICE_MENU] INTEGER_SCALE": "Escalat sencer",
|
||||||
|
|||||||
@@ -78,8 +78,9 @@
|
|||||||
"[SERVICE_MENU] SHUTDOWN": "Shutdown System",
|
"[SERVICE_MENU] SHUTDOWN": "Shutdown System",
|
||||||
"[SERVICE_MENU] FULLSCREEN": "Fullscreen",
|
"[SERVICE_MENU] FULLSCREEN": "Fullscreen",
|
||||||
"[SERVICE_MENU] WINDOW_SIZE": "Window Zoom",
|
"[SERVICE_MENU] WINDOW_SIZE": "Window Zoom",
|
||||||
"[SERVICE_MENU] POSTFX": "PostFX",
|
"[SERVICE_MENU] SHADER": "Shader",
|
||||||
"[SERVICE_MENU] POSTFX_PRESET": "PostFX Preset",
|
"[SERVICE_MENU] SHADER_DISABLED": "Disabled",
|
||||||
|
"[SERVICE_MENU] SHADER_PRESET": "Preset",
|
||||||
"[SERVICE_MENU] SUPERSAMPLING": "Supersampling",
|
"[SERVICE_MENU] SUPERSAMPLING": "Supersampling",
|
||||||
"[SERVICE_MENU] VSYNC": "V-Sync",
|
"[SERVICE_MENU] VSYNC": "V-Sync",
|
||||||
"[SERVICE_MENU] INTEGER_SCALE": "Integer Scale",
|
"[SERVICE_MENU] INTEGER_SCALE": "Integer Scale",
|
||||||
|
|||||||
@@ -78,8 +78,9 @@
|
|||||||
"[SERVICE_MENU] SHUTDOWN": "Apagar el sistema",
|
"[SERVICE_MENU] SHUTDOWN": "Apagar el sistema",
|
||||||
"[SERVICE_MENU] FULLSCREEN": "Pantalla completa",
|
"[SERVICE_MENU] FULLSCREEN": "Pantalla completa",
|
||||||
"[SERVICE_MENU] WINDOW_SIZE": "Zoom de ventana",
|
"[SERVICE_MENU] WINDOW_SIZE": "Zoom de ventana",
|
||||||
"[SERVICE_MENU] POSTFX": "PostFX",
|
"[SERVICE_MENU] SHADER": "Shader",
|
||||||
"[SERVICE_MENU] POSTFX_PRESET": "Preset PostFX",
|
"[SERVICE_MENU] SHADER_DISABLED": "Desactivado",
|
||||||
|
"[SERVICE_MENU] SHADER_PRESET": "Preset",
|
||||||
"[SERVICE_MENU] SUPERSAMPLING": "Supersampling",
|
"[SERVICE_MENU] SUPERSAMPLING": "Supersampling",
|
||||||
"[SERVICE_MENU] VSYNC": "Sincronismo vertical",
|
"[SERVICE_MENU] VSYNC": "Sincronismo vertical",
|
||||||
"[SERVICE_MENU] INTEGER_SCALE": "Escalado proporcional",
|
"[SERVICE_MENU] INTEGER_SCALE": "Escalado proporcional",
|
||||||
|
|||||||
152
data/shaders/crtpi_frag.glsl
Normal file
152
data/shaders/crtpi_frag.glsl
Normal 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);
|
||||||
|
}
|
||||||
48
data/shaders/downscale.frag
Normal file
48
data/shaders/downscale.frag
Normal 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);
|
||||||
|
}
|
||||||
15
data/shaders/upscale.frag
Normal file
15
data/shaders/upscale.frag
Normal 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);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "balloon_formations.hpp"
|
#include "balloon_formations.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para max, min, copy
|
#include <algorithm> // Para max, min, copy
|
||||||
#include <utility> // Para std::cmp_less
|
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <cctype> // Para isdigit
|
#include <cctype> // Para isdigit
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
@@ -11,6 +10,7 @@
|
|||||||
#include <map> // Para map, operator==, _Rb_tree_iterator
|
#include <map> // Para map, operator==, _Rb_tree_iterator
|
||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <string> // Para string, char_traits, allocator, operator==, stoi, getline, operator<=>, basic_string
|
#include <string> // Para string, char_traits, allocator, operator==, stoi, getline, operator<=>, basic_string
|
||||||
|
#include <utility> // Para std::cmp_less
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "asset.hpp" // Para Asset
|
||||||
#include "balloon.hpp" // Para Balloon
|
#include "balloon.hpp" // Para Balloon
|
||||||
|
|||||||
255
source/card_sprite.cpp
Normal file
255
source/card_sprite.cpp
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
#include "card_sprite.hpp"
|
||||||
|
|
||||||
|
#include <algorithm> // Para std::clamp
|
||||||
|
#include <functional> // Para function
|
||||||
|
#include <utility> // Para move
|
||||||
|
|
||||||
|
#include "texture.hpp" // Para Texture
|
||||||
|
#include "utils.hpp" // Para easeOutBounce, easeOutCubic
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
|
||||||
|
: MovingSprite(std::move(texture)),
|
||||||
|
entry_easing_(easeOutBounce) {}
|
||||||
|
|
||||||
|
// Inicia la animación de entrada (solo si está en IDLE)
|
||||||
|
auto CardSprite::enable() -> bool {
|
||||||
|
if (state_ != CardState::IDLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = CardState::ENTERING;
|
||||||
|
entry_elapsed_ = 0.0F;
|
||||||
|
first_touch_ = false;
|
||||||
|
|
||||||
|
// Posición inicial (borde de pantalla)
|
||||||
|
setPos(entry_start_x_, entry_start_y_);
|
||||||
|
|
||||||
|
// Zoom inicial grande (como si estuviera cerca de la cámara)
|
||||||
|
horizontal_zoom_ = start_zoom_;
|
||||||
|
vertical_zoom_ = start_zoom_;
|
||||||
|
|
||||||
|
// Ángulo inicial
|
||||||
|
rotate_.angle = start_angle_;
|
||||||
|
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
|
||||||
|
|
||||||
|
shadow_visible_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicia la animación de salida (solo si está en LANDED)
|
||||||
|
void CardSprite::startExit() {
|
||||||
|
if (state_ != CardState::LANDED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = CardState::EXITING;
|
||||||
|
shadow_visible_ = true;
|
||||||
|
|
||||||
|
// Velocidad y aceleración de salida
|
||||||
|
vx_ = exit_vx_;
|
||||||
|
vy_ = exit_vy_;
|
||||||
|
ax_ = exit_ax_;
|
||||||
|
ay_ = exit_ay_;
|
||||||
|
|
||||||
|
// Rotación continua
|
||||||
|
rotate_.enabled = true;
|
||||||
|
rotate_.amount = exit_rotate_amount_;
|
||||||
|
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza según el estado
|
||||||
|
void CardSprite::update(float delta_time) {
|
||||||
|
switch (state_) {
|
||||||
|
case CardState::ENTERING:
|
||||||
|
updateEntering(delta_time);
|
||||||
|
break;
|
||||||
|
case CardState::EXITING:
|
||||||
|
updateExiting(delta_time);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animación de entrada: interpola posición, zoom y ángulo
|
||||||
|
void CardSprite::updateEntering(float delta_time) {
|
||||||
|
entry_elapsed_ += delta_time;
|
||||||
|
|
||||||
|
float progress = std::clamp(entry_elapsed_ / entry_duration_s_, 0.0F, 1.0F);
|
||||||
|
double eased = entry_easing_(static_cast<double>(progress));
|
||||||
|
|
||||||
|
// Zoom: de start_zoom_ a 1.0 con rebote
|
||||||
|
auto current_zoom = static_cast<float>(start_zoom_ + (1.0 - start_zoom_) * eased);
|
||||||
|
horizontal_zoom_ = current_zoom;
|
||||||
|
vertical_zoom_ = current_zoom;
|
||||||
|
|
||||||
|
// Ángulo: de start_angle_ a 0 con rebote
|
||||||
|
rotate_.angle = start_angle_ * (1.0 - eased);
|
||||||
|
|
||||||
|
// Posición: de entry_start a landing con easing suave (sin rebote)
|
||||||
|
// Usamos easeOutCubic para que el desplazamiento sea fluido
|
||||||
|
double pos_eased = easeOutCubic(static_cast<double>(progress));
|
||||||
|
auto current_x = static_cast<float>(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased);
|
||||||
|
auto current_y = static_cast<float>(entry_start_y_ + (landing_y_ - entry_start_y_) * pos_eased);
|
||||||
|
setPos(current_x, current_y);
|
||||||
|
|
||||||
|
// Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez)
|
||||||
|
if (!first_touch_ && eased >= FIRST_TOUCH_THRESHOLD) {
|
||||||
|
first_touch_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transición a LANDED cuando termina la animación completa
|
||||||
|
if (progress >= 1.0F) {
|
||||||
|
horizontal_zoom_ = 1.0F;
|
||||||
|
vertical_zoom_ = 1.0F;
|
||||||
|
rotate_.angle = 0.0;
|
||||||
|
setPos(landing_x_, landing_y_);
|
||||||
|
state_ = CardState::LANDED;
|
||||||
|
first_touch_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animación de salida: movimiento + rotación continua + zoom opcional
|
||||||
|
void CardSprite::updateExiting(float delta_time) {
|
||||||
|
move(delta_time);
|
||||||
|
rotate(delta_time);
|
||||||
|
|
||||||
|
// Ganar altura gradualmente (zoom hacia el objetivo)
|
||||||
|
if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) {
|
||||||
|
float new_zoom = horizontal_zoom_ + exit_zoom_speed_ * delta_time;
|
||||||
|
if (new_zoom > exit_target_zoom_) {
|
||||||
|
new_zoom = exit_target_zoom_;
|
||||||
|
}
|
||||||
|
horizontal_zoom_ = new_zoom;
|
||||||
|
vertical_zoom_ = new_zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOffScreen()) {
|
||||||
|
state_ = CardState::FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderiza el sprite y su sombra
|
||||||
|
void CardSprite::render() {
|
||||||
|
if (state_ == CardState::IDLE || state_ == CardState::FINISHED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sombra primero (debajo de la tarjeta)
|
||||||
|
if (shadow_visible_ && shadow_texture_) {
|
||||||
|
renderShadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tarjeta
|
||||||
|
MovingSprite::render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderiza la sombra con efecto de perspectiva 2D→3D (efecto helicóptero)
|
||||||
|
//
|
||||||
|
// Fuente de luz en la esquina superior izquierda (0,0).
|
||||||
|
// La sombra se mueve con la tarjeta pero desplazada en dirección opuesta a la luz
|
||||||
|
// (abajo-derecha a 45°). Cuanto más alta la tarjeta (zoom > 1.0):
|
||||||
|
// - Más separada de la tarjeta (offset grande)
|
||||||
|
// - Más pequeña (proyección lejana)
|
||||||
|
// Cuando la tarjeta está en la mesa (zoom=1.0):
|
||||||
|
// - Sombra pegada con offset base
|
||||||
|
// - Tamaño real
|
||||||
|
void CardSprite::renderShadow() {
|
||||||
|
// Altura sobre la mesa: 0.0 = en la mesa, 0.8 = alta (zoom 1.8)
|
||||||
|
float height = horizontal_zoom_ - 1.0F;
|
||||||
|
|
||||||
|
// Escala: más pequeña cuanto más alta
|
||||||
|
float shadow_zoom = 1.0F / horizontal_zoom_;
|
||||||
|
|
||||||
|
// Offset respecto a la tarjeta: base + extra proporcional a la altura
|
||||||
|
// La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0)
|
||||||
|
float offset_x = shadow_offset_x_ + height * SHADOW_HEIGHT_MULTIPLIER;
|
||||||
|
float offset_y = shadow_offset_y_ + height * SHADOW_HEIGHT_MULTIPLIER;
|
||||||
|
|
||||||
|
shadow_texture_->render(
|
||||||
|
pos_.x + offset_x,
|
||||||
|
pos_.y + offset_y,
|
||||||
|
&sprite_clip_,
|
||||||
|
shadow_zoom,
|
||||||
|
shadow_zoom,
|
||||||
|
rotate_.angle,
|
||||||
|
&rotate_.center,
|
||||||
|
flip_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comprueba si el sprite está fuera de pantalla
|
||||||
|
auto CardSprite::isOffScreen() const -> bool {
|
||||||
|
float effective_width = pos_.w * horizontal_zoom_;
|
||||||
|
float effective_height = pos_.h * vertical_zoom_;
|
||||||
|
return (pos_.x + effective_width < -OFF_SCREEN_MARGIN ||
|
||||||
|
pos_.x > screen_width_ + OFF_SCREEN_MARGIN ||
|
||||||
|
pos_.y + effective_height < -OFF_SCREEN_MARGIN ||
|
||||||
|
pos_.y > screen_height_ + OFF_SCREEN_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Consultas de estado ---
|
||||||
|
auto CardSprite::hasLanded() const -> bool {
|
||||||
|
return state_ == CardState::LANDED || state_ == CardState::EXITING || state_ == CardState::FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CardSprite::hasFirstTouch() const -> bool {
|
||||||
|
return first_touch_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CardSprite::hasFinished() const -> bool {
|
||||||
|
return state_ == CardState::FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CardSprite::isExiting() const -> bool {
|
||||||
|
return state_ == CardState::EXITING;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CardSprite::getState() const -> CardState {
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Configuración ---
|
||||||
|
void CardSprite::setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing) {
|
||||||
|
start_zoom_ = start_zoom;
|
||||||
|
start_angle_ = start_angle;
|
||||||
|
entry_duration_s_ = duration_s;
|
||||||
|
entry_easing_ = std::move(easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setEntryPosition(float start_x, float start_y) {
|
||||||
|
entry_start_x_ = start_x;
|
||||||
|
entry_start_y_ = start_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setLandingPosition(float x, float y) {
|
||||||
|
landing_x_ = x;
|
||||||
|
landing_y_ = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setExitParams(float vx, float vy, float ax, float ay, double rotate_amount) {
|
||||||
|
exit_vx_ = vx;
|
||||||
|
exit_vy_ = vy;
|
||||||
|
exit_ax_ = ax;
|
||||||
|
exit_ay_ = ay;
|
||||||
|
exit_rotate_amount_ = rotate_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setExitLift(float target_zoom, float zoom_speed) {
|
||||||
|
exit_target_zoom_ = target_zoom;
|
||||||
|
exit_zoom_speed_ = zoom_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setShadowTexture(std::shared_ptr<Texture> texture) {
|
||||||
|
shadow_texture_ = std::move(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setShadowOffset(float offset_x, float offset_y) {
|
||||||
|
shadow_offset_x_ = offset_x;
|
||||||
|
shadow_offset_y_ = offset_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardSprite::setScreenBounds(float width, float height) {
|
||||||
|
screen_width_ = width;
|
||||||
|
screen_height_ = height;
|
||||||
|
}
|
||||||
109
source/card_sprite.hpp
Normal file
109
source/card_sprite.hpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_FPoint
|
||||||
|
|
||||||
|
#include <functional> // Para function
|
||||||
|
#include <memory> // Para shared_ptr
|
||||||
|
|
||||||
|
#include "moving_sprite.hpp" // Para MovingSprite
|
||||||
|
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
// --- Estados de la tarjeta ---
|
||||||
|
enum class CardState {
|
||||||
|
IDLE, // No activada todavía
|
||||||
|
ENTERING, // Animación de entrada (zoom + rotación + desplazamiento con rebote)
|
||||||
|
LANDED, // En reposo sobre la mesa
|
||||||
|
EXITING, // Saliendo de pantalla girando
|
||||||
|
FINISHED, // Fuera de pantalla
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Clase CardSprite: tarjeta animada con zoom, rotación y sombra integrada ---
|
||||||
|
//
|
||||||
|
// Simula una tarjeta lanzada sobre una mesa desde un borde de la pantalla.
|
||||||
|
// Durante la entrada, interpola posición, zoom y rotación con easing (rebote).
|
||||||
|
// Durante la salida, se desplaza fuera de pantalla girando, sin sombra.
|
||||||
|
class CardSprite : public MovingSprite {
|
||||||
|
public:
|
||||||
|
explicit CardSprite(std::shared_ptr<Texture> texture);
|
||||||
|
~CardSprite() override = default;
|
||||||
|
|
||||||
|
// --- Ciclo principal ---
|
||||||
|
void update(float delta_time) override;
|
||||||
|
void render() override;
|
||||||
|
|
||||||
|
// --- Control de estado ---
|
||||||
|
auto enable() -> bool; // Inicia la animación de entrada (true si se activó)
|
||||||
|
void startExit(); // Inicia la animación de salida
|
||||||
|
|
||||||
|
// --- Consultas de estado ---
|
||||||
|
[[nodiscard]] auto hasLanded() const -> bool; // ¿Ha aterrizado definitivamente?
|
||||||
|
[[nodiscard]] auto hasFirstTouch() const -> bool; // ¿Ha tocado la mesa por primera vez? (primer rebote)
|
||||||
|
[[nodiscard]] auto hasFinished() const -> bool; // ¿Ha terminado completamente?
|
||||||
|
[[nodiscard]] auto isExiting() const -> bool; // ¿Está saliendo de pantalla?
|
||||||
|
[[nodiscard]] auto getState() const -> CardState; // Estado actual
|
||||||
|
|
||||||
|
// --- Configuración de entrada ---
|
||||||
|
void setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing);
|
||||||
|
void setEntryPosition(float start_x, float start_y); // Posición inicial (borde de pantalla)
|
||||||
|
void setLandingPosition(float x, float y); // Posición final centrada
|
||||||
|
|
||||||
|
// --- Configuración de salida ---
|
||||||
|
void setExitParams(float vx, float vy, float ax, float ay, double rotate_amount);
|
||||||
|
void setExitLift(float target_zoom, float zoom_speed); // Ganar altura al salir (zoom > 1.0)
|
||||||
|
|
||||||
|
// --- Sombra ---
|
||||||
|
void setShadowTexture(std::shared_ptr<Texture> texture);
|
||||||
|
void setShadowOffset(float offset_x, float offset_y);
|
||||||
|
|
||||||
|
// --- Limites de pantalla (para detectar salida) ---
|
||||||
|
void setScreenBounds(float width, float height);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --- Estado ---
|
||||||
|
CardState state_ = CardState::IDLE;
|
||||||
|
bool first_touch_ = false; // Primer contacto con la mesa (eased >= umbral)
|
||||||
|
|
||||||
|
// --- Umbral para detectar el primer toque ---
|
||||||
|
static constexpr double FIRST_TOUCH_THRESHOLD = 0.98;
|
||||||
|
|
||||||
|
// --- Parámetros de entrada ---
|
||||||
|
float start_zoom_ = 1.8F;
|
||||||
|
double start_angle_ = 15.0;
|
||||||
|
float entry_duration_s_ = 1.5F;
|
||||||
|
float entry_elapsed_ = 0.0F;
|
||||||
|
std::function<double(double)> entry_easing_;
|
||||||
|
float entry_start_x_ = 0.0F; // Posición inicial X (borde)
|
||||||
|
float entry_start_y_ = 0.0F; // Posición inicial Y (borde)
|
||||||
|
float landing_x_ = 0.0F;
|
||||||
|
float landing_y_ = 0.0F;
|
||||||
|
|
||||||
|
// --- Parámetros de salida ---
|
||||||
|
float exit_vx_ = 0.0F;
|
||||||
|
float exit_vy_ = 0.0F;
|
||||||
|
float exit_ax_ = 0.0F;
|
||||||
|
float exit_ay_ = 0.0F;
|
||||||
|
double exit_rotate_amount_ = 0.0;
|
||||||
|
float exit_target_zoom_ = 1.0F; // Zoom objetivo al salir (>1.0 = se eleva)
|
||||||
|
float exit_zoom_speed_ = 0.0F; // Velocidad de cambio de zoom por segundo
|
||||||
|
|
||||||
|
// --- Sombra ---
|
||||||
|
std::shared_ptr<Texture> shadow_texture_;
|
||||||
|
float shadow_offset_x_ = 8.0F;
|
||||||
|
float shadow_offset_y_ = 8.0F;
|
||||||
|
bool shadow_visible_ = true;
|
||||||
|
|
||||||
|
// --- Límites de pantalla ---
|
||||||
|
float screen_width_ = 320.0F;
|
||||||
|
float screen_height_ = 240.0F;
|
||||||
|
|
||||||
|
// --- Constantes ---
|
||||||
|
static constexpr float OFF_SCREEN_MARGIN = 50.0F; // Margen fuera de pantalla para considerar FINISHED
|
||||||
|
static constexpr float SHADOW_HEIGHT_MULTIPLIER = 400.0F; // Pixels de separación de sombra por unidad de altura
|
||||||
|
|
||||||
|
// --- Métodos internos ---
|
||||||
|
void updateEntering(float delta_time);
|
||||||
|
void updateExiting(float delta_time);
|
||||||
|
void renderShadow();
|
||||||
|
[[nodiscard]] auto isOffScreen() const -> bool;
|
||||||
|
};
|
||||||
@@ -186,8 +186,11 @@ namespace Defaults::Video {
|
|||||||
constexpr bool FULLSCREEN = false;
|
constexpr bool FULLSCREEN = false;
|
||||||
constexpr bool VSYNC = true;
|
constexpr bool VSYNC = true;
|
||||||
constexpr bool INTEGER_SCALE = true;
|
constexpr bool INTEGER_SCALE = true;
|
||||||
constexpr bool POSTFX = false;
|
constexpr bool GPU_ACCELERATION = true;
|
||||||
constexpr int SUPERSAMPLING = 1;
|
constexpr bool SHADER_ENABLED = false;
|
||||||
|
constexpr bool SUPERSAMPLING = false;
|
||||||
|
constexpr bool LINEAR_UPSCALE = false;
|
||||||
|
constexpr int DOWNSCALE_ALGO = 1;
|
||||||
} // namespace Defaults::Video
|
} // namespace Defaults::Video
|
||||||
|
|
||||||
namespace Defaults::Music {
|
namespace Defaults::Music {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
|
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
|
||||||
#include <ctime> // Para time
|
#include <ctime> // Para time
|
||||||
#include <filesystem> // Para path, absolute
|
#include <filesystem> // Para path, absolute
|
||||||
|
#include <fstream> // Para ifstream, ofstream
|
||||||
#include <iostream> // Para basic_ostream, operator<<, cerr
|
#include <iostream> // Para basic_ostream, operator<<, cerr
|
||||||
#include <memory> // Para make_unique, unique_ptr
|
#include <memory> // Para make_unique, unique_ptr
|
||||||
#include <span> // Para span
|
#include <span> // Para span
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "asset.hpp" // Para Asset
|
||||||
#include "audio.hpp" // Para Audio
|
#include "audio.hpp" // Para Audio
|
||||||
|
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||||
#include "input.hpp" // Para Input
|
#include "input.hpp" // Para Input
|
||||||
#include "lang.hpp" // Para setLanguage
|
#include "lang.hpp" // Para setLanguage
|
||||||
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable
|
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable
|
||||||
@@ -40,16 +42,6 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Director::Director(int argc, std::span<char*> argv) {
|
Director::Director(int argc, std::span<char*> argv) {
|
||||||
#ifdef RECORDING
|
|
||||||
Section::name = Section::Name::GAME;
|
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
|
||||||
#elif _DEBUG
|
|
||||||
Section::name = Section::Name::GAME;
|
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
|
||||||
#else // NORMAL GAME
|
|
||||||
Section::name = Section::Name::LOGO;
|
|
||||||
Section::options = Section::Options::NONE;
|
|
||||||
#endif
|
|
||||||
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
|
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
|
||||||
|
|
||||||
// Establece el nivel de prioridad de la categoría de registro
|
// Establece el nivel de prioridad de la categoría de registro
|
||||||
@@ -68,6 +60,17 @@ Director::Director(int argc, std::span<char*> argv) {
|
|||||||
createSystemFolder("jailgames");
|
createSystemFolder("jailgames");
|
||||||
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
|
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
|
||||||
|
|
||||||
|
// Establecer sección inicial según modo de compilación
|
||||||
|
#ifdef RECORDING
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
#elif _DEBUG
|
||||||
|
loadDebugConfig();
|
||||||
|
#else
|
||||||
|
Section::name = Section::Name::LOGO;
|
||||||
|
Section::options = Section::Options::NONE;
|
||||||
|
#endif
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,8 +109,10 @@ void Director::init() {
|
|||||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||||
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
||||||
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
||||||
|
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
||||||
Options::loadFromFile(); // Carga el archivo de configuración
|
Options::loadFromFile(); // Carga el archivo de configuración
|
||||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||||
|
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
||||||
loadParams(); // Carga los parámetros del programa
|
loadParams(); // Carga los parámetros del programa
|
||||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||||
|
|
||||||
@@ -122,9 +127,9 @@ void Director::init() {
|
|||||||
|
|
||||||
Logger::section("INIT RESOURCES");
|
Logger::section("INIT RESOURCES");
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
Resource::init(Resource::LoadingMode::PRELOAD); // Inicializa el sistema de gestión de recursos
|
Resource::init(debug_config.resource_loading == "lazy" ? Resource::LoadingMode::LAZY_LOAD : Resource::LoadingMode::PRELOAD);
|
||||||
#else
|
#else
|
||||||
Resource::init(Resource::LoadingMode::PRELOAD); // Inicializa el sistema de gestión de recursos
|
Resource::init(Resource::LoadingMode::PRELOAD);
|
||||||
#endif
|
#endif
|
||||||
ServiceMenu::init(); // Inicializa el menú de servicio
|
ServiceMenu::init(); // Inicializa el menú de servicio
|
||||||
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
||||||
@@ -222,6 +227,103 @@ void Director::checkProgramArguments(int argc, std::span<char*> argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Carga debug.yaml desde la carpeta del sistema (solo en _DEBUG)
|
||||||
|
void Director::loadDebugConfig() {
|
||||||
|
const std::string DEBUG_FILE = system_folder_ + "/debug.yaml";
|
||||||
|
|
||||||
|
std::ifstream file(DEBUG_FILE);
|
||||||
|
if (!file.good()) {
|
||||||
|
// Crear fichero por defecto
|
||||||
|
std::ofstream out(DEBUG_FILE);
|
||||||
|
if (out.is_open()) {
|
||||||
|
out << "# Coffee Crisis Arcade Edition - Debug Configuration\n";
|
||||||
|
out << "# This file is only read in DEBUG builds.\n";
|
||||||
|
out << "#\n";
|
||||||
|
out << "# initial_section: logo, intro, title, game, credits, instructions, hiscore\n";
|
||||||
|
out << "# initial_options: none, 1p, 2p, both\n";
|
||||||
|
out << "# initial_stage: 0-based stage index (only when section is game)\n";
|
||||||
|
out << "# show_render_info: show FPS/driver/preset overlay\n";
|
||||||
|
out << "# resource_loading: preload, lazy\n";
|
||||||
|
out << "\n";
|
||||||
|
out << "initial_section: game\n";
|
||||||
|
out << "initial_options: 1p\n";
|
||||||
|
out << "initial_stage: 0\n";
|
||||||
|
out << "show_render_info: true\n";
|
||||||
|
out << "resource_loading: preload\n";
|
||||||
|
out.close();
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config created: %s", DEBUG_FILE.c_str());
|
||||||
|
}
|
||||||
|
// Usar defaults de DebugConfig
|
||||||
|
} else {
|
||||||
|
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(content);
|
||||||
|
if (yaml.contains("initial_section")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_section = yaml["initial_section"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("initial_options")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("initial_stage")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("show_render_info")) {
|
||||||
|
try {
|
||||||
|
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("resource_loading")) {
|
||||||
|
try {
|
||||||
|
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config loaded: section=%s options=%s stage=%d", debug_config.initial_section.c_str(), debug_config.initial_options.c_str(), debug_config.initial_stage);
|
||||||
|
} catch (...) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing debug.yaml, using defaults");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapear strings a enums
|
||||||
|
const auto& sec = debug_config.initial_section;
|
||||||
|
if (sec == "logo") {
|
||||||
|
Section::name = Section::Name::LOGO;
|
||||||
|
} else if (sec == "intro") {
|
||||||
|
Section::name = Section::Name::INTRO;
|
||||||
|
} else if (sec == "title") {
|
||||||
|
Section::name = Section::Name::TITLE;
|
||||||
|
} else if (sec == "game") {
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
} else if (sec == "credits") {
|
||||||
|
Section::name = Section::Name::CREDITS;
|
||||||
|
} else if (sec == "instructions") {
|
||||||
|
Section::name = Section::Name::INSTRUCTIONS;
|
||||||
|
} else if (sec == "hiscore") {
|
||||||
|
Section::name = Section::Name::HI_SCORE_TABLE;
|
||||||
|
} else {
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& opt = debug_config.initial_options;
|
||||||
|
if (opt == "none") {
|
||||||
|
Section::options = Section::Options::NONE;
|
||||||
|
} else if (opt == "1p") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
} else if (opt == "2p") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_2P;
|
||||||
|
} else if (opt == "both") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_BOTH;
|
||||||
|
} else {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Crea la carpeta del sistema donde guardar datos
|
// Crea la carpeta del sistema donde guardar datos
|
||||||
void Director::createSystemFolder(const std::string& folder) {
|
void Director::createSystemFolder(const std::string& folder) {
|
||||||
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
|
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
|
||||||
@@ -270,7 +372,7 @@ void Director::runGame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
constexpr int CURRENT_STAGE = 0;
|
const int CURRENT_STAGE = debug_config.initial_stage;
|
||||||
#else
|
#else
|
||||||
constexpr int CURRENT_STAGE = 0;
|
constexpr int CURRENT_STAGE = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,6 +17,21 @@ class Director {
|
|||||||
// --- Bucle principal ---
|
// --- Bucle principal ---
|
||||||
static auto run() -> int;
|
static auto run() -> int;
|
||||||
|
|
||||||
|
// --- Debug config (accesible desde otras clases) ---
|
||||||
|
struct DebugConfig {
|
||||||
|
std::string initial_section;
|
||||||
|
std::string initial_options;
|
||||||
|
int initial_stage = 0;
|
||||||
|
bool show_render_info = true;
|
||||||
|
std::string resource_loading;
|
||||||
|
|
||||||
|
DebugConfig()
|
||||||
|
: initial_section("game"),
|
||||||
|
initial_options("1p"),
|
||||||
|
resource_loading("preload") {}
|
||||||
|
};
|
||||||
|
static inline DebugConfig debug_config;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Variables internas ---
|
// --- Variables internas ---
|
||||||
std::string executable_path_; // Ruta del ejecutable
|
std::string executable_path_; // Ruta del ejecutable
|
||||||
@@ -30,6 +45,7 @@ class Director {
|
|||||||
static void loadParams(); // Carga los parámetros del programa
|
static void loadParams(); // Carga los parámetros del programa
|
||||||
static void loadScoreFile(); // Carga el fichero de puntuaciones
|
static void loadScoreFile(); // Carga el fichero de puntuaciones
|
||||||
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
|
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
|
||||||
|
void loadDebugConfig(); // Carga debug.yaml (solo en _DEBUG)
|
||||||
|
|
||||||
// --- Gestión de entrada y archivos ---
|
// --- Gestión de entrada y archivos ---
|
||||||
void loadAssets(); // Crea el índice de archivos disponibles
|
void loadAssets(); // Crea el índice de archivos disponibles
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace GlobalEvents {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_WINDOW_RESIZED:
|
case SDL_EVENT_WINDOW_RESIZED:
|
||||||
Screen::initPostFX();
|
Screen::initShaders();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -62,25 +62,36 @@ namespace GlobalInputs {
|
|||||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.vsync)});
|
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.vsync)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activa o desactiva los efectos PostFX
|
// Activa o desactiva los shaders
|
||||||
void togglePostFX() {
|
void toggleShaders() {
|
||||||
Screen::togglePostFX();
|
Screen::toggleShaders();
|
||||||
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.postfx)});
|
Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shader.enabled)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avanza al siguiente preset PostFX
|
// Cambia entre PostFX y CrtPi
|
||||||
void nextPostFXPreset() {
|
void nextShader() {
|
||||||
|
Screen::nextShader();
|
||||||
|
const std::string SHADER_NAME = (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) ? "CrtPi" : "PostFX";
|
||||||
|
Notifier::get()->show({"Shader: " + SHADER_NAME});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avanza al siguiente preset PostFX o CrtPi según shader activo
|
||||||
|
void nextPreset() {
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
Screen::nextCrtPiPreset();
|
||||||
|
const std::string name = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
|
||||||
|
Notifier::get()->show({"CrtPi: " + name});
|
||||||
|
} else {
|
||||||
Screen::nextPostFXPreset();
|
Screen::nextPostFXPreset();
|
||||||
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset)).name;
|
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name;
|
||||||
Notifier::get()->show({"PostFX: " + name});
|
Notifier::get()->show({"PostFX: " + name});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activa o desactiva el supersampling 3x
|
// Activa o desactiva el supersampling
|
||||||
void toggleSupersampling() {
|
void toggleSupersampling() {
|
||||||
Screen::toggleSupersampling();
|
Screen::toggleSupersampling();
|
||||||
const int SS = Options::video.supersampling;
|
Notifier::get()->show({"SS: " + std::string(Options::video.supersampling.enabled ? "ON" : "OFF")});
|
||||||
const std::string SS_LABEL = (SS <= 1) ? "OFF" : (std::to_string(SS) + "\xC3\x97");
|
|
||||||
Notifier::get()->show({"SS: " + SS_LABEL});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cambia al siguiente idioma
|
// Cambia al siguiente idioma
|
||||||
@@ -202,11 +213,15 @@ namespace GlobalInputs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_POSTFX, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
if (Input::get()->checkAction(Input::Action::TOGGLE_VIDEO_POSTFX, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||||
togglePostFX();
|
toggleShaders();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkAction(Input::Action::NEXT_SHADER, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||||
|
nextShader();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Input::get()->checkAction(Input::Action::NEXT_POSTFX_PRESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
if (Input::get()->checkAction(Input::Action::NEXT_POSTFX_PRESET, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||||
nextPostFXPreset();
|
nextPreset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Input::get()->checkAction(Input::Action::TOGGLE_SUPERSAMPLING, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
if (Input::get()->checkAction(Input::Action::TOGGLE_SUPERSAMPLING, Input::DO_NOT_ALLOW_REPEAT, Input::CHECK_KEYBOARD)) {
|
||||||
|
|||||||
@@ -325,17 +325,6 @@ void Input::initSDLGamePad() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::resetJustPressed() {
|
|
||||||
for (auto& key : keyboard_.bindings) {
|
|
||||||
key.second.just_pressed = false;
|
|
||||||
}
|
|
||||||
for (auto& gamepad : gamepads_) {
|
|
||||||
for (auto& binding : gamepad->bindings) {
|
|
||||||
binding.second.just_pressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Input::resetInputStates() {
|
void Input::resetInputStates() {
|
||||||
// Resetear todos los KeyBindings.active a false
|
// Resetear todos los KeyBindings.active a false
|
||||||
for (auto& key : keyboard_.bindings) {
|
for (auto& key : keyboard_.bindings) {
|
||||||
@@ -360,7 +349,7 @@ void Input::update() {
|
|||||||
bool key_is_down_now = key_states[binding.second.scancode];
|
bool key_is_down_now = key_states[binding.second.scancode];
|
||||||
|
|
||||||
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
|
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
|
||||||
binding.second.just_pressed = binding.second.just_pressed || (key_is_down_now && !binding.second.is_held);
|
binding.second.just_pressed = key_is_down_now && !binding.second.is_held;
|
||||||
binding.second.is_held = key_is_down_now;
|
binding.second.is_held = key_is_down_now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,16 +367,6 @@ void Input::update() {
|
|||||||
|
|
||||||
auto Input::handleEvent(const SDL_Event& event) -> std::string {
|
auto Input::handleEvent(const SDL_Event& event) -> std::string {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_KEY_DOWN:
|
|
||||||
if (!event.key.repeat) {
|
|
||||||
for (auto& [action, binding] : keyboard_.bindings) {
|
|
||||||
if (binding.scancode == event.key.scancode) {
|
|
||||||
binding.just_pressed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_GAMEPAD_ADDED:
|
case SDL_EVENT_GAMEPAD_ADDED:
|
||||||
return addGamepad(event.gdevice.which);
|
return addGamepad(event.gdevice.which);
|
||||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class Input {
|
|||||||
{Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
|
{Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
|
||||||
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
||||||
{Action::TOGGLE_VIDEO_POSTFX, KeyState(SDL_SCANCODE_F4)},
|
{Action::TOGGLE_VIDEO_POSTFX, KeyState(SDL_SCANCODE_F4)},
|
||||||
|
{Action::NEXT_SHADER, KeyState(SDL_SCANCODE_8)},
|
||||||
{Action::NEXT_POSTFX_PRESET, KeyState(SDL_SCANCODE_9)},
|
{Action::NEXT_POSTFX_PRESET, KeyState(SDL_SCANCODE_9)},
|
||||||
{Action::TOGGLE_SUPERSAMPLING, KeyState(SDL_SCANCODE_0)},
|
{Action::TOGGLE_SUPERSAMPLING, KeyState(SDL_SCANCODE_0)},
|
||||||
{Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
|
{Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
|
||||||
@@ -176,7 +177,6 @@ class Input {
|
|||||||
|
|
||||||
// --- Métodos de reseteo de estado de entrada ---
|
// --- Métodos de reseteo de estado de entrada ---
|
||||||
void resetInputStates();
|
void resetInputStates();
|
||||||
void resetJustPressed();
|
|
||||||
|
|
||||||
// --- Eventos ---
|
// --- Eventos ---
|
||||||
auto handleEvent(const SDL_Event& event) -> std::string;
|
auto handleEvent(const SDL_Event& event) -> std::string;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
|
|||||||
{InputAction::WINDOW_INC_SIZE, "WINDOW_INC_SIZE"},
|
{InputAction::WINDOW_INC_SIZE, "WINDOW_INC_SIZE"},
|
||||||
{InputAction::WINDOW_DEC_SIZE, "WINDOW_DEC_SIZE"},
|
{InputAction::WINDOW_DEC_SIZE, "WINDOW_DEC_SIZE"},
|
||||||
{InputAction::TOGGLE_VIDEO_POSTFX, "TOGGLE_VIDEO_POSTFX"},
|
{InputAction::TOGGLE_VIDEO_POSTFX, "TOGGLE_VIDEO_POSTFX"},
|
||||||
|
{InputAction::NEXT_SHADER, "NEXT_SHADER"},
|
||||||
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"},
|
{InputAction::NEXT_POSTFX_PRESET, "NEXT_POSTFX_PRESET"},
|
||||||
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
{InputAction::TOGGLE_SUPERSAMPLING, "TOGGLE_SUPERSAMPLING"},
|
||||||
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, "TOGGLE_VIDEO_INTEGER_SCALE"},
|
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, "TOGGLE_VIDEO_INTEGER_SCALE"},
|
||||||
@@ -54,6 +55,7 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
|||||||
{"WINDOW_INC_SIZE", InputAction::WINDOW_INC_SIZE},
|
{"WINDOW_INC_SIZE", InputAction::WINDOW_INC_SIZE},
|
||||||
{"WINDOW_DEC_SIZE", InputAction::WINDOW_DEC_SIZE},
|
{"WINDOW_DEC_SIZE", InputAction::WINDOW_DEC_SIZE},
|
||||||
{"TOGGLE_VIDEO_POSTFX", InputAction::TOGGLE_VIDEO_POSTFX},
|
{"TOGGLE_VIDEO_POSTFX", InputAction::TOGGLE_VIDEO_POSTFX},
|
||||||
|
{"NEXT_SHADER", InputAction::NEXT_SHADER},
|
||||||
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET},
|
{"NEXT_POSTFX_PRESET", InputAction::NEXT_POSTFX_PRESET},
|
||||||
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
{"TOGGLE_SUPERSAMPLING", InputAction::TOGGLE_SUPERSAMPLING},
|
||||||
{"TOGGLE_VIDEO_INTEGER_SCALE", InputAction::TOGGLE_VIDEO_INTEGER_SCALE},
|
{"TOGGLE_VIDEO_INTEGER_SCALE", InputAction::TOGGLE_VIDEO_INTEGER_SCALE},
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
|
|||||||
WINDOW_INC_SIZE,
|
WINDOW_INC_SIZE,
|
||||||
WINDOW_DEC_SIZE,
|
WINDOW_DEC_SIZE,
|
||||||
TOGGLE_VIDEO_POSTFX,
|
TOGGLE_VIDEO_POSTFX,
|
||||||
|
NEXT_SHADER,
|
||||||
NEXT_POSTFX_PRESET,
|
NEXT_POSTFX_PRESET,
|
||||||
TOGGLE_SUPERSAMPLING,
|
TOGGLE_SUPERSAMPLING,
|
||||||
TOGGLE_VIDEO_INTEGER_SCALE,
|
TOGGLE_VIDEO_INTEGER_SCALE,
|
||||||
|
|||||||
@@ -24,16 +24,17 @@ namespace Options {
|
|||||||
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
GamepadManager gamepad_manager; // Opciones de mando para cada jugador
|
||||||
Keyboard keyboard; // Opciones para el teclado
|
Keyboard keyboard; // Opciones para el teclado
|
||||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
std::vector<PostFXPreset> postfx_presets = { // Lista de presets de PostFX
|
std::vector<PostFXPreset> postfx_presets = {
|
||||||
{"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F},
|
{"CRT", 0.15F, 0.7F, 0.2F, 0.5F, 0.1F, 0.0F, 0.0F, 0.0F},
|
||||||
{"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F},
|
{"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F},
|
||||||
{"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F},
|
{"Curved", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F},
|
||||||
{"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F},
|
{"Scanlines", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F},
|
||||||
{"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F},
|
{"Subtle", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F},
|
||||||
{"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F},
|
{"CRT Live", 0.15F, 0.6F, 0.3F, 0.3F, 0.1F, 0.0F, 0.4F, 0.8F},
|
||||||
};
|
};
|
||||||
int current_postfx_preset = 0; // Índice del preset PostFX activo
|
std::string postfx_file_path;
|
||||||
std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
std::vector<CrtPiPreset> crtpi_presets;
|
||||||
|
std::string crtpi_file_path;
|
||||||
|
|
||||||
// Establece el fichero de configuración
|
// Establece el fichero de configuración
|
||||||
void setConfigFile(const std::string& file_path) { settings.config_file = file_path; }
|
void setConfigFile(const std::string& file_path) { settings.config_file = file_path; }
|
||||||
@@ -44,6 +45,9 @@ namespace Options {
|
|||||||
// Establece la ruta del fichero de PostFX
|
// Establece la ruta del fichero de PostFX
|
||||||
void setPostFXFile(const std::string& path) { postfx_file_path = path; }
|
void setPostFXFile(const std::string& path) { postfx_file_path = path; }
|
||||||
|
|
||||||
|
// Establece la ruta del fichero de CrtPi
|
||||||
|
void setCrtPiFile(const std::string& path) { crtpi_file_path = path; }
|
||||||
|
|
||||||
// Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión
|
// Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión
|
||||||
static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) {
|
static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) {
|
||||||
if (node.contains(key)) {
|
if (node.contains(key)) {
|
||||||
@@ -89,11 +93,21 @@ namespace Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!postfx_presets.empty()) {
|
if (!postfx_presets.empty()) {
|
||||||
current_postfx_preset = std::clamp(
|
// Resolver nombre → índice
|
||||||
current_postfx_preset, 0,
|
if (!video.shader.current_postfx_preset_name.empty()) {
|
||||||
|
for (int i = 0; i < static_cast<int>(postfx_presets.size()); ++i) {
|
||||||
|
if (postfx_presets[static_cast<size_t>(i)].name == video.shader.current_postfx_preset_name) {
|
||||||
|
video.shader.current_postfx_preset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
video.shader.current_postfx_preset = std::clamp(
|
||||||
|
video.shader.current_postfx_preset,
|
||||||
|
0,
|
||||||
static_cast<int>(postfx_presets.size()) - 1);
|
static_cast<int>(postfx_presets.size()) - 1);
|
||||||
} else {
|
} else {
|
||||||
current_postfx_preset = 0;
|
video.shader.current_postfx_preset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "PostFX file loaded: %zu preset(s)", postfx_presets.size());
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "PostFX file loaded: %zu preset(s)", postfx_presets.size());
|
||||||
@@ -130,11 +144,11 @@ namespace Options {
|
|||||||
file << "\n";
|
file << "\n";
|
||||||
file << "presets:\n";
|
file << "presets:\n";
|
||||||
file << " - name: \"CRT\"\n";
|
file << " - name: \"CRT\"\n";
|
||||||
file << " vignette: 0.6\n";
|
file << " vignette: 0.15\n";
|
||||||
file << " scanlines: 0.7\n";
|
file << " scanlines: 0.7\n";
|
||||||
file << " chroma: 0.15\n";
|
file << " chroma: 0.2\n";
|
||||||
file << " mask: 0.5\n";
|
file << " mask: 0.5\n";
|
||||||
file << " gamma: 0.5\n";
|
file << " gamma: 0.1\n";
|
||||||
file << " curvature: 0.0\n";
|
file << " curvature: 0.0\n";
|
||||||
file << " bleeding: 0.0\n";
|
file << " bleeding: 0.0\n";
|
||||||
file << " flicker: 0.0\n";
|
file << " flicker: 0.0\n";
|
||||||
@@ -147,7 +161,7 @@ namespace Options {
|
|||||||
file << " curvature: 0.0\n";
|
file << " curvature: 0.0\n";
|
||||||
file << " bleeding: 0.6\n";
|
file << " bleeding: 0.6\n";
|
||||||
file << " flicker: 0.0\n";
|
file << " flicker: 0.0\n";
|
||||||
file << " - name: \"CURVED\"\n";
|
file << " - name: \"Curved\"\n";
|
||||||
file << " vignette: 0.5\n";
|
file << " vignette: 0.5\n";
|
||||||
file << " scanlines: 0.6\n";
|
file << " scanlines: 0.6\n";
|
||||||
file << " chroma: 0.1\n";
|
file << " chroma: 0.1\n";
|
||||||
@@ -156,7 +170,7 @@ namespace Options {
|
|||||||
file << " curvature: 0.8\n";
|
file << " curvature: 0.8\n";
|
||||||
file << " bleeding: 0.0\n";
|
file << " bleeding: 0.0\n";
|
||||||
file << " flicker: 0.0\n";
|
file << " flicker: 0.0\n";
|
||||||
file << " - name: \"SCANLINES\"\n";
|
file << " - name: \"Scanlines\"\n";
|
||||||
file << " vignette: 0.0\n";
|
file << " vignette: 0.0\n";
|
||||||
file << " scanlines: 0.8\n";
|
file << " scanlines: 0.8\n";
|
||||||
file << " chroma: 0.0\n";
|
file << " chroma: 0.0\n";
|
||||||
@@ -165,7 +179,7 @@ namespace Options {
|
|||||||
file << " curvature: 0.0\n";
|
file << " curvature: 0.0\n";
|
||||||
file << " bleeding: 0.0\n";
|
file << " bleeding: 0.0\n";
|
||||||
file << " flicker: 0.0\n";
|
file << " flicker: 0.0\n";
|
||||||
file << " - name: \"SUBTLE\"\n";
|
file << " - name: \"Subtle\"\n";
|
||||||
file << " vignette: 0.3\n";
|
file << " vignette: 0.3\n";
|
||||||
file << " scanlines: 0.4\n";
|
file << " scanlines: 0.4\n";
|
||||||
file << " chroma: 0.05\n";
|
file << " chroma: 0.05\n";
|
||||||
@@ -174,13 +188,13 @@ namespace Options {
|
|||||||
file << " curvature: 0.0\n";
|
file << " curvature: 0.0\n";
|
||||||
file << " bleeding: 0.0\n";
|
file << " bleeding: 0.0\n";
|
||||||
file << " flicker: 0.0\n";
|
file << " flicker: 0.0\n";
|
||||||
file << " - name: \"CRT LIVE\"\n";
|
file << " - name: \"CRT Live\"\n";
|
||||||
file << " vignette: 0.5\n";
|
file << " vignette: 0.15\n";
|
||||||
file << " scanlines: 0.6\n";
|
file << " scanlines: 0.6\n";
|
||||||
file << " chroma: 0.3\n";
|
file << " chroma: 0.3\n";
|
||||||
file << " mask: 0.3\n";
|
file << " mask: 0.3\n";
|
||||||
file << " gamma: 0.4\n";
|
file << " gamma: 0.1\n";
|
||||||
file << " curvature: 0.3\n";
|
file << " curvature: 0.0\n";
|
||||||
file << " bleeding: 0.4\n";
|
file << " bleeding: 0.4\n";
|
||||||
file << " flicker: 0.8\n";
|
file << " flicker: 0.8\n";
|
||||||
|
|
||||||
@@ -190,17 +204,141 @@ namespace Options {
|
|||||||
|
|
||||||
// Cargar los presets recién escritos
|
// Cargar los presets recién escritos
|
||||||
postfx_presets.clear();
|
postfx_presets.clear();
|
||||||
postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F});
|
postfx_presets.push_back({"CRT", 0.15F, 0.7F, 0.2F, 0.5F, 0.1F, 0.0F, 0.0F, 0.0F});
|
||||||
postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F});
|
postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F});
|
||||||
postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F});
|
postfx_presets.push_back({"Curved", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F});
|
||||||
postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
|
postfx_presets.push_back({"Scanlines", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
|
||||||
postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F});
|
postfx_presets.push_back({"Subtle", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F});
|
||||||
postfx_presets.push_back({"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F});
|
postfx_presets.push_back({"CRT Live", 0.15F, 0.6F, 0.3F, 0.3F, 0.1F, 0.0F, 0.4F, 0.8F});
|
||||||
current_postfx_preset = 0;
|
video.shader.current_postfx_preset = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: extrae un campo bool de un nodo YAML si existe, ignorando errores
|
||||||
|
static void parseBoolField(const fkyaml::node& node, const std::string& key, bool& target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: extrae un campo int de un nodo YAML si existe, ignorando errores
|
||||||
|
static void parseIntField(const fkyaml::node& node, const std::string& key, int& target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rellena los presets CrtPi por defecto
|
||||||
|
static void populateDefaultCrtPiPresets() {
|
||||||
|
crtpi_presets.clear();
|
||||||
|
crtpi_presets.push_back({"Default", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false});
|
||||||
|
crtpi_presets.push_back({"Curved", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, true, false});
|
||||||
|
crtpi_presets.push_back({"Sharp", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, false, true, false, true});
|
||||||
|
crtpi_presets.push_back({"Minimal", 8.0F, 0.05F, 2.0F, 2.4F, 2.2F, 1.00F, 0.0F, 0.0F, 0, true, false, false, false, false});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escribe los presets CrtPi por defecto al fichero
|
||||||
|
static auto saveCrtPiDefaults() -> bool {
|
||||||
|
if (crtpi_file_path.empty()) { return false; }
|
||||||
|
std::ofstream file(crtpi_file_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened for writing", crtpi_file_path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file << "# Coffee Crisis Arcade Edition - CrtPi Shader Presets\n";
|
||||||
|
file << "# scanline_weight: gaussian adjustment (higher = narrower scanlines, default 6.0)\n";
|
||||||
|
file << "# scanline_gap_brightness: min brightness between scanlines (0.0-1.0, default 0.12)\n";
|
||||||
|
file << "# bloom_factor: brightness for bright areas (default 3.5)\n";
|
||||||
|
file << "# input_gamma: input gamma - linearization (default 2.4)\n";
|
||||||
|
file << "# output_gamma: output gamma - encoding (default 2.2)\n";
|
||||||
|
file << "# mask_brightness: sub-pixel brightness (default 0.80)\n";
|
||||||
|
file << "# curvature_x/y: barrel CRT distortion (0.0 = flat)\n";
|
||||||
|
file << "# mask_type: 0=none, 1=green/magenta, 2=RGB phosphor\n";
|
||||||
|
file << "# enable_scanlines/multisample/gamma/curvature/sharper: true/false\n";
|
||||||
|
file << "\npresets:\n";
|
||||||
|
file << " - name: \"Default\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: true\n enable_gamma: true\n enable_curvature: false\n enable_sharper: false\n";
|
||||||
|
file << " - name: \"Curved\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: true\n enable_gamma: true\n enable_curvature: true\n enable_sharper: false\n";
|
||||||
|
file << " - name: \"Sharp\"\n scanline_weight: 6.0\n scanline_gap_brightness: 0.12\n bloom_factor: 3.5\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 0.80\n curvature_x: 0.05\n curvature_y: 0.10\n mask_type: 2\n enable_scanlines: true\n enable_multisample: false\n enable_gamma: true\n enable_curvature: false\n enable_sharper: true\n";
|
||||||
|
file << " - name: \"Minimal\"\n scanline_weight: 8.0\n scanline_gap_brightness: 0.05\n bloom_factor: 2.0\n input_gamma: 2.4\n output_gamma: 2.2\n mask_brightness: 1.00\n curvature_x: 0.0\n curvature_y: 0.0\n mask_type: 0\n enable_scanlines: true\n enable_multisample: false\n enable_gamma: false\n enable_curvature: false\n enable_sharper: false\n";
|
||||||
|
file.close();
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file created with defaults: %s", crtpi_file_path.c_str());
|
||||||
|
populateDefaultCrtPiPresets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga los presets de CrtPi desde el fichero
|
||||||
|
auto loadCrtPiFromFile() -> bool {
|
||||||
|
crtpi_presets.clear();
|
||||||
|
|
||||||
|
std::ifstream file(crtpi_file_path);
|
||||||
|
if (!file.good()) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file not found, creating default: %s", crtpi_file_path.c_str());
|
||||||
|
return saveCrtPiDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(content);
|
||||||
|
|
||||||
|
if (yaml.contains("presets")) {
|
||||||
|
const auto& presets = yaml["presets"];
|
||||||
|
for (const auto& p : presets) {
|
||||||
|
CrtPiPreset preset;
|
||||||
|
if (p.contains("name")) {
|
||||||
|
preset.name = p["name"].get_value<std::string>();
|
||||||
|
}
|
||||||
|
parseFloatField(p, "scanline_weight", preset.scanline_weight);
|
||||||
|
parseFloatField(p, "scanline_gap_brightness", preset.scanline_gap_brightness);
|
||||||
|
parseFloatField(p, "bloom_factor", preset.bloom_factor);
|
||||||
|
parseFloatField(p, "input_gamma", preset.input_gamma);
|
||||||
|
parseFloatField(p, "output_gamma", preset.output_gamma);
|
||||||
|
parseFloatField(p, "mask_brightness", preset.mask_brightness);
|
||||||
|
parseFloatField(p, "curvature_x", preset.curvature_x);
|
||||||
|
parseFloatField(p, "curvature_y", preset.curvature_y);
|
||||||
|
parseIntField(p, "mask_type", preset.mask_type);
|
||||||
|
parseBoolField(p, "enable_scanlines", preset.enable_scanlines);
|
||||||
|
parseBoolField(p, "enable_multisample", preset.enable_multisample);
|
||||||
|
parseBoolField(p, "enable_gamma", preset.enable_gamma);
|
||||||
|
parseBoolField(p, "enable_curvature", preset.enable_curvature);
|
||||||
|
parseBoolField(p, "enable_sharper", preset.enable_sharper);
|
||||||
|
crtpi_presets.push_back(preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!crtpi_presets.empty()) {
|
||||||
|
// Resolver nombre → índice
|
||||||
|
if (!video.shader.current_crtpi_preset_name.empty()) {
|
||||||
|
for (int i = 0; i < static_cast<int>(crtpi_presets.size()); ++i) {
|
||||||
|
if (crtpi_presets[static_cast<size_t>(i)].name == video.shader.current_crtpi_preset_name) {
|
||||||
|
video.shader.current_crtpi_preset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
video.shader.current_crtpi_preset = std::clamp(
|
||||||
|
video.shader.current_crtpi_preset,
|
||||||
|
0,
|
||||||
|
static_cast<int>(crtpi_presets.size()) - 1);
|
||||||
|
} else {
|
||||||
|
video.shader.current_crtpi_preset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CrtPi file loaded: %zu preset(s)", crtpi_presets.size());
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const fkyaml::exception& e) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing CrtPi YAML: %s. Recreating defaults.", e.what());
|
||||||
|
return saveCrtPiDefaults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Inicializa las opciones del programa
|
// Inicializa las opciones del programa
|
||||||
void init() {
|
void init() {
|
||||||
// Dificultades
|
// Dificultades
|
||||||
@@ -234,84 +372,126 @@ namespace Options {
|
|||||||
const auto& vid = yaml["video"];
|
const auto& vid = yaml["video"];
|
||||||
|
|
||||||
if (vid.contains("fullscreen")) {
|
if (vid.contains("fullscreen")) {
|
||||||
try { video.fullscreen = vid["fullscreen"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
video.fullscreen = vid["fullscreen"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("scale_mode")) {
|
if (vid.contains("scale_mode")) {
|
||||||
try { video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>()); } catch (...) {}
|
try {
|
||||||
|
video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>());
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("vsync")) {
|
if (vid.contains("vsync")) {
|
||||||
try { video.vsync = vid["vsync"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
video.vsync = vid["vsync"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("integer_scale")) {
|
if (vid.contains("integer_scale")) {
|
||||||
try { video.integer_scale = vid["integer_scale"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
video.integer_scale = vid["integer_scale"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("postfx")) {
|
|
||||||
try { video.postfx = vid["postfx"].get_value<bool>(); } catch (...) {}
|
// --- GPU ---
|
||||||
|
if (vid.contains("gpu")) {
|
||||||
|
const auto& gpu_node = vid["gpu"];
|
||||||
|
if (gpu_node.contains("acceleration")) {
|
||||||
|
try {
|
||||||
|
video.gpu.acceleration = gpu_node["acceleration"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
// Nuevo formato: supersampling (bool) + supersampling_amount (int)
|
if (gpu_node.contains("preferred_driver")) {
|
||||||
// Backward compat: si solo existe supersampling como int, también funciona
|
try {
|
||||||
{
|
video.gpu.preferred_driver = gpu_node["preferred_driver"].get_value<std::string>();
|
||||||
bool ss_enabled = false;
|
} catch (...) {}
|
||||||
int ss_amount = 3;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Shader config ---
|
||||||
|
if (vid.contains("shader")) {
|
||||||
|
const auto& sh = vid["shader"];
|
||||||
|
if (sh.contains("enabled")) {
|
||||||
|
try {
|
||||||
|
video.shader.enabled = sh["enabled"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (sh.contains("current_shader")) {
|
||||||
|
try {
|
||||||
|
auto s = sh["current_shader"].get_value<std::string>();
|
||||||
|
video.shader.current_shader = (s == "crtpi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (sh.contains("postfx_preset")) {
|
||||||
|
try {
|
||||||
|
video.shader.current_postfx_preset_name = sh["postfx_preset"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (sh.contains("crtpi_preset")) {
|
||||||
|
try {
|
||||||
|
video.shader.current_crtpi_preset_name = sh["crtpi_preset"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Supersampling ---
|
||||||
if (vid.contains("supersampling")) {
|
if (vid.contains("supersampling")) {
|
||||||
|
const auto& ss_node = vid["supersampling"];
|
||||||
|
if (ss_node.contains("enabled")) {
|
||||||
try {
|
try {
|
||||||
const auto& node = vid["supersampling"];
|
video.supersampling.enabled = ss_node["enabled"].get_value<bool>();
|
||||||
if (node.is_boolean()) {
|
|
||||||
ss_enabled = node.get_value<bool>();
|
|
||||||
} else {
|
|
||||||
// Formato antiguo: int directamente
|
|
||||||
int factor = node.get_value<int>();
|
|
||||||
ss_enabled = factor >= 2;
|
|
||||||
ss_amount = (factor >= 2) ? factor : 3;
|
|
||||||
}
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (vid.contains("supersampling_amount")) {
|
if (ss_node.contains("linear_upscale")) {
|
||||||
try {
|
try {
|
||||||
int amount = vid["supersampling_amount"].get_value<int>();
|
video.supersampling.linear_upscale = ss_node["linear_upscale"].get_value<bool>();
|
||||||
if (amount >= 2) { ss_amount = amount; }
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
video.supersampling = ss_enabled ? ss_amount : 1;
|
if (ss_node.contains("downscale_algo")) {
|
||||||
}
|
|
||||||
if (vid.contains("postfx_preset")) {
|
|
||||||
try {
|
try {
|
||||||
int preset = vid["postfx_preset"].get_value<int>();
|
video.supersampling.downscale_algo = ss_node["downscale_algo"].get_value<int>();
|
||||||
// No validamos contra postfx_presets.size() aquí porque postfx.yaml
|
|
||||||
// aún no se ha cargado. El clamp se hace en loadPostFXFromFile().
|
|
||||||
if (preset >= 0) {
|
|
||||||
current_postfx_preset = preset;
|
|
||||||
}
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void loadAudioFromYaml(const fkyaml::node& yaml) {
|
void loadAudioFromYaml(const fkyaml::node& yaml) {
|
||||||
if (!yaml.contains("audio")) { return; }
|
if (!yaml.contains("audio")) { return; }
|
||||||
const auto& aud = yaml["audio"];
|
const auto& aud = yaml["audio"];
|
||||||
|
|
||||||
if (aud.contains("enabled")) {
|
if (aud.contains("enabled")) {
|
||||||
try { audio.enabled = aud["enabled"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
audio.enabled = aud["enabled"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (aud.contains("volume")) {
|
if (aud.contains("volume")) {
|
||||||
try { audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
try {
|
||||||
|
audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (aud.contains("music")) {
|
if (aud.contains("music")) {
|
||||||
const auto& mus = aud["music"];
|
const auto& mus = aud["music"];
|
||||||
if (mus.contains("enabled")) {
|
if (mus.contains("enabled")) {
|
||||||
try { audio.music.enabled = mus["enabled"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
audio.music.enabled = mus["enabled"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (mus.contains("volume")) {
|
if (mus.contains("volume")) {
|
||||||
try { audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
try {
|
||||||
|
audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aud.contains("sound")) {
|
if (aud.contains("sound")) {
|
||||||
const auto& snd = aud["sound"];
|
const auto& snd = aud["sound"];
|
||||||
if (snd.contains("enabled")) {
|
if (snd.contains("enabled")) {
|
||||||
try { audio.sound.enabled = snd["enabled"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
audio.sound.enabled = snd["enabled"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (snd.contains("volume")) {
|
if (snd.contains("volume")) {
|
||||||
try { audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100); } catch (...) {}
|
try {
|
||||||
|
audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,13 +518,19 @@ namespace Options {
|
|||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (game.contains("autofire")) {
|
if (game.contains("autofire")) {
|
||||||
try { settings.autofire = game["autofire"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
settings.autofire = game["autofire"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (game.contains("shutdown_enabled")) {
|
if (game.contains("shutdown_enabled")) {
|
||||||
try { settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>(); } catch (...) {}
|
try {
|
||||||
|
settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (game.contains("params_file")) {
|
if (game.contains("params_file")) {
|
||||||
try { settings.params_file = game["params_file"].get_value<std::string>(); } catch (...) {}
|
try {
|
||||||
|
settings.params_file = game["params_file"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,10 +542,14 @@ namespace Options {
|
|||||||
for (const auto& ctrl : controllers) {
|
for (const auto& ctrl : controllers) {
|
||||||
if (i >= GamepadManager::size()) { break; }
|
if (i >= GamepadManager::size()) { break; }
|
||||||
if (ctrl.contains("name")) {
|
if (ctrl.contains("name")) {
|
||||||
try { gamepad_manager[i].name = ctrl["name"].get_value<std::string>(); } catch (...) {}
|
try {
|
||||||
|
gamepad_manager[i].name = ctrl["name"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (ctrl.contains("path")) {
|
if (ctrl.contains("path")) {
|
||||||
try { gamepad_manager[i].path = ctrl["path"].get_value<std::string>(); } catch (...) {}
|
try {
|
||||||
|
gamepad_manager[i].path = ctrl["path"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
if (ctrl.contains("player")) {
|
if (ctrl.contains("player")) {
|
||||||
try {
|
try {
|
||||||
@@ -379,7 +569,9 @@ namespace Options {
|
|||||||
if (!yaml.contains("keyboard")) { return; }
|
if (!yaml.contains("keyboard")) { return; }
|
||||||
const auto& kb = yaml["keyboard"];
|
const auto& kb = yaml["keyboard"];
|
||||||
if (kb.contains("player")) {
|
if (kb.contains("player")) {
|
||||||
try { keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>()); } catch (...) {}
|
try {
|
||||||
|
keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>());
|
||||||
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,6 +593,20 @@ namespace Options {
|
|||||||
try {
|
try {
|
||||||
auto yaml = fkyaml::node::deserialize(content);
|
auto yaml = fkyaml::node::deserialize(content);
|
||||||
|
|
||||||
|
// Comprobar versión: si no coincide, regenerar config por defecto
|
||||||
|
int file_version = 0;
|
||||||
|
if (yaml.contains("version")) {
|
||||||
|
try {
|
||||||
|
file_version = yaml["version"].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (file_version != Settings::CURRENT_CONFIG_VERSION) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Config version %d != expected %d. Recreating defaults.", file_version, Settings::CURRENT_CONFIG_VERSION);
|
||||||
|
init();
|
||||||
|
saveToFile();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
loadWindowFromYaml(yaml);
|
loadWindowFromYaml(yaml);
|
||||||
loadVideoFromYaml(yaml);
|
loadVideoFromYaml(yaml);
|
||||||
loadAudioFromYaml(yaml);
|
loadAudioFromYaml(yaml);
|
||||||
@@ -452,10 +658,26 @@ namespace Options {
|
|||||||
file << " scale_mode: " << static_cast<int>(video.scale_mode) << " # " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": linear\n";
|
file << " scale_mode: " << static_cast<int>(video.scale_mode) << " # " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": linear\n";
|
||||||
file << " vsync: " << boolToString(video.vsync) << "\n";
|
file << " vsync: " << boolToString(video.vsync) << "\n";
|
||||||
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
||||||
file << " postfx: " << boolToString(video.postfx) << "\n";
|
file << " gpu:\n";
|
||||||
file << " postfx_preset: " << current_postfx_preset << "\n";
|
file << " acceleration: " << boolToString(video.gpu.acceleration) << "\n";
|
||||||
file << " supersampling: " << boolToString(video.supersampling > 1) << "\n";
|
file << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\n";
|
||||||
file << " supersampling_amount: " << std::max(2, video.supersampling) << "\n";
|
file << " shader:\n";
|
||||||
|
file << " enabled: " << boolToString(video.shader.enabled) << "\n";
|
||||||
|
file << " current_shader: " << (video.shader.current_shader == Rendering::ShaderType::CRTPI ? "crtpi" : "postfx") << "\n";
|
||||||
|
{
|
||||||
|
std::string postfx_name = (!postfx_presets.empty() && video.shader.current_postfx_preset < static_cast<int>(postfx_presets.size()))
|
||||||
|
? postfx_presets[static_cast<size_t>(video.shader.current_postfx_preset)].name
|
||||||
|
: "";
|
||||||
|
std::string crtpi_name = (!crtpi_presets.empty() && video.shader.current_crtpi_preset < static_cast<int>(crtpi_presets.size()))
|
||||||
|
? crtpi_presets[static_cast<size_t>(video.shader.current_crtpi_preset)].name
|
||||||
|
: "";
|
||||||
|
file << " postfx_preset: \"" << postfx_name << "\"\n";
|
||||||
|
file << " crtpi_preset: \"" << crtpi_name << "\"\n";
|
||||||
|
}
|
||||||
|
file << " supersampling:\n";
|
||||||
|
file << " enabled: " << boolToString(video.supersampling.enabled) << "\n";
|
||||||
|
file << " linear_upscale: " << boolToString(video.supersampling.linear_upscale) << "\n";
|
||||||
|
file << " downscale_algo: " << video.supersampling.downscale_algo << "\n";
|
||||||
file << "\n";
|
file << "\n";
|
||||||
|
|
||||||
// AUDIO
|
// AUDIO
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "lang.hpp" // for Code
|
#include "lang.hpp" // for Code
|
||||||
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
#include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table
|
||||||
#include "player.hpp" // for Player
|
#include "player.hpp" // for Player
|
||||||
|
#include "rendering/shader_backend.hpp" // for Rendering::ShaderType
|
||||||
|
|
||||||
// --- Namespace Options: gestión de configuración y opciones del juego ---
|
// --- Namespace Options: gestión de configuración y opciones del juego ---
|
||||||
namespace Options {
|
namespace Options {
|
||||||
@@ -36,20 +37,59 @@ namespace Options {
|
|||||||
float flicker{0.0F};
|
float flicker{0.0F};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CrtPiPreset {
|
||||||
|
std::string name;
|
||||||
|
float scanline_weight{6.0F};
|
||||||
|
float scanline_gap_brightness{0.12F};
|
||||||
|
float bloom_factor{3.5F};
|
||||||
|
float input_gamma{2.4F};
|
||||||
|
float output_gamma{2.2F};
|
||||||
|
float mask_brightness{0.80F};
|
||||||
|
float curvature_x{0.05F};
|
||||||
|
float curvature_y{0.10F};
|
||||||
|
int mask_type{2};
|
||||||
|
bool enable_scanlines{true};
|
||||||
|
bool enable_multisample{true};
|
||||||
|
bool enable_gamma{true};
|
||||||
|
bool enable_curvature{false};
|
||||||
|
bool enable_sharper{false};
|
||||||
|
};
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
std::string caption = Defaults::Window::CAPTION; // Texto que aparece en la barra de título de la ventana
|
std::string caption = Defaults::Window::CAPTION;
|
||||||
int zoom = Defaults::Window::ZOOM; // Valor por el que se multiplica el tamaño de la ventana
|
int zoom = Defaults::Window::ZOOM;
|
||||||
int max_zoom = Defaults::Window::MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla
|
int max_zoom = Defaults::Window::MAX_ZOOM;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPU {
|
||||||
|
bool acceleration{Defaults::Video::GPU_ACCELERATION};
|
||||||
|
std::string preferred_driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Supersampling {
|
||||||
|
bool enabled{Defaults::Video::SUPERSAMPLING};
|
||||||
|
bool linear_upscale{Defaults::Video::LINEAR_UPSCALE};
|
||||||
|
int downscale_algo{Defaults::Video::DOWNSCALE_ALGO};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderConfig {
|
||||||
|
bool enabled{Defaults::Video::SHADER_ENABLED};
|
||||||
|
Rendering::ShaderType current_shader{Rendering::ShaderType::POSTFX};
|
||||||
|
std::string current_postfx_preset_name;
|
||||||
|
std::string current_crtpi_preset_name;
|
||||||
|
int current_postfx_preset{0};
|
||||||
|
int current_crtpi_preset{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Video {
|
struct Video {
|
||||||
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE; // Filtro usado para el escalado de la imagen
|
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE;
|
||||||
bool fullscreen = Defaults::Video::FULLSCREEN; // Indica si se usa pantalla completa
|
bool fullscreen = Defaults::Video::FULLSCREEN;
|
||||||
bool vsync = Defaults::Video::VSYNC; // Indica si se usa vsync
|
bool vsync = Defaults::Video::VSYNC;
|
||||||
bool integer_scale = Defaults::Video::INTEGER_SCALE; // Indica si se usa escalado entero
|
bool integer_scale = Defaults::Video::INTEGER_SCALE;
|
||||||
bool postfx = Defaults::Video::POSTFX; // Indica si se usan efectos PostFX
|
std::string info;
|
||||||
int supersampling = Defaults::Video::SUPERSAMPLING; // Factor de supersampling: 1=off, 2=2×, 3=3×
|
GPU gpu{};
|
||||||
std::string info; // Información sobre el modo de vídeo
|
Supersampling supersampling{};
|
||||||
|
ShaderConfig shader{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Music {
|
struct Music {
|
||||||
@@ -70,7 +110,8 @@ namespace Options {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
int config_version = 2; // Versión del archivo de configuración
|
static constexpr int CURRENT_CONFIG_VERSION = 3; // Versión esperada del fichero
|
||||||
|
int config_version = CURRENT_CONFIG_VERSION; // Versión del archivo de configuración
|
||||||
Difficulty::Code difficulty = Difficulty::Code::NORMAL; // Dificultad del juego
|
Difficulty::Code difficulty = Difficulty::Code::NORMAL; // Dificultad del juego
|
||||||
Lang::Code language = Lang::Code::VALENCIAN; // Idioma usado en el juego
|
Lang::Code language = Lang::Code::VALENCIAN; // Idioma usado en el juego
|
||||||
bool autofire = Defaults::Settings::AUTOFIRE; // Indicador de autofire
|
bool autofire = Defaults::Settings::AUTOFIRE; // Indicador de autofire
|
||||||
@@ -292,16 +333,19 @@ namespace Options {
|
|||||||
extern Keyboard keyboard; // Opciones para el teclado
|
extern Keyboard keyboard; // Opciones para el teclado
|
||||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||||
extern std::vector<PostFXPreset> postfx_presets; // Lista de presets de PostFX
|
extern std::vector<PostFXPreset> postfx_presets; // Lista de presets de PostFX
|
||||||
extern int current_postfx_preset; // Índice del preset PostFX activo
|
|
||||||
extern std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
extern std::string postfx_file_path; // Ruta al fichero de presets PostFX
|
||||||
|
extern std::vector<CrtPiPreset> crtpi_presets; // Lista de presets de CrtPi
|
||||||
|
extern std::string crtpi_file_path; // Ruta al fichero de presets CrtPi
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
void init(); // Inicializa las opciones del programa
|
void init(); // Inicializa las opciones del programa
|
||||||
void setConfigFile(const std::string& file_path); // Establece el fichero de configuración
|
void setConfigFile(const std::string& file_path); // Establece el fichero de configuración
|
||||||
void setControllersFile(const std::string& file_path); // Establece el fichero de configuración de mandos
|
void setControllersFile(const std::string& file_path); // Establece el fichero de configuración de mandos
|
||||||
void setPostFXFile(const std::string& path); // Establece el fichero de presets PostFX
|
void setPostFXFile(const std::string& path); // Establece el fichero de presets PostFX
|
||||||
|
void setCrtPiFile(const std::string& path); // Establece el fichero de presets CrtPi
|
||||||
auto loadPostFXFromFile() -> bool; // Carga los presets PostFX desde fichero
|
auto loadPostFXFromFile() -> bool; // Carga los presets PostFX desde fichero
|
||||||
auto savePostFXToFile() -> bool; // Guarda los presets PostFX por defecto al fichero
|
auto savePostFXToFile() -> bool; // Guarda los presets PostFX por defecto al fichero
|
||||||
|
auto loadCrtPiFromFile() -> bool; // Carga los presets CrtPi desde fichero
|
||||||
auto loadFromFile() -> bool; // Carga el fichero de configuración
|
auto loadFromFile() -> bool; // Carga el fichero de configuración
|
||||||
auto saveToFile() -> bool; // Guarda el fichero de configuración
|
auto saveToFile() -> bool; // Guarda el fichero de configuración
|
||||||
void setKeyboardToPlayer(Player::Id player_id); // Asigna el teclado al jugador
|
void setKeyboardToPlayer(Player::Id player_id); // Asigna el teclado al jugador
|
||||||
|
|||||||
10361
source/rendering/sdl3gpu/crtpi_frag_spv.h
Normal file
10361
source/rendering/sdl3gpu/crtpi_frag_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
4253
source/rendering/sdl3gpu/downscale_frag_spv.h
Normal file
4253
source/rendering/sdl3gpu/downscale_frag_spv.h
Normal file
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
@@ -3,24 +3,56 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_gpu.h>
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "rendering/shader_backend.hpp"
|
#include "rendering/shader_backend.hpp"
|
||||||
|
|
||||||
// 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.
|
|
||||||
// 12 floats = 48 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;
|
||||||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
float chroma_strength;
|
||||||
float scanline_strength; // 0 = off, 1 = full
|
float scanline_strength;
|
||||||
float screen_height; // logical height in pixels (used by bleeding effect)
|
float screen_height;
|
||||||
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
float mask_strength;
|
||||||
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
float gamma_strength;
|
||||||
float curvature; // 0 = flat, 1 = max barrel distortion
|
float curvature;
|
||||||
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
float bleeding;
|
||||||
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
float pixel_scale;
|
||||||
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
float time;
|
||||||
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
float oversample;
|
||||||
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
|
float flicker;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CrtPi uniforms pushed to fragment stage each frame.
|
||||||
|
// 16 fields = 64 bytes — 4 × 16-byte alignment.
|
||||||
|
struct CrtPiUniforms {
|
||||||
|
float scanline_weight;
|
||||||
|
float scanline_gap_brightness;
|
||||||
|
float bloom_factor;
|
||||||
|
float input_gamma;
|
||||||
|
float output_gamma;
|
||||||
|
float mask_brightness;
|
||||||
|
float curvature_x;
|
||||||
|
float curvature_y;
|
||||||
|
int mask_type;
|
||||||
|
int enable_scanlines;
|
||||||
|
int enable_multisample;
|
||||||
|
int enable_gamma;
|
||||||
|
int enable_curvature;
|
||||||
|
int enable_sharper;
|
||||||
|
float texture_width;
|
||||||
|
float texture_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Downscale uniforms for Lanczos downscale fragment stage.
|
||||||
|
// 1 int + 3 floats = 16 bytes.
|
||||||
|
struct DownscaleUniforms {
|
||||||
|
int algorithm;
|
||||||
|
float pad0;
|
||||||
|
float pad1;
|
||||||
|
float pad2;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
@@ -28,9 +60,8 @@ namespace Rendering {
|
|||||||
/**
|
/**
|
||||||
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
||||||
*
|
*
|
||||||
* Backend de shaders PostFX para macOS (Metal) y Win/Linux (Vulkan/SPIR-V).
|
|
||||||
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
||||||
* → PostFX render pass → swapchain → present
|
* → [Upscale →] PostFX/CrtPi render pass → [Lanczos downscale →] swapchain → present
|
||||||
*/
|
*/
|
||||||
class SDL3GPUShader : public ShaderBackend {
|
class SDL3GPUShader : public ShaderBackend {
|
||||||
public:
|
public:
|
||||||
@@ -44,25 +75,28 @@ namespace Rendering {
|
|||||||
|
|
||||||
void render() override;
|
void render() override;
|
||||||
void setTextureSize(float width, float height) override {}
|
void setTextureSize(float width, float height) override {}
|
||||||
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
|
void cleanup() final;
|
||||||
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
|
void destroy();
|
||||||
[[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_; }
|
||||||
|
|
||||||
// Sube píxeles ARGB8888 desde CPU; llamado antes de render()
|
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
|
||||||
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
||||||
|
|
||||||
// Actualiza los parámetros de intensidad de los efectos PostFX
|
|
||||||
void setPostFXParams(const PostFXParams& p) override;
|
void setPostFXParams(const PostFXParams& p) override;
|
||||||
|
|
||||||
// Activa/desactiva VSync en el swapchain
|
|
||||||
void setVSync(bool vsync) override;
|
void setVSync(bool vsync) override;
|
||||||
|
|
||||||
// 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;
|
void setOversample(int factor) override;
|
||||||
|
|
||||||
|
void setLinearUpscale(bool linear) override;
|
||||||
|
[[nodiscard]] auto isLinearUpscale() const -> bool override { return linear_upscale_; }
|
||||||
|
void setDownscaleAlgo(int algo) override;
|
||||||
|
[[nodiscard]] auto getDownscaleAlgo() const -> int override { return downscale_algo_; }
|
||||||
|
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
|
||||||
|
|
||||||
|
void setActiveShader(ShaderType type) override;
|
||||||
|
void setCrtPiParams(const CrtPiParams& p) override;
|
||||||
|
[[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,
|
||||||
@@ -80,27 +114,41 @@ namespace Rendering {
|
|||||||
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||||||
|
|
||||||
auto createPipeline() -> bool;
|
auto createPipeline() -> bool;
|
||||||
auto reinitTexturesAndBuffer() -> bool; // Recrea textura y buffer con oversample actual
|
auto createCrtPiPipeline() -> bool;
|
||||||
|
auto reinitTexturesAndBuffer() -> bool;
|
||||||
|
auto recreateScaledTexture(int factor) -> bool;
|
||||||
|
static auto calcSsFactor(float zoom) -> int;
|
||||||
|
[[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;
|
||||||
|
SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr;
|
||||||
|
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr;
|
||||||
|
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr;
|
||||||
|
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr;
|
||||||
SDL_GPUTexture* scene_texture_ = nullptr;
|
SDL_GPUTexture* scene_texture_ = nullptr;
|
||||||
|
SDL_GPUTexture* scaled_texture_ = nullptr;
|
||||||
|
SDL_GPUTexture* postfx_texture_ = nullptr;
|
||||||
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||||||
SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling
|
SDL_GPUSampler* sampler_ = nullptr;
|
||||||
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling
|
SDL_GPUSampler* linear_sampler_ = nullptr;
|
||||||
|
|
||||||
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};
|
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;
|
||||||
|
|
||||||
int game_width_ = 0; // Dimensiones originales del canvas (sin SS)
|
int game_width_ = 0;
|
||||||
int game_height_ = 0;
|
int game_height_ = 0;
|
||||||
int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_)
|
int ss_factor_ = 0;
|
||||||
int tex_height_ = 0;
|
int oversample_ = 1;
|
||||||
int oversample_ = 1; // Factor SS actual (1 o 3)
|
int downscale_algo_ = 1;
|
||||||
float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU
|
std::string driver_name_;
|
||||||
|
std::string preferred_driver_;
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
633
source/rendering/sdl3gpu/upscale_frag_spv.h
Normal file
633
source/rendering/sdl3gpu/upscale_frag_spv.h
Normal 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;
|
||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
@@ -20,57 +25,64 @@ namespace Rendering {
|
|||||||
float flicker = 0.0F;
|
float flicker = 0.0F;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
|
||||||
|
*/
|
||||||
|
struct CrtPiParams {
|
||||||
|
float scanline_weight{6.0F};
|
||||||
|
float scanline_gap_brightness{0.12F};
|
||||||
|
float bloom_factor{3.5F};
|
||||||
|
float input_gamma{2.4F};
|
||||||
|
float output_gamma{2.2F};
|
||||||
|
float mask_brightness{0.80F};
|
||||||
|
float curvature_x{0.05F};
|
||||||
|
float curvature_y{0.10F};
|
||||||
|
int mask_type{2};
|
||||||
|
bool enable_scanlines{true};
|
||||||
|
bool enable_multisample{true};
|
||||||
|
bool enable_gamma{true};
|
||||||
|
bool enable_curvature{false};
|
||||||
|
bool enable_sharper{false};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Interfaz abstracta para backends de renderizado con shaders
|
* @brief Interfaz abstracta para backends de renderizado con shaders
|
||||||
*
|
|
||||||
* Esta interfaz define el contrato que todos los backends de shaders
|
|
||||||
* deben cumplir (Metal, Vulkan, etc.)
|
|
||||||
*/
|
*/
|
||||||
class ShaderBackend {
|
class ShaderBackend {
|
||||||
public:
|
public:
|
||||||
virtual ~ShaderBackend() = default;
|
virtual ~ShaderBackend() = default;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Inicializa el backend de shaders
|
|
||||||
* @param window Ventana SDL
|
|
||||||
* @param texture Textura de backbuffer a la que aplicar shaders
|
|
||||||
* @param vertex_source Código fuente del vertex shader
|
|
||||||
* @param fragment_source Código fuente del fragment shader
|
|
||||||
* @return true si la inicialización fue exitosa
|
|
||||||
*/
|
|
||||||
virtual auto init(SDL_Window* window,
|
virtual auto init(SDL_Window* window,
|
||||||
SDL_Texture* texture,
|
SDL_Texture* texture,
|
||||||
const std::string& vertex_source,
|
const std::string& vertex_source,
|
||||||
const std::string& fragment_source) -> bool = 0;
|
const std::string& fragment_source) -> bool = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Renderiza la textura con los shaders aplicados
|
|
||||||
*/
|
|
||||||
virtual void render() = 0;
|
virtual void render() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Establece el tamaño de la textura como parámetro del shader
|
|
||||||
* @param width Ancho de la textura
|
|
||||||
* @param height Alto de la textura
|
|
||||||
*/
|
|
||||||
virtual void setTextureSize(float width, float height) = 0;
|
virtual void setTextureSize(float width, float height) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Limpia y libera recursos del backend
|
|
||||||
*/
|
|
||||||
virtual void cleanup() = 0;
|
virtual void cleanup() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Verifica si el backend está usando aceleración por hardware
|
|
||||||
* @return true si usa aceleración por hardware
|
|
||||||
*/
|
|
||||||
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
|
|
||||||
|
|
||||||
virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}
|
virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}
|
||||||
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
||||||
virtual void setVSync(bool /*vsync*/) {}
|
virtual void setVSync(bool /*vsync*/) {}
|
||||||
virtual void setScaleMode(bool /*integer_scale*/) {}
|
virtual void setScaleMode(bool /*integer_scale*/) {}
|
||||||
virtual void setOversample(int /*factor*/) {}
|
virtual void setOversample(int /*factor*/) {}
|
||||||
|
|
||||||
|
virtual void setLinearUpscale(bool /*linear*/) {}
|
||||||
|
[[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }
|
||||||
|
|
||||||
|
virtual void setDownscaleAlgo(int /*algo*/) {}
|
||||||
|
[[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }
|
||||||
|
virtual void setPreferredDriver(const std::string& /*driver*/) {}
|
||||||
|
|
||||||
|
virtual void setActiveShader(ShaderType /*type*/) {}
|
||||||
|
virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}
|
||||||
|
[[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
@@ -9,15 +9,18 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "asset.hpp" // Para Asset
|
||||||
|
#include "director.hpp" // Para Director::debug_config
|
||||||
#include "mouse.hpp" // Para updateCursorVisibility
|
#include "mouse.hpp" // Para updateCursorVisibility
|
||||||
#include "options.hpp" // Para Video, video, Window, window
|
#include "options.hpp" // Para Video, video, Window, window
|
||||||
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
|
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||||
#include "rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
#include "rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||||
|
#include "resource.hpp" // Para Resource
|
||||||
#include "text.hpp" // Para Text
|
#include "text.hpp" // Para Text
|
||||||
#include "texture.hpp" // Para Texture
|
#include "texture.hpp" // Para Texture
|
||||||
#include "ui/logger.hpp" // Para info
|
#include "ui/logger.hpp" // Para info
|
||||||
#include "ui/notifier.hpp" // Para Notifier
|
#include "ui/notifier.hpp" // Para Notifier
|
||||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
#include "ui/service_menu.hpp" // Para ServiceMenu
|
||||||
|
#include "utils.hpp" // Para toLower
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Screen* Screen::instance = nullptr;
|
Screen* Screen::instance = nullptr;
|
||||||
@@ -25,7 +28,7 @@ Screen* Screen::instance = nullptr;
|
|||||||
// Inicializa la instancia única del singleton
|
// Inicializa la instancia única del singleton
|
||||||
void Screen::init() {
|
void Screen::init() {
|
||||||
Screen::instance = new Screen();
|
Screen::instance = new Screen();
|
||||||
Screen::initPostFX(); // Llamar aquí para que Screen::get() ya devuelva la instancia
|
Screen::initShaders(); // Llamar aquí para que Screen::get() ya devuelva la instancia
|
||||||
}
|
}
|
||||||
|
|
||||||
// Libera la instancia
|
// Libera la instancia
|
||||||
@@ -58,7 +61,7 @@ Screen::Screen()
|
|||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
debug_info_.text = text_;
|
debug_info_.text = text_;
|
||||||
setDebugInfoEnabled(true);
|
setDebugInfoEnabled(Director::debug_config.show_render_info);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Renderizar una vez la textura vacía para que tenga contenido válido antes de inicializar los shaders (evita pantalla negra)
|
// Renderizar una vez la textura vacía para que tenga contenido válido antes de inicializar los shaders (evita pantalla negra)
|
||||||
@@ -68,7 +71,6 @@ Screen::Screen()
|
|||||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||||
SDL_RenderClear(renderer_);
|
SDL_RenderClear(renderer_);
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -112,13 +114,11 @@ void Screen::renderPresent() {
|
|||||||
SDL_Surface* surface = SDL_RenderReadPixels(renderer_, nullptr);
|
SDL_Surface* surface = SDL_RenderReadPixels(renderer_, nullptr);
|
||||||
if (surface != nullptr) {
|
if (surface != nullptr) {
|
||||||
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
||||||
std::memcpy(pixel_buffer_.data(), surface->pixels,
|
std::memcpy(pixel_buffer_.data(), surface->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
pixel_buffer_.size() * sizeof(Uint32));
|
|
||||||
} else {
|
} else {
|
||||||
SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
SDL_Surface* converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
||||||
if (converted != nullptr) {
|
if (converted != nullptr) {
|
||||||
std::memcpy(pixel_buffer_.data(), converted->pixels,
|
std::memcpy(pixel_buffer_.data(), converted->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
pixel_buffer_.size() * sizeof(Uint32));
|
|
||||||
SDL_DestroySurface(converted);
|
SDL_DestroySurface(converted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,39 +243,83 @@ void Screen::renderShake() {
|
|||||||
// Muestra información por pantalla
|
// Muestra información por pantalla
|
||||||
void Screen::renderInfo() const {
|
void Screen::renderInfo() const {
|
||||||
if (debug_info_.show) {
|
if (debug_info_.show) {
|
||||||
// Resolution
|
const Color GOLD(0xFF, 0xD7, 0x00);
|
||||||
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, param.game.width - debug_info_.text->length(Options::video.info) - 2, 1, Options::video.info, 1, param.debug.color, 1, param.debug.color.DARKEN(150));
|
const Color GOLD_SHADOW = GOLD.DARKEN(150);
|
||||||
|
|
||||||
|
// Construir texto: fps - driver - preset
|
||||||
|
std::string info_text = std::to_string(fps_.last_value) + " fps";
|
||||||
|
|
||||||
|
// Driver GPU
|
||||||
|
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
|
||||||
|
const std::string DRIVER = shader_backend_->getDriverName();
|
||||||
|
if (!DRIVER.empty()) {
|
||||||
|
info_text += " - " + toLower(DRIVER);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info_text += " - sdl";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shader + preset
|
||||||
|
if (Options::video.shader.enabled) {
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
const std::string PRESET_NAME = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
|
||||||
|
info_text += " - crtpi " + toLower(PRESET_NAME);
|
||||||
|
} else {
|
||||||
|
const std::string PRESET_NAME = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name;
|
||||||
|
info_text += " - postfx " + toLower(PRESET_NAME);
|
||||||
|
if (Options::video.supersampling.enabled) { info_text += " (ss)"; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Centrado arriba
|
||||||
|
const int TEXT_WIDTH = debug_info_.text->length(info_text);
|
||||||
|
const int X_POS = (static_cast<int>(param.game.width) - TEXT_WIDTH) / 2;
|
||||||
|
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, X_POS, 1, info_text, 1, GOLD, 1, GOLD_SHADOW);
|
||||||
|
|
||||||
// FPS
|
|
||||||
const std::string FPS_TEXT = std::to_string(fps_.last_value) + " FPS";
|
|
||||||
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, param.game.width - debug_info_.text->length(FPS_TEXT) - 2, 1 + debug_info_.text->getCharacterSize(), FPS_TEXT, 1, param.debug.color, 1, param.debug.color.DARKEN(150));
|
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
// RECORDING
|
const std::string REC_TEXT = "recording";
|
||||||
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, param.game.width - debug_info_.text->length("RECORDING"), 2 * (1 + debug_info_.text->getCharacterSize()), "RECORDING", 1, param.debug.color, 1, param.debug.color.DARKEN(150));
|
const int REC_WIDTH = debug_info_.text->length(REC_TEXT);
|
||||||
|
const int REC_X = (static_cast<int>(param.game.width) - REC_WIDTH) / 2;
|
||||||
|
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, REC_X, 1 + debug_info_.text->getCharacterSize(), REC_TEXT, 1, GOLD, 1, GOLD_SHADOW);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Inicializa PostFX (SDL3GPU)
|
// Inicializa shaders (SDL3GPU)
|
||||||
void Screen::initPostFX() {
|
void Screen::initShaders() {
|
||||||
#ifndef NO_SHADERS
|
#ifndef NO_SHADERS
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self == nullptr) {
|
if (self == nullptr) {
|
||||||
SDL_Log("Screen::initPostFX: instance is null, skipping");
|
SDL_Log("Screen::initShaders: instance is null, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!self->shader_backend_) {
|
if (!self->shader_backend_) {
|
||||||
self->shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
self->shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
||||||
|
const std::string FALLBACK_DRIVER = "none";
|
||||||
|
self->shader_backend_->setPreferredDriver(
|
||||||
|
Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER);
|
||||||
}
|
}
|
||||||
if (!self->shader_backend_->isHardwareAccelerated()) {
|
if (!self->shader_backend_->isHardwareAccelerated()) {
|
||||||
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
||||||
SDL_Log("Screen::initPostFX: SDL3GPUShader::init() = %s", ok ? "OK" : "FAILED");
|
SDL_Log("Screen::initShaders: SDL3GPUShader::init() = %s", ok ? "OK" : "FAILED");
|
||||||
}
|
}
|
||||||
SDL_Log("Screen::initPostFX: presets=%d current=%d postfx=%s",
|
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||||
static_cast<int>(Options::postfx_presets.size()),
|
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
|
||||||
Options::current_postfx_preset,
|
self->shader_backend_->setDownscaleAlgo(Options::video.supersampling.downscale_algo);
|
||||||
Options::video.postfx ? "ON" : "OFF");
|
self->shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||||
|
|
||||||
|
if (!Options::video.shader.enabled) {
|
||||||
|
// Passthrough: POSTFX con parámetros a cero
|
||||||
|
self->shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
|
||||||
|
self->shader_backend_->setPostFXParams(Rendering::PostFXParams{});
|
||||||
|
} else if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
self->shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
} else {
|
||||||
|
self->shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
|
||||||
self->applyCurrentPostFXPreset();
|
self->applyCurrentPostFXPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,11 +482,34 @@ void Screen::getDisplayInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alterna entre activar y desactivar los efectos PostFX
|
// Alterna activar/desactivar shaders
|
||||||
void Screen::togglePostFX() {
|
void Screen::toggleShaders() {
|
||||||
Options::video.postfx = !Options::video.postfx;
|
Options::video.shader.enabled = !Options::video.shader.enabled;
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr) {
|
if (self != nullptr) {
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
} else {
|
||||||
|
self->applyCurrentPostFXPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cambia entre PostFX y CrtPi
|
||||||
|
void Screen::nextShader() {
|
||||||
|
auto* self = Screen::get();
|
||||||
|
if (self == nullptr || !self->shader_backend_ || !self->shader_backend_->isHardwareAccelerated()) { return; }
|
||||||
|
|
||||||
|
const auto NEXT = (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX)
|
||||||
|
? Rendering::ShaderType::CRTPI
|
||||||
|
: Rendering::ShaderType::POSTFX;
|
||||||
|
|
||||||
|
Options::video.shader.current_shader = NEXT;
|
||||||
|
self->shader_backend_->setActiveShader(NEXT);
|
||||||
|
|
||||||
|
if (NEXT == Rendering::ShaderType::CRTPI) {
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
} else {
|
||||||
self->applyCurrentPostFXPreset();
|
self->applyCurrentPostFXPreset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,31 +517,44 @@ void Screen::togglePostFX() {
|
|||||||
// Avanza al siguiente preset PostFX
|
// Avanza al siguiente preset PostFX
|
||||||
void Screen::nextPostFXPreset() {
|
void Screen::nextPostFXPreset() {
|
||||||
if (Options::postfx_presets.empty()) { return; }
|
if (Options::postfx_presets.empty()) { return; }
|
||||||
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
|
Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr) {
|
if (self != nullptr) {
|
||||||
self->applyCurrentPostFXPreset();
|
self->applyCurrentPostFXPreset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alterna entre activar y desactivar el supersampling 3x
|
// Avanza al siguiente preset CrtPi
|
||||||
|
void Screen::nextCrtPiPreset() {
|
||||||
|
if (Options::crtpi_presets.empty()) { return; }
|
||||||
|
Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
|
||||||
|
auto* self = Screen::get();
|
||||||
|
if (self != nullptr) {
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alterna supersampling
|
||||||
void Screen::toggleSupersampling() {
|
void Screen::toggleSupersampling() {
|
||||||
Options::video.supersampling = (Options::video.supersampling % 3) + 1;
|
Options::video.supersampling.enabled = !Options::video.supersampling.enabled;
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
if (self != nullptr && self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||||
|
self->shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
self->applyCurrentCrtPiPreset();
|
||||||
|
} else {
|
||||||
self->applyCurrentPostFXPreset();
|
self->applyCurrentPostFXPreset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Aplica el preset PostFX activo al backend
|
// Aplica el preset PostFX activo al backend
|
||||||
void Screen::applyCurrentPostFXPreset() {
|
void Screen::applyCurrentPostFXPreset() {
|
||||||
if (!shader_backend_) { return; }
|
if (!shader_backend_) { return; }
|
||||||
// setOversample PRIMERO: puede recrear texturas antes de que setPostFXParams
|
shader_backend_->setOversample(Options::video.supersampling.enabled ? 3 : 1);
|
||||||
// decida si hornear scanlines en CPU o aplicarlas en GPU.
|
|
||||||
shader_backend_->setOversample(Options::video.supersampling);
|
|
||||||
Rendering::PostFXParams p{};
|
Rendering::PostFXParams p{};
|
||||||
if (Options::video.postfx && !Options::postfx_presets.empty()) {
|
if (Options::video.shader.enabled && !Options::postfx_presets.empty()) {
|
||||||
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::current_postfx_preset));
|
const auto& preset = Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset));
|
||||||
p.vignette = preset.vignette;
|
p.vignette = preset.vignette;
|
||||||
p.scanlines = preset.scanlines;
|
p.scanlines = preset.scanlines;
|
||||||
p.chroma = preset.chroma;
|
p.chroma = preset.chroma;
|
||||||
@@ -483,16 +563,41 @@ void Screen::applyCurrentPostFXPreset() {
|
|||||||
p.curvature = preset.curvature;
|
p.curvature = preset.curvature;
|
||||||
p.bleeding = preset.bleeding;
|
p.bleeding = preset.bleeding;
|
||||||
p.flicker = preset.flicker;
|
p.flicker = preset.flicker;
|
||||||
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f ss=%d×",
|
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f",
|
||||||
preset.name.c_str(), p.scanlines, p.vignette, p.chroma, Options::video.supersampling);
|
preset.name.c_str(),
|
||||||
} else {
|
p.scanlines,
|
||||||
SDL_Log("Screen::applyCurrentPostFXPreset: PostFX=%s presets=%d → passthrough",
|
p.vignette,
|
||||||
Options::video.postfx ? "ON" : "OFF",
|
p.chroma);
|
||||||
static_cast<int>(Options::postfx_presets.size()));
|
|
||||||
}
|
}
|
||||||
shader_backend_->setPostFXParams(p);
|
shader_backend_->setPostFXParams(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Aplica el preset CrtPi activo al backend
|
||||||
|
void Screen::applyCurrentCrtPiPreset() {
|
||||||
|
if (!shader_backend_) { return; }
|
||||||
|
if (Options::video.shader.enabled && !Options::crtpi_presets.empty()) {
|
||||||
|
const auto& preset = Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset));
|
||||||
|
Rendering::CrtPiParams p{
|
||||||
|
.scanline_weight = preset.scanline_weight,
|
||||||
|
.scanline_gap_brightness = preset.scanline_gap_brightness,
|
||||||
|
.bloom_factor = preset.bloom_factor,
|
||||||
|
.input_gamma = preset.input_gamma,
|
||||||
|
.output_gamma = preset.output_gamma,
|
||||||
|
.mask_brightness = preset.mask_brightness,
|
||||||
|
.curvature_x = preset.curvature_x,
|
||||||
|
.curvature_y = preset.curvature_y,
|
||||||
|
.mask_type = preset.mask_type,
|
||||||
|
.enable_scanlines = preset.enable_scanlines,
|
||||||
|
.enable_multisample = preset.enable_multisample,
|
||||||
|
.enable_gamma = preset.enable_gamma,
|
||||||
|
.enable_curvature = preset.enable_curvature,
|
||||||
|
.enable_sharper = preset.enable_sharper,
|
||||||
|
};
|
||||||
|
shader_backend_->setCrtPiParams(p);
|
||||||
|
SDL_Log("Screen::applyCurrentCrtPiPreset: preset='%s'", preset.name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -521,6 +626,15 @@ void Screen::setVSync(bool enabled) {
|
|||||||
void Screen::getSingletons() {
|
void Screen::getSingletons() {
|
||||||
service_menu_ = ServiceMenu::get();
|
service_menu_ = ServiceMenu::get();
|
||||||
notifier_ = Notifier::get();
|
notifier_ = Notifier::get();
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Actualizar la fuente de debug a 8bithud (ahora Resource está disponible)
|
||||||
|
if (Resource::get() != nullptr) {
|
||||||
|
auto hud_text = Resource::get()->getText("8bithud");
|
||||||
|
if (hud_text) {
|
||||||
|
debug_info_.text = hud_text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aplica los valores de las opciones
|
// Aplica los valores de las opciones
|
||||||
|
|||||||
@@ -8,16 +8,13 @@
|
|||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
#include "options.hpp" // Para VideoOptions, video
|
#include "options.hpp" // Para VideoOptions, video
|
||||||
|
#include "rendering/shader_backend.hpp" // Para Rendering::ShaderType
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Notifier;
|
class Notifier;
|
||||||
class ServiceMenu;
|
class ServiceMenu;
|
||||||
class Text;
|
class Text;
|
||||||
|
|
||||||
namespace Rendering {
|
|
||||||
class ShaderBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
|
// --- Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales (singleton) ---
|
||||||
class Screen {
|
class Screen {
|
||||||
public:
|
public:
|
||||||
@@ -41,15 +38,17 @@ class Screen {
|
|||||||
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
|
auto decWindowSize() -> bool; // Reduce el tamaño de la ventana
|
||||||
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
|
auto incWindowSize() -> bool; // Aumenta el tamaño de la ventana
|
||||||
void applySettings(); // Aplica los valores de las opciones
|
void applySettings(); // Aplica los valores de las opciones
|
||||||
static void initPostFX(); // Inicializa PostFX (SDL3GPU)
|
static void initShaders(); // Inicializa shaders (SDL3GPU)
|
||||||
|
|
||||||
// --- Efectos visuales ---
|
// --- Efectos visuales ---
|
||||||
void shake(int desp = 2, float delay_s = 0.05F, float duration_s = 0.133F) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay_s, duration_s); } // Agita la pantalla (tiempo en segundos)
|
void shake(int desp = 2, float delay_s = 0.05F, float duration_s = 0.133F) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay_s, duration_s); }
|
||||||
void flash(Color color, float duration_s = 0.167F, float delay_s = 0.0F) { flash_effect_ = FlashEffect(true, duration_s, delay_s, color); } // Pone la pantalla de color (tiempo en segundos)
|
void flash(Color color, float duration_s = 0.167F, float delay_s = 0.0F) { flash_effect_ = FlashEffect(true, duration_s, delay_s, color); }
|
||||||
static void togglePostFX(); // Alterna entre activar y desactivar los efectos PostFX
|
static void toggleShaders(); // Alterna activar/desactivar shaders
|
||||||
|
static void nextShader(); // Cambia entre PostFX y CrtPi
|
||||||
static void nextPostFXPreset(); // Avanza al siguiente preset PostFX
|
static void nextPostFXPreset(); // Avanza al siguiente preset PostFX
|
||||||
static void toggleSupersampling(); // Alterna entre activar y desactivar el supersampling 3x
|
static void nextCrtPiPreset(); // Avanza al siguiente preset CrtPi
|
||||||
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
|
static void toggleSupersampling(); // Alterna supersampling
|
||||||
|
void toggleIntegerScale();
|
||||||
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
|
||||||
void setVSync(bool enabled); // Establece el estado del V-Sync
|
void setVSync(bool enabled); // Establece el estado del V-Sync
|
||||||
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla
|
||||||
@@ -239,6 +238,7 @@ class Screen {
|
|||||||
void renderInfo() const; // Muestra información por pantalla
|
void renderInfo() const; // Muestra información por pantalla
|
||||||
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
|
||||||
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
|
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
|
||||||
|
void applyCurrentCrtPiPreset(); // Aplica el preset CrtPi activo al backend
|
||||||
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
void adjustWindowSize(); // Calcula el tamaño de la ventana
|
||||||
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
void getDisplayInfo(); // Obtiene información sobre la pantalla
|
||||||
void renderOverlays(); // Renderiza todos los overlays y efectos
|
void renderOverlays(); // Renderiza todos los overlays y efectos
|
||||||
|
|||||||
@@ -1128,7 +1128,6 @@ auto Game::allPlayersAreNotPlaying() -> bool {
|
|||||||
|
|
||||||
// Comprueba los eventos que hay en cola
|
// Comprueba los eventos que hay en cola
|
||||||
void Game::handleEvents() {
|
void Game::handleEvents() {
|
||||||
input_->resetJustPressed();
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
|
|||||||
@@ -3,25 +3,24 @@
|
|||||||
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL_FRect, SDL_RenderFillRect, SDL_GetRenderTarget, SDL_RenderClear, SDL_RenderRect, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_PollEvent, SDL_RenderTexture, SDL_TextureAccess, SDL_Event, Uint64
|
#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL_FRect, SDL_RenderFillRect, SDL_GetRenderTarget, SDL_RenderClear, SDL_RenderRect, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_PollEvent, SDL_RenderTexture, SDL_TextureAccess, SDL_Event, Uint64
|
||||||
|
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <functional> // Para function
|
|
||||||
#include <string> // Para basic_string, string
|
#include <string> // Para basic_string, string
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "audio.hpp" // Para Audio
|
#include "audio.hpp" // Para Audio
|
||||||
|
#include "card_sprite.hpp" // Para CardSprite
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
#include "global_events.hpp" // Para handle
|
#include "global_events.hpp" // Para handle
|
||||||
#include "global_inputs.hpp" // Para check
|
#include "global_inputs.hpp" // Para check
|
||||||
#include "input.hpp" // Para Input
|
#include "input.hpp" // Para Input
|
||||||
#include "lang.hpp" // Para getText
|
#include "lang.hpp" // Para getText
|
||||||
#include "param.hpp" // Para Param, param, ParamGame, ParamIntro, ParamTitle
|
#include "param.hpp" // Para Param, param, ParamGame, ParamIntro, ParamTitle
|
||||||
#include "path_sprite.hpp" // Para PathSprite, PathType
|
|
||||||
#include "resource.hpp" // Para Resource
|
#include "resource.hpp" // Para Resource
|
||||||
#include "screen.hpp" // Para Screen
|
#include "screen.hpp" // Para Screen
|
||||||
#include "section.hpp" // Para Name, name, Options, options
|
#include "section.hpp" // Para Name, name, Options, options
|
||||||
#include "text.hpp" // Para Text
|
#include "text.hpp" // Para Text
|
||||||
#include "texture.hpp" // Para Texture
|
#include "texture.hpp" // Para Texture
|
||||||
#include "tiled_bg.hpp" // Para TiledBG, TiledBGMode
|
#include "tiled_bg.hpp" // Para TiledBG, TiledBGMode
|
||||||
#include "utils.hpp" // Para Zone, easeInOutExpo, easeInElastic, easeOutBounce, easeOutElastic, easeOutQuad, easeOutQuint
|
#include "utils.hpp" // Para easeOutBounce
|
||||||
#include "writer.hpp" // Para Writer
|
#include "writer.hpp" // Para Writer
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -31,7 +30,7 @@ Intro::Intro()
|
|||||||
Section::name = Section::Name::INTRO;
|
Section::name = Section::Name::INTRO;
|
||||||
Section::options = Section::Options::NONE;
|
Section::options = Section::Options::NONE;
|
||||||
|
|
||||||
// Inicializa las imagens
|
// Inicializa las tarjetas
|
||||||
initSprites();
|
initSprites();
|
||||||
|
|
||||||
// Inicializa los textos
|
// Inicializa los textos
|
||||||
@@ -58,6 +57,22 @@ void Intro::checkInput() {
|
|||||||
|
|
||||||
// Actualiza las escenas de la intro
|
// Actualiza las escenas de la intro
|
||||||
void Intro::updateScenes() {
|
void Intro::updateScenes() {
|
||||||
|
// Sonido al lanzar la tarjeta (enable() devuelve true solo la primera vez)
|
||||||
|
if (card_sprites_.at(scene_)->enable()) {
|
||||||
|
Audio::get()->playSound(SFX_CARD_THROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cuando la tarjeta actual toca la mesa por primera vez: shake + sonido + la anterior sale despedida
|
||||||
|
if (!shake_done_ && card_sprites_.at(scene_)->hasFirstTouch()) {
|
||||||
|
Screen::get()->shake();
|
||||||
|
Audio::get()->playSound(SFX_CARD_IMPACT);
|
||||||
|
shake_done_ = true;
|
||||||
|
|
||||||
|
if (scene_ > 0) {
|
||||||
|
card_sprites_.at(scene_ - 1)->startExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (scene_) {
|
switch (scene_) {
|
||||||
case 0:
|
case 0:
|
||||||
updateScene0();
|
updateScene0();
|
||||||
@@ -83,37 +98,32 @@ void Intro::updateScenes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene0() {
|
void Intro::updateScene0() {
|
||||||
// Primera imagen - UPV
|
// Primer texto cuando aterriza
|
||||||
enableCardAndShadow(0);
|
if (card_sprites_.at(0)->hasLanded() && !texts_.at(0)->hasFinished()) {
|
||||||
|
|
||||||
// Primer texto de la primera imagen
|
|
||||||
if (card_sprites_.at(0)->hasFinished() && !texts_.at(0)->hasFinished()) {
|
|
||||||
texts_.at(0)->setEnabled(true);
|
texts_.at(0)->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Segundo texto de la primera imagen
|
// Segundo texto
|
||||||
if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) {
|
if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) {
|
||||||
switchText(0, 1);
|
switchText(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tercer texto de la primera imagen
|
// Tercer texto
|
||||||
if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) {
|
if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) {
|
||||||
switchText(1, 2);
|
switchText(1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fin de la primera escena
|
// Fin de la primera escena: la tarjeta sale despedida
|
||||||
if (texts_.at(2)->hasFinished()) {
|
if (texts_.at(2)->hasFinished()) {
|
||||||
texts_.at(2)->setEnabled(false);
|
texts_.at(2)->setEnabled(false);
|
||||||
scene_++;
|
scene_++;
|
||||||
|
shake_done_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene1() {
|
void Intro::updateScene1() {
|
||||||
// Segunda imagen - Máquina
|
// Texto cuando aterriza
|
||||||
enableCardAndShadow(1);
|
if (card_sprites_.at(1)->hasLanded() && !texts_.at(3)->hasFinished()) {
|
||||||
|
|
||||||
// Primer texto de la segunda imagen
|
|
||||||
if (card_sprites_.at(1)->hasFinished() && !texts_.at(3)->hasFinished()) {
|
|
||||||
texts_.at(3)->setEnabled(true);
|
texts_.at(3)->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,63 +131,59 @@ void Intro::updateScene1() {
|
|||||||
if (texts_.at(3)->hasFinished()) {
|
if (texts_.at(3)->hasFinished()) {
|
||||||
texts_.at(3)->setEnabled(false);
|
texts_.at(3)->setEnabled(false);
|
||||||
scene_++;
|
scene_++;
|
||||||
|
shake_done_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene2() {
|
void Intro::updateScene2() {
|
||||||
// Tercera imagen junto con primer texto - GRITO
|
// Tercera imagen - GRITO: tarjeta y texto a la vez
|
||||||
if (!texts_.at(4)->hasFinished()) {
|
if (!texts_.at(4)->hasFinished()) {
|
||||||
enableCardAndShadow(2);
|
|
||||||
texts_.at(4)->setEnabled(true);
|
texts_.at(4)->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fin de la tercera escena
|
// Fin de la tercera escena
|
||||||
if (card_sprites_.at(2)->hasFinished() && texts_.at(4)->hasFinished()) {
|
if (card_sprites_.at(2)->hasLanded() && texts_.at(4)->hasFinished()) {
|
||||||
texts_.at(4)->setEnabled(false);
|
texts_.at(4)->setEnabled(false);
|
||||||
scene_++;
|
scene_++;
|
||||||
|
shake_done_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene3() {
|
void Intro::updateScene3() {
|
||||||
// Cuarta imagen junto con primer texto - Reflexión
|
// Cuarta imagen - Reflexión
|
||||||
enableCardAndShadow(3);
|
|
||||||
|
|
||||||
if (!texts_.at(5)->hasFinished()) {
|
if (!texts_.at(5)->hasFinished()) {
|
||||||
texts_.at(5)->setEnabled(true);
|
texts_.at(5)->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Segundo texto de la cuarta imagen
|
// Segundo texto
|
||||||
if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) {
|
if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) {
|
||||||
switchText(5, 6);
|
switchText(5, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fin de la cuarta escena
|
// Fin de la cuarta escena
|
||||||
if (card_sprites_.at(3)->hasFinished() && texts_.at(6)->hasFinished()) {
|
if (card_sprites_.at(3)->hasLanded() && texts_.at(6)->hasFinished()) {
|
||||||
texts_.at(6)->setEnabled(false);
|
texts_.at(6)->setEnabled(false);
|
||||||
scene_++;
|
scene_++;
|
||||||
|
shake_done_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene4() {
|
void Intro::updateScene4() {
|
||||||
// Quinta imagen - Patada
|
// Quinta imagen - Patada
|
||||||
enableCardAndShadow(4);
|
|
||||||
|
|
||||||
// Primer texto de la quinta imagen
|
|
||||||
if (!texts_.at(7)->hasFinished()) {
|
if (!texts_.at(7)->hasFinished()) {
|
||||||
texts_.at(7)->setEnabled(true);
|
texts_.at(7)->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fin de la quinta escena
|
// Fin de la quinta escena
|
||||||
if (card_sprites_.at(4)->hasFinished() && texts_.at(7)->hasFinished()) {
|
if (card_sprites_.at(4)->hasLanded() && texts_.at(7)->hasFinished()) {
|
||||||
texts_.at(7)->setEnabled(false);
|
texts_.at(7)->setEnabled(false);
|
||||||
scene_++;
|
scene_++;
|
||||||
|
shake_done_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Intro::updateScene5() {
|
void Intro::updateScene5() {
|
||||||
// Sexta imagen junto con texto - Globos de café
|
// Sexta imagen - Globos de café
|
||||||
enableCardAndShadow(5);
|
|
||||||
|
|
||||||
if (!texts_.at(8)->hasFinished()) {
|
if (!texts_.at(8)->hasFinished()) {
|
||||||
texts_.at(8)->setEnabled(true);
|
texts_.at(8)->setEnabled(true);
|
||||||
}
|
}
|
||||||
@@ -187,19 +193,14 @@ void Intro::updateScene5() {
|
|||||||
texts_.at(8)->setEnabled(false);
|
texts_.at(8)->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acaba la ultima imagen
|
// Última tarjeta: sale "como si se la llevara el viento" y transición a POST
|
||||||
if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) {
|
if (card_sprites_.at(5)->hasLanded() && texts_.at(8)->hasFinished()) {
|
||||||
|
card_sprites_.at(5)->startExit();
|
||||||
state_ = State::POST;
|
state_ = State::POST;
|
||||||
state_start_time_ = SDL_GetTicks() / 1000.0F;
|
state_start_time_ = SDL_GetTicks() / 1000.0F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods to reduce code duplication
|
|
||||||
void Intro::enableCardAndShadow(int index) {
|
|
||||||
card_sprites_.at(index)->enable();
|
|
||||||
shadow_sprites_.at(index)->enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Intro::switchText(int from_index, int to_index) {
|
void Intro::switchText(int from_index, int to_index) {
|
||||||
texts_.at(from_index)->setEnabled(false);
|
texts_.at(from_index)->setEnabled(false);
|
||||||
texts_.at(to_index)->setEnabled(true);
|
texts_.at(to_index)->setEnabled(true);
|
||||||
@@ -215,12 +216,18 @@ void Intro::update(float delta_time) {
|
|||||||
|
|
||||||
switch (state_) {
|
switch (state_) {
|
||||||
case State::SCENES:
|
case State::SCENES:
|
||||||
|
// Pausa inicial antes de empezar
|
||||||
|
if (initial_elapsed_ < INITIAL_DELAY_S) {
|
||||||
|
initial_elapsed_ += delta_time;
|
||||||
|
break;
|
||||||
|
}
|
||||||
updateSprites(delta_time);
|
updateSprites(delta_time);
|
||||||
updateTexts(delta_time);
|
updateTexts(delta_time);
|
||||||
updateScenes();
|
updateScenes();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::POST:
|
case State::POST:
|
||||||
|
updateSprites(delta_time); // La última tarjeta puede estar saliendo durante POST
|
||||||
updatePostState();
|
updatePostState();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -243,6 +250,7 @@ void Intro::render() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::POST:
|
case State::POST:
|
||||||
|
renderSprites(); // La última tarjeta puede estar saliendo
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +280,7 @@ void Intro::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa las imagens
|
// Inicializa las tarjetas
|
||||||
void Intro::initSprites() {
|
void Intro::initSprites() {
|
||||||
// Listado de imagenes a usar
|
// Listado de imagenes a usar
|
||||||
const std::array<std::string, 6> TEXTURE_LIST = {
|
const std::array<std::string, 6> TEXTURE_LIST = {
|
||||||
@@ -291,24 +299,21 @@ void Intro::initSprites() {
|
|||||||
const float CARD_WIDTH = texture->getWidth() + (BORDER * 2);
|
const float CARD_WIDTH = texture->getWidth() + (BORDER * 2);
|
||||||
const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2);
|
const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2);
|
||||||
|
|
||||||
// Crea las texturas para las tarjetas
|
// Crea las texturas para las tarjetas (imagen con marco)
|
||||||
std::vector<std::shared_ptr<Texture>> card_textures;
|
std::vector<std::shared_ptr<Texture>> card_textures;
|
||||||
|
|
||||||
for (int i = 0; i < TOTAL_SPRITES; ++i) {
|
for (int i = 0; i < TOTAL_SPRITES; ++i) {
|
||||||
// Crea la textura
|
|
||||||
auto card_texture = std::make_unique<Texture>(Screen::get()->getRenderer());
|
auto card_texture = std::make_unique<Texture>(Screen::get()->getRenderer());
|
||||||
card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
card_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
card_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
// Apuntamos el renderizador a la textura
|
|
||||||
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
||||||
card_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
card_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Limpia la textura
|
|
||||||
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
|
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
|
||||||
SDL_RenderClear(Screen::get()->getRenderer());
|
SDL_RenderClear(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Pone color en el marco de la textura
|
// Marco de la tarjeta
|
||||||
auto color = param.intro.card_color;
|
auto color = param.intro.card_color;
|
||||||
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a);
|
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a);
|
||||||
SDL_FRect rect1 = {.x = 1, .y = 0, .w = CARD_WIDTH - 2, .h = CARD_HEIGHT};
|
SDL_FRect rect1 = {.x = 1, .y = 0, .w = CARD_WIDTH - 2, .h = CARD_HEIGHT};
|
||||||
@@ -316,85 +321,108 @@ void Intro::initSprites() {
|
|||||||
SDL_RenderRect(Screen::get()->getRenderer(), &rect1);
|
SDL_RenderRect(Screen::get()->getRenderer(), &rect1);
|
||||||
SDL_RenderRect(Screen::get()->getRenderer(), &rect2);
|
SDL_RenderRect(Screen::get()->getRenderer(), &rect2);
|
||||||
|
|
||||||
// Copia la textura con la imagen dentro del marco
|
// Imagen dentro del marco
|
||||||
SDL_FRect dest = {.x = BORDER, .y = BORDER, .w = CARD_WIDTH - (BORDER * 2), .h = CARD_HEIGHT - (BORDER * 2)};
|
SDL_FRect dest = {.x = BORDER, .y = BORDER, .w = CARD_WIDTH - (BORDER * 2), .h = CARD_HEIGHT - (BORDER * 2)};
|
||||||
SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest);
|
SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest);
|
||||||
|
|
||||||
// Deja el renderizador como estaba y añade la textura a la lista
|
|
||||||
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
|
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
|
||||||
card_textures.push_back(std::move(card_texture));
|
card_textures.push_back(std::move(card_texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa los sprites para las tarjetas
|
// Crea la textura de sombra (compartida entre todas las tarjetas)
|
||||||
for (int i = 0; i < TOTAL_SPRITES; ++i) {
|
|
||||||
auto sprite = std::make_unique<PathSprite>(card_textures.at(i));
|
|
||||||
sprite->setWidth(CARD_WIDTH);
|
|
||||||
sprite->setHeight(CARD_HEIGHT);
|
|
||||||
sprite->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT);
|
|
||||||
card_sprites_.push_back(std::move(sprite));
|
|
||||||
}
|
|
||||||
|
|
||||||
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2);
|
|
||||||
const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
|
|
||||||
|
|
||||||
card_sprites_.at(0)->addPath(-CARD_WIDTH - CARD_OFFSET_MARGIN, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0F);
|
|
||||||
card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0F);
|
|
||||||
card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0F);
|
|
||||||
card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0F);
|
|
||||||
card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0F);
|
|
||||||
card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
|
|
||||||
card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0F);
|
|
||||||
|
|
||||||
// Constantes
|
|
||||||
const float DESP = SHADOW_OFFSET;
|
|
||||||
const float SHADOW_SPRITE_WIDTH = CARD_WIDTH;
|
|
||||||
const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT;
|
|
||||||
|
|
||||||
// Crea la textura para las sombras de las tarjetas
|
|
||||||
auto shadow_texture = std::make_shared<Texture>(Screen::get()->getRenderer());
|
auto shadow_texture = std::make_shared<Texture>(Screen::get()->getRenderer());
|
||||||
shadow_texture->createBlank(SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
shadow_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
// Apuntamos el renderizador a la textura
|
|
||||||
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
auto* temp = SDL_GetRenderTarget(Screen::get()->getRenderer());
|
||||||
shadow_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
shadow_texture->setAsRenderTarget(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Limpia la textura
|
|
||||||
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
|
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0);
|
||||||
SDL_RenderClear(Screen::get()->getRenderer());
|
SDL_RenderClear(Screen::get()->getRenderer());
|
||||||
|
|
||||||
// Dibuja la sombra sobre la textura
|
|
||||||
auto shadow_color = param.intro.shadow_color;
|
auto shadow_color = param.intro.shadow_color;
|
||||||
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), shadow_color.r, shadow_color.g, shadow_color.b, Color::MAX_ALPHA_VALUE);
|
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), shadow_color.r, shadow_color.g, shadow_color.b, Color::MAX_ALPHA_VALUE);
|
||||||
SDL_FRect rect1 = {.x = 1, .y = 0, .w = SHADOW_SPRITE_WIDTH - 2, .h = SHADOW_SPRITE_HEIGHT};
|
SDL_FRect shadow_rect1 = {.x = 1, .y = 0, .w = CARD_WIDTH - 2, .h = CARD_HEIGHT};
|
||||||
SDL_FRect rect2 = {.x = 0, .y = 1, .w = SHADOW_SPRITE_WIDTH, .h = SHADOW_SPRITE_HEIGHT - 2};
|
SDL_FRect shadow_rect2 = {.x = 0, .y = 1, .w = CARD_WIDTH, .h = CARD_HEIGHT - 2};
|
||||||
SDL_RenderFillRect(Screen::get()->getRenderer(), &rect1);
|
SDL_RenderFillRect(Screen::get()->getRenderer(), &shadow_rect1);
|
||||||
SDL_RenderFillRect(Screen::get()->getRenderer(), &rect2);
|
SDL_RenderFillRect(Screen::get()->getRenderer(), &shadow_rect2);
|
||||||
|
|
||||||
// Deja el renderizador como estaba y añade la textura a la lista
|
|
||||||
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
|
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
|
||||||
|
shadow_texture->setAlpha(shadow_color.a);
|
||||||
|
|
||||||
// Inicializa los sprites para la sombras usando la texturas con la sombra
|
// Posición de aterrizaje (centro de la zona de juego)
|
||||||
|
const float X_DEST = param.game.game_area.center_x - (CARD_WIDTH / 2);
|
||||||
|
const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4);
|
||||||
|
|
||||||
|
// Configuración por tarjeta: posición de entrada, ángulo, salida
|
||||||
|
// Cada tarjeta viene de un borde diferente (gente alrededor de una mesa lanzando cartas al centro)
|
||||||
|
struct CardConfig {
|
||||||
|
float entry_x; // Posición inicial X
|
||||||
|
float entry_y; // Posición inicial Y
|
||||||
|
double entry_angle; // Ángulo de entrada
|
||||||
|
float exit_vx; // Velocidad de salida X
|
||||||
|
float exit_vy; // Velocidad de salida Y
|
||||||
|
float exit_ax; // Aceleración de salida X
|
||||||
|
float exit_ay; // Aceleración de salida Y
|
||||||
|
double exit_rotation; // Velocidad de rotación de salida
|
||||||
|
};
|
||||||
|
|
||||||
|
const float W = param.game.width;
|
||||||
|
const float H = param.game.height;
|
||||||
|
const float S = CARD_EXIT_SPEED;
|
||||||
|
const float A = CARD_EXIT_ACCEL;
|
||||||
|
const double R = CARD_EXIT_ROTATION;
|
||||||
|
|
||||||
|
const CardConfig CARD_CONFIGS[] = {
|
||||||
|
// 0: Entra desde la izquierda. La 1 entra desde la derecha → sale empujada hacia la izquierda
|
||||||
|
{-CARD_WIDTH, Y_DEST - 20.0F, CARD_ANGLE_0, -S, S * 0.1F, -A, 0.0F, -R},
|
||||||
|
// 1: Entra desde la derecha. La 2 entra desde arriba → sale empujada hacia abajo
|
||||||
|
{W + CARD_WIDTH, Y_DEST + 15.0F, CARD_ANGLE_1, S * 0.15F, S, 0.0F, A, R * 1.1},
|
||||||
|
// 2: Entra desde arriba. La 3 entra desde abajo → sale empujada hacia arriba
|
||||||
|
{X_DEST + 30.0F, -CARD_HEIGHT, CARD_ANGLE_2, -S * 0.15F, -S, 0.0F, -A, -R * 0.9},
|
||||||
|
// 3: Entra desde abajo. La 4 entra desde arriba-izquierda → sale empujada hacia abajo-derecha
|
||||||
|
{X_DEST - 25.0F, H + CARD_HEIGHT, CARD_ANGLE_3, S * 0.8F, S * 0.6F, A * 0.5F, A * 0.4F, R},
|
||||||
|
// 4: Entra desde arriba-izquierda. La 5 entra desde derecha-abajo → sale empujada hacia arriba-izquierda
|
||||||
|
{-CARD_WIDTH * 0.5F, -CARD_HEIGHT, CARD_ANGLE_4, -S * 0.7F, -S * 0.5F, -A * 0.5F, -A * 0.3F, -R * 1.2},
|
||||||
|
// 5: Entra desde la derecha-abajo. Última: sale hacia la izquierda suave (viento)
|
||||||
|
{W + CARD_WIDTH, H * 0.6F, CARD_ANGLE_5, -S * 0.6F, -S * 0.1F, -A * 0.5F, 0.0F, -R * 0.7},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inicializa los CardSprites
|
||||||
for (int i = 0; i < TOTAL_SPRITES; ++i) {
|
for (int i = 0; i < TOTAL_SPRITES; ++i) {
|
||||||
auto shadow_color = param.intro.shadow_color;
|
auto card = std::make_unique<CardSprite>(card_textures.at(i));
|
||||||
auto sprite = std::make_unique<PathSprite>(shadow_texture);
|
card->setWidth(CARD_WIDTH);
|
||||||
sprite->setWidth(SHADOW_SPRITE_WIDTH);
|
card->setHeight(CARD_HEIGHT);
|
||||||
sprite->setHeight(SHADOW_SPRITE_HEIGHT);
|
card->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT);
|
||||||
sprite->setSpriteClip(0, 0, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT);
|
|
||||||
sprite->getTexture()->setAlpha(shadow_color.a);
|
const auto& cfg = CARD_CONFIGS[i];
|
||||||
shadow_sprites_.push_back(std::move(sprite));
|
|
||||||
|
// Posición de aterrizaje (centro)
|
||||||
|
card->setLandingPosition(X_DEST, Y_DEST);
|
||||||
|
|
||||||
|
// Posición de entrada (borde de pantalla)
|
||||||
|
card->setEntryPosition(cfg.entry_x, cfg.entry_y);
|
||||||
|
|
||||||
|
// Parámetros de entrada: zoom, ángulo, duración, easing
|
||||||
|
card->setEntryParams(CARD_START_ZOOM, cfg.entry_angle, CARD_ENTRY_DURATION_S, easeOutBounce);
|
||||||
|
|
||||||
|
// Parámetros de salida
|
||||||
|
card->setExitParams(cfg.exit_vx, cfg.exit_vy, cfg.exit_ax, cfg.exit_ay, cfg.exit_rotation);
|
||||||
|
|
||||||
|
// Sombra
|
||||||
|
card->setShadowTexture(shadow_texture);
|
||||||
|
card->setShadowOffset(SHADOW_OFFSET, SHADOW_OFFSET);
|
||||||
|
|
||||||
|
// Límites de pantalla
|
||||||
|
card->setScreenBounds(param.game.width, param.game.height);
|
||||||
|
|
||||||
|
// Última tarjeta: gana algo de altura al salir (se la lleva el viento)
|
||||||
|
if (i == TOTAL_SPRITES - 1) {
|
||||||
|
card->setExitLift(1.2F, 0.15F); // Hasta zoom 1.2, a 0.15/s
|
||||||
}
|
}
|
||||||
|
|
||||||
const float S_X_DEST = X_DEST + DESP;
|
card_sprites_.push_back(std::move(card));
|
||||||
const float S_Y_DEST = Y_DEST + DESP;
|
}
|
||||||
|
|
||||||
shadow_sprites_.at(0)->addPath(param.game.height + CARD_OFFSET_MARGIN, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeInOutExpo, 0.0F);
|
|
||||||
shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_NORMAL, easeOutBounce, 0.0F);
|
|
||||||
shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_FAST, easeOutQuint, 0.0F);
|
|
||||||
shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_VERY_SLOW, easeInOutExpo, 0.0F);
|
|
||||||
shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, CARD_ANIM_DURATION_MEDIUM, easeOutElastic, 0.0F);
|
|
||||||
shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SLOW, easeOutQuad, CARD_ANIM_DELAY_LONG_S);
|
|
||||||
shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, CARD_ANIM_DURATION_SHORT, easeInElastic, 0.0F);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa los textos
|
// Inicializa los textos
|
||||||
@@ -420,7 +448,7 @@ void Intro::initTexts() {
|
|||||||
|
|
||||||
// Fins que un desaprensiu...
|
// Fins que un desaprensiu...
|
||||||
texts_.at(2)->setCaption(Lang::getText("[INTRO] 3"));
|
texts_.at(2)->setCaption(Lang::getText("[INTRO] 3"));
|
||||||
texts_.at(2)->setSpeedS(TEXT_SPEED_FAST);
|
texts_.at(2)->setSpeedS(TEXT_SPEED_SLOW);
|
||||||
|
|
||||||
// HEY! ME ANE A FERME UN CORTAET...
|
// HEY! ME ANE A FERME UN CORTAET...
|
||||||
texts_.at(3)->setCaption(Lang::getText("[INTRO] 4"));
|
texts_.at(3)->setCaption(Lang::getText("[INTRO] 4"));
|
||||||
@@ -428,23 +456,23 @@ void Intro::initTexts() {
|
|||||||
|
|
||||||
// UAAAAAAAAAAAAA!!!
|
// UAAAAAAAAAAAAA!!!
|
||||||
texts_.at(4)->setCaption(Lang::getText("[INTRO] 5"));
|
texts_.at(4)->setCaption(Lang::getText("[INTRO] 5"));
|
||||||
texts_.at(4)->setSpeedS(TEXT_SPEED_VERY_SLOW);
|
texts_.at(4)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
|
||||||
|
|
||||||
// Espera un moment...
|
// Espera un moment...
|
||||||
texts_.at(5)->setCaption(Lang::getText("[INTRO] 6"));
|
texts_.at(5)->setCaption(Lang::getText("[INTRO] 6"));
|
||||||
texts_.at(5)->setSpeedS(TEXT_SPEED_VERY_FAST);
|
texts_.at(5)->setSpeedS(TEXT_SPEED_VERY_SLOW);
|
||||||
|
|
||||||
// Si resulta que no tinc solt!
|
// Si resulta que no tinc solt!
|
||||||
texts_.at(6)->setCaption(Lang::getText("[INTRO] 7"));
|
texts_.at(6)->setCaption(Lang::getText("[INTRO] 7"));
|
||||||
texts_.at(6)->setSpeedS(TEXT_SPEED_SLOW);
|
texts_.at(6)->setSpeedS(TEXT_SPEED_VERY_FAST);
|
||||||
|
|
||||||
// MERDA DE MAQUINA!
|
// MERDA DE MAQUINA!
|
||||||
texts_.at(7)->setCaption(Lang::getText("[INTRO] 8"));
|
texts_.at(7)->setCaption(Lang::getText("[INTRO] 8"));
|
||||||
texts_.at(7)->setSpeedS(TEXT_SPEED_MEDIUM_SLOW);
|
texts_.at(7)->setSpeedS(TEXT_SPEED_FAST);
|
||||||
|
|
||||||
// Blop... blop... blop...
|
// Blop... blop... blop...
|
||||||
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
|
texts_.at(8)->setCaption(Lang::getText("[INTRO] 9"));
|
||||||
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_FAST);
|
texts_.at(8)->setSpeedS(TEXT_SPEED_ULTRA_SLOW);
|
||||||
|
|
||||||
for (auto& text : texts_) {
|
for (auto& text : texts_) {
|
||||||
text->center(param.game.game_area.center_x);
|
text->center(param.game.game_area.center_x);
|
||||||
@@ -456,10 +484,6 @@ void Intro::updateSprites(float delta_time) {
|
|||||||
for (auto& sprite : card_sprites_) {
|
for (auto& sprite : card_sprites_) {
|
||||||
sprite->update(delta_time);
|
sprite->update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& sprite : shadow_sprites_) {
|
|
||||||
sprite->update(delta_time);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los textos
|
// Actualiza los textos
|
||||||
@@ -469,10 +493,11 @@ void Intro::updateTexts(float delta_time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuja los sprites
|
// Dibuja los sprites (todas las tarjetas activas, para que convivan la saliente y la entrante)
|
||||||
void Intro::renderSprites() {
|
void Intro::renderSprites() {
|
||||||
shadow_sprites_.at(scene_)->render();
|
for (auto& card : card_sprites_) {
|
||||||
card_sprites_.at(scene_)->render();
|
card->render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuja los textos
|
// Dibuja los textos
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
#include <memory> // Para unique_ptr
|
#include <memory> // Para unique_ptr
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "card_sprite.hpp" // Para CardSprite
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
#include "param.hpp" // Para Param, ParamIntro, param
|
#include "param.hpp" // Para Param, ParamIntro, param
|
||||||
#include "path_sprite.hpp" // Para PathSprite
|
|
||||||
#include "tiled_bg.hpp" // Para TiledBG
|
#include "tiled_bg.hpp" // Para TiledBG
|
||||||
#include "writer.hpp" // Para Writer
|
#include "writer.hpp" // Para Writer
|
||||||
|
|
||||||
@@ -18,14 +18,11 @@
|
|||||||
//
|
//
|
||||||
// Funcionalidades principales:
|
// Funcionalidades principales:
|
||||||
// • Sistema de escenas secuencial: 6 escenas con transiciones automáticas
|
// • Sistema de escenas secuencial: 6 escenas con transiciones automáticas
|
||||||
// • Animaciones de tarjetas: efectos de entrada con diferentes tipos de easing
|
// • Animaciones de tarjetas: efecto de lanzamiento sobre mesa con zoom, rotación y rebote
|
||||||
// • Texto narrativo: velocidades de escritura configurables por escena
|
// • Texto narrativo: velocidades de escritura configurables por escena
|
||||||
// • Efectos visuales: sombras, bordes y transiciones de color
|
// • Efectos visuales: sombras, bordes y transiciones de color
|
||||||
// • Audio sincronizado: música de fondo durante toda la secuencia
|
// • Audio sincronizado: música de fondo durante toda la secuencia
|
||||||
// • Estado POST: transición suave hacia el menú principal
|
// • Estado POST: transición suave hacia el menú principal
|
||||||
//
|
|
||||||
// Todas las duraciones y velocidades están configuradas mediante constantes
|
|
||||||
// para facilitar el ajuste fino de la experiencia cinematográfica.
|
|
||||||
|
|
||||||
class Intro {
|
class Intro {
|
||||||
public:
|
public:
|
||||||
@@ -38,9 +35,14 @@ class Intro {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Constantes de tiempo (en segundos) ---
|
// --- Constantes de tiempo (en segundos) ---
|
||||||
static constexpr float TEXT_DISPLAY_DURATION_S = 3.0F; // Duración de visualización de texto (180 frames a 60fps)
|
static constexpr float TEXT_DISPLAY_DURATION_S = 3.0F; // Duración de visualización de texto
|
||||||
static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo
|
static constexpr float POST_BG_STOP_DELAY_S = 1.0F; // Retraso antes de detener el fondo
|
||||||
static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro
|
static constexpr float POST_END_DELAY_S = 1.0F; // Retraso antes de finalizar intro
|
||||||
|
static constexpr float INITIAL_DELAY_S = 2.0F; // Pausa antes de empezar las escenas
|
||||||
|
|
||||||
|
// --- Constantes de sonido ---
|
||||||
|
static constexpr const char* SFX_CARD_THROW = "service_menu_select.wav"; // Sonido al lanzar una tarjeta
|
||||||
|
static constexpr const char* SFX_CARD_IMPACT = "player_collision.wav"; // Sonido al impactar en la mesa
|
||||||
|
|
||||||
// --- Constantes de layout ---
|
// --- Constantes de layout ---
|
||||||
static constexpr float CARD_BORDER_SIZE = 2.0F; // Tamaño del borde de tarjetas
|
static constexpr float CARD_BORDER_SIZE = 2.0F; // Tamaño del borde de tarjetas
|
||||||
@@ -48,25 +50,29 @@ class Intro {
|
|||||||
static constexpr float TILED_BG_SPEED = 18.0F; // Velocidad del fondo mosaico (pixels/segundo)
|
static constexpr float TILED_BG_SPEED = 18.0F; // Velocidad del fondo mosaico (pixels/segundo)
|
||||||
static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres
|
static constexpr int TEXT_KERNING = -2; // Espaciado entre caracteres
|
||||||
|
|
||||||
// --- Constantes de velocidades de texto (segundos entre caracteres) ---
|
// --- Constantes de velocidades de texto (segundos entre caracteres, menor = más rápido) ---
|
||||||
static constexpr float TEXT_SPEED_NORMAL = 0.133F; // Velocidad normal (8 frames * 16.67ms = 133ms)
|
static constexpr float TEXT_SPEED_ULTRA_FAST = 0.0167F; // Ultra rápida (1 frame a 60fps)
|
||||||
static constexpr float TEXT_SPEED_FAST = 0.2F; // Velocidad rápida (12 frames * 16.67ms = 200ms)
|
static constexpr float TEXT_SPEED_VERY_FAST = 0.033F; // Muy rápida (2 frames a 60fps)
|
||||||
static constexpr float TEXT_SPEED_VERY_SLOW = 0.0167F; // Velocidad muy lenta (1 frame * 16.67ms = 16.7ms)
|
static constexpr float TEXT_SPEED_FAST = 0.05F; // Rápida (3 frames a 60fps)
|
||||||
static constexpr float TEXT_SPEED_VERY_FAST = 0.267F; // Velocidad muy rápida (16 frames * 16.67ms = 267ms)
|
static constexpr float TEXT_SPEED_NORMAL = 0.133F; // Normal (8 frames a 60fps)
|
||||||
static constexpr float TEXT_SPEED_SLOW = 0.033F; // Velocidad lenta (2 frames * 16.67ms = 33ms)
|
static constexpr float TEXT_SPEED_SLOW = 0.2F; // Lenta (12 frames a 60fps)
|
||||||
static constexpr float TEXT_SPEED_MEDIUM_SLOW = 0.05F; // Velocidad medio-lenta (3 frames * 16.67ms = 50ms)
|
static constexpr float TEXT_SPEED_VERY_SLOW = 0.267F; // Muy lenta (16 frames a 60fps)
|
||||||
static constexpr float TEXT_SPEED_ULTRA_FAST = 0.333F; // Velocidad ultra rápida (20 frames * 16.67ms = 333ms)
|
static constexpr float TEXT_SPEED_ULTRA_SLOW = 0.333F; // Ultra lenta (20 frames a 60fps)
|
||||||
|
|
||||||
// --- Constantes de animaciones de tarjetas (duraciones en segundos) ---
|
// --- Constantes de animaciones de tarjetas ---
|
||||||
static constexpr float CARD_ANIM_DURATION_NORMAL = 100.0F / 60.0F; // ≈ 1.6667 s
|
static constexpr float CARD_ENTRY_DURATION_S = 1.5F; // Duración de la animación de entrada
|
||||||
static constexpr float CARD_ANIM_DURATION_FAST = 40.0F / 60.0F; // ≈ 0.6667 s
|
static constexpr float CARD_START_ZOOM = 1.8F; // Zoom inicial (como si estuviera cerca)
|
||||||
static constexpr float CARD_ANIM_DURATION_MEDIUM = 70.0F / 60.0F; // ≈ 1.1667 s
|
static constexpr float CARD_EXIT_SPEED = 400.0F; // Velocidad base de salida (pixels/s)
|
||||||
static constexpr float CARD_ANIM_DURATION_SHORT = 80.0F / 60.0F; // ≈ 1.3333 s
|
static constexpr float CARD_EXIT_ACCEL = 200.0F; // Aceleración de salida (pixels/s²)
|
||||||
static constexpr float CARD_ANIM_DURATION_SLOW = 250.0F / 60.0F; // ≈ 4.1667 s
|
static constexpr double CARD_EXIT_ROTATION = 450.0; // Velocidad de rotación en salida (grados/s)
|
||||||
static constexpr float CARD_ANIM_DURATION_VERY_SLOW = 300.0F / 60.0F; // ≈ 5.0000 s
|
|
||||||
|
|
||||||
static constexpr float CARD_ANIM_DELAY_LONG_S = 7.5F; // Retraso largo antes de animación
|
// --- Ángulos iniciales de entrada por tarjeta (grados) ---
|
||||||
static constexpr float CARD_OFFSET_MARGIN = 10.0F; // Margen fuera de pantalla
|
static constexpr double CARD_ANGLE_0 = 12.0;
|
||||||
|
static constexpr double CARD_ANGLE_1 = -15.0;
|
||||||
|
static constexpr double CARD_ANGLE_2 = 8.0;
|
||||||
|
static constexpr double CARD_ANGLE_3 = -10.0;
|
||||||
|
static constexpr double CARD_ANGLE_4 = 18.0;
|
||||||
|
static constexpr double CARD_ANGLE_5 = -7.0;
|
||||||
|
|
||||||
// --- Estados internos ---
|
// --- Estados internos ---
|
||||||
enum class State {
|
enum class State {
|
||||||
@@ -80,8 +86,7 @@ class Intro {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// --- Objetos ---
|
// --- Objetos ---
|
||||||
std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro
|
std::vector<std::unique_ptr<CardSprite>> card_sprites_; // Tarjetas animadas con sombra integrada
|
||||||
std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras
|
|
||||||
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
|
std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro
|
||||||
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
|
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
|
||||||
|
|
||||||
@@ -90,8 +95,10 @@ class Intro {
|
|||||||
int scene_ = 0; // Indica qué escena está activa
|
int scene_ = 0; // Indica qué escena está activa
|
||||||
State state_ = State::SCENES; // Estado principal de la intro
|
State state_ = State::SCENES; // Estado principal de la intro
|
||||||
PostState post_state_ = PostState::STOP_BG; // Estado POST
|
PostState post_state_ = PostState::STOP_BG; // Estado POST
|
||||||
float state_start_time_; // Tiempo de inicio del estado actual (segundos)
|
float state_start_time_ = 0.0F; // Tiempo de inicio del estado actual (segundos)
|
||||||
Color bg_color_ = param.intro.bg_color; // Color de fondo
|
Color bg_color_ = param.intro.bg_color; // Color de fondo
|
||||||
|
bool shake_done_ = false; // Evita shake repetido en la misma escena
|
||||||
|
float initial_elapsed_ = 0.0F; // Tiempo acumulado antes de empezar
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void update(float delta_time); // Actualiza las variables del objeto
|
void update(float delta_time); // Actualiza las variables del objeto
|
||||||
@@ -99,13 +106,13 @@ class Intro {
|
|||||||
static void checkInput(); // Comprueba las entradas
|
static void checkInput(); // Comprueba las entradas
|
||||||
static void checkEvents(); // Comprueba los eventos
|
static void checkEvents(); // Comprueba los eventos
|
||||||
void updateScenes(); // Actualiza las escenas de la intro
|
void updateScenes(); // Actualiza las escenas de la intro
|
||||||
void initSprites(); // Inicializa las imágenes
|
void initSprites(); // Inicializa las tarjetas
|
||||||
void initTexts(); // Inicializa los textos
|
void initTexts(); // Inicializa los textos
|
||||||
void updateSprites(float delta_time); // Actualiza los sprites
|
void updateSprites(float delta_time); // Actualiza los sprites
|
||||||
void updateTexts(float delta_time); // Actualiza los textos
|
void updateTexts(float delta_time); // Actualiza los textos
|
||||||
void renderSprites(); // Dibuja los sprites
|
void renderSprites(); // Dibuja los sprites
|
||||||
void renderTexts(); // Dibuja los textos
|
void renderTexts(); // Dibuja los textos
|
||||||
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
|
static void renderTextRect(); // Dibuja el rectángulo de fondo del texto
|
||||||
void updatePostState(); // Actualiza el estado POST
|
void updatePostState(); // Actualiza el estado POST
|
||||||
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
|
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
|
||||||
|
|
||||||
@@ -117,7 +124,6 @@ class Intro {
|
|||||||
void updateScene4();
|
void updateScene4();
|
||||||
void updateScene5();
|
void updateScene5();
|
||||||
|
|
||||||
// --- Métodos auxiliares para reducir duplicación de código ---
|
// --- Métodos auxiliares ---
|
||||||
void enableCardAndShadow(int index);
|
|
||||||
void switchText(int from_index, int to_index);
|
void switchText(int from_index, int to_index);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,11 +30,6 @@
|
|||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
#include <iomanip> // Para operator<<, setfill, setw
|
|
||||||
#include <iostream> // Para basic_ostream, basic_ostream::operator<<, operator<<, cout, hex
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Title::Title()
|
Title::Title()
|
||||||
: text_(Resource::get()->getText("smb2_grad")),
|
: text_(Resource::get()->getText("smb2_grad")),
|
||||||
@@ -43,12 +38,7 @@ Title::Title()
|
|||||||
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
|
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
|
||||||
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
|
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
|
||||||
state_(State::LOGO_ANIMATING),
|
state_(State::LOGO_ANIMATING),
|
||||||
num_controllers_(Input::get()->getNumGamepads())
|
num_controllers_(Input::get()->getNumGamepads()) {
|
||||||
#ifdef _DEBUG
|
|
||||||
,
|
|
||||||
debug_color_(param.title.bg_color)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// Configura objetos
|
// Configura objetos
|
||||||
tiled_bg_->setColor(param.title.bg_color);
|
tiled_bg_->setColor(param.title.bg_color);
|
||||||
tiled_bg_->setSpeed(0.0F);
|
tiled_bg_->setSpeed(0.0F);
|
||||||
@@ -132,87 +122,8 @@ void Title::checkEvents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Title::handleKeyDownEvent(const SDL_Event& event) {
|
void Title::handleKeyDownEvent(const SDL_Event& /*event*/) {
|
||||||
#ifdef _DEBUG
|
|
||||||
bool is_repeat = static_cast<int>(event.key.repeat) == 1;
|
|
||||||
if (is_repeat) {
|
|
||||||
handleDebugColorKeys(event.key.key);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
void Title::handleDebugColorKeys(SDL_Keycode key) {
|
|
||||||
adjustColorComponent(key, debug_color_);
|
|
||||||
|
|
||||||
counter_time_ = 0.0F;
|
|
||||||
tiled_bg_->setColor(debug_color_);
|
|
||||||
printColorValue(debug_color_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::adjustColorComponent(SDL_Keycode key, Color& color) {
|
|
||||||
switch (key) {
|
|
||||||
case SDLK_A:
|
|
||||||
incrementColorComponent(color.r);
|
|
||||||
break;
|
|
||||||
case SDLK_Z:
|
|
||||||
decrementColorComponent(color.r);
|
|
||||||
break;
|
|
||||||
case SDLK_S:
|
|
||||||
incrementColorComponent(color.g);
|
|
||||||
break;
|
|
||||||
case SDLK_X:
|
|
||||||
decrementColorComponent(color.g);
|
|
||||||
break;
|
|
||||||
case SDLK_D:
|
|
||||||
incrementColorComponent(color.b);
|
|
||||||
break;
|
|
||||||
case SDLK_C:
|
|
||||||
decrementColorComponent(color.b);
|
|
||||||
break;
|
|
||||||
case SDLK_F:
|
|
||||||
incrementAllComponents(color);
|
|
||||||
break;
|
|
||||||
case SDLK_V:
|
|
||||||
decrementAllComponents(color);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::incrementColorComponent(uint8_t& component) {
|
|
||||||
if (component < 255) {
|
|
||||||
++component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::decrementColorComponent(uint8_t& component) {
|
|
||||||
if (component > 0) {
|
|
||||||
--component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::incrementAllComponents(Color& color) {
|
|
||||||
incrementColorComponent(color.r);
|
|
||||||
incrementColorComponent(color.g);
|
|
||||||
incrementColorComponent(color.b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::decrementAllComponents(Color& color) {
|
|
||||||
decrementColorComponent(color.r);
|
|
||||||
decrementColorComponent(color.g);
|
|
||||||
decrementColorComponent(color.b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title::printColorValue(const Color& color) {
|
|
||||||
std::cout << "#"
|
|
||||||
<< std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(color.r)
|
|
||||||
<< std::setw(2) << std::setfill('0') << static_cast<int>(color.g)
|
|
||||||
<< std::setw(2) << std::setfill('0') << static_cast<int>(color.b)
|
|
||||||
<< '\n';
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void Title::checkInput() {
|
void Title::checkInput() {
|
||||||
|
|||||||
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_Keycode, SDL_Event, Uint64
|
#include <SDL3/SDL.h> // Para SDL_Keycode, SDL_Event, Uint64
|
||||||
|
|
||||||
#include <cstdint> // Para uint8_t
|
|
||||||
#include <memory> // Para shared_ptr, unique_ptr
|
#include <memory> // Para shared_ptr, unique_ptr
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // for Color
|
|
||||||
#include "player.hpp" // for Player
|
#include "player.hpp" // for Player
|
||||||
#include "section.hpp" // for Options, Name (ptr only)
|
#include "section.hpp" // for Options, Name (ptr only)
|
||||||
|
|
||||||
@@ -33,7 +31,6 @@ namespace Options {
|
|||||||
// • Efectos visuales: parpadeos, transiciones y efectos de fondo
|
// • Efectos visuales: parpadeos, transiciones y efectos de fondo
|
||||||
// • Gestión de controles: soporte para teclado y múltiples gamepads
|
// • Gestión de controles: soporte para teclado y múltiples gamepads
|
||||||
// • Timeouts automáticos: transición automática si no hay interacción
|
// • Timeouts automáticos: transición automática si no hay interacción
|
||||||
// • Debug de colores: herramientas de depuración para ajustes visuales
|
|
||||||
//
|
//
|
||||||
// La clase utiliza un sistema de tiempo basado en segundos para garantizar
|
// La clase utiliza un sistema de tiempo basado en segundos para garantizar
|
||||||
// comportamiento consistente independientemente del framerate.
|
// comportamiento consistente independientemente del framerate.
|
||||||
@@ -101,10 +98,6 @@ class Title {
|
|||||||
bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1
|
bool player1_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 1
|
||||||
bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2
|
bool player2_start_pressed_ = false; // Indica si se ha pulsado el botón de empezar para el jugador 2
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
Color debug_color_; // Color para depuración en modo debug
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --- Ciclo de vida del título ---
|
// --- Ciclo de vida del título ---
|
||||||
void update(float delta_time); // Actualiza las variables del objeto
|
void update(float delta_time); // Actualiza las variables del objeto
|
||||||
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
|
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
|
||||||
@@ -142,15 +135,4 @@ class Title {
|
|||||||
static void swapControllers(); // Intercambia la asignación de mandos a los jugadores
|
static void swapControllers(); // Intercambia la asignación de mandos a los jugadores
|
||||||
static void swapKeyboard(); // Intercambia el teclado de jugador
|
static void swapKeyboard(); // Intercambia el teclado de jugador
|
||||||
static void showControllers(); // Muestra información sobre los controles y los jugadores
|
static void showControllers(); // Muestra información sobre los controles y los jugadores
|
||||||
|
|
||||||
// --- Depuración (solo en modo DEBUG) ---
|
|
||||||
#ifdef _DEBUG
|
|
||||||
void handleDebugColorKeys(SDL_Keycode key); // Maneja las teclas de depuración para colores
|
|
||||||
static void adjustColorComponent(SDL_Keycode key, Color& color); // Ajusta un componente del color según la tecla
|
|
||||||
static void incrementColorComponent(uint8_t& component); // Incrementa un componente de color
|
|
||||||
static void decrementColorComponent(uint8_t& component); // Decrementa un componente de color
|
|
||||||
static void incrementAllComponents(Color& color); // Incrementa todos los componentes del color
|
|
||||||
static void decrementAllComponents(Color& color); // Decrementa todos los componentes del color
|
|
||||||
static void printColorValue(const Color& color); // Imprime el valor actual del color en consola
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
#include <SDL3/SDL.h> // Para SDL_FRect, Uint8, SDL_GetRenderTarget, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_TextureAccess, SDL_GetTextureAlphaMod
|
#include <SDL3/SDL.h> // Para SDL_FRect, Uint8, SDL_GetRenderTarget, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_TextureAccess, SDL_GetTextureAlphaMod
|
||||||
|
|
||||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, istream, ifstream, istringstream
|
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, istream, ifstream, istringstream
|
||||||
#include <utility> // Para std::cmp_less_equal
|
|
||||||
#include <iostream> // Para cerr
|
#include <iostream> // Para cerr
|
||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
|
#include <utility> // Para std::cmp_less_equal
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "color.hpp" // Para Color
|
||||||
|
|||||||
@@ -217,3 +217,25 @@ class ActionListOption : public MenuOption {
|
|||||||
void updateCurrentIndex();
|
void updateCurrentIndex();
|
||||||
[[nodiscard]] auto findCurrentIndex() const -> size_t;
|
[[nodiscard]] auto findCurrentIndex() const -> size_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Opción genérica con callbacks: getter para mostrar, adjuster(bool up) para cambiar valor
|
||||||
|
class CallbackOption : public MenuOption {
|
||||||
|
public:
|
||||||
|
CallbackOption(const std::string& cap, ServiceMenu::SettingsGroup grp, std::function<std::string()> getter, std::function<void(bool)> adjuster, std::function<int(Text*)> max_width_fn = nullptr)
|
||||||
|
: MenuOption(cap, grp),
|
||||||
|
getter_(std::move(getter)),
|
||||||
|
adjuster_(std::move(adjuster)),
|
||||||
|
max_width_fn_(std::move(max_width_fn)) {}
|
||||||
|
|
||||||
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
||||||
|
[[nodiscard]] auto getValueAsString() const -> std::string override { return getter_(); }
|
||||||
|
void adjustValue(bool adjust_up) override { adjuster_(adjust_up); }
|
||||||
|
auto getMaxValueWidth(Text* text_renderer) const -> int override {
|
||||||
|
return max_width_fn_ ? max_width_fn_(text_renderer) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<std::string()> getter_;
|
||||||
|
std::function<void(bool)> adjuster_;
|
||||||
|
std::function<int(Text*)> max_width_fn_;
|
||||||
|
};
|
||||||
@@ -363,22 +363,81 @@ void ServiceMenu::initializeOptions() {
|
|||||||
Options::window.max_zoom,
|
Options::window.max_zoom,
|
||||||
1));
|
1));
|
||||||
|
|
||||||
|
// Shader: Desactivat / PostFX / CrtPi
|
||||||
|
{
|
||||||
|
const std::string DISABLED_TEXT = Lang::getText("[SERVICE_MENU] SHADER_DISABLED");
|
||||||
|
std::vector<std::string> shader_values = {DISABLED_TEXT, "PostFX", "CrtPi"};
|
||||||
|
auto shader_getter = [DISABLED_TEXT]() -> std::string {
|
||||||
|
if (!Options::video.shader.enabled) { return DISABLED_TEXT; }
|
||||||
|
return (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) ? "CrtPi" : "PostFX";
|
||||||
|
};
|
||||||
|
auto shader_setter = [DISABLED_TEXT](const std::string& val) {
|
||||||
|
if (val == DISABLED_TEXT) {
|
||||||
|
Options::video.shader.enabled = false;
|
||||||
|
} else {
|
||||||
|
Options::video.shader.enabled = true;
|
||||||
|
const auto TYPE = (val == "CrtPi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
|
||||||
|
Options::video.shader.current_shader = TYPE;
|
||||||
|
auto* screen = Screen::get();
|
||||||
|
if (screen != nullptr) {
|
||||||
|
screen->applySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Screen::initShaders();
|
||||||
|
};
|
||||||
|
options_.push_back(std::make_unique<ListOption>(
|
||||||
|
Lang::getText("[SERVICE_MENU] SHADER"),
|
||||||
|
SettingsGroup::VIDEO,
|
||||||
|
shader_values,
|
||||||
|
shader_getter,
|
||||||
|
shader_setter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preset: muestra nombre, cicla circularmente entre presets del shader activo
|
||||||
|
{
|
||||||
|
auto preset_getter = []() -> std::string {
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
if (Options::crtpi_presets.empty()) { return ""; }
|
||||||
|
return Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
|
||||||
|
}
|
||||||
|
if (Options::postfx_presets.empty()) { return ""; }
|
||||||
|
return Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name;
|
||||||
|
};
|
||||||
|
auto preset_adjuster = [](bool up) {
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||||
|
if (Options::crtpi_presets.empty()) { return; }
|
||||||
|
const int SIZE = static_cast<int>(Options::crtpi_presets.size());
|
||||||
|
Options::video.shader.current_crtpi_preset = up
|
||||||
|
? (Options::video.shader.current_crtpi_preset + 1) % SIZE
|
||||||
|
: (Options::video.shader.current_crtpi_preset + SIZE - 1) % SIZE;
|
||||||
|
} else {
|
||||||
|
if (Options::postfx_presets.empty()) { return; }
|
||||||
|
const int SIZE = static_cast<int>(Options::postfx_presets.size());
|
||||||
|
Options::video.shader.current_postfx_preset = up
|
||||||
|
? (Options::video.shader.current_postfx_preset + 1) % SIZE
|
||||||
|
: (Options::video.shader.current_postfx_preset + SIZE - 1) % SIZE;
|
||||||
|
}
|
||||||
|
Screen::initShaders();
|
||||||
|
};
|
||||||
|
auto preset_max_width = [](Text* text) -> int {
|
||||||
|
int max_w = 0;
|
||||||
|
for (const auto& p : Options::postfx_presets) { max_w = std::max(max_w, text->length(p.name, -2)); }
|
||||||
|
for (const auto& p : Options::crtpi_presets) { max_w = std::max(max_w, text->length(p.name, -2)); }
|
||||||
|
return max_w;
|
||||||
|
};
|
||||||
|
|
||||||
|
options_.push_back(std::make_unique<CallbackOption>(
|
||||||
|
Lang::getText("[SERVICE_MENU] SHADER_PRESET"),
|
||||||
|
SettingsGroup::VIDEO,
|
||||||
|
preset_getter,
|
||||||
|
preset_adjuster,
|
||||||
|
preset_max_width));
|
||||||
|
}
|
||||||
|
|
||||||
options_.push_back(std::make_unique<BoolOption>(
|
options_.push_back(std::make_unique<BoolOption>(
|
||||||
Lang::getText("[SERVICE_MENU] POSTFX"),
|
|
||||||
SettingsGroup::VIDEO,
|
|
||||||
&Options::video.postfx));
|
|
||||||
|
|
||||||
options_.push_back(std::make_unique<IntOption>(
|
|
||||||
Lang::getText("[SERVICE_MENU] POSTFX_PRESET"),
|
|
||||||
SettingsGroup::VIDEO,
|
|
||||||
&Options::current_postfx_preset,
|
|
||||||
0, static_cast<int>(Options::postfx_presets.size()) - 1, 1));
|
|
||||||
|
|
||||||
options_.push_back(std::make_unique<IntOption>(
|
|
||||||
Lang::getText("[SERVICE_MENU] SUPERSAMPLING"),
|
Lang::getText("[SERVICE_MENU] SUPERSAMPLING"),
|
||||||
SettingsGroup::VIDEO,
|
SettingsGroup::VIDEO,
|
||||||
&Options::video.supersampling,
|
&Options::video.supersampling.enabled));
|
||||||
1, 3, 1));
|
|
||||||
|
|
||||||
options_.push_back(std::make_unique<BoolOption>(
|
options_.push_back(std::make_unique<BoolOption>(
|
||||||
Lang::getText("[SERVICE_MENU] VSYNC"),
|
Lang::getText("[SERVICE_MENU] VSYNC"),
|
||||||
@@ -568,6 +627,20 @@ void ServiceMenu::setHiddenOptions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SHADER_PRESET"));
|
||||||
|
if (option != nullptr) {
|
||||||
|
option->setHidden(!Options::video.shader.enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* option = getOptionByCaption(Lang::getText("[SERVICE_MENU] SUPERSAMPLING"));
|
||||||
|
if (option != nullptr) {
|
||||||
|
option->setHidden(!Options::video.shader.enabled || Options::video.shader.current_shader != Rendering::ShaderType::POSTFX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateMenu(); // El menú debe refrescarse si algo se oculta
|
updateMenu(); // El menú debe refrescarse si algo se oculta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
CXX := g++
|
CXX := g++
|
||||||
CXXFLAGS := -std=c++20 -Wall -Os -I../../
|
CXXFLAGS := -std=c++20 -Wall -Os
|
||||||
SOURCES := pack_resources.cpp ../../source/resource_pack.cpp
|
SOURCES := pack_resources.cpp ../../source/resource_pack.cpp
|
||||||
TARGET := pack_resources
|
TARGET := pack_resources
|
||||||
CLEAN_FILES := pack_resources *.pack *.o
|
CLEAN_FILES := pack_resources *.pack *.o
|
||||||
@@ -21,7 +21,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Reglas principales
|
# Reglas principales
|
||||||
.PHONY: all pack_tool clean help test_pack
|
.PHONY: all pack_tool pack clean help test_pack
|
||||||
|
|
||||||
# Compilar herramienta de empaquetado
|
# Compilar herramienta de empaquetado
|
||||||
all: pack_tool
|
all: pack_tool
|
||||||
@@ -37,6 +37,14 @@ clean:
|
|||||||
$(CLEAN_CMD) $(call FixPath,$(CLEAN_FILES))
|
$(CLEAN_CMD) $(call FixPath,$(CLEAN_FILES))
|
||||||
@echo "✓ Archivos limpiados"
|
@echo "✓ Archivos limpiados"
|
||||||
|
|
||||||
|
# Crear pack de recursos final (invocado desde Makefile raíz)
|
||||||
|
pack: pack_tool
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
.\$(TARGET) ..\..\data ..\..\resources.pack
|
||||||
|
else
|
||||||
|
./$(TARGET) ../../data ../../resources.pack
|
||||||
|
endif
|
||||||
|
|
||||||
# Crear pack de recursos de prueba
|
# Crear pack de recursos de prueba
|
||||||
test_pack: pack_tool
|
test_pack: pack_tool
|
||||||
@echo "Creando pack de recursos de prueba..."
|
@echo "Creando pack de recursos de prueba..."
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "../source/resource_pack.hpp"
|
#include "../../source/resource_pack.hpp"
|
||||||
#include "../build/version.h" // Para Version::APP_NAME
|
#include "../../build/version.h" // Para Version::APP_NAME
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ echo "Compiling SPIR-V shaders..."
|
|||||||
|
|
||||||
glslc "${SHADERS_DIR}/postfx.vert" -o /tmp/postfx.vert.spv
|
glslc "${SHADERS_DIR}/postfx.vert" -o /tmp/postfx.vert.spv
|
||||||
glslc "${SHADERS_DIR}/postfx.frag" -o /tmp/postfx.frag.spv
|
glslc "${SHADERS_DIR}/postfx.frag" -o /tmp/postfx.frag.spv
|
||||||
|
glslc -fshader-stage=fragment "${SHADERS_DIR}/crtpi_frag.glsl" -o /tmp/crtpi_frag.spv
|
||||||
|
glslc "${SHADERS_DIR}/upscale.frag" -o /tmp/upscale.frag.spv
|
||||||
|
glslc "${SHADERS_DIR}/downscale.frag" -o /tmp/downscale.frag.spv
|
||||||
|
|
||||||
echo "Generating C++ headers..."
|
echo "Generating C++ headers..."
|
||||||
|
|
||||||
@@ -33,12 +36,30 @@ xxd -i /tmp/postfx.frag.spv | \
|
|||||||
sed 's/unsigned int .*postfx_frag_spv_len/static const size_t kpostfx_frag_spv_size/' \
|
sed 's/unsigned int .*postfx_frag_spv_len/static const size_t kpostfx_frag_spv_size/' \
|
||||||
> "${HEADERS_DIR}/postfx_frag_spv.h"
|
> "${HEADERS_DIR}/postfx_frag_spv.h"
|
||||||
|
|
||||||
|
xxd -i /tmp/crtpi_frag.spv | \
|
||||||
|
sed 's/unsigned char .*crtpi_frag_spv\[\]/static const uint8_t kcrtpi_frag_spv[]/' | \
|
||||||
|
sed 's/unsigned int .*crtpi_frag_spv_len/static const size_t kcrtpi_frag_spv_size/' \
|
||||||
|
> "${HEADERS_DIR}/crtpi_frag_spv.h"
|
||||||
|
|
||||||
|
xxd -i /tmp/upscale.frag.spv | \
|
||||||
|
sed 's/unsigned char .*upscale_frag_spv\[\]/static const uint8_t kupscale_frag_spv[]/' | \
|
||||||
|
sed 's/unsigned int .*upscale_frag_spv_len/static const size_t kupscale_frag_spv_size/' \
|
||||||
|
> "${HEADERS_DIR}/upscale_frag_spv.h"
|
||||||
|
|
||||||
|
xxd -i /tmp/downscale.frag.spv | \
|
||||||
|
sed 's/unsigned char .*downscale_frag_spv\[\]/static const uint8_t kdownscale_frag_spv[]/' | \
|
||||||
|
sed 's/unsigned int .*downscale_frag_spv_len/static const size_t kdownscale_frag_spv_size/' \
|
||||||
|
> "${HEADERS_DIR}/downscale_frag_spv.h"
|
||||||
|
|
||||||
# Prepend required includes to the headers
|
# Prepend required includes to the headers
|
||||||
for f in "${HEADERS_DIR}/postfx_vert_spv.h" "${HEADERS_DIR}/postfx_frag_spv.h"; do
|
for f in "${HEADERS_DIR}/postfx_vert_spv.h" "${HEADERS_DIR}/postfx_frag_spv.h" "${HEADERS_DIR}/crtpi_frag_spv.h" "${HEADERS_DIR}/upscale_frag_spv.h" "${HEADERS_DIR}/downscale_frag_spv.h"; do
|
||||||
echo -e "#pragma once\n#include <cstdint>\n#include <cstddef>\n$(cat "$f")" > "$f"
|
echo -e "#pragma once\n#include <cstdint>\n#include <cstddef>\n$(cat "$f")" > "$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
||||||
echo " postfx_vert_spv.h"
|
echo " postfx_vert_spv.h"
|
||||||
echo " postfx_frag_spv.h"
|
echo " postfx_frag_spv.h"
|
||||||
|
echo " crtpi_frag_spv.h"
|
||||||
|
echo " upscale_frag_spv.h"
|
||||||
|
echo " downscale_frag_spv.h"
|
||||||
echo "Rebuild the project to use the new shaders."
|
echo "Rebuild the project to use the new shaders."
|
||||||
|
|||||||
Reference in New Issue
Block a user