Compare commits
6 Commits
55caef3210
...
20b9a95619
| Author | SHA1 | Date | |
|---|---|---|---|
| 20b9a95619 | |||
| 513eacf356 | |||
| 5889df2a47 | |||
| 7f703390f9 | |||
| 1bb0ebdef8 | |||
| 5fec0110b3 |
108
CLAUDE.md
108
CLAUDE.md
@@ -4,67 +4,90 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
Coffee Crisis is a C++20 arcade game built with SDL2. The player controls a character defending the UPV (university) from bouncing coffee-ball enemies across 10 stages. Supports 1-2 players, keyboard and gamepad input, and multiple languages (Spanish, Basque, English).
|
Coffee Crisis is a C++20 arcade game built with SDL3. The player controls a character defending the UPV (university) from bouncing coffee-ball enemies across 10 stages. Supports 1-2 players, keyboard and gamepad input, and multiple languages (Spanish, Basque, English).
|
||||||
|
|
||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
Dependencies: `libsdl2-dev` and `g++` (Linux) or `clang++` (macOS).
|
Dependencies: `libsdl3-dev` and `g++` (Linux) or `clang++` (macOS). Build system is CMake (driven by `Makefile` wrappers).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux
|
make # Release build
|
||||||
make linux # Release build → ./coffee_crisis
|
make debug # Debug build (defines DEBUG and PAUSE)
|
||||||
make linux_debug # Debug build (defines DEBUG and PAUSE) → ./coffee_crisis_debug
|
make release # Empaqueta .tar.gz / .dmg / .zip segons SO
|
||||||
|
make pack # Regenera resources.pack
|
||||||
# macOS
|
make compile_shaders # Compila shaders GLSL → headers SPIR-V (requereix glslc)
|
||||||
make macos # Release build with clang++
|
make controllerdb # Descarga gamecontrollerdb.txt
|
||||||
make macos_debug # Debug build
|
make format # clang-format -i
|
||||||
|
make tidy # clang-tidy
|
||||||
# Windows (MinGW)
|
make cppcheck # cppcheck
|
||||||
make windows # Release build → coffee_crisis.exe
|
|
||||||
make windows_debug # Debug build
|
|
||||||
|
|
||||||
# Release packaging
|
|
||||||
make linux_release # Builds and creates .tar.gz
|
|
||||||
make macos_release # Builds Intel + Apple Silicon .dmg files
|
|
||||||
make windows_release # Builds and creates .zip
|
|
||||||
```
|
```
|
||||||
|
|
||||||
There is also a CMakeLists.txt available as an alternative build system.
|
|
||||||
|
|
||||||
There are no tests or linter configured.
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
All source code is in `source/`. The game uses a section-based architecture controlled by the **Director** class:
|
Source layout (alineat amb la resta de projectes germans):
|
||||||
|
|
||||||
- **Director** (`director.h/cpp`): Top-level controller. Initializes SDL, manages the window/renderer, and runs sections in sequence: Logo → Intro → Title → Game → Quit. Owns all shared objects (Screen, Input, Lang, Asset).
|
```
|
||||||
- **Game** (`game.h/cpp`): Core gameplay logic. Manages players, balloons (enemies), bullets, items, stages, menace level, and collision detection. Contains its own update/render loop plus sub-loops for pause and game over screens.
|
source/
|
||||||
- **Screen** (`screen.h/cpp`): Rendering abstraction. Manages a virtual canvas (256×192) that gets scaled to the actual window. Handles fullscreen/windowed modes, border rendering, and fade effects.
|
├── main.cpp
|
||||||
- **Input** (`input.h/cpp`): Abstracts keyboard and gamepad input.
|
├── core/
|
||||||
- **Asset** (`asset.h/cpp`): Resource file index. Files are registered with `add()` and retrieved by name with `get()`. All paths are relative to the executable.
|
│ ├── audio/ jail_audio.hpp
|
||||||
- **Lang** (`lang.h/cpp`): i18n system loading text strings from files in `data/lang/`.
|
│ ├── input/ input.*, mouse.*
|
||||||
|
│ ├── locale/ lang.*
|
||||||
|
│ ├── rendering/ screen, fade, text, writer, texture, sprite + animated/moving/smart
|
||||||
|
│ │ ├── shader_backend.hpp (interfície abstracta de post-procesado)
|
||||||
|
│ │ └── sdl3gpu/ (pipeline SDL3 GPU)
|
||||||
|
│ │ ├── sdl3gpu_shader.* (implementació del backend GPU)
|
||||||
|
│ │ └── spv/ (headers SPIR-V generats — protegits amb dummies `.clang-*`)
|
||||||
|
│ ├── resources/ asset, resource, resource_pack, resource_loader, resource_helper
|
||||||
|
│ └── system/ director
|
||||||
|
├── game/
|
||||||
|
│ ├── defaults.hpp (constants de gameplay: block size, canvas, áreas, colors)
|
||||||
|
│ ├── game.* (hub de gameplay)
|
||||||
|
│ ├── entities/ player, balloon, bullet, item
|
||||||
|
│ ├── scenes/ logo, intro, title, instructions
|
||||||
|
│ └── ui/ menu
|
||||||
|
├── utils/
|
||||||
|
│ ├── defines.hpp (macros de build)
|
||||||
|
│ └── utils.* (helpers, enum de dificultat, circle_t, ...)
|
||||||
|
└── external/ (stb_image, stb_vorbis — protegits amb dummies `.clang-*`)
|
||||||
|
```
|
||||||
|
|
||||||
### Sprite hierarchy
|
Flux general controlat per la classe **Director** (`core/system/director.h`): inicialitza SDL, finestra/renderer i executa seccions en seqüència **Logo → Intro → Title → Game → Quit**. Les classes principals:
|
||||||
|
|
||||||
- **Sprite** → base class for drawing from a PNG spritesheet
|
- **Game** (`game/game.h`): gameplay nuclear. Gestiona jugadors, balloons, bullets, items, stages, nivell d'amenaça i col·lisions. Té el seu bucle d'update/render i sub-bucles per pausa i game-over.
|
||||||
- **AnimatedSprite** → extends Sprite with frame-based animation (loaded from `.ani` files)
|
- **Screen** (`core/rendering/screen.h`): abstracció de render. Canvas virtual 256×192 escalat a la finestra. Fullscreen/windowed, borders, fades.
|
||||||
- **MovingSprite** → sprite with movement
|
- **Input** (`core/input/input.h`): abstracció de teclat i gamepad.
|
||||||
- **SmartSprite** → sprite with autonomous behavior (score popups, thrown items)
|
- **Asset** (`core/resources/asset.h`): índex de fitxers de recurs (`add`/`get` per nom).
|
||||||
|
- **Lang** (`core/locale/lang.h`): i18n, carrega strings des de `data/lang/`.
|
||||||
|
|
||||||
### Game entities
|
### Sprite hierarchy (`core/rendering/`)
|
||||||
|
|
||||||
- **Player** (`player.h/cpp`): Player character state and rendering
|
- **Sprite** → base per dibuixar des d'un spritesheet PNG
|
||||||
- **Balloon** (`balloon.h/cpp`): Enemy entities with multiple types and split-on-pop behavior
|
- **AnimatedSprite** → afegeix animació per frames (arxius `.ani`)
|
||||||
- **Bullet** (`bullet.h/cpp`): Projectiles fired by the player (left/center/right)
|
- **MovingSprite** → sprite amb posició/velocitat
|
||||||
- **Item** (`item.h/cpp`): Collectible items (points, clock, coffee, power-ups)
|
- **SmartSprite** → sprite autònom (score popups, objectes llençats)
|
||||||
|
|
||||||
### Audio
|
### Audio
|
||||||
|
|
||||||
**jail_audio** (`jail_audio.h/cpp`): Custom audio library wrapping SDL2 audio. Uses stb_vorbis for OGG decoding. Provides `JA_*` functions for music and sound effects with channel-based mixing.
|
**jail_audio** (`core/audio/jail_audio.hpp`): wrapper audio SDL3 first-party. Usa stb_vorbis per OGG. API `JA_*` per música i efectes amb mesclat per canals.
|
||||||
|
|
||||||
### Key constants
|
### GPU / shaders (post-procesado)
|
||||||
|
|
||||||
Defined in `const.h`: block size (8px), virtual canvas (256×192), play area bounds, section/subsection IDs, and color definitions.
|
Pipeline SDL3 GPU portat de `coffee_crisis_arcade_edition`. El canvas 256×192 es pot passar per un backend GPU que aplica PostFX (vinyeta, scanlines, chroma, gamma, mask, curvatura, bleeding, flicker) o CrtPi (scanlines continues amb bloom). Fallback transparent al `SDL_Renderer` clàssic si la GPU falla o si es desactiva.
|
||||||
|
|
||||||
|
- **Interfície**: `core/rendering/shader_backend.hpp` (`Rendering::ShaderBackend`).
|
||||||
|
- **Implementació**: `core/rendering/sdl3gpu/sdl3gpu_shader.*` + shaders GLSL a `data/shaders/` compilats a `spv/*_spv.h` via `glslc` (o precompilats si no hi ha `glslc`).
|
||||||
|
- **Emscripten**: compile-time `NO_SHADERS` → sempre ruta clàssica.
|
||||||
|
- **macOS**: shaders Metal (MSL) inline dins `sdl3gpu_shader.cpp`; no SPIR-V.
|
||||||
|
- **Opcions persistents** a `config.txt` (migració a YAML pendent):
|
||||||
|
- `videoGpuAcceleration` (bool)
|
||||||
|
- `videoGpuPreferredDriver` (string, buit = auto)
|
||||||
|
- `videoShaderEnabled` (bool)
|
||||||
|
- `videoShaderType` (0=POSTFX, 1=CRTPI)
|
||||||
|
- **Hotkeys** (provisionals fins que hi hagi menú d'opcions): `F9` toggle GPU · `F10` toggle shader · `F11` alterna POSTFX ↔ CRTPI.
|
||||||
|
- **API** a `Screen`: `setGpuAcceleration`/`toggleGpuAcceleration`/`isGpuAccelerated`, `setShaderEnabled`/`toggleShaderEnabled`/`isShaderEnabled`, `setActiveShader`/`toggleActiveShader`/`getActiveShader`.
|
||||||
|
|
||||||
|
Presets PostFX/CrtPi i cicle de presets encara **no** estan implementats — arribaran amb la migració a YAML. Per ara, valors per defecte hardcoded.
|
||||||
|
|
||||||
## Data Directory
|
## Data Directory
|
||||||
|
|
||||||
@@ -74,6 +97,7 @@ Defined in `const.h`: block size (8px), virtual canvas (256×192), play area bou
|
|||||||
- `data/lang/` — language files (es_ES, ba_BA, en_UK)
|
- `data/lang/` — language files (es_ES, ba_BA, en_UK)
|
||||||
- `data/demo/` — demo recording data (gamecontrollerdb.txt vive en la raíz del proyecto, fuera del pack)
|
- `data/demo/` — demo recording data (gamecontrollerdb.txt vive en la raíz del proyecto, fuera del pack)
|
||||||
- `data/menu/` — menu definition files
|
- `data/menu/` — menu definition files
|
||||||
|
- `data/shaders/` — fonts GLSL per al post-procesado SDL3 GPU (no van al pack; s'empotren al binari via headers SPIR-V)
|
||||||
|
|
||||||
## Language
|
## Language
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,15 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -ffunction-sections
|
|||||||
# Define el directorio de los archivos fuente
|
# Define el directorio de los archivos fuente
|
||||||
set(DIR_SOURCES "${CMAKE_SOURCE_DIR}/source")
|
set(DIR_SOURCES "${CMAKE_SOURCE_DIR}/source")
|
||||||
|
|
||||||
# Cargar todos los archivos fuente en DIR_SOURCES
|
# Cargar todos los archivos fuente en DIR_SOURCES (recursivo, sin external/)
|
||||||
file(GLOB SOURCES "${DIR_SOURCES}/*.cpp")
|
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "${DIR_SOURCES}/*.cpp")
|
||||||
|
list(FILTER SOURCES EXCLUDE REGEX "${DIR_SOURCES}/external/.*")
|
||||||
|
|
||||||
|
# En Emscripten no compilamos sdl3gpu_shader (SDL3 GPU no está soportado en WebGL2).
|
||||||
|
# Define NO_SHADERS más abajo y filtra el fuente aquí.
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
list(REMOVE_ITEM SOURCES "${DIR_SOURCES}/core/rendering/sdl3gpu/sdl3gpu_shader.cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Verificar si se encontraron archivos fuente
|
# Verificar si se encontraron archivos fuente
|
||||||
if(NOT SOURCES)
|
if(NOT SOURCES)
|
||||||
@@ -54,9 +61,81 @@ else()
|
|||||||
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
|
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# --- SHADER COMPILATION (Linux/Windows only - macOS uses Metal, Emscripten no soporta SDL3 GPU) ---
|
||||||
|
if(NOT APPLE AND NOT EMSCRIPTEN)
|
||||||
|
find_program(GLSLC_EXE NAMES glslc)
|
||||||
|
|
||||||
|
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||||
|
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/core/rendering/sdl3gpu/spv/postfx_vert_spv.h")
|
||||||
|
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/spv/postfx_frag_spv.h")
|
||||||
|
set(SHADER_CRTPI_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/spv/crtpi_frag_spv.h")
|
||||||
|
set(SHADER_UPSCALE_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/spv/upscale_frag_spv.h")
|
||||||
|
set(SHADER_DOWNSCALE_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/spv/downscale_frag_spv.h")
|
||||||
|
|
||||||
|
set(ALL_SHADER_HEADERS "${SHADER_VERT_H}" "${SHADER_FRAG_H}" "${SHADER_CRTPI_H}" "${SHADER_UPSCALE_H}" "${SHADER_DOWNSCALE_H}")
|
||||||
|
|
||||||
|
if(GLSLC_EXE)
|
||||||
|
set(COMPILE_SHADER_SCRIPT "${CMAKE_SOURCE_DIR}/tools/shaders/compile_shader.cmake")
|
||||||
|
|
||||||
|
macro(add_shader SRC_FILE OUT_H VAR_NAME)
|
||||||
|
cmake_parse_arguments(S "" "STAGE" "" ${ARGN})
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${OUT_H}"
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
"-DGLSLC=${GLSLC_EXE}"
|
||||||
|
"-DSRC=${SRC_FILE}"
|
||||||
|
"-DOUT_H=${OUT_H}"
|
||||||
|
"-DVAR=${VAR_NAME}"
|
||||||
|
"-DSTAGE=${S_STAGE}"
|
||||||
|
-P "${COMPILE_SHADER_SCRIPT}"
|
||||||
|
DEPENDS "${SRC_FILE}" "${COMPILE_SHADER_SCRIPT}"
|
||||||
|
COMMENT "Compilando shader: ${VAR_NAME}"
|
||||||
|
)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
add_shader("${SHADER_VERT_SRC}" "${SHADER_VERT_H}" "postfx_vert_spv")
|
||||||
|
add_shader("${SHADER_FRAG_SRC}" "${SHADER_FRAG_H}" "postfx_frag_spv")
|
||||||
|
add_shader("${SHADER_CRTPI_SRC}" "${SHADER_CRTPI_H}" "crtpi_frag_spv" STAGE fragment)
|
||||||
|
add_shader("${SHADER_UPSCALE_SRC}" "${SHADER_UPSCALE_H}" "upscale_frag_spv")
|
||||||
|
add_shader("${SHADER_DOWNSCALE_SRC}" "${SHADER_DOWNSCALE_H}" "downscale_frag_spv")
|
||||||
|
|
||||||
|
add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
|
||||||
|
message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
|
||||||
|
else()
|
||||||
|
foreach(_h IN LISTS ALL_SHADER_HEADERS)
|
||||||
|
if(NOT EXISTS "${_h}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"glslc no encontrado y header SPIR-V no existe: ${_h}\n"
|
||||||
|
" Instala glslc: sudo apt install glslang-tools (Linux)\n"
|
||||||
|
" choco install vulkan-sdk (Windows)"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
message(STATUS "Emscripten: shaders SPIR-V omitidos (SDL3 GPU no soportado en WebGL2)")
|
||||||
|
else()
|
||||||
|
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal inline)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Añadir ejecutable principal
|
# Añadir ejecutable principal
|
||||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
|
||||||
|
if(NOT APPLE AND NOT EMSCRIPTEN AND GLSLC_EXE)
|
||||||
|
add_dependencies(${PROJECT_NAME} shaders)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Includes relatius a source/ (p.e. `#include "core/rendering/texture.h"`)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${DIR_SOURCES})
|
||||||
|
|
||||||
# Configuración de salida: el ejecutable principal va a la raíz del proyecto.
|
# Configuración de salida: el ejecutable principal va a la raíz del proyecto.
|
||||||
# Per-target (no global) perquè `pack_resources` acabe a `build/` com la resta
|
# Per-target (no global) perquè `pack_resources` acabe a `build/` com la resta
|
||||||
# de projectes.
|
# de projectes.
|
||||||
@@ -92,7 +171,7 @@ elseif(APPLE)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
elseif(EMSCRIPTEN)
|
elseif(EMSCRIPTEN)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE EMSCRIPTEN_BUILD)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE EMSCRIPTEN_BUILD NO_SHADERS)
|
||||||
# En wasm NO empaquetamos un resources.pack: el propio --preload-file de
|
# En wasm NO empaquetamos un resources.pack: el propio --preload-file de
|
||||||
# emscripten ya hace el mismo trabajo (bundle del directorio en un .data),
|
# emscripten ya hace el mismo trabajo (bundle del directorio en un .data),
|
||||||
# así que metemos directamente 'data' y dejamos que el Resource lea por
|
# así que metemos directamente 'data' y dejamos que el Resource lea por
|
||||||
@@ -204,7 +283,7 @@ endif()
|
|||||||
if(NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
add_executable(pack_resources EXCLUDE_FROM_ALL
|
add_executable(pack_resources EXCLUDE_FROM_ALL
|
||||||
tools/pack_resources/pack_resources.cpp
|
tools/pack_resources/pack_resources.cpp
|
||||||
source/resource_pack.cpp
|
source/core/resources/resource_pack.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source")
|
target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source")
|
||||||
target_compile_options(pack_resources PRIVATE -Wall)
|
target_compile_options(pack_resources PRIVATE -Wall)
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -19,9 +19,9 @@ RESOURCE_FILE := release/windows/coffee.res
|
|||||||
# VERSION (extracted from defines.hpp)
|
# VERSION (extracted from defines.hpp)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
VERSION := $(shell powershell -Command "(Select-String -Path 'source/defines.hpp' -Pattern 'constexpr const char\* VERSION = \"(.+?)\"').Matches.Groups[1].Value")
|
VERSION := $(shell powershell -Command "(Select-String -Path 'source/utils/defines.hpp' -Pattern 'constexpr const char\* VERSION = \"(.+?)\"').Matches.Groups[1].Value")
|
||||||
else
|
else
|
||||||
VERSION := $(shell grep 'constexpr const char\* VERSION' source/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
VERSION := $(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -349,6 +349,11 @@ cppcheck:
|
|||||||
@cmake $(CMAKE_GEN) -S . -B build -DCMAKE_BUILD_TYPE=Release -DGIT_HASH=$(GIT_HASH)
|
@cmake $(CMAKE_GEN) -S . -B build -DCMAKE_BUILD_TYPE=Release -DGIT_HASH=$(GIT_HASH)
|
||||||
@cmake --build build --target cppcheck
|
@cmake --build build --target cppcheck
|
||||||
|
|
||||||
|
# SHADERS (SPIR-V) — sólo Linux/Windows. Requiere glslc en el PATH.
|
||||||
|
compile_shaders:
|
||||||
|
@cmake $(CMAKE_GEN) -S . -B build -DCMAKE_BUILD_TYPE=Release -DGIT_HASH=$(GIT_HASH)
|
||||||
|
@cmake --build build --target shaders
|
||||||
|
|
||||||
# DESCARGA DE GAMECONTROLLERDB
|
# DESCARGA DE GAMECONTROLLERDB
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
controllerdb:
|
controllerdb:
|
||||||
@@ -378,6 +383,7 @@ help:
|
|||||||
@echo ""
|
@echo ""
|
||||||
@echo " Herramientas:"
|
@echo " Herramientas:"
|
||||||
@echo " make pack - Empaquetar recursos a resources.pack"
|
@echo " make pack - Empaquetar recursos a resources.pack"
|
||||||
|
@echo " make compile_shaders - Compilar shaders GLSL a headers SPIR-V (requiere glslc)"
|
||||||
@echo " make controllerdb - Descargar gamecontrollerdb.txt actualizado"
|
@echo " make controllerdb - Descargar gamecontrollerdb.txt actualizado"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " Calidad de codigo:"
|
@echo " Calidad de codigo:"
|
||||||
@@ -391,4 +397,4 @@ help:
|
|||||||
@echo " make show_version - Mostrar version actual ($(VERSION))"
|
@echo " make show_version - Mostrar version actual ($(VERSION))"
|
||||||
@echo " make help - Mostrar esta ayuda"
|
@echo " make help - Mostrar esta ayuda"
|
||||||
|
|
||||||
.PHONY: all debug release _windows_release _macos_release _linux_release wasm wasm_debug controllerdb pack format format-check tidy tidy-fix cppcheck show_version help
|
.PHONY: all debug release _windows_release _macos_release _linux_release wasm wasm_debug controllerdb pack format format-check tidy tidy-fix cppcheck compile_shaders show_version help
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
142
data/shaders/postfx.frag
Normal file
142
data/shaders/postfx.frag
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Vulkan GLSL fragment shader — PostFX effects
|
||||||
|
// Used for SDL3 GPU API (SPIR-V path, Win/Linux).
|
||||||
|
// Compile: glslc postfx.frag -o postfx.frag.spv
|
||||||
|
// xxd -i postfx.frag.spv > ../../source/core/rendering/sdl3gpu/postfx_frag_spv.h
|
||||||
|
//
|
||||||
|
// PostFXUniforms must match exactly the C++ struct in sdl3gpu_shader.hpp
|
||||||
|
// (8 floats, 32 bytes, std140/scalar layout).
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 v_uv;
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
layout(set = 2, binding = 0) uniform sampler2D scene;
|
||||||
|
|
||||||
|
layout(set = 3, binding = 0) uniform PostFXUniforms {
|
||||||
|
float vignette_strength;
|
||||||
|
float chroma_strength;
|
||||||
|
float scanline_strength;
|
||||||
|
float screen_height;
|
||||||
|
float mask_strength;
|
||||||
|
float gamma_strength;
|
||||||
|
float curvature;
|
||||||
|
float bleeding;
|
||||||
|
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||||
|
float time; // seconds since SDL init
|
||||||
|
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||||||
|
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — 48 bytes total (3 × 16)
|
||||||
|
} u;
|
||||||
|
|
||||||
|
// YCbCr helpers for NTSC bleeding
|
||||||
|
vec3 rgb_to_ycc(vec3 rgb) {
|
||||||
|
return vec3(
|
||||||
|
0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b,
|
||||||
|
-0.169*rgb.r - 0.331*rgb.g + 0.500*rgb.b + 0.5,
|
||||||
|
0.500*rgb.r - 0.419*rgb.g - 0.081*rgb.b + 0.5
|
||||||
|
);
|
||||||
|
}
|
||||||
|
vec3 ycc_to_rgb(vec3 ycc) {
|
||||||
|
float y = ycc.x;
|
||||||
|
float cb = ycc.y - 0.5;
|
||||||
|
float cr = ycc.z - 0.5;
|
||||||
|
return clamp(vec3(
|
||||||
|
y + 1.402*cr,
|
||||||
|
y - 0.344*cb - 0.714*cr,
|
||||||
|
y + 1.772*cb
|
||||||
|
), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = v_uv;
|
||||||
|
|
||||||
|
// Curvatura barrel CRT
|
||||||
|
if (u.curvature > 0.0) {
|
||||||
|
vec2 c = uv - 0.5;
|
||||||
|
float rsq = dot(c, c);
|
||||||
|
vec2 dist = vec2(0.05, 0.1) * u.curvature;
|
||||||
|
vec2 barrelScale = vec2(1.0) - 0.23 * dist;
|
||||||
|
c += c * (dist * rsq);
|
||||||
|
c *= barrelScale;
|
||||||
|
if (abs(c.x) >= 0.5 || abs(c.y) >= 0.5) {
|
||||||
|
out_color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uv = c + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Muestra base
|
||||||
|
vec3 base = texture(scene, uv).rgb;
|
||||||
|
|
||||||
|
// Sangrado NTSC — difuminado horizontal de crominancia.
|
||||||
|
// step = 1 pixel lógico de juego en UV (corrige SS: textureSize.x = game_w * oversample).
|
||||||
|
vec3 colour;
|
||||||
|
if (u.bleeding > 0.0) {
|
||||||
|
float tw = float(textureSize(scene, 0).x);
|
||||||
|
float step = u.oversample / tw; // 1 pixel lógico en UV
|
||||||
|
vec3 ycc = rgb_to_ycc(base);
|
||||||
|
vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0*step, 0.0)).rgb);
|
||||||
|
vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0*step, 0.0)).rgb);
|
||||||
|
vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0*step, 0.0)).rgb);
|
||||||
|
vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + vec2(2.0*step, 0.0)).rgb);
|
||||||
|
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
|
||||||
|
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
||||||
|
} else {
|
||||||
|
colour = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aberración cromática (drift animado con time para efecto NTSC real)
|
||||||
|
float ca = u.chroma_strength * 0.005 * (1.0 + 0.15 * sin(u.time * 7.3));
|
||||||
|
colour.r = texture(scene, uv + vec2(ca, 0.0)).r;
|
||||||
|
colour.b = texture(scene, uv - vec2(ca, 0.0)).b;
|
||||||
|
|
||||||
|
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||||
|
if (u.gamma_strength > 0.0) {
|
||||||
|
vec3 lin = pow(colour, vec3(2.4));
|
||||||
|
colour = mix(colour, lin, u.gamma_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scanlines — 1 pixel físico oscuro por fila lógica.
|
||||||
|
// Modelo sustractivo: las filas de scanline se oscurecen, las demás no cambian.
|
||||||
|
// Esto evita el efecto de sobrebrillo en contenido con colores vivos.
|
||||||
|
if (u.scanline_strength > 0.0) {
|
||||||
|
float ps = max(1.0, round(u.pixel_scale));
|
||||||
|
float frac_in_row = fract(uv.y * u.screen_height);
|
||||||
|
float row_pos = floor(frac_in_row * ps);
|
||||||
|
float is_dark = step(ps - 1.0, row_pos);
|
||||||
|
float scan = mix(1.0, 0.0, is_dark);
|
||||||
|
colour *= mix(1.0, scan, u.scanline_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.gamma_strength > 0.0) {
|
||||||
|
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||||
|
colour = mix(colour, enc, u.gamma_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viñeta
|
||||||
|
vec2 d = uv - 0.5;
|
||||||
|
float vignette = 1.0 - dot(d, d) * u.vignette_strength;
|
||||||
|
colour *= clamp(vignette, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Máscara de fósforo RGB — después de scanlines (orden original):
|
||||||
|
// filas brillantes saturadas → máscara invisible, filas oscuras → RGB visible.
|
||||||
|
if (u.mask_strength > 0.0) {
|
||||||
|
float whichMask = fract(gl_FragCoord.x * 0.3333333);
|
||||||
|
vec3 mask = vec3(0.80);
|
||||||
|
if (whichMask < 0.3333333)
|
||||||
|
mask.x = 1.0;
|
||||||
|
else if (whichMask < 0.6666666)
|
||||||
|
mask.y = 1.0;
|
||||||
|
else
|
||||||
|
mask.z = 1.0;
|
||||||
|
colour = mix(colour, colour * mask, u.mask_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parpadeo de fósforo CRT (~50 Hz)
|
||||||
|
if (u.flicker > 0.0) {
|
||||||
|
float flicker_wave = sin(u.time * 100.0) * 0.5 + 0.5;
|
||||||
|
colour *= 1.0 - u.flicker * 0.04 * flicker_wave;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_color = vec4(colour, 1.0);
|
||||||
|
}
|
||||||
24
data/shaders/postfx.vert
Normal file
24
data/shaders/postfx.vert
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
// Vulkan GLSL vertex shader — postfx full-screen triangle
|
||||||
|
// Used for SDL3 GPU API (SPIR-V path, Win/Linux).
|
||||||
|
// Compile: glslc postfx.vert -o postfx.vert.spv
|
||||||
|
// xxd -i postfx.vert.spv > ../../source/core/rendering/sdl3gpu/postfx_vert_spv.h
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 v_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Full-screen triangle (no vertex buffer needed)
|
||||||
|
const vec2 positions[3] = vec2[3](
|
||||||
|
vec2(-1.0, -1.0),
|
||||||
|
vec2( 3.0, -1.0),
|
||||||
|
vec2(-1.0, 3.0)
|
||||||
|
);
|
||||||
|
const vec2 uvs[3] = vec2[3](
|
||||||
|
vec2(0.0, 1.0),
|
||||||
|
vec2(2.0, 1.0),
|
||||||
|
vec2(0.0,-1.0)
|
||||||
|
);
|
||||||
|
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||||
|
v_uv = uvs[gl_VertexIndex];
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
@@ -96,15 +96,15 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) {
|
|||||||
if (!music || !music->vorbis || !music->stream) return 0;
|
if (!music || !music->vorbis || !music->stream) return 0;
|
||||||
|
|
||||||
short chunk[JA_MUSIC_CHUNK_SHORTS];
|
short chunk[JA_MUSIC_CHUNK_SHORTS];
|
||||||
const int channels = music->spec.channels;
|
const int numChannels = music->spec.channels;
|
||||||
const int samples_per_channel = stb_vorbis_get_samples_short_interleaved(
|
const int samples_per_channel = stb_vorbis_get_samples_short_interleaved(
|
||||||
music->vorbis,
|
music->vorbis,
|
||||||
channels,
|
numChannels,
|
||||||
chunk,
|
chunk,
|
||||||
JA_MUSIC_CHUNK_SHORTS);
|
JA_MUSIC_CHUNK_SHORTS);
|
||||||
if (samples_per_channel <= 0) return 0;
|
if (samples_per_channel <= 0) return 0;
|
||||||
|
|
||||||
const int bytes = samples_per_channel * channels * JA_MUSIC_BYTES_PER_SAMPLE;
|
const int bytes = samples_per_channel * numChannels * JA_MUSIC_BYTES_PER_SAMPLE;
|
||||||
SDL_PutAudioStreamData(music->stream, chunk, bytes);
|
SDL_PutAudioStreamData(music->stream, chunk, bytes);
|
||||||
return samples_per_channel;
|
return samples_per_channel;
|
||||||
}
|
}
|
||||||
42
source/core/input/global_inputs.cpp
Normal file
42
source/core/input/global_inputs.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include "core/input/global_inputs.hpp"
|
||||||
|
|
||||||
|
#include "core/input/input.h"
|
||||||
|
#include "core/rendering/screen.h"
|
||||||
|
|
||||||
|
namespace GlobalInputs {
|
||||||
|
|
||||||
|
auto handle() -> bool {
|
||||||
|
if (Screen::get() == nullptr || Input::get() == nullptr) { return false; }
|
||||||
|
|
||||||
|
if (Input::get()->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->toggleVideoMode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->decWindowZoom();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->incWindowZoom();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_prev_preset, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->prevPreset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_next_preset, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->nextPreset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_toggle_shader, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->toggleShaderEnabled();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input::get()->checkInput(input_toggle_shader_type, REPEAT_FALSE)) {
|
||||||
|
Screen::get()->toggleActiveShader();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GlobalInputs
|
||||||
10
source/core/input/global_inputs.hpp
Normal file
10
source/core/input/global_inputs.hpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace GlobalInputs {
|
||||||
|
// Gestiona els atalls globals disponibles en qualsevol escena: zoom de
|
||||||
|
// finestra (F1/F2), fullscreen (F3), presets de shader (F8/F9), toggle
|
||||||
|
// shader (F10) i tipus de shader POSTFX↔CRTPI (F11). Retorna true si ha
|
||||||
|
// consumit alguna tecla (per si la capa cridant vol suprimir-la del
|
||||||
|
// processament específic de l'escena).
|
||||||
|
auto handle() -> bool;
|
||||||
|
} // namespace GlobalInputs
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "input.h"
|
#include "core/input/input.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <algorithm> // for any_of
|
||||||
#include <iostream> // for basic_ostream, operator<<, cout, basi...
|
#include <iostream> // for basic_ostream, operator<<, cout, basi...
|
||||||
|
|
||||||
// Emscripten-only: SDL 3.4+ ja no casa el GUID dels mandos de Chrome Android
|
// Emscripten-only: SDL 3.4+ ja no casa el GUID dels mandos de Chrome Android
|
||||||
@@ -39,11 +40,30 @@ static void installWebStandardMapping(SDL_JoystickID jid) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Instancia única
|
||||||
Input::Input(std::string file) {
|
Input *Input::instance = nullptr;
|
||||||
// Fichero gamecontrollerdb.txt
|
|
||||||
dbPath = file;
|
|
||||||
|
|
||||||
|
// Singleton API
|
||||||
|
void Input::init(const std::string &gameControllerDbPath) {
|
||||||
|
Input::instance = new Input(gameControllerDbPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::destroy() {
|
||||||
|
delete Input::instance;
|
||||||
|
Input::instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Input::get() -> Input * {
|
||||||
|
return Input::instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Input::Input(const std::string &file)
|
||||||
|
: numGamepads(0),
|
||||||
|
dbPath(file),
|
||||||
|
verbose(true),
|
||||||
|
disabledUntil(d_notDisabled),
|
||||||
|
enabled(true) {
|
||||||
// Inicializa las variables
|
// Inicializa las variables
|
||||||
keyBindings_t kb;
|
keyBindings_t kb;
|
||||||
kb.scancode = 0;
|
kb.scancode = 0;
|
||||||
@@ -54,10 +74,6 @@ Input::Input(std::string file) {
|
|||||||
gcb.button = SDL_GAMEPAD_BUTTON_INVALID;
|
gcb.button = SDL_GAMEPAD_BUTTON_INVALID;
|
||||||
gcb.active = false;
|
gcb.active = false;
|
||||||
gameControllerBindings.resize(input_number_of_inputs, gcb);
|
gameControllerBindings.resize(input_number_of_inputs, gcb);
|
||||||
|
|
||||||
numGamepads = 0;
|
|
||||||
verbose = true;
|
|
||||||
enabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -294,11 +310,9 @@ bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Si el mando ya está registrado no hace nada (ej. evento retroactivo tras el scan inicial)
|
// Si el mando ya está registrado no hace nada (ej. evento retroactivo tras el scan inicial)
|
||||||
for (SDL_JoystickID existing : connectedControllerIds) {
|
if (std::any_of(connectedControllerIds.begin(), connectedControllerIds.end(), [jid](SDL_JoystickID existing) { return existing == jid; })) {
|
||||||
if (existing == jid) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
installWebStandardMapping(jid);
|
installWebStandardMapping(jid);
|
||||||
SDL_Gamepad *pad = SDL_OpenGamepad(jid);
|
SDL_Gamepad *pad = SDL_OpenGamepad(jid);
|
||||||
@@ -34,6 +34,12 @@ enum inputs_e {
|
|||||||
input_window_inc_size,
|
input_window_inc_size,
|
||||||
input_window_dec_size,
|
input_window_dec_size,
|
||||||
|
|
||||||
|
// GPU / shaders (hotkeys provisionales hasta que haya menú de opciones)
|
||||||
|
input_prev_preset,
|
||||||
|
input_next_preset,
|
||||||
|
input_toggle_shader,
|
||||||
|
input_toggle_shader_type,
|
||||||
|
|
||||||
// Input obligatorio
|
// Input obligatorio
|
||||||
input_number_of_inputs
|
input_number_of_inputs
|
||||||
};
|
};
|
||||||
@@ -73,9 +79,17 @@ class Input {
|
|||||||
// Construye el nombre visible de un mando (name truncado + sufijo #N)
|
// Construye el nombre visible de un mando (name truncado + sufijo #N)
|
||||||
std::string buildControllerName(SDL_Gamepad *pad, int padIndex);
|
std::string buildControllerName(SDL_Gamepad *pad, int padIndex);
|
||||||
|
|
||||||
|
// Constructor privado (usar Input::init)
|
||||||
|
explicit Input(const std::string &file);
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
static Input *instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Singleton API
|
||||||
Input(std::string file);
|
static void init(const std::string &gameControllerDbPath); // Crea la instancia
|
||||||
|
static void destroy(); // Libera la instancia
|
||||||
|
static auto get() -> Input *; // Obtiene el puntero a la instancia
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Input();
|
~Input();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "mouse.hpp"
|
#include "core/input/mouse.hpp"
|
||||||
|
|
||||||
namespace Mouse {
|
namespace Mouse {
|
||||||
Uint32 cursorHideTime = 3000; // Tiempo en milisegundos para ocultar el cursor por inactividad
|
Uint32 cursorHideTime = 3000; // Tiempo en milisegundos para ocultar el cursor por inactividad
|
||||||
@@ -1,14 +1,30 @@
|
|||||||
#include "lang.h"
|
#include "core/locale/lang.h"
|
||||||
|
|
||||||
#include <fstream> // for basic_ifstream, basic_istream, ifstream
|
#include <fstream> // for basic_ifstream, basic_istream, ifstream
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/resources/asset.h" // for Asset
|
||||||
#include "resource_helper.h"
|
#include "core/resources/resource_helper.h"
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
Lang *Lang::instance = nullptr;
|
||||||
|
|
||||||
|
// Singleton API
|
||||||
|
void Lang::init() {
|
||||||
|
Lang::instance = new Lang();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lang::destroy() {
|
||||||
|
delete Lang::instance;
|
||||||
|
Lang::instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Lang::get() -> Lang * {
|
||||||
|
return Lang::instance;
|
||||||
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Lang::Lang(Asset *mAsset) {
|
Lang::Lang() {
|
||||||
this->mAsset = mAsset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -21,19 +37,19 @@ bool Lang::setLang(Uint8 lang) {
|
|||||||
|
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case es_ES:
|
case es_ES:
|
||||||
file = mAsset->get("es_ES.txt");
|
file = Asset::get()->get("es_ES.txt");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case en_UK:
|
case en_UK:
|
||||||
file = mAsset->get("en_UK.txt");
|
file = Asset::get()->get("en_UK.txt");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ba_BA:
|
case ba_BA:
|
||||||
file = mAsset->get("ba_BA.txt");
|
file = Asset::get()->get("ba_BA.txt");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
file = mAsset->get("en_UK.txt");
|
file = Asset::get()->get("en_UK.txt");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <string> // for string, basic_string
|
#include <string> // for string, basic_string
|
||||||
class Asset;
|
|
||||||
|
|
||||||
// Códigos de idioma
|
// Códigos de idioma
|
||||||
constexpr int es_ES = 0;
|
constexpr int es_ES = 0;
|
||||||
@@ -17,12 +16,19 @@ constexpr int MAX_TEXT_STRINGS = 100;
|
|||||||
// Clase Lang
|
// Clase Lang
|
||||||
class Lang {
|
class Lang {
|
||||||
private:
|
private:
|
||||||
Asset *mAsset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
std::string mTextStrings[MAX_TEXT_STRINGS]; // Vector con los textos
|
std::string mTextStrings[MAX_TEXT_STRINGS]; // Vector con los textos
|
||||||
|
|
||||||
|
// Constructor privado (usar Lang::init)
|
||||||
|
Lang();
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
static Lang *instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Singleton API
|
||||||
Lang(Asset *mAsset);
|
static void init(); // Crea la instancia
|
||||||
|
static void destroy(); // Libera la instancia
|
||||||
|
static auto get() -> Lang *; // Obtiene el puntero a la instancia
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Lang();
|
~Lang();
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#include "animatedsprite.h"
|
#include "core/rendering/animatedsprite.h"
|
||||||
|
|
||||||
#include <fstream> // for basic_ostream, operator<<, basic_istream, basic...
|
#include <fstream> // for basic_ostream, operator<<, basic_istream, basic...
|
||||||
#include <iostream> // for cout
|
#include <iostream> // for cout
|
||||||
#include <sstream> // for basic_stringstream
|
#include <sstream> // for basic_stringstream
|
||||||
|
|
||||||
#include "texture.h" // for Texture
|
#include "core/rendering/texture.h" // for Texture
|
||||||
|
|
||||||
// Parser compartido: lee un istream con el formato .ani
|
// Parser compartido: lee un istream con el formato .ani
|
||||||
static animatedSprite_t parseAnimationStream(std::istream &file, Texture *texture, const std::string &filename, bool verbose) {
|
static animatedSprite_t parseAnimationStream(std::istream &file, Texture *texture, const std::string &filename, bool verbose) {
|
||||||
@@ -22,6 +22,8 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
|||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
if (line == "[animation]") {
|
if (line == "[animation]") {
|
||||||
animation_t buffer;
|
animation_t buffer;
|
||||||
|
buffer.speed = 0;
|
||||||
|
buffer.loop = -1;
|
||||||
buffer.counter = 0;
|
buffer.counter = 0;
|
||||||
buffer.currentFrame = 0;
|
buffer.currentFrame = 0;
|
||||||
buffer.completed = false;
|
buffer.completed = false;
|
||||||
@@ -82,7 +84,7 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga la animación desde un fichero
|
// Carga la animación desde un fichero
|
||||||
animatedSprite_t loadAnimationFromFile(Texture *texture, std::string filePath, bool verbose) {
|
animatedSprite_t loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose) {
|
||||||
const std::string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
|
const std::string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
|
||||||
std::ifstream file(filePath);
|
std::ifstream file(filePath);
|
||||||
if (!file.good()) {
|
if (!file.good()) {
|
||||||
@@ -109,42 +111,34 @@ animatedSprite_t loadAnimationFromMemory(Texture *texture, const std::vector<uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
AnimatedSprite::AnimatedSprite(Texture *texture, SDL_Renderer *renderer, std::string file, std::vector<std::string> *buffer) {
|
AnimatedSprite::AnimatedSprite(Texture *texture, SDL_Renderer *renderer, const std::string &file, std::vector<std::string> *buffer)
|
||||||
|
: currentAnimation(0) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
setTexture(texture);
|
setTexture(texture);
|
||||||
setRenderer(renderer);
|
setRenderer(renderer);
|
||||||
|
|
||||||
// Carga las animaciones
|
// Carga las animaciones
|
||||||
if (file != "") {
|
if (!file.empty()) {
|
||||||
animatedSprite_t as = loadAnimationFromFile(texture, file);
|
animatedSprite_t as = loadAnimationFromFile(texture, file);
|
||||||
|
|
||||||
// Copia los datos de las animaciones
|
// Copia los datos de las animaciones
|
||||||
for (auto animation : as.animations) {
|
animation.insert(animation.end(), as.animations.begin(), as.animations.end());
|
||||||
this->animation.push_back(animation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (buffer) {
|
else if (buffer) {
|
||||||
loadFromVector(buffer);
|
loadFromVector(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa variables
|
|
||||||
currentAnimation = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
AnimatedSprite::AnimatedSprite(SDL_Renderer *renderer, animatedSprite_t *animation) {
|
AnimatedSprite::AnimatedSprite(SDL_Renderer *renderer, animatedSprite_t *animation)
|
||||||
|
: currentAnimation(0) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
setTexture(animation->texture);
|
setTexture(animation->texture);
|
||||||
setRenderer(renderer);
|
setRenderer(renderer);
|
||||||
|
|
||||||
// Inicializa variables
|
|
||||||
currentAnimation = 0;
|
|
||||||
|
|
||||||
// Copia los datos de las animaciones
|
// Copia los datos de las animaciones
|
||||||
for (auto a : animation->animations) {
|
this->animation.insert(this->animation.end(), animation->animations.begin(), animation->animations.end());
|
||||||
this->animation.push_back(a);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -156,10 +150,10 @@ AnimatedSprite::~AnimatedSprite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el indice de la animación a partir del nombre
|
// Obtiene el indice de la animación a partir del nombre
|
||||||
int AnimatedSprite::getIndex(std::string name) {
|
int AnimatedSprite::getIndex(const std::string &name) {
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
|
||||||
for (auto a : animation) {
|
for (const auto &a : animation) {
|
||||||
index++;
|
index++;
|
||||||
if (a.name == name) {
|
if (a.name == name) {
|
||||||
return index;
|
return index;
|
||||||
@@ -222,12 +216,12 @@ void AnimatedSprite::setCurrentFrame(int num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece el valor del contador
|
// Establece el valor del contador
|
||||||
void AnimatedSprite::setAnimationCounter(std::string name, int num) {
|
void AnimatedSprite::setAnimationCounter(const std::string &name, int num) {
|
||||||
animation[getIndex(name)].counter = num;
|
animation[getIndex(name)].counter = num;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establece la velocidad de una animación
|
// Establece la velocidad de una animación
|
||||||
void AnimatedSprite::setAnimationSpeed(std::string name, int speed) {
|
void AnimatedSprite::setAnimationSpeed(const std::string &name, int speed) {
|
||||||
animation[getIndex(name)].counter = speed;
|
animation[getIndex(name)].counter = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +231,7 @@ void AnimatedSprite::setAnimationSpeed(int index, int speed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece si la animación se reproduce en bucle
|
// Establece si la animación se reproduce en bucle
|
||||||
void AnimatedSprite::setAnimationLoop(std::string name, int loop) {
|
void AnimatedSprite::setAnimationLoop(const std::string &name, int loop) {
|
||||||
animation[getIndex(name)].loop = loop;
|
animation[getIndex(name)].loop = loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +241,7 @@ void AnimatedSprite::setAnimationLoop(int index, int loop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
void AnimatedSprite::setAnimationCompleted(std::string name, bool value) {
|
void AnimatedSprite::setAnimationCompleted(const std::string &name, bool value) {
|
||||||
animation[getIndex(name)].completed = value;
|
animation[getIndex(name)].completed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +256,7 @@ bool AnimatedSprite::animationIsCompleted() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve el rectangulo de una animación y frame concreto
|
// Devuelve el rectangulo de una animación y frame concreto
|
||||||
SDL_Rect AnimatedSprite::getAnimationClip(std::string name, Uint8 index) {
|
SDL_Rect AnimatedSprite::getAnimationClip(const std::string &name, Uint8 index) {
|
||||||
return animation[getIndex(name)].frames[index];
|
return animation[getIndex(name)].frames[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +286,8 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
|||||||
// Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación
|
// Si la linea contiene el texto [animation] se realiza el proceso de carga de una animación
|
||||||
if (line == "[animation]") {
|
if (line == "[animation]") {
|
||||||
animation_t buffer;
|
animation_t buffer;
|
||||||
|
buffer.speed = 0;
|
||||||
|
buffer.loop = -1;
|
||||||
buffer.counter = 0;
|
buffer.counter = 0;
|
||||||
buffer.currentFrame = 0;
|
buffer.currentFrame = 0;
|
||||||
buffer.completed = false;
|
buffer.completed = false;
|
||||||
@@ -391,7 +387,7 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece la animacion actual
|
// Establece la animacion actual
|
||||||
void AnimatedSprite::setCurrentAnimation(std::string name) {
|
void AnimatedSprite::setCurrentAnimation(const std::string &name) {
|
||||||
const int newAnimation = getIndex(name);
|
const int newAnimation = getIndex(name);
|
||||||
if (currentAnimation != newAnimation) {
|
if (currentAnimation != newAnimation) {
|
||||||
currentAnimation = newAnimation;
|
currentAnimation = newAnimation;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <string> // for string, basic_string
|
#include <string> // for string, basic_string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "movingsprite.h" // for MovingSprite
|
#include "core/rendering/movingsprite.h" // for MovingSprite
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
struct animation_t {
|
struct animation_t {
|
||||||
@@ -25,7 +25,7 @@ struct animatedSprite_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Carga la animación desde un fichero
|
// Carga la animación desde un fichero
|
||||||
animatedSprite_t loadAnimationFromFile(Texture *texture, std::string filePath, bool verbose = false);
|
animatedSprite_t loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose = false);
|
||||||
|
|
||||||
// Carga la animación desde bytes en memoria
|
// Carga la animación desde bytes en memoria
|
||||||
animatedSprite_t loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "", bool verbose = false);
|
animatedSprite_t loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "", bool verbose = false);
|
||||||
@@ -38,7 +38,7 @@ class AnimatedSprite : public MovingSprite {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
AnimatedSprite(Texture *texture = nullptr, SDL_Renderer *renderer = nullptr, std::string file = "", std::vector<std::string> *buffer = nullptr);
|
explicit AnimatedSprite(Texture *texture = nullptr, SDL_Renderer *renderer = nullptr, const std::string &file = "", std::vector<std::string> *buffer = nullptr);
|
||||||
AnimatedSprite(SDL_Renderer *renderer, animatedSprite_t *animation);
|
AnimatedSprite(SDL_Renderer *renderer, animatedSprite_t *animation);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -54,35 +54,35 @@ class AnimatedSprite : public MovingSprite {
|
|||||||
void setCurrentFrame(int num);
|
void setCurrentFrame(int num);
|
||||||
|
|
||||||
// Establece el valor del contador
|
// Establece el valor del contador
|
||||||
void setAnimationCounter(std::string name, int num);
|
void setAnimationCounter(const std::string &name, int num);
|
||||||
|
|
||||||
// Establece la velocidad de una animación
|
// Establece la velocidad de una animación
|
||||||
void setAnimationSpeed(std::string name, int speed);
|
void setAnimationSpeed(const std::string &name, int speed);
|
||||||
void setAnimationSpeed(int index, int speed);
|
void setAnimationSpeed(int index, int speed);
|
||||||
|
|
||||||
// Establece el frame al que vuelve la animación al finalizar
|
// Establece el frame al que vuelve la animación al finalizar
|
||||||
void setAnimationLoop(std::string name, int loop);
|
void setAnimationLoop(const std::string &name, int loop);
|
||||||
void setAnimationLoop(int index, int loop);
|
void setAnimationLoop(int index, int loop);
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
void setAnimationCompleted(std::string name, bool value);
|
void setAnimationCompleted(const std::string &name, bool value);
|
||||||
void setAnimationCompleted(int index, bool value);
|
void setAnimationCompleted(int index, bool value);
|
||||||
|
|
||||||
// Comprueba si ha terminado la animación
|
// Comprueba si ha terminado la animación
|
||||||
bool animationIsCompleted();
|
bool animationIsCompleted();
|
||||||
|
|
||||||
// Devuelve el rectangulo de una animación y frame concreto
|
// Devuelve el rectangulo de una animación y frame concreto
|
||||||
SDL_Rect getAnimationClip(std::string name = "default", Uint8 index = 0);
|
SDL_Rect getAnimationClip(const std::string &name = "default", Uint8 index = 0);
|
||||||
SDL_Rect getAnimationClip(int indexA = 0, Uint8 indexF = 0);
|
SDL_Rect getAnimationClip(int indexA = 0, Uint8 indexF = 0);
|
||||||
|
|
||||||
// Obtiene el indice de la animación a partir del nombre
|
// Obtiene el indice de la animación a partir del nombre
|
||||||
int getIndex(std::string name);
|
int getIndex(const std::string &name);
|
||||||
|
|
||||||
// Carga la animación desde un vector
|
// Carga la animación desde un vector
|
||||||
bool loadFromVector(std::vector<std::string> *source);
|
bool loadFromVector(std::vector<std::string> *source);
|
||||||
|
|
||||||
// Establece la animacion actual
|
// Establece la animacion actual
|
||||||
void setCurrentAnimation(std::string name = "default");
|
void setCurrentAnimation(const std::string &name = "default");
|
||||||
void setCurrentAnimation(int index = 0);
|
void setCurrentAnimation(int index = 0);
|
||||||
|
|
||||||
// Actualiza las variables del objeto
|
// Actualiza las variables del objeto
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
#include "fade.h"
|
#include "core/rendering/fade.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stdlib.h> // for rand
|
#include <stdlib.h> // for rand
|
||||||
|
|
||||||
#include <iostream> // for char_traits, basic_ostream, operator<<
|
#include <iostream> // for char_traits, basic_ostream, operator<<
|
||||||
|
|
||||||
#include "const.h" // for GAMECANVAS_HEIGHT, GAMECANVAS_WIDTH
|
#include "game/defaults.hpp" // for GAMECANVAS_HEIGHT, GAMECANVAS_WIDTH
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Fade::Fade(SDL_Renderer *renderer) {
|
Fade::Fade(SDL_Renderer *renderer)
|
||||||
mRenderer = renderer;
|
: mRenderer(renderer) {
|
||||||
|
|
||||||
mBackbuffer = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
|
mBackbuffer = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
|
||||||
if (mBackbuffer != nullptr) {
|
if (mBackbuffer != nullptr) {
|
||||||
SDL_SetTextureScaleMode(mBackbuffer, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(mBackbuffer, SDL_SCALEMODE_NEAREST);
|
||||||
@@ -53,7 +52,6 @@ void Fade::render() {
|
|||||||
|
|
||||||
int alpha = mCounter * 4;
|
int alpha = mCounter * 4;
|
||||||
if (alpha >= 255) {
|
if (alpha >= 255) {
|
||||||
alpha = 255;
|
|
||||||
mFullscreenDone = true;
|
mFullscreenDone = true;
|
||||||
|
|
||||||
// Deja todos los buffers del mismo color
|
// Deja todos los buffers del mismo color
|
||||||
54
source/core/rendering/fade.h
Normal file
54
source/core/rendering/fade.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
// Tipos de fundido
|
||||||
|
constexpr int FADE_FULLSCREEN = 0;
|
||||||
|
constexpr int FADE_CENTER = 1;
|
||||||
|
constexpr int FADE_RANDOM_SQUARE = 2;
|
||||||
|
|
||||||
|
// Clase Fade
|
||||||
|
class Fade {
|
||||||
|
private:
|
||||||
|
SDL_Renderer *mRenderer = nullptr; // El renderizador de la ventana
|
||||||
|
SDL_Texture *mBackbuffer = nullptr; // Textura para usar como backbuffer
|
||||||
|
Uint8 mFadeType = FADE_FULLSCREEN; // Tipo de fade a realizar
|
||||||
|
Uint16 mCounter = 0; // Contador interno
|
||||||
|
bool mEnabled = false; // Indica si el fade está activo
|
||||||
|
bool mFinished = false; // Indica si ha terminado la transición
|
||||||
|
Uint8 mR = 0, mG = 0, mB = 0; // Colores para el fade
|
||||||
|
Uint8 mROriginal = 0, mGOriginal = 0, mBOriginal = 0; // Colores originales para FADE_RANDOM_SQUARE
|
||||||
|
Uint32 mLastSquareTicks = 0; // Ticks del último cuadrado dibujado (FADE_RANDOM_SQUARE)
|
||||||
|
Uint16 mSquaresDrawn = 0; // Número de cuadrados dibujados (FADE_RANDOM_SQUARE)
|
||||||
|
bool mFullscreenDone = false; // Indica si el fade fullscreen ha terminado la fase de fundido
|
||||||
|
SDL_Rect mRect1{}; // Rectangulo usado para crear los efectos de transición
|
||||||
|
SDL_Rect mRect2{}; // Rectangulo usado para crear los efectos de transición
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
explicit Fade(SDL_Renderer *renderer);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Fade();
|
||||||
|
|
||||||
|
// Inicializa las variables
|
||||||
|
void init(Uint8 r, Uint8 g, Uint8 b);
|
||||||
|
|
||||||
|
// Pinta una transición en pantalla
|
||||||
|
void render();
|
||||||
|
|
||||||
|
// Actualiza las variables internas
|
||||||
|
void update();
|
||||||
|
|
||||||
|
// Activa el fade
|
||||||
|
void activateFade();
|
||||||
|
|
||||||
|
// Comprueba si ha terminado la transicion
|
||||||
|
bool hasEnded();
|
||||||
|
|
||||||
|
// Comprueba si está activo
|
||||||
|
bool isEnabled();
|
||||||
|
|
||||||
|
// Establece el tipo de fade
|
||||||
|
void setFadeType(Uint8 fadeType);
|
||||||
|
};
|
||||||
@@ -1,55 +1,28 @@
|
|||||||
#include "movingsprite.h"
|
#include "core/rendering/movingsprite.h"
|
||||||
|
|
||||||
#include "texture.h" // for Texture
|
#include "core/rendering/texture.h" // for Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
MovingSprite::MovingSprite(float x, float y, int w, int h, float velx, float vely, float accelx, float accely, Texture *texture, SDL_Renderer *renderer) {
|
MovingSprite::MovingSprite(float x, float y, int w, int h, float velx, float vely, float accelx, float accely, Texture *texture, SDL_Renderer *renderer)
|
||||||
// Copia los punteros
|
: Sprite(0, 0, w, h, texture, renderer),
|
||||||
this->texture = texture;
|
x(x),
|
||||||
this->renderer = renderer;
|
y(y),
|
||||||
|
xPrev(x),
|
||||||
// Establece el alto y el ancho del sprite
|
yPrev(y),
|
||||||
this->w = w;
|
vx(velx),
|
||||||
this->h = h;
|
vy(vely),
|
||||||
|
ax(accelx),
|
||||||
// Establece la posición X,Y del sprite
|
ay(accely),
|
||||||
this->x = x;
|
zoomW(1),
|
||||||
this->y = y;
|
zoomH(1),
|
||||||
xPrev = x;
|
angle(0.0),
|
||||||
yPrev = y;
|
rotateEnabled(false),
|
||||||
|
rotateSpeed(0),
|
||||||
// Establece la velocidad X,Y del sprite
|
rotateAmount(0.0),
|
||||||
vx = velx;
|
counter(0),
|
||||||
vy = vely;
|
center(nullptr),
|
||||||
|
currentFlip(SDL_FLIP_NONE) {
|
||||||
// Establece la aceleración X,Y del sprite
|
}
|
||||||
ax = accelx;
|
|
||||||
ay = accely;
|
|
||||||
|
|
||||||
// Establece el zoom W,H del sprite
|
|
||||||
zoomW = 1;
|
|
||||||
zoomH = 1;
|
|
||||||
|
|
||||||
// Establece el angulo con el que se dibujará
|
|
||||||
angle = (double)0;
|
|
||||||
|
|
||||||
// Establece los valores de rotacion
|
|
||||||
rotateEnabled = false;
|
|
||||||
rotateSpeed = 0;
|
|
||||||
rotateAmount = (double)0;
|
|
||||||
|
|
||||||
// Contador interno
|
|
||||||
counter = 0;
|
|
||||||
|
|
||||||
// Establece el rectangulo de donde coger la imagen
|
|
||||||
spriteClip = {0, 0, w, h};
|
|
||||||
|
|
||||||
// Establece el centro de rotación
|
|
||||||
center = nullptr;
|
|
||||||
|
|
||||||
// Establece el tipo de volteado
|
|
||||||
currentFlip = SDL_FLIP_NONE;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reinicia todas las variables
|
// Reinicia todas las variables
|
||||||
void MovingSprite::clear() {
|
void MovingSprite::clear() {
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Clase MovingSprite. Añade posicion y velocidad en punto flotante
|
// Clase MovingSprite. Añade posicion y velocidad en punto flotante
|
||||||
@@ -33,7 +33,7 @@ class MovingSprite : public Sprite {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
MovingSprite(float x = 0, float y = 0, int w = 0, int h = 0, float velx = 0, float vely = 0, float accelx = 0, float accely = 0, Texture *texture = nullptr, SDL_Renderer *renderer = nullptr);
|
explicit MovingSprite(float x = 0, float y = 0, int w = 0, int h = 0, float velx = 0, float vely = 0, float accelx = 0, float accely = 0, Texture *texture = nullptr, SDL_Renderer *renderer = nullptr);
|
||||||
|
|
||||||
// Mueve el sprite
|
// Mueve el sprite
|
||||||
void move();
|
void move();
|
||||||
722
source/core/rendering/screen.cpp
Normal file
722
source/core/rendering/screen.cpp
Normal file
@@ -0,0 +1,722 @@
|
|||||||
|
#include "core/rendering/screen.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <algorithm> // for max, min
|
||||||
|
#include <cstring> // for memcpy
|
||||||
|
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
||||||
|
#include <string> // for basic_string, char_traits, string
|
||||||
|
|
||||||
|
#include "core/input/mouse.hpp" // for Mouse::cursorVisible, Mouse::lastMouseMoveTime
|
||||||
|
#include "core/rendering/text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_STROKE
|
||||||
|
#include "core/resources/resource.h"
|
||||||
|
#include "game/defaults.hpp" // for GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT
|
||||||
|
#include "game/options.hpp" // for Options::video, Options::settings
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // for Rendering::SDL3GPUShader
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include <emscripten.h>
|
||||||
|
#include <emscripten/html5.h>
|
||||||
|
|
||||||
|
// --- Fix per a fullscreen/resize en Emscripten ---
|
||||||
|
//
|
||||||
|
// SDL3 + Emscripten no emet de forma fiable SDL_EVENT_WINDOW_LEAVE_FULLSCREEN
|
||||||
|
// (libsdl-org/SDL#13300) ni SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED /
|
||||||
|
// SDL_EVENT_DISPLAY_ORIENTATION (libsdl-org/SDL#11389). Quan l'usuari ix de
|
||||||
|
// fullscreen amb Esc o rota el mòbil, el canvas HTML torna al tamany correcte
|
||||||
|
// però l'estat intern de SDL creu que segueix en fullscreen amb la resolució
|
||||||
|
// anterior i el viewport queda desencuadrat.
|
||||||
|
//
|
||||||
|
// Solució: registrar callbacks natius d'Emscripten, diferir la feina un tick
|
||||||
|
// del event loop (el canvas encara no està estable en el moment del callback)
|
||||||
|
// i cridar setVideoMode() amb el flag de fullscreen actualitzat. La crida
|
||||||
|
// interna a SDL_SetWindowFullscreen(false) és la peça que realment fa eixir
|
||||||
|
// SDL del seu estat intern de fullscreen — sense això res més funciona.
|
||||||
|
namespace {
|
||||||
|
Screen *g_screen_instance = nullptr;
|
||||||
|
|
||||||
|
void deferredCanvasResize(void * /*userData*/) {
|
||||||
|
if (g_screen_instance) {
|
||||||
|
g_screen_instance->handleCanvasResized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EM_BOOL onEmFullscreenChange(int /*eventType*/, const EmscriptenFullscreenChangeEvent *event, void * /*userData*/) {
|
||||||
|
if (g_screen_instance && event) {
|
||||||
|
g_screen_instance->syncFullscreenFlagFromBrowser(event->isFullscreen != 0);
|
||||||
|
}
|
||||||
|
emscripten_async_call(deferredCanvasResize, nullptr, 0);
|
||||||
|
return EM_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EM_BOOL onEmOrientationChange(int /*eventType*/, const EmscriptenOrientationChangeEvent * /*event*/, void * /*userData*/) {
|
||||||
|
emscripten_async_call(deferredCanvasResize, nullptr, 0);
|
||||||
|
return EM_FALSE;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif // __EMSCRIPTEN__
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
Screen *Screen::instance = nullptr;
|
||||||
|
|
||||||
|
// Singleton API
|
||||||
|
void Screen::init(SDL_Window *window, SDL_Renderer *renderer) {
|
||||||
|
Screen::instance = new Screen(window, renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::destroy() {
|
||||||
|
delete Screen::instance;
|
||||||
|
Screen::instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::get() -> Screen * {
|
||||||
|
return Screen::instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
|
||||||
|
: borderColor{0x00, 0x00, 0x00} {
|
||||||
|
// Inicializa variables
|
||||||
|
this->window = window;
|
||||||
|
this->renderer = renderer;
|
||||||
|
|
||||||
|
gameCanvasWidth = GAMECANVAS_WIDTH;
|
||||||
|
gameCanvasHeight = GAMECANVAS_HEIGHT;
|
||||||
|
|
||||||
|
// Establece el modo de video (fullscreen/ventana + logical presentation)
|
||||||
|
// ANTES de crear la textura — SDL3 GPU necesita la logical presentation
|
||||||
|
// del renderer ya aplicada al swapchain quan es reclama la ventana per a GPU.
|
||||||
|
// Mirror del pattern de jaildoctors_dilemma (que usa exactament 256×192 i
|
||||||
|
// funciona) on `initSDLVideo` configura la presentation abans de crear cap
|
||||||
|
// textura.
|
||||||
|
setVideoMode(Options::video.fullscreen);
|
||||||
|
|
||||||
|
// Força al window manager a completar el resize/posicionat abans de passar
|
||||||
|
// la ventana al dispositiu GPU. Sense açò en Linux/X11 hi ha un race
|
||||||
|
// condition que deixa el swapchain en estat inestable i fa crashear el
|
||||||
|
// driver Vulkan en `SDL_CreateGPUGraphicsPipeline`.
|
||||||
|
SDL_SyncWindow(window);
|
||||||
|
|
||||||
|
// Crea la textura donde se dibujan los graficos del juego.
|
||||||
|
// ARGB8888 per simplificar el readback cap al pipeline SDL3 GPU.
|
||||||
|
gameCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, gameCanvasWidth, gameCanvasHeight);
|
||||||
|
if (gameCanvas != nullptr) {
|
||||||
|
SDL_SetTextureScaleMode(gameCanvas, Options::video.scale_mode);
|
||||||
|
}
|
||||||
|
if (gameCanvas == nullptr) {
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << "gameCanvas could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// Buffer de readback del gameCanvas (lo dimensionamos una vez)
|
||||||
|
pixel_buffer_.resize(static_cast<size_t>(gameCanvasWidth) * static_cast<size_t>(gameCanvasHeight));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Renderiza una vez la textura vacía al renderer abans d'inicialitzar els
|
||||||
|
// shaders: jaildoctors_dilemma ho fa així i evita que el driver Vulkan
|
||||||
|
// crashegi en la creació del pipeline gràfic. `initShaders()` es crida
|
||||||
|
// després des de `Director` amb el swapchain ja estable.
|
||||||
|
SDL_RenderTexture(renderer, gameCanvas, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Estado inicial de las notificaciones. El Text real se enlaza después vía
|
||||||
|
// `initNotifications()` quan `Resource` ja estigui inicialitzat. Dividim
|
||||||
|
// això del constructor perquè `initShaders()` (GPU) ha de cridar-se ABANS
|
||||||
|
// de carregar recursos: si el SDL_Renderer ha fet abans moltes
|
||||||
|
// allocacions (carrega de textures), el driver Vulkan crasheja quan
|
||||||
|
// després es reclama la ventana per al dispositiu GPU.
|
||||||
|
notificationText = nullptr;
|
||||||
|
notificationMessage = "";
|
||||||
|
notificationTextColor = {0xFF, 0xFF, 0xFF};
|
||||||
|
notificationOutlineColor = {0x00, 0x00, 0x00};
|
||||||
|
notificationEndTime = 0;
|
||||||
|
notificationY = 2;
|
||||||
|
|
||||||
|
// Registra callbacks natius d'Emscripten per a fullscreen/orientation
|
||||||
|
registerEmscriptenEventCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enllaça el Text de les notificacions amb el recurs compartit de `Resource`.
|
||||||
|
// S'ha de cridar després de `Resource::init(...)`.
|
||||||
|
void Screen::initNotifications() {
|
||||||
|
notificationText = Resource::get()->getText("8bithud");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
Screen::~Screen() {
|
||||||
|
// notificationText es propiedad de Resource — no liberar.
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
shutdownShaders();
|
||||||
|
#endif
|
||||||
|
SDL_DestroyTexture(gameCanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limpia la pantalla
|
||||||
|
void Screen::clean(color_t color) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 0xFF);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
|
void Screen::start() {
|
||||||
|
SDL_SetRenderTarget(renderer, gameCanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
|
void Screen::blit() {
|
||||||
|
// Dibuja la notificación activa sobre el gameCanvas antes de presentar
|
||||||
|
SDL_SetRenderTarget(renderer, gameCanvas);
|
||||||
|
renderNotification();
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// Si el backend GPU està viu i accelerat, passem sempre per ell (tant amb
|
||||||
|
// shaders com sense). Seguim el mateix pattern que aee_plus: quan shader
|
||||||
|
// està desactivat, forcem POSTFX + params a zero només per a aquest frame
|
||||||
|
// i restaurem el shader actiu, així CRTPI no aplica les seues scanlines
|
||||||
|
// quan l'usuari ho ha desactivat.
|
||||||
|
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
|
||||||
|
SDL_Surface *surface = SDL_RenderReadPixels(renderer, nullptr);
|
||||||
|
if (surface != nullptr) {
|
||||||
|
if (surface->format == SDL_PIXELFORMAT_ARGB8888) {
|
||||||
|
std::memcpy(pixel_buffer_.data(), surface->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
|
} else {
|
||||||
|
SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
|
||||||
|
if (converted != nullptr) {
|
||||||
|
std::memcpy(pixel_buffer_.data(), converted->pixels, pixel_buffer_.size() * sizeof(Uint32));
|
||||||
|
SDL_DestroySurface(converted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
}
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
if (Options::video.shader.enabled) {
|
||||||
|
// Ruta normal: shader amb els seus params.
|
||||||
|
shader_backend_->uploadPixels(pixel_buffer_.data(), gameCanvasWidth, gameCanvasHeight);
|
||||||
|
shader_backend_->render();
|
||||||
|
} else {
|
||||||
|
// Shader off: POSTFX amb params zero (passa-per-aquí). CRTPI no
|
||||||
|
// val perque sempre aplica els seus efectes interns; salvem i
|
||||||
|
// restaurem el shader actiu.
|
||||||
|
const auto PREV_SHADER = shader_backend_->getActiveShader();
|
||||||
|
if (PREV_SHADER != Rendering::ShaderType::POSTFX) {
|
||||||
|
shader_backend_->setActiveShader(Rendering::ShaderType::POSTFX);
|
||||||
|
}
|
||||||
|
shader_backend_->setPostFXParams(Rendering::PostFXParams{});
|
||||||
|
shader_backend_->uploadPixels(pixel_buffer_.data(), gameCanvasWidth, gameCanvasHeight);
|
||||||
|
shader_backend_->render();
|
||||||
|
if (PREV_SHADER != Rendering::ShaderType::POSTFX) {
|
||||||
|
shader_backend_->setActiveShader(PREV_SHADER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Vuelve a dejar el renderizador en modo normal
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Borra el contenido previo
|
||||||
|
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
// Copia la textura de juego en el renderizador en la posición adecuada
|
||||||
|
SDL_FRect fdest = {(float)dest.x, (float)dest.y, (float)dest.w, (float)dest.h};
|
||||||
|
SDL_RenderTexture(renderer, gameCanvas, nullptr, &fdest);
|
||||||
|
|
||||||
|
// Muestra por pantalla el renderizador
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Video y ventana
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Establece el modo de video
|
||||||
|
void Screen::setVideoMode(bool fullscreen) {
|
||||||
|
applyFullscreen(fullscreen);
|
||||||
|
if (fullscreen) {
|
||||||
|
applyFullscreenLayout();
|
||||||
|
} else {
|
||||||
|
applyWindowedLayout();
|
||||||
|
}
|
||||||
|
applyLogicalPresentation(fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cambia entre pantalla completa y ventana
|
||||||
|
void Screen::toggleVideoMode() {
|
||||||
|
setVideoMode(!Options::video.fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce el zoom de la ventana
|
||||||
|
auto Screen::decWindowZoom() -> bool {
|
||||||
|
if (Options::video.fullscreen) { return false; }
|
||||||
|
const int PREV = Options::window.zoom;
|
||||||
|
Options::window.zoom = std::max(Options::window.zoom - 1, WINDOW_ZOOM_MIN);
|
||||||
|
if (Options::window.zoom == PREV) { return false; }
|
||||||
|
setVideoMode(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aumenta el zoom de la ventana
|
||||||
|
auto Screen::incWindowZoom() -> bool {
|
||||||
|
if (Options::video.fullscreen) { return false; }
|
||||||
|
const int PREV = Options::window.zoom;
|
||||||
|
Options::window.zoom = std::min(Options::window.zoom + 1, WINDOW_ZOOM_MAX);
|
||||||
|
if (Options::window.zoom == PREV) { return false; }
|
||||||
|
setVideoMode(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el zoom de la ventana directamente
|
||||||
|
auto Screen::setWindowZoom(int zoom) -> bool {
|
||||||
|
if (Options::video.fullscreen) { return false; }
|
||||||
|
if (zoom < WINDOW_ZOOM_MIN || zoom > WINDOW_ZOOM_MAX) { return false; }
|
||||||
|
if (zoom == Options::window.zoom) { return false; }
|
||||||
|
Options::window.zoom = zoom;
|
||||||
|
setVideoMode(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el escalado entero
|
||||||
|
void Screen::setIntegerScale(bool enabled) {
|
||||||
|
if (Options::video.integer_scale == enabled) { return; }
|
||||||
|
Options::video.integer_scale = enabled;
|
||||||
|
setVideoMode(Options::video.fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alterna el escalado entero
|
||||||
|
void Screen::toggleIntegerScale() {
|
||||||
|
setIntegerScale(!Options::video.integer_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establece el V-Sync
|
||||||
|
void Screen::setVSync(bool enabled) {
|
||||||
|
Options::video.vsync = enabled;
|
||||||
|
SDL_SetRenderVSync(renderer, enabled ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (shader_backend_) {
|
||||||
|
shader_backend_->setVSync(enabled);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alterna el V-Sync
|
||||||
|
void Screen::toggleVSync() {
|
||||||
|
setVSync(!Options::video.vsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cambia el color del borde
|
||||||
|
void Screen::setBorderColor(color_t color) {
|
||||||
|
borderColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Helpers privados de setVideoMode
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// SDL_SetWindowFullscreen + visibilidad del cursor
|
||||||
|
void Screen::applyFullscreen(bool fullscreen) {
|
||||||
|
SDL_SetWindowFullscreen(window, fullscreen);
|
||||||
|
if (fullscreen) {
|
||||||
|
SDL_HideCursor();
|
||||||
|
Mouse::cursorVisible = false;
|
||||||
|
} else {
|
||||||
|
SDL_ShowCursor();
|
||||||
|
Mouse::cursorVisible = true;
|
||||||
|
Mouse::lastMouseMoveTime = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcula windowWidth/Height/dest para el modo ventana y aplica SDL_SetWindowSize
|
||||||
|
void Screen::applyWindowedLayout() {
|
||||||
|
windowWidth = gameCanvasWidth;
|
||||||
|
windowHeight = gameCanvasHeight;
|
||||||
|
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
windowWidth *= WASM_RENDER_SCALE;
|
||||||
|
windowHeight *= WASM_RENDER_SCALE;
|
||||||
|
dest.w *= WASM_RENDER_SCALE;
|
||||||
|
dest.h *= WASM_RENDER_SCALE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Modifica el tamaño de la ventana
|
||||||
|
SDL_SetWindowSize(window, windowWidth * Options::window.zoom, windowHeight * Options::window.zoom);
|
||||||
|
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtiene el tamaño de la ventana en fullscreen y calcula el rect del juego
|
||||||
|
void Screen::applyFullscreenLayout() {
|
||||||
|
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
||||||
|
computeFullscreenGameRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcula el rectángulo dest para fullscreen: integer_scale / aspect ratio
|
||||||
|
void Screen::computeFullscreenGameRect() {
|
||||||
|
if (Options::video.integer_scale) {
|
||||||
|
// Calcula el tamaño de la escala máxima
|
||||||
|
int scale = 0;
|
||||||
|
while (((gameCanvasWidth * (scale + 1)) <= windowWidth) && ((gameCanvasHeight * (scale + 1)) <= windowHeight)) {
|
||||||
|
scale++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.w = gameCanvasWidth * scale;
|
||||||
|
dest.h = gameCanvasHeight * scale;
|
||||||
|
dest.x = (windowWidth - dest.w) / 2;
|
||||||
|
dest.y = (windowHeight - dest.h) / 2;
|
||||||
|
} else {
|
||||||
|
// Manté la relació d'aspecte sense escalat enter (letterbox/pillarbox).
|
||||||
|
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
|
||||||
|
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight)) {
|
||||||
|
dest.h = windowHeight;
|
||||||
|
dest.w = (int)((windowHeight * ratio) + 0.5f);
|
||||||
|
dest.x = (windowWidth - dest.w) / 2;
|
||||||
|
dest.y = (windowHeight - dest.h) / 2;
|
||||||
|
} else {
|
||||||
|
dest.w = windowWidth;
|
||||||
|
dest.h = (int)((windowWidth / ratio) + 0.5f);
|
||||||
|
dest.x = (windowWidth - dest.w) / 2;
|
||||||
|
dest.y = (windowHeight - dest.h) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplica la logical presentation y persiste el estado en options
|
||||||
|
void Screen::applyLogicalPresentation(bool fullscreen) {
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer, windowWidth, windowHeight, SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
||||||
|
Options::video.fullscreen = fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Notificaciones
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Muestra una notificación en la línea superior durante durationMs
|
||||||
|
void Screen::notify(const std::string &text, color_t textColor, color_t outlineColor, Uint32 durationMs) {
|
||||||
|
notificationMessage = text;
|
||||||
|
notificationTextColor = textColor;
|
||||||
|
notificationOutlineColor = outlineColor;
|
||||||
|
notificationEndTime = SDL_GetTicks() + durationMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limpia la notificación actual
|
||||||
|
void Screen::clearNotification() {
|
||||||
|
notificationEndTime = 0;
|
||||||
|
notificationMessage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dibuja la notificación activa (si la hay) sobre el gameCanvas
|
||||||
|
void Screen::renderNotification() {
|
||||||
|
if (notificationText == nullptr || SDL_GetTicks() >= notificationEndTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notificationText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE,
|
||||||
|
gameCanvasWidth / 2,
|
||||||
|
notificationY,
|
||||||
|
notificationMessage,
|
||||||
|
1,
|
||||||
|
notificationTextColor,
|
||||||
|
1,
|
||||||
|
notificationOutlineColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Emscripten — fix per a fullscreen/resize (veure el bloc de comentaris al
|
||||||
|
// principi del fitxer i l'anonymous namespace amb els callbacks natius).
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void Screen::handleCanvasResized() {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// La crida a SDL_SetWindowFullscreen + SDL_SetRenderLogicalPresentation
|
||||||
|
// que fa setVideoMode és l'única manera de resincronitzar l'estat intern
|
||||||
|
// de SDL amb el canvas HTML real.
|
||||||
|
setVideoMode(Options::video.fullscreen);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::syncFullscreenFlagFromBrowser(bool isFullscreen) {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Options::video.fullscreen = isFullscreen;
|
||||||
|
#else
|
||||||
|
(void)isFullscreen;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::registerEmscriptenEventCallbacks() {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// IMPORTANT: NO registrem resize callback. En mòbil, fer scroll fa que el
|
||||||
|
// navegador oculti/mostri la barra d'URL, disparant un resize del DOM per
|
||||||
|
// cada scroll. Això portava a cridar setVideoMode per cada scroll, que
|
||||||
|
// re-aplicava la logical presentation i corrompia el viewport intern de SDL.
|
||||||
|
g_screen_instance = this;
|
||||||
|
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_TRUE, onEmFullscreenChange);
|
||||||
|
emscripten_set_orientationchange_callback(nullptr, EM_TRUE, onEmOrientationChange);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// GPU / shaders (SDL3 GPU post-procesado). En builds con NO_SHADERS (Emscripten)
|
||||||
|
// las operaciones son no-op; la ruta clásica sigue siendo la única disponible.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// Aplica al backend el shader actiu + els seus presets PostFX i CrtPi.
|
||||||
|
// Només s'ha de cridar quan `videoShaderEnabled=true` (en cas contrari el
|
||||||
|
// blit() ja força POSTFX+zero params per a desactivar els efectes sense
|
||||||
|
// tocar els paràmetres emmagatzemats).
|
||||||
|
void Screen::applyShaderParams() {
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shader_backend_->setActiveShader(Options::video.shader.current_shader);
|
||||||
|
applyCurrentPostFXPreset();
|
||||||
|
applyCurrentCrtPiPreset();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Screen::initShaders() {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_) {
|
||||||
|
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
||||||
|
const std::string FALLBACK_DRIVER = "none";
|
||||||
|
shader_backend_->setPreferredDriver(
|
||||||
|
Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER);
|
||||||
|
}
|
||||||
|
if (!shader_backend_->isHardwareAccelerated()) {
|
||||||
|
const bool ok = shader_backend_->init(window, gameCanvas, "", "");
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << "Screen::initShaders: SDL3GPUShader::init() = " << (ok ? "OK" : "FAILED") << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shader_backend_->isHardwareAccelerated()) {
|
||||||
|
shader_backend_->setScaleMode(Options::video.integer_scale);
|
||||||
|
shader_backend_->setVSync(Options::video.vsync);
|
||||||
|
|
||||||
|
// Resol els índexs de preset a partir del nom emmagatzemat al config.
|
||||||
|
// Si el nom no existeix (preset esborrat del YAML), es queda en 0.
|
||||||
|
for (int i = 0; i < static_cast<int>(Options::postfx_presets.size()); ++i) {
|
||||||
|
if (Options::postfx_presets[i].name == Options::video.shader.current_postfx_preset_name) {
|
||||||
|
Options::current_postfx_preset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < static_cast<int>(Options::crtpi_presets.size()); ++i) {
|
||||||
|
if (Options::crtpi_presets[i].name == Options::video.shader.current_crtpi_preset_name) {
|
||||||
|
Options::current_crtpi_preset = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyShaderParams(); // aplica preset del shader actiu
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::shutdownShaders() {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// Només es crida des del destructor de Screen. Els toggles runtime NO la
|
||||||
|
// poden cridar: destruir + recrear el dispositiu SDL3 GPU amb la ventana
|
||||||
|
// ja reclamada és inestable (Vulkan/Radeon crasheja en el següent claim).
|
||||||
|
if (shader_backend_) {
|
||||||
|
shader_backend_->cleanup();
|
||||||
|
shader_backend_.reset();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::isGpuAccelerated() const -> bool {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
return shader_backend_ && shader_backend_->isHardwareAccelerated();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::setShaderEnabled(bool enabled) {
|
||||||
|
if (Options::video.shader.enabled == enabled) { return; }
|
||||||
|
Options::video.shader.enabled = enabled;
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (enabled) {
|
||||||
|
applyShaderParams(); // restaura preset del shader actiu
|
||||||
|
}
|
||||||
|
// Si enabled=false, blit() forçarà POSTFX+zero per frame — no cal tocar
|
||||||
|
// res ara.
|
||||||
|
#endif
|
||||||
|
const color_t CYAN = {0x00, 0xFF, 0xFF};
|
||||||
|
const color_t BLACK = {0x00, 0x00, 0x00};
|
||||||
|
const Uint32 DUR_MS = 1500;
|
||||||
|
notify(enabled ? "Shader: ON" : "Shader: OFF", CYAN, BLACK, DUR_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::toggleShaderEnabled() {
|
||||||
|
setShaderEnabled(!Options::video.shader.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::isShaderEnabled() const -> bool {
|
||||||
|
return Options::video.shader.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
void Screen::setActiveShader(Rendering::ShaderType type) {
|
||||||
|
Options::video.shader.current_shader = type;
|
||||||
|
if (Options::video.shader.enabled) {
|
||||||
|
applyShaderParams();
|
||||||
|
}
|
||||||
|
const color_t MAGENTA = {0xFF, 0x00, 0xFF};
|
||||||
|
const color_t BLACK = {0x00, 0x00, 0x00};
|
||||||
|
const Uint32 DUR_MS = 1500;
|
||||||
|
notify(type == Rendering::ShaderType::CRTPI ? "Shader: CRTPI" : "Shader: POSTFX", MAGENTA, BLACK, DUR_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::getActiveShader() const -> Rendering::ShaderType {
|
||||||
|
return Options::video.shader.current_shader;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Screen::toggleActiveShader() {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
const Rendering::ShaderType NEXT = getActiveShader() == Rendering::ShaderType::POSTFX
|
||||||
|
? Rendering::ShaderType::CRTPI
|
||||||
|
: Rendering::ShaderType::POSTFX;
|
||||||
|
setActiveShader(NEXT);
|
||||||
|
#else
|
||||||
|
Options::video.shader.current_shader = Options::video.shader.current_shader == Rendering::ShaderType::POSTFX
|
||||||
|
? Rendering::ShaderType::CRTPI
|
||||||
|
: Rendering::ShaderType::POSTFX;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Presets de shaders
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void Screen::applyCurrentPostFXPreset() {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) { return; }
|
||||||
|
if (Options::postfx_presets.empty()) { return; }
|
||||||
|
if (Options::current_postfx_preset < 0 || Options::current_postfx_preset >= static_cast<int>(Options::postfx_presets.size())) {
|
||||||
|
Options::current_postfx_preset = 0;
|
||||||
|
}
|
||||||
|
const auto &PRESET = Options::postfx_presets[Options::current_postfx_preset];
|
||||||
|
Rendering::PostFXParams p;
|
||||||
|
p.vignette = PRESET.vignette;
|
||||||
|
p.scanlines = PRESET.scanlines;
|
||||||
|
p.chroma = PRESET.chroma;
|
||||||
|
p.mask = PRESET.mask;
|
||||||
|
p.gamma = PRESET.gamma;
|
||||||
|
p.curvature = PRESET.curvature;
|
||||||
|
p.bleeding = PRESET.bleeding;
|
||||||
|
p.flicker = PRESET.flicker;
|
||||||
|
shader_backend_->setPostFXParams(p);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::applyCurrentCrtPiPreset() {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) { return; }
|
||||||
|
if (Options::crtpi_presets.empty()) { return; }
|
||||||
|
if (Options::current_crtpi_preset < 0 || Options::current_crtpi_preset >= static_cast<int>(Options::crtpi_presets.size())) {
|
||||||
|
Options::current_crtpi_preset = 0;
|
||||||
|
}
|
||||||
|
const auto &PRESET = Options::crtpi_presets[Options::current_crtpi_preset];
|
||||||
|
Rendering::CrtPiParams p;
|
||||||
|
p.scanline_weight = PRESET.scanline_weight;
|
||||||
|
p.scanline_gap_brightness = PRESET.scanline_gap_brightness;
|
||||||
|
p.bloom_factor = PRESET.bloom_factor;
|
||||||
|
p.input_gamma = PRESET.input_gamma;
|
||||||
|
p.output_gamma = PRESET.output_gamma;
|
||||||
|
p.mask_brightness = PRESET.mask_brightness;
|
||||||
|
p.curvature_x = PRESET.curvature_x;
|
||||||
|
p.curvature_y = PRESET.curvature_y;
|
||||||
|
p.mask_type = PRESET.mask_type;
|
||||||
|
p.enable_scanlines = PRESET.enable_scanlines;
|
||||||
|
p.enable_multisample = PRESET.enable_multisample;
|
||||||
|
p.enable_gamma = PRESET.enable_gamma;
|
||||||
|
p.enable_curvature = PRESET.enable_curvature;
|
||||||
|
p.enable_sharper = PRESET.enable_sharper;
|
||||||
|
shader_backend_->setCrtPiParams(p);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::getCurrentPresetName() const -> const char * {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) { return "---"; }
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX) {
|
||||||
|
if (Options::current_postfx_preset >= 0 && Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size())) {
|
||||||
|
return Options::postfx_presets[Options::current_postfx_preset].name.c_str();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Options::current_crtpi_preset >= 0 && Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size())) {
|
||||||
|
return Options::crtpi_presets[Options::current_crtpi_preset].name.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::nextPreset() -> bool {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) { return false; }
|
||||||
|
if (!Options::video.shader.enabled) { return false; }
|
||||||
|
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX) {
|
||||||
|
if (Options::postfx_presets.empty()) { return false; }
|
||||||
|
const int N = static_cast<int>(Options::postfx_presets.size());
|
||||||
|
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % N;
|
||||||
|
Options::video.shader.current_postfx_preset_name =
|
||||||
|
Options::postfx_presets[Options::current_postfx_preset].name;
|
||||||
|
applyCurrentPostFXPreset();
|
||||||
|
} else {
|
||||||
|
if (Options::crtpi_presets.empty()) { return false; }
|
||||||
|
const int N = static_cast<int>(Options::crtpi_presets.size());
|
||||||
|
Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % N;
|
||||||
|
Options::video.shader.current_crtpi_preset_name =
|
||||||
|
Options::crtpi_presets[Options::current_crtpi_preset].name;
|
||||||
|
applyCurrentCrtPiPreset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const color_t GREEN = {0x00, 0xFF, 0x80};
|
||||||
|
const color_t BLACK = {0x00, 0x00, 0x00};
|
||||||
|
const Uint32 DUR_MS = 1500;
|
||||||
|
notify(std::string("Preset: ") + getCurrentPresetName(), GREEN, BLACK, DUR_MS);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Screen::prevPreset() -> bool {
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) { return false; }
|
||||||
|
if (!Options::video.shader.enabled) { return false; }
|
||||||
|
|
||||||
|
if (Options::video.shader.current_shader == Rendering::ShaderType::POSTFX) {
|
||||||
|
if (Options::postfx_presets.empty()) { return false; }
|
||||||
|
const int N = static_cast<int>(Options::postfx_presets.size());
|
||||||
|
Options::current_postfx_preset = (Options::current_postfx_preset - 1 + N) % N;
|
||||||
|
Options::video.shader.current_postfx_preset_name =
|
||||||
|
Options::postfx_presets[Options::current_postfx_preset].name;
|
||||||
|
applyCurrentPostFXPreset();
|
||||||
|
} else {
|
||||||
|
if (Options::crtpi_presets.empty()) { return false; }
|
||||||
|
const int N = static_cast<int>(Options::crtpi_presets.size());
|
||||||
|
Options::current_crtpi_preset = (Options::current_crtpi_preset - 1 + N) % N;
|
||||||
|
Options::video.shader.current_crtpi_preset_name =
|
||||||
|
Options::crtpi_presets[Options::current_crtpi_preset].name;
|
||||||
|
applyCurrentCrtPiPreset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const color_t GREEN = {0x00, 0xFF, 0x80};
|
||||||
|
const color_t BLACK = {0x00, 0x00, 0x00};
|
||||||
|
const Uint32 DUR_MS = 1500;
|
||||||
|
notify(std::string("Preset: ") + getCurrentPresetName(), GREEN, BLACK, DUR_MS);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -2,16 +2,21 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <memory> // for unique_ptr
|
||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "utils/utils.h" // for color_t
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
#include "core/rendering/shader_backend.hpp" // for Rendering::ShaderType
|
||||||
|
namespace Rendering {
|
||||||
|
class ShaderBackend;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils.h" // for color_t
|
|
||||||
class Asset;
|
|
||||||
class Text;
|
class Text;
|
||||||
|
|
||||||
// Tipos de filtro
|
|
||||||
constexpr int FILTER_NEAREST = 0;
|
|
||||||
constexpr int FILTER_LINEAL = 1;
|
|
||||||
|
|
||||||
class Screen {
|
class Screen {
|
||||||
public:
|
public:
|
||||||
// Constantes
|
// Constantes
|
||||||
@@ -24,8 +29,12 @@ class Screen {
|
|||||||
static constexpr int WASM_RENDER_SCALE = 3;
|
static constexpr int WASM_RENDER_SCALE = 3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Constructor y destructor
|
// Singleton API
|
||||||
Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options);
|
static void init(SDL_Window *window, SDL_Renderer *renderer); // Crea la instancia
|
||||||
|
static void destroy(); // Libera la instancia
|
||||||
|
static auto get() -> Screen *; // Obtiene el puntero a la instancia
|
||||||
|
|
||||||
|
// Destructor (público por requisitos de `delete` desde destroy())
|
||||||
~Screen();
|
~Screen();
|
||||||
|
|
||||||
// Render loop
|
// Render loop
|
||||||
@@ -50,10 +59,38 @@ class Screen {
|
|||||||
void setBorderColor(color_t color); // Cambia el color del borde
|
void setBorderColor(color_t color); // Cambia el color del borde
|
||||||
|
|
||||||
// Notificaciones
|
// Notificaciones
|
||||||
|
void initNotifications(); // Enllaça el Text de notificacions amb `Resource`. A cridar després de `Resource::init(...)`.
|
||||||
void notify(const std::string &text, color_t textColor, color_t outlineColor, Uint32 durationMs); // Muestra una notificación en la línea superior del canvas durante durationMs. Sobrescribe cualquier notificación activa (sin apilación).
|
void notify(const std::string &text, color_t textColor, color_t outlineColor, Uint32 durationMs); // Muestra una notificación en la línea superior del canvas durante durationMs. Sobrescribe cualquier notificación activa (sin apilación).
|
||||||
void clearNotification(); // Limpia la notificación actual
|
void clearNotification(); // Limpia la notificación actual
|
||||||
|
|
||||||
|
// GPU / shaders (post-procesado). En builds con NO_SHADERS (Emscripten) son no-op.
|
||||||
|
void initShaders(); // Crea el backend GPU si no existe y lo inicializa
|
||||||
|
void shutdownShaders(); // Libera el backend GPU
|
||||||
|
auto isGpuAccelerated() const -> bool; // true si el backend existe y reporta hardware OK
|
||||||
|
void setShaderEnabled(bool enabled); // Activa o desactiva el post-procesado (persiste)
|
||||||
|
void toggleShaderEnabled(); // Alterna post-procesado
|
||||||
|
auto isShaderEnabled() const -> bool; // Estado actual (lee options)
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
void setActiveShader(Rendering::ShaderType type); // POSTFX o CRTPI
|
||||||
|
auto getActiveShader() const -> Rendering::ShaderType;
|
||||||
|
#endif
|
||||||
|
void toggleActiveShader(); // Alterna POSTFX ↔ CRTPI
|
||||||
|
|
||||||
|
// Presets de shaders (PostFX/CrtPi). Operen sobre el shader actiu.
|
||||||
|
// Retornen false si GPU off / shaders off / llista buida (igual que a aee_plus).
|
||||||
|
auto nextPreset() -> bool;
|
||||||
|
auto prevPreset() -> bool;
|
||||||
|
auto getCurrentPresetName() const -> const char *;
|
||||||
|
void applyCurrentPostFXPreset(); // Escriu el preset PostFX actiu al backend
|
||||||
|
void applyCurrentCrtPiPreset(); // Escriu el preset CrtPi actiu al backend
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Constructor privado (usar Screen::init)
|
||||||
|
Screen(SDL_Window *window, SDL_Renderer *renderer);
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
static Screen *instance;
|
||||||
|
|
||||||
// Helpers internos de setVideoMode
|
// Helpers internos de setVideoMode
|
||||||
void applyFullscreen(bool fullscreen); // SDL_SetWindowFullscreen + visibilidad del cursor
|
void applyFullscreen(bool fullscreen); // SDL_SetWindowFullscreen + visibilidad del cursor
|
||||||
void applyWindowedLayout(); // Calcula windowWidth/Height/dest + SDL_SetWindowSize + SDL_SetWindowPosition
|
void applyWindowedLayout(); // Calcula windowWidth/Height/dest + SDL_SetWindowSize + SDL_SetWindowPosition
|
||||||
@@ -67,20 +104,22 @@ class Screen {
|
|||||||
// Notificaciones
|
// Notificaciones
|
||||||
void renderNotification(); // Dibuja la notificación activa (si la hay) sobre el gameCanvas
|
void renderNotification(); // Dibuja la notificación activa (si la hay) sobre el gameCanvas
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// Aplica els paràmetres actuals del shader al backend segons options
|
||||||
|
// (pass-through si `videoShaderEnabled==false`, preset per defecte si true).
|
||||||
|
void applyShaderParams();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Window *window; // Ventana de la aplicación
|
SDL_Window *window; // Ventana de la aplicación
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Asset *asset; // Objeto con el listado de recursos
|
|
||||||
SDL_Texture *gameCanvas; // Textura para completar la ventana de juego hasta la pantalla completa
|
SDL_Texture *gameCanvas; // Textura para completar la ventana de juego hasta la pantalla completa
|
||||||
options_t *options; // Variable con todas las opciones del programa
|
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
int windowWidth; // Ancho de la pantalla o ventana
|
int windowWidth; // Ancho de la pantalla o ventana
|
||||||
int windowHeight; // Alto de la pantalla o ventana
|
int windowHeight; // Alto de la pantalla o ventana
|
||||||
int gameCanvasWidth; // Resolución interna del juego. Es el ancho de la textura donde se dibuja el juego
|
int gameCanvasWidth; // Resolución interna del juego. Es el ancho de la textura donde se dibuja el juego
|
||||||
int gameCanvasHeight; // Resolución interna del juego. Es el alto de la textura donde se dibuja el juego
|
int gameCanvasHeight; // Resolución interna del juego. Es el alto de la textura donde se dibuja el juego
|
||||||
int borderWidth; // Anchura del borde
|
|
||||||
int borderHeight; // Altura del borde
|
|
||||||
SDL_Rect dest; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
|
SDL_Rect dest; // Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
|
||||||
color_t borderColor; // Color del borde añadido a la textura de juego para rellenar la pantalla
|
color_t borderColor; // Color del borde añadido a la textura de juego para rellenar la pantalla
|
||||||
|
|
||||||
@@ -91,4 +130,10 @@ class Screen {
|
|||||||
color_t notificationOutlineColor; // Color del outline
|
color_t notificationOutlineColor; // Color del outline
|
||||||
Uint32 notificationEndTime; // SDL_GetTicks() hasta el cual se muestra
|
Uint32 notificationEndTime; // SDL_GetTicks() hasta el cual se muestra
|
||||||
int notificationY; // Fila vertical en el canvas virtual
|
int notificationY; // Fila vertical en el canvas virtual
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
// GPU / shaders
|
||||||
|
std::unique_ptr<Rendering::ShaderBackend> shader_backend_; // Backend GPU (nullptr si inactivo)
|
||||||
|
std::vector<Uint32> pixel_buffer_; // Buffer de readback del gameCanvas (ARGB8888)
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
1329
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
Normal file
1329
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
178
source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp
Normal file
178
source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
|
#include "core/rendering/shader_backend.hpp"
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
struct PostFXUniforms {
|
||||||
|
float vignette_strength; // 0 = none, ~0.8 = subtle
|
||||||
|
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
||||||
|
float scanline_strength; // 0 = off, 1 = full
|
||||||
|
float screen_height; // logical height in pixels (used by bleeding effect)
|
||||||
|
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
||||||
|
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
||||||
|
float curvature; // 0 = flat, 1 = max barrel distortion
|
||||||
|
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
||||||
|
float pixel_scale; // physical pixels per logical pixel (vh / tex_height_)
|
||||||
|
float time; // seconds since SDL init (SDL_GetTicks() / 1000.0f)
|
||||||
|
float oversample; // supersampling factor (1.0 = off, 3.0 = 3×SS)
|
||||||
|
float flicker; // 0 = off, 1 = phosphor flicker ~50 Hz — keep struct at 48 bytes (3 × 16)
|
||||||
|
};
|
||||||
|
|
||||||
|
// CrtPi uniforms pushed to fragment stage each frame.
|
||||||
|
// Must match the MSL struct and GLSL uniform block layout.
|
||||||
|
// 14 fields (8 floats + 6 ints) + 2 floats (texture size) = 16 fields = 64 bytes — 4 × 16-byte alignment.
|
||||||
|
struct CrtPiUniforms {
|
||||||
|
// vec4 #0
|
||||||
|
float scanline_weight; // Ajuste gaussiano (default 6.0)
|
||||||
|
float scanline_gap_brightness; // Brillo mínimo entre scanlines (default 0.12)
|
||||||
|
float bloom_factor; // Factor brillo zonas iluminadas (default 3.5)
|
||||||
|
float input_gamma; // Gamma de entrada (default 2.4)
|
||||||
|
// vec4 #1
|
||||||
|
float output_gamma; // Gamma de salida (default 2.2)
|
||||||
|
float mask_brightness; // Brillo sub-píxeles máscara (default 0.80)
|
||||||
|
float curvature_x; // Distorsión barrel X (default 0.05)
|
||||||
|
float curvature_y; // Distorsión barrel Y (default 0.10)
|
||||||
|
// vec4 #2
|
||||||
|
int mask_type; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
|
||||||
|
int enable_scanlines; // 0 = off, 1 = on
|
||||||
|
int enable_multisample; // 0 = off, 1 = on (antialiasing analítico)
|
||||||
|
int enable_gamma; // 0 = off, 1 = on
|
||||||
|
// vec4 #3
|
||||||
|
int enable_curvature; // 0 = off, 1 = on
|
||||||
|
int enable_sharper; // 0 = off, 1 = on
|
||||||
|
float texture_width; // Ancho del canvas en píxeles (inyectado en render)
|
||||||
|
float texture_height; // Alto del canvas en píxeles (inyectado en render)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Downscale uniforms pushed to the Lanczos downscale fragment stage.
|
||||||
|
// 1 int + 3 floats = 16 bytes — meets Metal/Vulkan alignment.
|
||||||
|
struct DownscaleUniforms {
|
||||||
|
int algorithm; // 0 = Lanczos2 (ventana 2), 1 = Lanczos3 (ventana 3)
|
||||||
|
float pad0;
|
||||||
|
float pad1;
|
||||||
|
float pad2;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Rendering {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Backend de shaders usando SDL3 GPU API (Metal en macOS, Vulkan/SPIR-V en Win/Linux)
|
||||||
|
*
|
||||||
|
* Reemplaza el backend OpenGL para que los shaders PostFX funcionen en macOS.
|
||||||
|
* Pipeline: Surface pixels (CPU) → SDL_GPUTransferBuffer → SDL_GPUTexture (scene)
|
||||||
|
* → PostFX render pass → swapchain → present
|
||||||
|
*/
|
||||||
|
class SDL3GPUShader : public ShaderBackend {
|
||||||
|
public:
|
||||||
|
SDL3GPUShader() = default;
|
||||||
|
~SDL3GPUShader() override;
|
||||||
|
|
||||||
|
auto init(SDL_Window* window,
|
||||||
|
SDL_Texture* texture,
|
||||||
|
const std::string& vertex_source,
|
||||||
|
const std::string& fragment_source) -> bool override;
|
||||||
|
|
||||||
|
void render() override;
|
||||||
|
void setTextureSize(float width, float height) override {}
|
||||||
|
void cleanup() final; // Libera pipeline/texturas pero mantiene el device vivo
|
||||||
|
void destroy(); // Limpieza completa (device + swapchain); llamar solo al cerrar
|
||||||
|
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||||||
|
[[nodiscard]] auto getDriverName() const -> std::string override { return driver_name_; }
|
||||||
|
|
||||||
|
// Establece el driver GPU preferido (vacío = auto). Debe llamarse antes de init().
|
||||||
|
void setPreferredDriver(const std::string& driver) override { preferred_driver_ = driver; }
|
||||||
|
|
||||||
|
// Sube píxeles ARGB8888 desde CPU; llamado antes de render()
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Activa/desactiva VSync en el swapchain
|
||||||
|
void setVSync(bool vsync) override;
|
||||||
|
|
||||||
|
// Activa/desactiva escalado entero (integer scale)
|
||||||
|
void setScaleMode(bool integer_scale) override;
|
||||||
|
|
||||||
|
// Establece factor de supersampling (1 = off, 3 = 3×SS)
|
||||||
|
void setOversample(int factor) override;
|
||||||
|
|
||||||
|
// Activa/desactiva interpolación LINEAR en el upscale (false = NEAREST)
|
||||||
|
void setLinearUpscale(bool linear) override;
|
||||||
|
|
||||||
|
// Selecciona algoritmo de downscale: 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
|
||||||
|
void setDownscaleAlgo(int algo) override;
|
||||||
|
|
||||||
|
// Devuelve las dimensiones de la textura de supersampling (0,0 si SS desactivado)
|
||||||
|
[[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int> override;
|
||||||
|
|
||||||
|
// Selecciona el shader de post-procesado activo (POSTFX o CRTPI)
|
||||||
|
void setActiveShader(ShaderType type) override;
|
||||||
|
|
||||||
|
// Actualiza los parámetros del shader CRT-Pi
|
||||||
|
void setCrtPiParams(const CrtPiParams& p) override;
|
||||||
|
|
||||||
|
// Devuelve el shader activo
|
||||||
|
[[nodiscard]] auto getActiveShader() const -> ShaderType override { return active_shader_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||||
|
const char* msl_source,
|
||||||
|
const char* entrypoint,
|
||||||
|
SDL_GPUShaderStage stage,
|
||||||
|
Uint32 num_samplers,
|
||||||
|
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||||||
|
|
||||||
|
static auto createShaderSPIRV(SDL_GPUDevice* device,
|
||||||
|
const uint8_t* spv_code,
|
||||||
|
size_t spv_size,
|
||||||
|
const char* entrypoint,
|
||||||
|
SDL_GPUShaderStage stage,
|
||||||
|
Uint32 num_samplers,
|
||||||
|
Uint32 num_uniform_buffers) -> SDL_GPUShader*;
|
||||||
|
|
||||||
|
auto createPipeline() -> bool;
|
||||||
|
auto createCrtPiPipeline() -> bool; // Pipeline dedicado para el shader CrtPi
|
||||||
|
auto reinitTexturesAndBuffer() -> bool; // Recrea scene_texture_ y upload_buffer_
|
||||||
|
auto recreateScaledTexture(int factor) -> bool; // Recrea scaled_texture_ para factor dado
|
||||||
|
static auto calcSsFactor(float zoom) -> int; // Primer múltiplo de 3 >= zoom (mín 3)
|
||||||
|
// Devuelve el mejor present mode disponible: IMMEDIATE > MAILBOX > VSYNC
|
||||||
|
[[nodiscard]] auto bestPresentMode(bool vsync) const -> SDL_GPUPresentMode;
|
||||||
|
|
||||||
|
SDL_Window* window_ = nullptr;
|
||||||
|
SDL_GPUDevice* device_ = nullptr;
|
||||||
|
SDL_GPUGraphicsPipeline* pipeline_ = nullptr; // PostFX pass (→ swapchain o → postfx_texture_)
|
||||||
|
SDL_GPUGraphicsPipeline* crtpi_pipeline_ = nullptr; // CrtPi pass (→ swapchain directo, sin SS)
|
||||||
|
SDL_GPUGraphicsPipeline* postfx_offscreen_pipeline_ = nullptr; // PostFX → postfx_texture_ (B8G8R8A8, solo con Lanczos)
|
||||||
|
SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr; // Upscale pass (solo con SS)
|
||||||
|
SDL_GPUGraphicsPipeline* downscale_pipeline_ = nullptr; // Lanczos downscale (solo con SS + algo > 0)
|
||||||
|
SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del juego (game_width_ × game_height_)
|
||||||
|
SDL_GPUTexture* scaled_texture_ = nullptr; // Upscale target (game×factor), solo con SS
|
||||||
|
SDL_GPUTexture* postfx_texture_ = nullptr; // PostFX output a resolución escalada, solo con Lanczos
|
||||||
|
SDL_GPUTransferBuffer* upload_buffer_ = nullptr;
|
||||||
|
SDL_GPUSampler* sampler_ = nullptr; // NEAREST
|
||||||
|
SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR
|
||||||
|
|
||||||
|
PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F};
|
||||||
|
CrtPiUniforms crtpi_uniforms_{.scanline_weight = 6.0F, .scanline_gap_brightness = 0.12F, .bloom_factor = 3.5F, .input_gamma = 2.4F, .output_gamma = 2.2F, .mask_brightness = 0.80F, .curvature_x = 0.05F, .curvature_y = 0.10F, .mask_type = 2, .enable_scanlines = 1, .enable_multisample = 1, .enable_gamma = 1};
|
||||||
|
ShaderType active_shader_ = ShaderType::POSTFX; // Shader de post-procesado activo
|
||||||
|
|
||||||
|
int game_width_ = 0; // Dimensiones originales del canvas
|
||||||
|
int game_height_ = 0;
|
||||||
|
int ss_factor_ = 0; // Factor SS activo (3, 6, 9...) o 0 si SS desactivado
|
||||||
|
int oversample_ = 1; // SS on/off (1 = off, >1 = on)
|
||||||
|
int downscale_algo_ = 1; // 0 = bilinear legacy, 1 = Lanczos2, 2 = Lanczos3
|
||||||
|
std::string driver_name_;
|
||||||
|
std::string preferred_driver_; // Driver preferido; vacío = auto (SDL elige)
|
||||||
|
bool is_initialized_ = false;
|
||||||
|
bool vsync_ = true;
|
||||||
|
bool integer_scale_ = false;
|
||||||
|
bool linear_upscale_ = false; // Upscale NEAREST (false) o LINEAR (true)
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Rendering
|
||||||
2
source/core/rendering/sdl3gpu/spv/.clang-format
Normal file
2
source/core/rendering/sdl3gpu/spv/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
DisableFormat: true
|
||||||
|
SortIncludes: Never
|
||||||
4
source/core/rendering/sdl3gpu/spv/.clang-tidy
Normal file
4
source/core/rendering/sdl3gpu/spv/.clang-tidy
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# source/core/rendering/sdl3gpu/spv/.clang-tidy
|
||||||
|
Checks: '-*'
|
||||||
|
WarningsAsErrors: ''
|
||||||
|
HeaderFilterRegex: ''
|
||||||
10362
source/core/rendering/sdl3gpu/spv/crtpi_frag_spv.h
Normal file
10362
source/core/rendering/sdl3gpu/spv/crtpi_frag_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
4253
source/core/rendering/sdl3gpu/spv/downscale_frag_spv.h
Normal file
4253
source/core/rendering/sdl3gpu/spv/downscale_frag_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
11717
source/core/rendering/sdl3gpu/spv/postfx_frag_spv.h
Normal file
11717
source/core/rendering/sdl3gpu/spv/postfx_frag_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
1449
source/core/rendering/sdl3gpu/spv/postfx_vert_spv.h
Normal file
1449
source/core/rendering/sdl3gpu/spv/postfx_vert_spv.h
Normal file
File diff suppressed because it is too large
Load Diff
633
source/core/rendering/sdl3gpu/spv/upscale_frag_spv.h
Normal file
633
source/core/rendering/sdl3gpu/spv/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;
|
||||||
175
source/core/rendering/shader_backend.hpp
Normal file
175
source/core/rendering/shader_backend.hpp
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Rendering {
|
||||||
|
|
||||||
|
/** @brief Identificador del shader de post-procesado activo */
|
||||||
|
enum class ShaderType { POSTFX,
|
||||||
|
CRTPI };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parámetros de intensidad de los efectos PostFX
|
||||||
|
* Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp
|
||||||
|
*/
|
||||||
|
struct PostFXParams {
|
||||||
|
float vignette = 0.0F; // Intensidad de la viñeta
|
||||||
|
float scanlines = 0.0F; // Intensidad de las scanlines
|
||||||
|
float chroma = 0.0F; // Aberración cromática
|
||||||
|
float mask = 0.0F; // Máscara de fósforo RGB
|
||||||
|
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
|
||||||
|
float curvature = 0.0F; // Curvatura barrel CRT
|
||||||
|
float bleeding = 0.0F; // Sangrado de color NTSC
|
||||||
|
float flicker = 0.0F; // Parpadeo de fósforo CRT ~50 Hz
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
|
||||||
|
* Diferente al PostFX: usa pesos gaussianos por distancia subpixel y bloom.
|
||||||
|
*/
|
||||||
|
struct CrtPiParams {
|
||||||
|
float scanline_weight{6.0F}; // Ajuste gaussiano (mayor = scanlines más estrechas)
|
||||||
|
float scanline_gap_brightness{0.12F}; // Brillo mínimo en las ranuras entre scanlines
|
||||||
|
float bloom_factor{3.5F}; // Factor de brillo para zonas iluminadas
|
||||||
|
float input_gamma{2.4F}; // Gamma de entrada (linealización)
|
||||||
|
float output_gamma{2.2F}; // Gamma de salida (codificación)
|
||||||
|
float mask_brightness{0.80F}; // Sub-píxeles tenues en la máscara de fósforo
|
||||||
|
float curvature_x{0.05F}; // Distorsión barrel eje X
|
||||||
|
float curvature_y{0.10F}; // Distorsión barrel eje Y
|
||||||
|
int mask_type{2}; // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
|
||||||
|
bool enable_scanlines{true}; // Activar efecto de scanlines
|
||||||
|
bool enable_multisample{true}; // Antialiasing analítico de scanlines
|
||||||
|
bool enable_gamma{true}; // Corrección gamma
|
||||||
|
bool enable_curvature{false}; // Distorsión barrel CRT
|
||||||
|
bool enable_sharper{false}; // Submuestreo más nítido (modo SHARPER)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interfaz abstracta para backends de renderizado con shaders
|
||||||
|
*
|
||||||
|
* Esta interfaz define el contrato que todos los backends de shaders
|
||||||
|
* deben cumplir (OpenGL, Metal, Vulkan, etc.)
|
||||||
|
*/
|
||||||
|
class ShaderBackend {
|
||||||
|
public:
|
||||||
|
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,
|
||||||
|
SDL_Texture* texture,
|
||||||
|
const std::string& vertex_source,
|
||||||
|
const std::string& fragment_source) -> bool = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renderiza la textura con los shaders aplicados
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Limpia y libera recursos del backend
|
||||||
|
*/
|
||||||
|
virtual void cleanup() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sube píxeles ARGB8888 desde la CPU al backend de shaders
|
||||||
|
* Usado por SDL3GPUShader para evitar pasar por SDL_Texture
|
||||||
|
*/
|
||||||
|
virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Establece los parámetros de intensidad de los efectos PostFX
|
||||||
|
* @param p Struct con todos los parámetros PostFX
|
||||||
|
*/
|
||||||
|
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activa o desactiva VSync en el swapchain del GPU device
|
||||||
|
*/
|
||||||
|
virtual void setVSync(bool /*vsync*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activa o desactiva el escalado entero (integer scale)
|
||||||
|
*/
|
||||||
|
virtual void setScaleMode(bool /*integer_scale*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Establece el factor de supersampling (1 = off, 3 = 3× SS)
|
||||||
|
* Con factor > 1, la textura GPU se crea a game×factor resolución y
|
||||||
|
* las scanlines se hornean en CPU (uploadPixels). El sampler usa LINEAR.
|
||||||
|
*/
|
||||||
|
virtual void setOversample(int /*factor*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activa/desactiva interpolación LINEAR en el paso de upscale (SS).
|
||||||
|
* Por defecto NEAREST (false). Solo tiene efecto con supersampling activo.
|
||||||
|
*/
|
||||||
|
virtual void setLinearUpscale(bool /*linear*/) {}
|
||||||
|
[[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Selecciona el algoritmo de downscale tras el PostFX (SS activo).
|
||||||
|
* 0 = bilinear legacy (comportamiento actual, sin textura intermedia),
|
||||||
|
* 1 = Lanczos2 (ventana 2, ~25 muestras), 2 = Lanczos3 (ventana 3, ~49 muestras).
|
||||||
|
*/
|
||||||
|
virtual void setDownscaleAlgo(int /*algo*/) {}
|
||||||
|
[[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Devuelve las dimensiones de la textura de supersampling.
|
||||||
|
* @return Par (ancho, alto) en píxeles; (0, 0) si SS está desactivado.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifica si el backend está usando aceleración por hardware
|
||||||
|
* @return true si usa aceleración (OpenGL/Metal/Vulkan)
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Nombre del driver GPU activo (p.ej. "vulkan", "metal", "direct3d12")
|
||||||
|
* @return Cadena vacía si no disponible
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Establece el driver GPU preferido antes de init().
|
||||||
|
* Vacío = selección automática de SDL. Implementado en SDL3GPUShader.
|
||||||
|
*/
|
||||||
|
virtual void setPreferredDriver(const std::string& /*driver*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Selecciona el shader de post-procesado activo (POSTFX o CRTPI).
|
||||||
|
* Debe llamarse antes de render(). No recrea pipelines.
|
||||||
|
*/
|
||||||
|
virtual void setActiveShader(ShaderType /*type*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Establece los parámetros del shader CRT-Pi.
|
||||||
|
*/
|
||||||
|
virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Devuelve el shader de post-procesado activo.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Rendering
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "smartsprite.h"
|
#include "core/rendering/smartsprite.h"
|
||||||
|
|
||||||
#include "movingsprite.h" // for MovingSprite
|
#include "core/rendering/movingsprite.h" // for MovingSprite
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "animatedsprite.h" // for AnimatedSprite
|
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Clase SmartSprite
|
// Clase SmartSprite
|
||||||
@@ -1,50 +1,28 @@
|
|||||||
#include "sprite.h"
|
#include "core/rendering/sprite.h"
|
||||||
|
|
||||||
#include "texture.h" // for Texture
|
#include "core/rendering/texture.h" // for Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Sprite::Sprite(int x, int y, int w, int h, Texture *texture, SDL_Renderer *renderer) {
|
Sprite::Sprite(int x, int y, int w, int h, Texture *texture, SDL_Renderer *renderer)
|
||||||
// Establece la posición X,Y del sprite
|
: x(x),
|
||||||
this->x = x;
|
y(y),
|
||||||
this->y = y;
|
w(w),
|
||||||
|
h(h),
|
||||||
// Establece el alto y el ancho del sprite
|
renderer(renderer),
|
||||||
this->w = w;
|
texture(texture),
|
||||||
this->h = h;
|
spriteClip{0, 0, w, h},
|
||||||
|
enabled(true) {
|
||||||
// Establece el puntero al renderizador de la ventana
|
|
||||||
this->renderer = renderer;
|
|
||||||
|
|
||||||
// Establece la textura donde están los gráficos para el sprite
|
|
||||||
this->texture = texture;
|
|
||||||
|
|
||||||
// Establece el rectangulo de donde coger la imagen
|
|
||||||
spriteClip = {0, 0, w, h};
|
|
||||||
|
|
||||||
// Inicializa variables
|
|
||||||
enabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite::Sprite(SDL_Rect rect, Texture *texture, SDL_Renderer *renderer) {
|
Sprite::Sprite(SDL_Rect rect, Texture *texture, SDL_Renderer *renderer)
|
||||||
// Establece la posición X,Y del sprite
|
: x(rect.x),
|
||||||
x = rect.x;
|
y(rect.y),
|
||||||
y = rect.y;
|
w(rect.w),
|
||||||
|
h(rect.h),
|
||||||
// Establece el alto y el ancho del sprite
|
renderer(renderer),
|
||||||
w = rect.w;
|
texture(texture),
|
||||||
h = rect.h;
|
spriteClip{0, 0, rect.w, rect.h},
|
||||||
|
enabled(true) {
|
||||||
// Establece el puntero al renderizador de la ventana
|
|
||||||
this->renderer = renderer;
|
|
||||||
|
|
||||||
// Establece la textura donde están los gráficos para el sprite
|
|
||||||
this->texture = texture;
|
|
||||||
|
|
||||||
// Establece el rectangulo de donde coger la imagen
|
|
||||||
spriteClip = {0, 0, w, h};
|
|
||||||
|
|
||||||
// Inicializa variables
|
|
||||||
enabled = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -19,7 +19,7 @@ class Sprite {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Sprite(int x = 0, int y = 0, int w = 0, int h = 0, Texture *texture = nullptr, SDL_Renderer *renderer = nullptr);
|
explicit Sprite(int x = 0, int y = 0, int w = 0, int h = 0, Texture *texture = nullptr, SDL_Renderer *renderer = nullptr);
|
||||||
Sprite(SDL_Rect rect, Texture *texture, SDL_Renderer *renderer);
|
Sprite(SDL_Rect rect, Texture *texture, SDL_Renderer *renderer);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
#include "text.h"
|
#include "core/rendering/text.h"
|
||||||
|
|
||||||
#include <fstream> // for char_traits, basic_ostream, basic_ifstream, ope...
|
#include <fstream> // for char_traits, basic_ostream, basic_ifstream, ope...
|
||||||
#include <iostream> // for cout
|
#include <iostream> // for cout
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "texture.h" // for Texture
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "utils.h" // for color_t
|
#include "utils/utils.h" // for color_t
|
||||||
|
|
||||||
// Parser compartido: rellena un textFile_t desde cualquier istream
|
// Parser compartido: rellena un textFile_t desde cualquier istream
|
||||||
static void parseTextFileStream(std::istream &rfile, textFile_t &tf) {
|
static void parseTextFileStream(std::istream &rfile, textFile_t &tf) {
|
||||||
@@ -39,8 +39,10 @@ static void computeTextFileOffsets(textFile_t &tf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Llena una estructuta textFile_t desde un fichero
|
// Llena una estructuta textFile_t desde un fichero
|
||||||
textFile_t LoadTextFile(std::string file, bool verbose) {
|
textFile_t LoadTextFile(const std::string &file, bool verbose) {
|
||||||
textFile_t tf;
|
textFile_t tf;
|
||||||
|
tf.boxWidth = 0;
|
||||||
|
tf.boxHeight = 0;
|
||||||
for (int i = 0; i < 128; ++i) {
|
for (int i = 0; i < 128; ++i) {
|
||||||
tf.offset[i].x = 0;
|
tf.offset[i].x = 0;
|
||||||
tf.offset[i].y = 0;
|
tf.offset[i].y = 0;
|
||||||
@@ -65,6 +67,8 @@ textFile_t LoadTextFile(std::string file, bool verbose) {
|
|||||||
// Llena una estructura textFile_t desde bytes en memoria
|
// Llena una estructura textFile_t desde bytes en memoria
|
||||||
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose) {
|
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose) {
|
||||||
textFile_t tf;
|
textFile_t tf;
|
||||||
|
tf.boxWidth = 0;
|
||||||
|
tf.boxHeight = 0;
|
||||||
for (int i = 0; i < 128; ++i) {
|
for (int i = 0; i < 128; ++i) {
|
||||||
tf.offset[i].x = 0;
|
tf.offset[i].x = 0;
|
||||||
tf.offset[i].y = 0;
|
tf.offset[i].y = 0;
|
||||||
@@ -83,7 +87,7 @@ textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbos
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Text::Text(std::string bitmapFile, std::string textFile, SDL_Renderer *renderer) {
|
Text::Text(const std::string &bitmapFile, const std::string &textFile, SDL_Renderer *renderer) {
|
||||||
// Carga los offsets desde el fichero
|
// Carga los offsets desde el fichero
|
||||||
textFile_t tf = LoadTextFile(textFile);
|
textFile_t tf = LoadTextFile(textFile);
|
||||||
|
|
||||||
@@ -105,7 +109,7 @@ Text::Text(std::string bitmapFile, std::string textFile, SDL_Renderer *renderer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Text::Text(std::string textFile, Texture *texture, SDL_Renderer *renderer) {
|
Text::Text(const std::string &textFile, Texture *texture, SDL_Renderer *renderer) {
|
||||||
// Carga los offsets desde el fichero
|
// Carga los offsets desde el fichero
|
||||||
textFile_t tf = LoadTextFile(textFile);
|
textFile_t tf = LoadTextFile(textFile);
|
||||||
|
|
||||||
@@ -172,7 +176,7 @@ Text::~Text() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escribe texto en pantalla
|
// Escribe texto en pantalla
|
||||||
void Text::write(int x, int y, std::string text, int kerning, int lenght) {
|
void Text::write(int x, int y, const std::string &text, int kerning, int lenght) {
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
|
|
||||||
if (lenght == -1) {
|
if (lenght == -1) {
|
||||||
@@ -192,14 +196,14 @@ void Text::write(int x, int y, std::string text, int kerning, int lenght) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escribe el texto con colores
|
// Escribe el texto con colores
|
||||||
void Text::writeColored(int x, int y, std::string text, color_t color, int kerning, int lenght) {
|
void Text::writeColored(int x, int y, const std::string &text, color_t color, int kerning, int lenght) {
|
||||||
sprite->getTexture()->setColor(color.r, color.g, color.b);
|
sprite->getTexture()->setColor(color.r, color.g, color.b);
|
||||||
write(x, y, text, kerning, lenght);
|
write(x, y, text, kerning, lenght);
|
||||||
sprite->getTexture()->setColor(255, 255, 255);
|
sprite->getTexture()->setColor(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escribe el texto con sombra
|
// Escribe el texto con sombra
|
||||||
void Text::writeShadowed(int x, int y, std::string text, color_t color, Uint8 shadowDistance, int kerning, int lenght) {
|
void Text::writeShadowed(int x, int y, const std::string &text, color_t color, Uint8 shadowDistance, int kerning, int lenght) {
|
||||||
sprite->getTexture()->setColor(color.r, color.g, color.b);
|
sprite->getTexture()->setColor(color.r, color.g, color.b);
|
||||||
write(x + shadowDistance, y + shadowDistance, text, kerning, lenght);
|
write(x + shadowDistance, y + shadowDistance, text, kerning, lenght);
|
||||||
sprite->getTexture()->setColor(255, 255, 255);
|
sprite->getTexture()->setColor(255, 255, 255);
|
||||||
@@ -207,13 +211,13 @@ void Text::writeShadowed(int x, int y, std::string text, color_t color, Uint8 sh
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escribe el texto centrado en un punto x
|
// Escribe el texto centrado en un punto x
|
||||||
void Text::writeCentered(int x, int y, std::string text, int kerning, int lenght) {
|
void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght) {
|
||||||
x -= (Text::lenght(text, kerning) / 2);
|
x -= (Text::lenght(text, kerning) / 2);
|
||||||
write(x, y, text, kerning, lenght);
|
write(x, y, text, kerning, lenght);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escribe texto con extras
|
// Escribe texto con extras
|
||||||
void Text::writeDX(Uint8 flags, int x, int y, std::string text, int kerning, color_t textColor, Uint8 shadowDistance, color_t shadowColor, int lenght) {
|
void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, color_t textColor, Uint8 shadowDistance, color_t shadowColor, int lenght) {
|
||||||
const bool centered = ((flags & TXT_CENTER) == TXT_CENTER);
|
const bool centered = ((flags & TXT_CENTER) == TXT_CENTER);
|
||||||
const bool shadowed = ((flags & TXT_SHADOW) == TXT_SHADOW);
|
const bool shadowed = ((flags & TXT_SHADOW) == TXT_SHADOW);
|
||||||
const bool colored = ((flags & TXT_COLOR) == TXT_COLOR);
|
const bool colored = ((flags & TXT_COLOR) == TXT_COLOR);
|
||||||
@@ -245,7 +249,7 @@ void Text::writeDX(Uint8 flags, int x, int y, std::string text, int kerning, col
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene la longitud en pixels de una cadena
|
// Obtiene la longitud en pixels de una cadena
|
||||||
int Text::lenght(std::string text, int kerning) {
|
int Text::lenght(const std::string &text, int kerning) {
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
|
|
||||||
for (int i = 0; i < (int)text.length(); ++i)
|
for (int i = 0; i < (int)text.length(); ++i)
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
#include "utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
// Opciones de texto
|
// Opciones de texto
|
||||||
constexpr int TXT_COLOR = 1;
|
constexpr int TXT_COLOR = 1;
|
||||||
@@ -28,7 +28,7 @@ struct textFile_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Llena una estructuta textFile_t desde un fichero
|
// Llena una estructuta textFile_t desde un fichero
|
||||||
textFile_t LoadTextFile(std::string file, bool verbose = false);
|
textFile_t LoadTextFile(const std::string &file, bool verbose = false);
|
||||||
|
|
||||||
// Llena una estructura textFile_t desde bytes en memoria
|
// Llena una estructura textFile_t desde bytes en memoria
|
||||||
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose = false);
|
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose = false);
|
||||||
@@ -48,8 +48,8 @@ class Text {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Text(std::string bitmapFile, std::string textFile, SDL_Renderer *renderer);
|
Text(const std::string &bitmapFile, const std::string &textFile, SDL_Renderer *renderer);
|
||||||
Text(std::string textFile, Texture *texture, SDL_Renderer *renderer);
|
Text(const std::string &textFile, Texture *texture, SDL_Renderer *renderer);
|
||||||
Text(textFile_t *textFile, Texture *texture, SDL_Renderer *renderer);
|
Text(textFile_t *textFile, Texture *texture, SDL_Renderer *renderer);
|
||||||
|
|
||||||
// Constructor desde bytes en memoria: comparte ownership del texture (no lo libera)
|
// Constructor desde bytes en memoria: comparte ownership del texture (no lo libera)
|
||||||
@@ -58,23 +58,27 @@ class Text {
|
|||||||
// Destructor
|
// Destructor
|
||||||
~Text();
|
~Text();
|
||||||
|
|
||||||
|
// No copiable (gestiona memoria dinámica)
|
||||||
|
Text(const Text &) = delete;
|
||||||
|
Text &operator=(const Text &) = delete;
|
||||||
|
|
||||||
// Escribe el texto en pantalla
|
// Escribe el texto en pantalla
|
||||||
void write(int x, int y, std::string text, int kerning = 1, int lenght = -1);
|
void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1);
|
||||||
|
|
||||||
// Escribe el texto con colores
|
// Escribe el texto con colores
|
||||||
void writeColored(int x, int y, std::string text, color_t color, int kerning = 1, int lenght = -1);
|
void writeColored(int x, int y, const std::string &text, color_t color, int kerning = 1, int lenght = -1);
|
||||||
|
|
||||||
// Escribe el texto con sombra
|
// Escribe el texto con sombra
|
||||||
void writeShadowed(int x, int y, std::string text, color_t color, Uint8 shadowDistance = 1, int kerning = 1, int lenght = -1);
|
void writeShadowed(int x, int y, const std::string &text, color_t color, Uint8 shadowDistance = 1, int kerning = 1, int lenght = -1);
|
||||||
|
|
||||||
// Escribe el texto centrado en un punto x
|
// Escribe el texto centrado en un punto x
|
||||||
void writeCentered(int x, int y, std::string text, int kerning = 1, int lenght = -1);
|
void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1);
|
||||||
|
|
||||||
// Escribe texto con extras
|
// Escribe texto con extras
|
||||||
void writeDX(Uint8 flags, int x, int y, std::string text, int kerning = 1, color_t textColor = color_t(255, 255, 255), Uint8 shadowDistance = 1, color_t shadowColor = color_t(0, 0, 0), int lenght = -1);
|
void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, color_t textColor = color_t(255, 255, 255), Uint8 shadowDistance = 1, color_t shadowColor = color_t(0, 0, 0), int lenght = -1);
|
||||||
|
|
||||||
// Obtiene la longitud en pixels de una cadena
|
// Obtiene la longitud en pixels de una cadena
|
||||||
int lenght(std::string text, int kerning = 1);
|
int lenght(const std::string &text, int kerning = 1);
|
||||||
|
|
||||||
// Devuelve el valor de la variable
|
// Devuelve el valor de la variable
|
||||||
int getCharacterSize();
|
int getCharacterSize();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#include "texture.h"
|
#include "core/rendering/texture.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stdlib.h> // for exit
|
#include <stdlib.h> // for exit
|
||||||
@@ -15,30 +15,25 @@ void Texture::setGlobalScaleMode(SDL_ScaleMode mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Texture::Texture(SDL_Renderer *renderer, std::string path, bool verbose) {
|
Texture::Texture(SDL_Renderer *renderer, const std::string &path, bool verbose)
|
||||||
// Copia punteros
|
: texture(nullptr),
|
||||||
this->renderer = renderer;
|
renderer(renderer),
|
||||||
this->path = path;
|
width(0),
|
||||||
|
height(0),
|
||||||
// Inicializa
|
path(path) {
|
||||||
texture = nullptr;
|
|
||||||
width = 0;
|
|
||||||
height = 0;
|
|
||||||
|
|
||||||
// Carga el fichero en la textura
|
// Carga el fichero en la textura
|
||||||
if (path != "") {
|
if (!path.empty()) {
|
||||||
loadFromFile(path, renderer, verbose);
|
loadFromFile(path, renderer, verbose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor desde bytes
|
// Constructor desde bytes
|
||||||
Texture::Texture(SDL_Renderer *renderer, const std::vector<uint8_t> &bytes, bool verbose) {
|
Texture::Texture(SDL_Renderer *renderer, const std::vector<uint8_t> &bytes, bool verbose)
|
||||||
this->renderer = renderer;
|
: texture(nullptr),
|
||||||
this->path = "";
|
renderer(renderer),
|
||||||
texture = nullptr;
|
width(0),
|
||||||
width = 0;
|
height(0),
|
||||||
height = 0;
|
path("") {
|
||||||
|
|
||||||
if (!bytes.empty()) {
|
if (!bytes.empty()) {
|
||||||
loadFromMemory(bytes.data(), bytes.size(), renderer, verbose);
|
loadFromMemory(bytes.data(), bytes.size(), renderer, verbose);
|
||||||
}
|
}
|
||||||
@@ -53,7 +48,7 @@ Texture::~Texture() {
|
|||||||
// Helper: convierte píxeles RGBA decodificados por stbi en SDL_Texture
|
// Helper: convierte píxeles RGBA decodificados por stbi en SDL_Texture
|
||||||
static SDL_Texture *createTextureFromPixels(SDL_Renderer *renderer, unsigned char *data, int w, int h, int *out_w, int *out_h) {
|
static SDL_Texture *createTextureFromPixels(SDL_Renderer *renderer, unsigned char *data, int w, int h, int *out_w, int *out_h) {
|
||||||
const int pitch = 4 * w;
|
const int pitch = 4 * w;
|
||||||
SDL_Surface *loadedSurface = SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_RGBA32, (void *)data, pitch);
|
SDL_Surface *loadedSurface = SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_RGBA32, static_cast<void *>(data), pitch);
|
||||||
if (loadedSurface == nullptr) {
|
if (loadedSurface == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -68,7 +63,7 @@ static SDL_Texture *createTextureFromPixels(SDL_Renderer *renderer, unsigned cha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga una imagen desde un fichero
|
// Carga una imagen desde un fichero
|
||||||
bool Texture::loadFromFile(std::string path, SDL_Renderer *renderer, bool verbose) {
|
bool Texture::loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose) {
|
||||||
const std::string filename = path.substr(path.find_last_of("\\/") + 1);
|
const std::string filename = path.substr(path.find_last_of("\\/") + 1);
|
||||||
int req_format = STBI_rgb_alpha;
|
int req_format = STBI_rgb_alpha;
|
||||||
int w, h, orig_format;
|
int w, h, orig_format;
|
||||||
@@ -24,7 +24,7 @@ class Texture {
|
|||||||
static void setGlobalScaleMode(SDL_ScaleMode mode);
|
static void setGlobalScaleMode(SDL_ScaleMode mode);
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Texture(SDL_Renderer *renderer, std::string path = "", bool verbose = false);
|
explicit Texture(SDL_Renderer *renderer, const std::string &path = "", bool verbose = false);
|
||||||
|
|
||||||
// Constructor desde bytes (PNG en memoria)
|
// Constructor desde bytes (PNG en memoria)
|
||||||
Texture(SDL_Renderer *renderer, const std::vector<uint8_t> &bytes, bool verbose = false);
|
Texture(SDL_Renderer *renderer, const std::vector<uint8_t> &bytes, bool verbose = false);
|
||||||
@@ -33,7 +33,7 @@ class Texture {
|
|||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
// Carga una imagen desde un fichero
|
// Carga una imagen desde un fichero
|
||||||
bool loadFromFile(std::string path, SDL_Renderer *renderer, bool verbose = false);
|
bool loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose = false);
|
||||||
|
|
||||||
// Carga una imagen desde bytes en memoria
|
// Carga una imagen desde bytes en memoria
|
||||||
bool loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose = false);
|
bool loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose = false);
|
||||||
@@ -1,25 +1,22 @@
|
|||||||
#include "writer.h"
|
#include "core/rendering/writer.h"
|
||||||
|
|
||||||
#include "text.h" // for Text
|
#include "core/rendering/text.h" // for Text
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Writer::Writer(Text *text) {
|
Writer::Writer(Text *text)
|
||||||
// Copia los punteros
|
: text(text),
|
||||||
this->text = text;
|
posX(0),
|
||||||
|
posY(0),
|
||||||
// Inicializa variables
|
kerning(0),
|
||||||
posX = 0;
|
caption(""),
|
||||||
posY = 0;
|
speed(0),
|
||||||
kerning = 0;
|
writingCounter(0),
|
||||||
caption = "";
|
index(0),
|
||||||
speed = 0;
|
lenght(0),
|
||||||
writingCounter = 0;
|
completed(false),
|
||||||
index = 0;
|
enabled(false),
|
||||||
lenght = 0;
|
enabledCounter(0),
|
||||||
completed = false;
|
finished(false) {
|
||||||
enabled = false;
|
|
||||||
enabledCounter = 0;
|
|
||||||
finished = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza el objeto
|
// Actualiza el objeto
|
||||||
@@ -73,7 +70,7 @@ void Writer::setKerning(int value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
void Writer::setCaption(std::string text) {
|
void Writer::setCaption(const std::string &text) {
|
||||||
caption = text;
|
caption = text;
|
||||||
lenght = text.length();
|
lenght = text.length();
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ class Writer {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Writer(Text *text);
|
explicit Writer(Text *text);
|
||||||
|
|
||||||
// Actualiza el objeto
|
// Actualiza el objeto
|
||||||
void update();
|
void update();
|
||||||
@@ -43,7 +43,7 @@ class Writer {
|
|||||||
void setKerning(int value);
|
void setKerning(int value);
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
void setCaption(std::string text);
|
void setCaption(const std::string &text);
|
||||||
|
|
||||||
// Establece el valor de la variable
|
// Establece el valor de la variable
|
||||||
void setSpeed(int value);
|
void setSpeed(int value);
|
||||||
@@ -1,21 +1,38 @@
|
|||||||
#include "asset.h"
|
#include "core/resources/asset.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stddef.h> // for size_t
|
#include <stddef.h> // for size_t
|
||||||
|
|
||||||
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
||||||
|
|
||||||
#include "resource_helper.h"
|
#include "core/resources/resource_helper.h"
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
Asset *Asset::instance = nullptr;
|
||||||
|
|
||||||
|
// Singleton API
|
||||||
|
void Asset::init(const std::string &executablePath) {
|
||||||
|
Asset::instance = new Asset(executablePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Asset::destroy() {
|
||||||
|
delete Asset::instance;
|
||||||
|
Asset::instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Asset::get() -> Asset * {
|
||||||
|
return Asset::instance;
|
||||||
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Asset::Asset(std::string executablePath) {
|
Asset::Asset(const std::string &executablePath)
|
||||||
this->executablePath = executablePath.substr(0, executablePath.find_last_of("\\/"));
|
: longestName(0),
|
||||||
longestName = 0;
|
executablePath(executablePath.substr(0, executablePath.find_last_of("\\/"))),
|
||||||
verbose = true;
|
verbose(true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Añade un elemento a la lista
|
// Añade un elemento a la lista
|
||||||
void Asset::add(std::string file, enum assetType type, bool required, bool absolute) {
|
void Asset::add(const std::string &file, enum assetType type, bool required, bool absolute) {
|
||||||
item_t temp;
|
item_t temp;
|
||||||
temp.file = absolute ? file : executablePath + file;
|
temp.file = absolute ? file : executablePath + file;
|
||||||
temp.type = type;
|
temp.type = type;
|
||||||
@@ -27,8 +44,8 @@ void Asset::add(std::string file, enum assetType type, bool required, bool absol
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve el fichero de un elemento de la lista a partir de una cadena
|
// Devuelve el fichero de un elemento de la lista a partir de una cadena
|
||||||
std::string Asset::get(std::string text) {
|
std::string Asset::get(const std::string &text) {
|
||||||
for (auto f : fileList) {
|
for (const auto &f : fileList) {
|
||||||
const size_t lastIndex = f.file.find_last_of("/") + 1;
|
const size_t lastIndex = f.file.find_last_of("/") + 1;
|
||||||
const std::string file = f.file.substr(lastIndex, std::string::npos);
|
const std::string file = f.file.substr(lastIndex, std::string::npos);
|
||||||
|
|
||||||
@@ -59,7 +76,7 @@ bool Asset::check() {
|
|||||||
// Comprueba si hay ficheros de ese tipo
|
// Comprueba si hay ficheros de ese tipo
|
||||||
bool any = false;
|
bool any = false;
|
||||||
|
|
||||||
for (auto f : fileList) {
|
for (const auto &f : fileList) {
|
||||||
if ((f.required) && (f.type == type)) {
|
if ((f.required) && (f.type == type)) {
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
@@ -71,7 +88,7 @@ bool Asset::check() {
|
|||||||
std::cout << "\n>> " << getTypeName(type).c_str() << " FILES" << std::endl;
|
std::cout << "\n>> " << getTypeName(type).c_str() << " FILES" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto f : fileList) {
|
for (const auto &f : fileList) {
|
||||||
if ((f.required) && (f.type == type)) {
|
if ((f.required) && (f.type == type)) {
|
||||||
success &= checkFile(f.file);
|
success &= checkFile(f.file);
|
||||||
}
|
}
|
||||||
@@ -94,7 +111,7 @@ bool Asset::check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba que existe un fichero
|
// Comprueba que existe un fichero
|
||||||
bool Asset::checkFile(std::string path) {
|
bool Asset::checkFile(const std::string &path) {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
std::string result = "ERROR";
|
std::string result = "ERROR";
|
||||||
|
|
||||||
@@ -34,20 +34,28 @@ class Asset {
|
|||||||
bool verbose; // Indica si ha de mostrar información por pantalla
|
bool verbose; // Indica si ha de mostrar información por pantalla
|
||||||
|
|
||||||
// Comprueba que existe un fichero
|
// Comprueba que existe un fichero
|
||||||
bool checkFile(std::string executablePath);
|
bool checkFile(const std::string &executablePath);
|
||||||
|
|
||||||
// Devuelve el nombre del tipo de recurso
|
// Devuelve el nombre del tipo de recurso
|
||||||
std::string getTypeName(int type);
|
std::string getTypeName(int type);
|
||||||
|
|
||||||
|
// Constructor privado (usar Asset::init)
|
||||||
|
explicit Asset(const std::string &path);
|
||||||
|
|
||||||
|
// Instancia única
|
||||||
|
static Asset *instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Singleton API
|
||||||
Asset(std::string path);
|
static void init(const std::string &executablePath); // Crea la instancia
|
||||||
|
static void destroy(); // Libera la instancia
|
||||||
|
static auto get() -> Asset *; // Obtiene el puntero a la instancia
|
||||||
|
|
||||||
// Añade un elemento a la lista
|
// Añade un elemento a la lista
|
||||||
void add(std::string file, enum assetType type, bool required = true, bool absolute = false);
|
void add(const std::string &file, enum assetType type, bool required = true, bool absolute = false);
|
||||||
|
|
||||||
// Devuelve un elemento de la lista a partir de una cadena
|
// Devuelve un elemento de la lista a partir de una cadena
|
||||||
std::string get(std::string text);
|
std::string get(const std::string &text);
|
||||||
|
|
||||||
// Devuelve toda la lista de items registrados
|
// Devuelve toda la lista de items registrados
|
||||||
const std::vector<item_t> &getAll() const { return fileList; }
|
const std::vector<item_t> &getAll() const { return fileList; }
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
#include "resource.h"
|
#include "core/resources/resource.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "asset.h"
|
#include "core/audio/jail_audio.hpp"
|
||||||
#include "jail_audio.hpp"
|
#include "core/rendering/text.h"
|
||||||
#include "menu.h"
|
#include "core/rendering/texture.h"
|
||||||
#include "resource_helper.h"
|
#include "core/resources/asset.h"
|
||||||
#include "text.h"
|
#include "core/resources/resource_helper.h"
|
||||||
#include "texture.h"
|
#include "game/ui/menu.h"
|
||||||
|
|
||||||
|
// Nota: Asset::get() e Input::get() se consultan en preloadAll y al construir
|
||||||
|
// los menús; no se guardan punteros en el objeto Resource.
|
||||||
|
|
||||||
Resource *Resource::instance_ = nullptr;
|
Resource *Resource::instance_ = nullptr;
|
||||||
|
|
||||||
@@ -25,9 +28,9 @@ static std::string stem(const std::string &path) {
|
|||||||
return b.substr(0, dot);
|
return b.substr(0, dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resource::init(SDL_Renderer *renderer, Asset *asset, Input *input) {
|
void Resource::init(SDL_Renderer *renderer) {
|
||||||
if (instance_ == nullptr) {
|
if (instance_ == nullptr) {
|
||||||
instance_ = new Resource(renderer, asset, input);
|
instance_ = new Resource(renderer);
|
||||||
instance_->preloadAll();
|
instance_->preloadAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,10 +44,8 @@ Resource *Resource::get() {
|
|||||||
return instance_;
|
return instance_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource::Resource(SDL_Renderer *renderer, Asset *asset, Input *input)
|
Resource::Resource(SDL_Renderer *renderer)
|
||||||
: renderer_(renderer),
|
: renderer_(renderer) {}
|
||||||
asset_(asset),
|
|
||||||
input_(input) {}
|
|
||||||
|
|
||||||
Resource::~Resource() {
|
Resource::~Resource() {
|
||||||
for (auto &[name, m] : menus_) delete m;
|
for (auto &[name, m] : menus_) delete m;
|
||||||
@@ -64,7 +65,7 @@ Resource::~Resource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Resource::preloadAll() {
|
void Resource::preloadAll() {
|
||||||
const auto &items = asset_->getAll();
|
const auto &items = Asset::get()->getAll();
|
||||||
|
|
||||||
// Pass 1: texturas, sonidos, músicas, animaciones (raw lines), demo, lenguajes
|
// Pass 1: texturas, sonidos, músicas, animaciones (raw lines), demo, lenguajes
|
||||||
for (const auto &it : items) {
|
for (const auto &it : items) {
|
||||||
@@ -155,7 +156,7 @@ void Resource::preloadAll() {
|
|||||||
if (bname.size() < 4 || bname.substr(bname.size() - 4) != ".men") continue;
|
if (bname.size() < 4 || bname.substr(bname.size() - 4) != ".men") continue;
|
||||||
auto bytes = ResourceHelper::loadFile(it.file);
|
auto bytes = ResourceHelper::loadFile(it.file);
|
||||||
if (bytes.empty()) continue;
|
if (bytes.empty()) continue;
|
||||||
Menu *m = new Menu(renderer_, asset_, input_, "");
|
Menu *m = new Menu(renderer_, "");
|
||||||
m->loadFromBytes(bytes, bname);
|
m->loadFromBytes(bytes, bname);
|
||||||
const std::string s = stem(it.file);
|
const std::string s = stem(it.file);
|
||||||
menus_[s] = m;
|
menus_[s] = m;
|
||||||
@@ -7,8 +7,6 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Asset;
|
|
||||||
class Input;
|
|
||||||
class Menu;
|
class Menu;
|
||||||
class Text;
|
class Text;
|
||||||
class Texture;
|
class Texture;
|
||||||
@@ -19,7 +17,7 @@ struct JA_Sound_t;
|
|||||||
// Singleton inicializado desde Director; las escenas consultan handles via get*().
|
// Singleton inicializado desde Director; las escenas consultan handles via get*().
|
||||||
class Resource {
|
class Resource {
|
||||||
public:
|
public:
|
||||||
static void init(SDL_Renderer *renderer, Asset *asset, Input *input);
|
static void init(SDL_Renderer *renderer);
|
||||||
static void destroy();
|
static void destroy();
|
||||||
static Resource *get();
|
static Resource *get();
|
||||||
|
|
||||||
@@ -32,14 +30,12 @@ class Resource {
|
|||||||
const std::vector<uint8_t> &getDemoBytes() const { return demoBytes_; }
|
const std::vector<uint8_t> &getDemoBytes() const { return demoBytes_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Resource(SDL_Renderer *renderer, Asset *asset, Input *input);
|
explicit Resource(SDL_Renderer *renderer);
|
||||||
~Resource();
|
~Resource();
|
||||||
|
|
||||||
void preloadAll();
|
void preloadAll();
|
||||||
|
|
||||||
SDL_Renderer *renderer_;
|
SDL_Renderer *renderer_;
|
||||||
Asset *asset_;
|
|
||||||
Input *input_;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, Texture *> textures_;
|
std::unordered_map<std::string, Texture *> textures_;
|
||||||
std::unordered_map<std::string, JA_Sound_t *> sounds_;
|
std::unordered_map<std::string, JA_Sound_t *> sounds_;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "resource_helper.h"
|
#include "core/resources/resource_helper.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "resource_loader.h"
|
#include "core/resources/resource_loader.h"
|
||||||
|
|
||||||
namespace ResourceHelper {
|
namespace ResourceHelper {
|
||||||
static bool resource_system_initialized = false;
|
static bool resource_system_initialized = false;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "resource_loader.h"
|
#include "core/resources/resource_loader.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "resource_pack.h"
|
#include "core/resources/resource_pack.h"
|
||||||
|
|
||||||
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||||
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#include "resource_pack.h"
|
#include "core/resources/resource_pack.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCRS_RESOURCES__2026";
|
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCRS_RESOURCES__2026";
|
||||||
@@ -17,11 +18,8 @@ ResourcePack::~ResourcePack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) {
|
uint32_t ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) {
|
||||||
uint32_t checksum = 0x12345678;
|
return std::accumulate(data.begin(), data.end(), uint32_t(0x12345678),
|
||||||
for (unsigned char i : data) {
|
[](uint32_t acc, uint8_t b) { return ((acc << 5) + acc) + b; });
|
||||||
checksum = ((checksum << 5) + checksum) + i;
|
|
||||||
}
|
|
||||||
return checksum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
|
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||||
652
source/core/system/director.cpp
Normal file
652
source/core/system/director.cpp
Normal file
@@ -0,0 +1,652 @@
|
|||||||
|
#include "core/system/director.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <errno.h> // for errno, EEXIST, EACCES, ENAMETOO...
|
||||||
|
#include <stdio.h> // for printf, perror
|
||||||
|
#include <string.h> // for strcmp
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
|
#include <sys/stat.h> // for mkdir, stat, S_IRWXU
|
||||||
|
#include <unistd.h> // for getuid
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib> // for exit, EXIT_FAILURE, srand
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream> // for basic_ostream, operator<<, basi...
|
||||||
|
#include <iostream> // for cout
|
||||||
|
#include <memory>
|
||||||
|
#include <string> // for basic_string, operator+, char_t...
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "core/audio/jail_audio.hpp" // for JA_Init
|
||||||
|
#include "core/input/input.h" // for Input, inputs_e, INPUT_USE_GAME...
|
||||||
|
#include "core/input/mouse.hpp" // for Mouse::handleEvent, Mouse::upda...
|
||||||
|
#include "core/locale/lang.h" // for Lang, MAX_LANGUAGES, ba_BA, en_UK
|
||||||
|
#include "core/rendering/screen.h" // for Screen
|
||||||
|
#include "core/rendering/texture.h" // for Texture
|
||||||
|
#include "core/resources/asset.h" // for Asset, assetType
|
||||||
|
#include "core/resources/resource.h"
|
||||||
|
#include "core/resources/resource_helper.h"
|
||||||
|
#include "game/defaults.hpp" // for SECTION_PROG_LOGO, GAMECANVAS_H...
|
||||||
|
#include "game/game.h" // for Game
|
||||||
|
#include "game/options.hpp" // for Options::init, loadFromFile...
|
||||||
|
#include "game/scenes/intro.h" // for Intro
|
||||||
|
#include "game/scenes/logo.h" // for Logo
|
||||||
|
#include "game/scenes/title.h" // for Title
|
||||||
|
#include "utils/utils.h" // for input_t, boolToString
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
|
||||||
|
#include <pwd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Director::Director(int argc, const char *argv[]) {
|
||||||
|
std::cout << "Game start" << std::endl;
|
||||||
|
// Inicializa variables
|
||||||
|
section = new section_t();
|
||||||
|
section->name = SECTION_PROG_LOGO;
|
||||||
|
|
||||||
|
// Inicializa las opciones del programa (defaults + dispositivos d'entrada)
|
||||||
|
Options::init();
|
||||||
|
|
||||||
|
// Comprueba los parametros del programa (pot activar console)
|
||||||
|
checkProgramArguments(argc, argv);
|
||||||
|
|
||||||
|
// Crea la carpeta del sistema donde guardar datos
|
||||||
|
createSystemFolder("jailgames");
|
||||||
|
#ifndef DEBUG
|
||||||
|
createSystemFolder("jailgames/coffee_crisis");
|
||||||
|
#else
|
||||||
|
createSystemFolder("jailgames/coffee_crisis_debug");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Estableix el fitxer de configuració i carrega les opcions (o crea el
|
||||||
|
// YAML amb defaults si no existeix).
|
||||||
|
Options::setConfigFile(systemFolder + "/config.yaml");
|
||||||
|
Options::loadFromFile();
|
||||||
|
|
||||||
|
// Presets de shaders (creats amb defaults si no existeixen).
|
||||||
|
Options::setPostFXFile(systemFolder + "/postfx.yaml");
|
||||||
|
Options::loadPostFXFromFile();
|
||||||
|
Options::setCrtPiFile(systemFolder + "/crtpi.yaml");
|
||||||
|
Options::loadCrtPiFromFile();
|
||||||
|
|
||||||
|
// Inicializa el sistema de recursos (pack + fallback).
|
||||||
|
// En wasm siempre se usa filesystem (MEMFS) porque el propio --preload-file
|
||||||
|
// de emscripten ya empaqueta data/ — no hay resources.pack.
|
||||||
|
{
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
const bool enable_fallback = true;
|
||||||
|
#elif defined(RELEASE_BUILD)
|
||||||
|
const bool enable_fallback = false;
|
||||||
|
#else
|
||||||
|
const bool enable_fallback = true;
|
||||||
|
#endif
|
||||||
|
if (!ResourceHelper::initializeResourceSystem("resources.pack", enable_fallback)) {
|
||||||
|
std::cerr << "Fatal: resource system init failed (missing resources.pack?)" << std::endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea el objeto que controla los ficheros de recursos
|
||||||
|
Asset::init(executablePath);
|
||||||
|
Asset::get()->setVerbose(Options::settings.console);
|
||||||
|
|
||||||
|
// Si falta algún fichero no inicia el programa
|
||||||
|
if (!setFileList()) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializa SDL
|
||||||
|
initSDL();
|
||||||
|
|
||||||
|
// Inicializa JailAudio
|
||||||
|
initJailAudio();
|
||||||
|
|
||||||
|
// Establece el modo de escalado de texturas
|
||||||
|
Texture::setGlobalScaleMode(Options::video.scale_mode);
|
||||||
|
|
||||||
|
// Crea los objetos
|
||||||
|
Lang::init();
|
||||||
|
Lang::get()->setLang(Options::settings.language);
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
Input::init("/gamecontrollerdb.txt");
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
const std::string binDir = std::filesystem::path(executablePath).parent_path().string();
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
Input::init(binDir + "/../Resources/gamecontrollerdb.txt");
|
||||||
|
#else
|
||||||
|
Input::init(binDir + "/gamecontrollerdb.txt");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
initInput();
|
||||||
|
|
||||||
|
// Orden importante: Screen + initShaders ANTES de Resource::init.
|
||||||
|
// Si `Resource::init` se ejecuta primero, carga ~100 texturas vía
|
||||||
|
// `SDL_CreateTexture` que dejan el SDL_Renderer con el swapchain en un
|
||||||
|
// estado que hace crashear al driver Vulkan cuando después `initShaders`
|
||||||
|
// intenta reclamar la ventana para el dispositivo SDL3 GPU.
|
||||||
|
//
|
||||||
|
// Por eso el constructor de Screen NO carga notificationText desde
|
||||||
|
// Resource; se enlaza después vía `Screen::get()->initNotifications()`.
|
||||||
|
Screen::init(window, renderer);
|
||||||
|
|
||||||
|
#ifndef NO_SHADERS
|
||||||
|
if (Options::video.gpu.acceleration) {
|
||||||
|
Screen::get()->initShaders();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ahora sí, precarga todos los recursos en memoria (texturas, sonidos,
|
||||||
|
// música, ...). Vivirán durante toda la vida de la app.
|
||||||
|
Resource::init(renderer);
|
||||||
|
|
||||||
|
// Completa el enlazado de Screen con recursos que necesitan Resource
|
||||||
|
// inicializado (actualmente sólo el Text de las notificaciones).
|
||||||
|
Screen::get()->initNotifications();
|
||||||
|
|
||||||
|
activeSection = ActiveSection::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Director::~Director() {
|
||||||
|
Options::saveToFile();
|
||||||
|
|
||||||
|
// Libera las secciones primero: sus destructores tocan audio/render SDL
|
||||||
|
// (p.ej. Intro::~Intro llama a JA_DeleteMusic) y deben ejecutarse antes
|
||||||
|
// de SDL_Quit().
|
||||||
|
logo.reset();
|
||||||
|
intro.reset();
|
||||||
|
title.reset();
|
||||||
|
game.reset();
|
||||||
|
|
||||||
|
// Screen puede tener referencias a Text propiedad de Resource: destruir
|
||||||
|
// Screen antes que Resource.
|
||||||
|
Screen::destroy();
|
||||||
|
|
||||||
|
// Libera todos los recursos precargados antes de cerrar SDL.
|
||||||
|
Resource::destroy();
|
||||||
|
|
||||||
|
Asset::destroy();
|
||||||
|
Input::destroy();
|
||||||
|
Lang::destroy();
|
||||||
|
delete section;
|
||||||
|
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
ResourceHelper::shutdownResourceSystem();
|
||||||
|
|
||||||
|
std::cout << "\nBye!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializa el objeto input
|
||||||
|
void Director::initInput() {
|
||||||
|
// Establece si ha de mostrar mensajes
|
||||||
|
Input::get()->setVerbose(Options::settings.console);
|
||||||
|
|
||||||
|
// Busca si hay un mando conectado
|
||||||
|
Input::get()->discoverGameController();
|
||||||
|
|
||||||
|
// Teclado - Movimiento del jugador
|
||||||
|
Input::get()->bindKey(input_up, SDL_SCANCODE_UP);
|
||||||
|
Input::get()->bindKey(input_down, SDL_SCANCODE_DOWN);
|
||||||
|
Input::get()->bindKey(input_left, SDL_SCANCODE_LEFT);
|
||||||
|
Input::get()->bindKey(input_right, SDL_SCANCODE_RIGHT);
|
||||||
|
Input::get()->bindKey(input_fire_left, SDL_SCANCODE_Q);
|
||||||
|
Input::get()->bindKey(input_fire_center, SDL_SCANCODE_W);
|
||||||
|
Input::get()->bindKey(input_fire_right, SDL_SCANCODE_E);
|
||||||
|
|
||||||
|
// Teclado - Otros
|
||||||
|
Input::get()->bindKey(input_accept, SDL_SCANCODE_RETURN);
|
||||||
|
Input::get()->bindKey(input_cancel, SDL_SCANCODE_ESCAPE);
|
||||||
|
Input::get()->bindKey(input_pause, SDL_SCANCODE_ESCAPE);
|
||||||
|
Input::get()->bindKey(input_exit, SDL_SCANCODE_ESCAPE);
|
||||||
|
Input::get()->bindKey(input_window_dec_size, SDL_SCANCODE_F1);
|
||||||
|
Input::get()->bindKey(input_window_inc_size, SDL_SCANCODE_F2);
|
||||||
|
Input::get()->bindKey(input_window_fullscreen, SDL_SCANCODE_F3);
|
||||||
|
Input::get()->bindKey(input_prev_preset, SDL_SCANCODE_F8);
|
||||||
|
Input::get()->bindKey(input_next_preset, SDL_SCANCODE_F9);
|
||||||
|
Input::get()->bindKey(input_toggle_shader, SDL_SCANCODE_F10);
|
||||||
|
Input::get()->bindKey(input_toggle_shader_type, SDL_SCANCODE_F11);
|
||||||
|
|
||||||
|
// Mando - Movimiento del jugador
|
||||||
|
Input::get()->bindGameControllerButton(input_up, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||||
|
Input::get()->bindGameControllerButton(input_down, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||||
|
Input::get()->bindGameControllerButton(input_left, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||||
|
Input::get()->bindGameControllerButton(input_right, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||||
|
Input::get()->bindGameControllerButton(input_fire_left, SDL_GAMEPAD_BUTTON_WEST);
|
||||||
|
Input::get()->bindGameControllerButton(input_fire_center, SDL_GAMEPAD_BUTTON_NORTH);
|
||||||
|
Input::get()->bindGameControllerButton(input_fire_right, SDL_GAMEPAD_BUTTON_EAST);
|
||||||
|
|
||||||
|
// Mando - Otros
|
||||||
|
// SOUTH queda sin asignar para evitar salidas accidentales: pausa/cancel se hace con START/BACK.
|
||||||
|
Input::get()->bindGameControllerButton(input_accept, SDL_GAMEPAD_BUTTON_EAST);
|
||||||
|
#ifdef GAME_CONSOLE
|
||||||
|
Input::get()->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_BACK);
|
||||||
|
Input::get()->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_START);
|
||||||
|
#else
|
||||||
|
Input::get()->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_START);
|
||||||
|
Input::get()->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_BACK);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializa JailAudio
|
||||||
|
void Director::initJailAudio() {
|
||||||
|
JA_Init(48000, SDL_AUDIO_S16, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arranca SDL y crea la ventana
|
||||||
|
bool Director::initSDL() {
|
||||||
|
// Indicador de éxito
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
// Inicializa SDL
|
||||||
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD)) {
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
// Inicia el generador de numeros aleatorios
|
||||||
|
std::srand(static_cast<unsigned int>(SDL_GetTicks()));
|
||||||
|
|
||||||
|
// Crea la ventana
|
||||||
|
window = SDL_CreateWindow(
|
||||||
|
Options::window.caption.c_str(),
|
||||||
|
GAMECANVAS_WIDTH * Options::window.zoom,
|
||||||
|
GAMECANVAS_HEIGHT * Options::window.zoom,
|
||||||
|
0);
|
||||||
|
if (window == nullptr) {
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||||
|
|
||||||
|
// Crea un renderizador para la ventana
|
||||||
|
renderer = SDL_CreateRenderer(window, NULL);
|
||||||
|
|
||||||
|
if (renderer == nullptr) {
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||||
|
}
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
// Modo de blending por defecto (consistente con CCAE):
|
||||||
|
// permite alpha blending para fades y notificaciones.
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
|
// Activa vsync si es necesario
|
||||||
|
if (Options::video.vsync) {
|
||||||
|
SDL_SetRenderVSync(renderer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializa el color de renderizado
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
|
||||||
|
|
||||||
|
// Establece el tamaño del buffer de renderizado
|
||||||
|
SDL_SetRenderLogicalPresentation(renderer, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
||||||
|
|
||||||
|
// Establece el modo de mezcla
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Options::settings.console) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea el indice de ficheros
|
||||||
|
bool Director::setFileList() {
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
const std::string prefix = "/../Resources";
|
||||||
|
#else
|
||||||
|
const std::string prefix = "";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ficheros de configuración
|
||||||
|
Asset::get()->add(systemFolder + "/score.bin", t_data, false, true);
|
||||||
|
Asset::get()->add(prefix + "/data/demo/demo.bin", t_data);
|
||||||
|
|
||||||
|
// Musicas
|
||||||
|
Asset::get()->add(prefix + "/data/music/intro.ogg", t_music);
|
||||||
|
Asset::get()->add(prefix + "/data/music/playing.ogg", t_music);
|
||||||
|
Asset::get()->add(prefix + "/data/music/title.ogg", t_music);
|
||||||
|
|
||||||
|
// Sonidos
|
||||||
|
Asset::get()->add(prefix + "/data/sound/balloon.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/bubble1.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/bubble2.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/bubble3.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/bubble4.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/bullet.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/coffeeout.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/hiscore.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/itemdrop.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/itempickup.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/menu_move.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/menu_select.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/player_collision.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/stage_change.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/title.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/clock.wav", t_sound);
|
||||||
|
Asset::get()->add(prefix + "/data/sound/powerball.wav", t_sound);
|
||||||
|
|
||||||
|
// Texturas
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon1.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon1.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon2.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon2.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon3.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon3.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon4.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/balloon4.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/bullet.png", t_bitmap);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_buildings.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_clouds.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_grass.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_power_meter.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_sky_colors.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/game_text.png", t_bitmap);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/intro.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/logo.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/menu_game_over.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/menu_game_over_end.png", t_bitmap);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points1_disk.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points1_disk.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points2_gavina.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points2_gavina.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points3_pacmar.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_points3_pacmar.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_clock.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_clock.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_coffee.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_coffee.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_coffee_machine.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/item_coffee_machine.ani", t_data);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_bg_tile.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_coffee.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_crisis.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_dust.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_dust.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/title_gradient.png", t_bitmap);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_head.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_body.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_legs.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_death.ani", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_fire.ani", t_data);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_bal1_head.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_bal1_body.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_bal1_legs.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_bal1_death.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_bal1_fire.png", t_bitmap);
|
||||||
|
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_arounder_head.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_arounder_body.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_arounder_legs.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_arounder_death.png", t_bitmap);
|
||||||
|
Asset::get()->add(prefix + "/data/gfx/player_arounder_fire.png", t_bitmap);
|
||||||
|
|
||||||
|
// Fuentes
|
||||||
|
Asset::get()->add(prefix + "/data/font/8bithud.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/8bithud.txt", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia_big2.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia.txt", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia2.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia2.txt", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/nokia_big2.txt", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/smb2_big.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/smb2_big.txt", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/smb2.png", t_font);
|
||||||
|
Asset::get()->add(prefix + "/data/font/smb2.txt", t_font);
|
||||||
|
|
||||||
|
// Textos
|
||||||
|
Asset::get()->add(prefix + "/data/lang/es_ES.txt", t_lang);
|
||||||
|
Asset::get()->add(prefix + "/data/lang/en_UK.txt", t_lang);
|
||||||
|
Asset::get()->add(prefix + "/data/lang/ba_BA.txt", t_lang);
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
Asset::get()->add(prefix + "/data/menu/title.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/title_gc.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/options.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/options_gc.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/pause.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/gameover.men", t_data);
|
||||||
|
Asset::get()->add(prefix + "/data/menu/player_select.men", t_data);
|
||||||
|
|
||||||
|
return Asset::get()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comprueba los parametros del programa
|
||||||
|
void Director::checkProgramArguments(int argc, const char *argv[]) {
|
||||||
|
// Establece la ruta del programa
|
||||||
|
executablePath = argv[0];
|
||||||
|
|
||||||
|
// Comprueba el resto de parametros
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
if (strcmp(argv[i], "--console") == 0) {
|
||||||
|
Options::settings.console = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea la carpeta del sistema donde guardar datos
|
||||||
|
void Director::createSystemFolder(const std::string &folder) {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En Emscripten usamos una carpeta en MEMFS (no persistente)
|
||||||
|
systemFolder = "/config/" + folder;
|
||||||
|
#elif _WIN32
|
||||||
|
systemFolder = std::string(getenv("APPDATA")) + "/" + folder;
|
||||||
|
#elif __APPLE__
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
const char *homedir = pw->pw_dir;
|
||||||
|
systemFolder = std::string(homedir) + "/Library/Application Support" + "/" + folder;
|
||||||
|
#elif __linux__
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
const char *homedir = pw->pw_dir;
|
||||||
|
systemFolder = std::string(homedir) + "/.config/" + folder;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Intenta crear ".config", per si no existeix
|
||||||
|
std::string config_base_folder = std::string(homedir) + "/.config";
|
||||||
|
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
|
||||||
|
if (ret == -1 && errno != EEXIST) {
|
||||||
|
printf("ERROR CREATING CONFIG BASE FOLDER.");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En Emscripten no necesitamos crear carpetas (MEMFS las crea automáticamente)
|
||||||
|
(void)folder;
|
||||||
|
#else
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat(systemFolder.c_str(), &st) == -1) {
|
||||||
|
errno = 0;
|
||||||
|
#ifdef _WIN32
|
||||||
|
int ret = mkdir(systemFolder.c_str());
|
||||||
|
#else
|
||||||
|
int ret = mkdir(systemFolder.c_str(), S_IRWXU);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
switch (errno) {
|
||||||
|
case EACCES:
|
||||||
|
printf("the parent directory does not allow write");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
case EEXIST:
|
||||||
|
printf("pathname already exists");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
printf("pathname is too long");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
default:
|
||||||
|
perror("mkdir");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestiona las transiciones entre secciones
|
||||||
|
void Director::handleSectionTransition() {
|
||||||
|
// Determina qué sección debería estar activa
|
||||||
|
ActiveSection targetSection = ActiveSection::None;
|
||||||
|
switch (section->name) {
|
||||||
|
case SECTION_PROG_LOGO:
|
||||||
|
targetSection = ActiveSection::Logo;
|
||||||
|
break;
|
||||||
|
case SECTION_PROG_INTRO:
|
||||||
|
targetSection = ActiveSection::Intro;
|
||||||
|
break;
|
||||||
|
case SECTION_PROG_TITLE:
|
||||||
|
targetSection = ActiveSection::Title;
|
||||||
|
break;
|
||||||
|
case SECTION_PROG_GAME:
|
||||||
|
targetSection = ActiveSection::Game;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no ha cambiado, no hay nada que hacer
|
||||||
|
if (targetSection == activeSection) return;
|
||||||
|
|
||||||
|
// Destruye la sección anterior
|
||||||
|
logo.reset();
|
||||||
|
intro.reset();
|
||||||
|
title.reset();
|
||||||
|
game.reset();
|
||||||
|
|
||||||
|
// Crea la nueva sección
|
||||||
|
activeSection = targetSection;
|
||||||
|
switch (activeSection) {
|
||||||
|
case ActiveSection::Logo:
|
||||||
|
logo = std::make_unique<Logo>(renderer, section);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Intro:
|
||||||
|
intro = std::make_unique<Intro>(renderer, section);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Title:
|
||||||
|
title = std::make_unique<Title>(renderer, section);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Game: {
|
||||||
|
const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2;
|
||||||
|
game = std::make_unique<Game>(numPlayers, 0, renderer, false, section);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActiveSection::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ejecuta un frame del juego
|
||||||
|
SDL_AppResult Director::iterate() {
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En WASM no se puede salir: reinicia al logo
|
||||||
|
if (section->name == SECTION_PROG_QUIT) {
|
||||||
|
section->name = SECTION_PROG_LOGO;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (section->name == SECTION_PROG_QUIT) {
|
||||||
|
return SDL_APP_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Actualiza la visibilidad del cursor del ratón
|
||||||
|
Mouse::updateCursorVisibility(Options::video.fullscreen);
|
||||||
|
|
||||||
|
// Gestiona las transiciones entre secciones
|
||||||
|
handleSectionTransition();
|
||||||
|
|
||||||
|
// Ejecuta un frame de la sección activa
|
||||||
|
switch (activeSection) {
|
||||||
|
case ActiveSection::Logo:
|
||||||
|
logo->iterate();
|
||||||
|
break;
|
||||||
|
case ActiveSection::Intro:
|
||||||
|
intro->iterate();
|
||||||
|
break;
|
||||||
|
case ActiveSection::Title:
|
||||||
|
title->iterate();
|
||||||
|
break;
|
||||||
|
case ActiveSection::Game:
|
||||||
|
game->iterate();
|
||||||
|
break;
|
||||||
|
case ActiveSection::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procesa un evento
|
||||||
|
SDL_AppResult Director::handleEvent(SDL_Event *event) {
|
||||||
|
#ifndef __EMSCRIPTEN__
|
||||||
|
// Evento de salida de la aplicación
|
||||||
|
if (event->type == SDL_EVENT_QUIT) {
|
||||||
|
section->name = SECTION_PROG_QUIT;
|
||||||
|
return SDL_APP_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Hot-plug de mandos
|
||||||
|
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
|
||||||
|
std::string name;
|
||||||
|
if (Input::get()->handleGamepadAdded(event->gdevice.which, name)) {
|
||||||
|
Screen::get()->notify(name + " " + Lang::get()->getText(94),
|
||||||
|
color_t{0x40, 0xFF, 0x40},
|
||||||
|
color_t{0, 0, 0},
|
||||||
|
2500);
|
||||||
|
}
|
||||||
|
} else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
std::string name;
|
||||||
|
if (Input::get()->handleGamepadRemoved(event->gdevice.which, name)) {
|
||||||
|
Screen::get()->notify(name + " " + Lang::get()->getText(95),
|
||||||
|
color_t{0xFF, 0x50, 0x50},
|
||||||
|
color_t{0, 0, 0},
|
||||||
|
2500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestiona la visibilidad del cursor según el movimiento del ratón
|
||||||
|
Mouse::handleEvent(*event, Options::video.fullscreen);
|
||||||
|
|
||||||
|
// Reenvía el evento a la sección activa
|
||||||
|
switch (activeSection) {
|
||||||
|
case ActiveSection::Logo:
|
||||||
|
logo->handleEvent(event);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Intro:
|
||||||
|
intro->handleEvent(event);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Title:
|
||||||
|
title->handleEvent(event);
|
||||||
|
break;
|
||||||
|
case ActiveSection::Game:
|
||||||
|
game->handleEvent(event);
|
||||||
|
break;
|
||||||
|
case ActiveSection::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
@@ -4,20 +4,12 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string> // for string, basic_string
|
#include <string> // for string, basic_string
|
||||||
class Asset;
|
|
||||||
class Game;
|
class Game;
|
||||||
class Input;
|
|
||||||
class Intro;
|
class Intro;
|
||||||
class Lang;
|
|
||||||
class Logo;
|
class Logo;
|
||||||
class Screen;
|
|
||||||
class Title;
|
class Title;
|
||||||
struct options_t;
|
|
||||||
struct section_t;
|
struct section_t;
|
||||||
|
|
||||||
// Textos
|
|
||||||
constexpr const char *WINDOW_CAPTION = "© 2020 Coffee Crisis — JailDesigner";
|
|
||||||
|
|
||||||
// Secciones activas del Director
|
// Secciones activas del Director
|
||||||
enum class ActiveSection { None,
|
enum class ActiveSection { None,
|
||||||
Logo,
|
Logo,
|
||||||
@@ -30,10 +22,6 @@ class Director {
|
|||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Window *window; // La ventana donde dibujamos
|
SDL_Window *window; // La ventana donde dibujamos
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
Input *input; // Objeto Input para gestionar las entradas
|
|
||||||
Lang *lang; // Objeto para gestionar los textos en diferentes idiomas
|
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
section_t *section; // Sección y subsección actual del programa;
|
section_t *section; // Sección y subsección actual del programa;
|
||||||
|
|
||||||
// Secciones del juego
|
// Secciones del juego
|
||||||
@@ -44,7 +32,6 @@ class Director {
|
|||||||
std::unique_ptr<Game> game;
|
std::unique_ptr<Game> game;
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
struct options_t *options; // Variable con todas las opciones del programa
|
|
||||||
std::string executablePath; // Path del ejecutable
|
std::string executablePath; // Path del ejecutable
|
||||||
std::string systemFolder; // Carpeta del sistema donde guardar datos
|
std::string systemFolder; // Carpeta del sistema donde guardar datos
|
||||||
|
|
||||||
@@ -57,21 +44,9 @@ class Director {
|
|||||||
// Inicializa el objeto input
|
// Inicializa el objeto input
|
||||||
void initInput();
|
void initInput();
|
||||||
|
|
||||||
// Inicializa las opciones del programa
|
|
||||||
void initOptions();
|
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
|
||||||
bool setOptions(options_t *options, std::string var, std::string value);
|
|
||||||
|
|
||||||
// Crea el indice de ficheros
|
// Crea el indice de ficheros
|
||||||
bool setFileList();
|
bool setFileList();
|
||||||
|
|
||||||
// Carga el fichero de configuración
|
|
||||||
bool loadConfigFile();
|
|
||||||
|
|
||||||
// Guarda el fichero de configuración
|
|
||||||
bool saveConfigFile();
|
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
// Comprueba los parametros del programa
|
||||||
void checkProgramArguments(int argc, const char *argv[]);
|
void checkProgramArguments(int argc, const char *argv[]);
|
||||||
|
|
||||||
@@ -1,864 +0,0 @@
|
|||||||
#include "director.h"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <errno.h> // for errno, EEXIST, EACCES, ENAMETOO...
|
|
||||||
#include <stdio.h> // for printf, perror
|
|
||||||
#include <string.h> // for strcmp
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
#include <sys/stat.h> // for mkdir, stat, S_IRWXU
|
|
||||||
#include <unistd.h> // for getuid
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstdlib> // for exit, EXIT_FAILURE, srand
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream> // for basic_ostream, operator<<, basi...
|
|
||||||
#include <iostream> // for cout
|
|
||||||
#include <memory>
|
|
||||||
#include <string> // for basic_string, operator+, char_t...
|
|
||||||
#include <vector> // for vector
|
|
||||||
|
|
||||||
#include "asset.h" // for Asset, assetType
|
|
||||||
#include "const.h" // for SECTION_PROG_LOGO, GAMECANVAS_H...
|
|
||||||
#include "game.h" // for Game
|
|
||||||
#include "input.h" // for Input, inputs_e, INPUT_USE_GAME...
|
|
||||||
#include "intro.h" // for Intro
|
|
||||||
#include "jail_audio.hpp" // for JA_Init
|
|
||||||
#include "lang.h" // for Lang, MAX_LANGUAGES, ba_BA, en_UK
|
|
||||||
#include "logo.h" // for Logo
|
|
||||||
#include "mouse.hpp" // for Mouse::handleEvent, Mouse::upda...
|
|
||||||
#include "resource.h"
|
|
||||||
#include "resource_helper.h"
|
|
||||||
#include "screen.h" // for FILTER_NEAREST, Screen, FILTER_...
|
|
||||||
#include "texture.h" // for Texture
|
|
||||||
#include "title.h" // for Title
|
|
||||||
#include "utils.h" // for options_t, input_t, boolToString
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
|
|
||||||
#include <pwd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
Director::Director(int argc, const char *argv[]) {
|
|
||||||
std::cout << "Game start" << std::endl;
|
|
||||||
// Inicializa variables
|
|
||||||
section = new section_t();
|
|
||||||
section->name = SECTION_PROG_LOGO;
|
|
||||||
|
|
||||||
// Inicializa las opciones del programa
|
|
||||||
initOptions();
|
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
|
||||||
checkProgramArguments(argc, argv);
|
|
||||||
|
|
||||||
// Crea la carpeta del sistema donde guardar datos
|
|
||||||
createSystemFolder("jailgames");
|
|
||||||
#ifndef DEBUG
|
|
||||||
createSystemFolder("jailgames/coffee_crisis");
|
|
||||||
#else
|
|
||||||
createSystemFolder("jailgames/coffee_crisis_debug");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Inicializa el sistema de recursos (pack + fallback).
|
|
||||||
// En wasm siempre se usa filesystem (MEMFS) porque el propio --preload-file
|
|
||||||
// de emscripten ya empaqueta data/ — no hay resources.pack.
|
|
||||||
{
|
|
||||||
#if defined(__EMSCRIPTEN__)
|
|
||||||
const bool enable_fallback = true;
|
|
||||||
#elif defined(RELEASE_BUILD)
|
|
||||||
const bool enable_fallback = false;
|
|
||||||
#else
|
|
||||||
const bool enable_fallback = true;
|
|
||||||
#endif
|
|
||||||
if (!ResourceHelper::initializeResourceSystem("resources.pack", enable_fallback)) {
|
|
||||||
std::cerr << "Fatal: resource system init failed (missing resources.pack?)" << std::endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea el objeto que controla los ficheros de recursos
|
|
||||||
asset = new Asset(executablePath);
|
|
||||||
asset->setVerbose(options->console);
|
|
||||||
|
|
||||||
// Si falta algún fichero no inicia el programa
|
|
||||||
if (!setFileList()) {
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga el fichero de configuración
|
|
||||||
loadConfigFile();
|
|
||||||
|
|
||||||
// Inicializa SDL
|
|
||||||
initSDL();
|
|
||||||
|
|
||||||
// Inicializa JailAudio
|
|
||||||
initJailAudio();
|
|
||||||
|
|
||||||
// Establece el modo de escalado de texturas
|
|
||||||
Texture::setGlobalScaleMode(options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
|
|
||||||
|
|
||||||
// Crea los objetos
|
|
||||||
lang = new Lang(asset);
|
|
||||||
lang->setLang(options->language);
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
input = new Input("/gamecontrollerdb.txt");
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
const std::string binDir = std::filesystem::path(executablePath).parent_path().string();
|
|
||||||
#ifdef MACOS_BUNDLE
|
|
||||||
input = new Input(binDir + "/../Resources/gamecontrollerdb.txt");
|
|
||||||
#else
|
|
||||||
input = new Input(binDir + "/gamecontrollerdb.txt");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
initInput();
|
|
||||||
|
|
||||||
// Precarga todos los recursos en memoria (texturas, sonidos, música, ...).
|
|
||||||
// Vivirán durante toda la vida de la app; las escenas leen handles compartidos.
|
|
||||||
// Debe ir antes de crear Screen porque Screen usa Text (propiedad de Resource).
|
|
||||||
Resource::init(renderer, asset, input);
|
|
||||||
|
|
||||||
screen = new Screen(window, renderer, asset, options);
|
|
||||||
|
|
||||||
activeSection = ActiveSection::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Director::~Director() {
|
|
||||||
saveConfigFile();
|
|
||||||
|
|
||||||
// Libera las secciones primero: sus destructores tocan audio/render SDL
|
|
||||||
// (p.ej. Intro::~Intro llama a JA_DeleteMusic) y deben ejecutarse antes
|
|
||||||
// de SDL_Quit().
|
|
||||||
logo.reset();
|
|
||||||
intro.reset();
|
|
||||||
title.reset();
|
|
||||||
game.reset();
|
|
||||||
|
|
||||||
// Screen puede tener referencias a Text propiedad de Resource: destruir
|
|
||||||
// Screen antes que Resource.
|
|
||||||
delete screen;
|
|
||||||
|
|
||||||
// Libera todos los recursos precargados antes de cerrar SDL.
|
|
||||||
Resource::destroy();
|
|
||||||
|
|
||||||
delete asset;
|
|
||||||
delete input;
|
|
||||||
delete lang;
|
|
||||||
delete options;
|
|
||||||
delete section;
|
|
||||||
|
|
||||||
SDL_DestroyRenderer(renderer);
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
ResourceHelper::shutdownResourceSystem();
|
|
||||||
|
|
||||||
std::cout << "\nBye!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa el objeto input
|
|
||||||
void Director::initInput() {
|
|
||||||
// Establece si ha de mostrar mensajes
|
|
||||||
input->setVerbose(options->console);
|
|
||||||
|
|
||||||
// Busca si hay un mando conectado
|
|
||||||
input->discoverGameController();
|
|
||||||
|
|
||||||
// Teclado - Movimiento del jugador
|
|
||||||
input->bindKey(input_up, SDL_SCANCODE_UP);
|
|
||||||
input->bindKey(input_down, SDL_SCANCODE_DOWN);
|
|
||||||
input->bindKey(input_left, SDL_SCANCODE_LEFT);
|
|
||||||
input->bindKey(input_right, SDL_SCANCODE_RIGHT);
|
|
||||||
input->bindKey(input_fire_left, SDL_SCANCODE_Q);
|
|
||||||
input->bindKey(input_fire_center, SDL_SCANCODE_W);
|
|
||||||
input->bindKey(input_fire_right, SDL_SCANCODE_E);
|
|
||||||
|
|
||||||
// Teclado - Otros
|
|
||||||
input->bindKey(input_accept, SDL_SCANCODE_RETURN);
|
|
||||||
input->bindKey(input_cancel, SDL_SCANCODE_ESCAPE);
|
|
||||||
input->bindKey(input_pause, SDL_SCANCODE_ESCAPE);
|
|
||||||
input->bindKey(input_exit, SDL_SCANCODE_ESCAPE);
|
|
||||||
input->bindKey(input_window_dec_size, SDL_SCANCODE_F1);
|
|
||||||
input->bindKey(input_window_inc_size, SDL_SCANCODE_F2);
|
|
||||||
input->bindKey(input_window_fullscreen, SDL_SCANCODE_F3);
|
|
||||||
|
|
||||||
// Mando - Movimiento del jugador
|
|
||||||
input->bindGameControllerButton(input_up, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
|
||||||
input->bindGameControllerButton(input_down, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
|
||||||
input->bindGameControllerButton(input_left, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
|
||||||
input->bindGameControllerButton(input_right, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
|
||||||
input->bindGameControllerButton(input_fire_left, SDL_GAMEPAD_BUTTON_WEST);
|
|
||||||
input->bindGameControllerButton(input_fire_center, SDL_GAMEPAD_BUTTON_NORTH);
|
|
||||||
input->bindGameControllerButton(input_fire_right, SDL_GAMEPAD_BUTTON_EAST);
|
|
||||||
|
|
||||||
// Mando - Otros
|
|
||||||
// SOUTH queda sin asignar para evitar salidas accidentales: pausa/cancel se hace con START/BACK.
|
|
||||||
input->bindGameControllerButton(input_accept, SDL_GAMEPAD_BUTTON_EAST);
|
|
||||||
#ifdef GAME_CONSOLE
|
|
||||||
input->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_BACK);
|
|
||||||
input->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_START);
|
|
||||||
#else
|
|
||||||
input->bindGameControllerButton(input_pause, SDL_GAMEPAD_BUTTON_START);
|
|
||||||
input->bindGameControllerButton(input_exit, SDL_GAMEPAD_BUTTON_BACK);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa JailAudio
|
|
||||||
void Director::initJailAudio() {
|
|
||||||
JA_Init(48000, SDL_AUDIO_S16, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arranca SDL y crea la ventana
|
|
||||||
bool Director::initSDL() {
|
|
||||||
// Indicador de éxito
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
// Inicializa SDL
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD)) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
} else {
|
|
||||||
// Inicia el generador de numeros aleatorios
|
|
||||||
std::srand(static_cast<unsigned int>(SDL_GetTicks()));
|
|
||||||
|
|
||||||
// Crea la ventana
|
|
||||||
int incW = 0;
|
|
||||||
int incH = 0;
|
|
||||||
if (options->borderEnabled) {
|
|
||||||
incW = options->borderWidth * 2;
|
|
||||||
incH = options->borderHeight * 2;
|
|
||||||
}
|
|
||||||
window = SDL_CreateWindow(WINDOW_CAPTION, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, 0);
|
|
||||||
if (window == nullptr) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
} else {
|
|
||||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
||||||
|
|
||||||
// Crea un renderizador para la ventana
|
|
||||||
renderer = SDL_CreateRenderer(window, NULL);
|
|
||||||
|
|
||||||
if (renderer == nullptr) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
} else {
|
|
||||||
// Activa vsync si es necesario
|
|
||||||
if (options->vSync) {
|
|
||||||
SDL_SetRenderVSync(renderer, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa el color de renderizado
|
|
||||||
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
|
|
||||||
|
|
||||||
// Establece el tamaño del buffer de renderizado
|
|
||||||
SDL_SetRenderLogicalPresentation(renderer, options->gameWidth, options->gameHeight, SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
|
||||||
|
|
||||||
// Establece el modo de mezcla
|
|
||||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea el indice de ficheros
|
|
||||||
bool Director::setFileList() {
|
|
||||||
#ifdef MACOS_BUNDLE
|
|
||||||
const std::string prefix = "/../Resources";
|
|
||||||
#else
|
|
||||||
const std::string prefix = "";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Ficheros de configuración
|
|
||||||
asset->add(systemFolder + "/config.txt", t_data, false, true);
|
|
||||||
asset->add(systemFolder + "/score.bin", t_data, false, true);
|
|
||||||
asset->add(prefix + "/data/demo/demo.bin", t_data);
|
|
||||||
|
|
||||||
// Musicas
|
|
||||||
asset->add(prefix + "/data/music/intro.ogg", t_music);
|
|
||||||
asset->add(prefix + "/data/music/playing.ogg", t_music);
|
|
||||||
asset->add(prefix + "/data/music/title.ogg", t_music);
|
|
||||||
|
|
||||||
// Sonidos
|
|
||||||
asset->add(prefix + "/data/sound/balloon.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/bubble1.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/bubble2.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/bubble3.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/bubble4.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/bullet.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/coffeeout.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/hiscore.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/itemdrop.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/itempickup.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/menu_move.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/menu_select.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/player_collision.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/stage_change.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/title.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/clock.wav", t_sound);
|
|
||||||
asset->add(prefix + "/data/sound/powerball.wav", t_sound);
|
|
||||||
|
|
||||||
// Texturas
|
|
||||||
asset->add(prefix + "/data/gfx/balloon1.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon1.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon2.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon2.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon3.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon3.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon4.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/balloon4.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/bullet.png", t_bitmap);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/game_buildings.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/game_clouds.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/game_grass.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/game_power_meter.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/game_sky_colors.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/game_text.png", t_bitmap);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/intro.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/logo.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/menu_game_over.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/menu_game_over_end.png", t_bitmap);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/item_points1_disk.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_points1_disk.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/item_points2_gavina.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_points2_gavina.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/item_points3_pacmar.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_points3_pacmar.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/item_clock.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_clock.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/item_coffee.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_coffee.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/item_coffee_machine.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/item_coffee_machine.ani", t_data);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/title_bg_tile.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/title_coffee.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/title_crisis.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/title_dust.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/title_dust.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/title_gradient.png", t_bitmap);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/player_head.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/player_body.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/player_legs.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/player_death.ani", t_data);
|
|
||||||
asset->add(prefix + "/data/gfx/player_fire.ani", t_data);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/player_bal1_head.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_bal1_body.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_bal1_legs.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_bal1_death.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_bal1_fire.png", t_bitmap);
|
|
||||||
|
|
||||||
asset->add(prefix + "/data/gfx/player_arounder_head.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_arounder_body.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_arounder_legs.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_arounder_death.png", t_bitmap);
|
|
||||||
asset->add(prefix + "/data/gfx/player_arounder_fire.png", t_bitmap);
|
|
||||||
|
|
||||||
// Fuentes
|
|
||||||
asset->add(prefix + "/data/font/8bithud.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/8bithud.txt", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia_big2.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia.txt", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia2.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia2.txt", t_font);
|
|
||||||
asset->add(prefix + "/data/font/nokia_big2.txt", t_font);
|
|
||||||
asset->add(prefix + "/data/font/smb2_big.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/smb2_big.txt", t_font);
|
|
||||||
asset->add(prefix + "/data/font/smb2.png", t_font);
|
|
||||||
asset->add(prefix + "/data/font/smb2.txt", t_font);
|
|
||||||
|
|
||||||
// Textos
|
|
||||||
asset->add(prefix + "/data/lang/es_ES.txt", t_lang);
|
|
||||||
asset->add(prefix + "/data/lang/en_UK.txt", t_lang);
|
|
||||||
asset->add(prefix + "/data/lang/ba_BA.txt", t_lang);
|
|
||||||
|
|
||||||
// Menus
|
|
||||||
asset->add(prefix + "/data/menu/title.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/title_gc.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/options.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/options_gc.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/pause.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/gameover.men", t_data);
|
|
||||||
asset->add(prefix + "/data/menu/player_select.men", t_data);
|
|
||||||
|
|
||||||
return asset->check();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa las opciones del programa
|
|
||||||
void Director::initOptions() {
|
|
||||||
// Crea el puntero a la estructura de opciones
|
|
||||||
options = new options_t;
|
|
||||||
|
|
||||||
// Pone unos valores por defecto para las opciones de control
|
|
||||||
options->input.clear();
|
|
||||||
|
|
||||||
input_t inp;
|
|
||||||
inp.id = 0;
|
|
||||||
inp.name = "KEYBOARD";
|
|
||||||
inp.deviceType = INPUT_USE_KEYBOARD;
|
|
||||||
options->input.push_back(inp);
|
|
||||||
|
|
||||||
inp.id = 0;
|
|
||||||
inp.name = "GAME CONTROLLER";
|
|
||||||
inp.deviceType = INPUT_USE_GAMECONTROLLER;
|
|
||||||
options->input.push_back(inp);
|
|
||||||
|
|
||||||
// Opciones de video
|
|
||||||
options->gameWidth = GAMECANVAS_WIDTH;
|
|
||||||
options->gameHeight = GAMECANVAS_HEIGHT;
|
|
||||||
options->videoMode = 0;
|
|
||||||
options->windowSize = 3;
|
|
||||||
options->filter = FILTER_NEAREST;
|
|
||||||
options->vSync = true;
|
|
||||||
options->integerScale = true;
|
|
||||||
options->keepAspect = true;
|
|
||||||
options->borderWidth = 0;
|
|
||||||
options->borderHeight = 0;
|
|
||||||
options->borderEnabled = false;
|
|
||||||
|
|
||||||
// Opciones varios
|
|
||||||
options->playerSelected = 0;
|
|
||||||
options->difficulty = DIFFICULTY_NORMAL;
|
|
||||||
options->language = ba_BA;
|
|
||||||
options->console = false;
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// En Emscripten la ventana la gestiona el navegador
|
|
||||||
options->windowSize = 4;
|
|
||||||
options->videoMode = 0;
|
|
||||||
options->integerScale = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
|
||||||
void Director::checkProgramArguments(int argc, const char *argv[]) {
|
|
||||||
// Establece la ruta del programa
|
|
||||||
executablePath = argv[0];
|
|
||||||
|
|
||||||
// Comprueba el resto de parametros
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
if (strcmp(argv[i], "--console") == 0) {
|
|
||||||
options->console = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea la carpeta del sistema donde guardar datos
|
|
||||||
void Director::createSystemFolder(const std::string &folder) {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// En Emscripten usamos una carpeta en MEMFS (no persistente)
|
|
||||||
systemFolder = "/config/" + folder;
|
|
||||||
#elif _WIN32
|
|
||||||
systemFolder = std::string(getenv("APPDATA")) + "/" + folder;
|
|
||||||
#elif __APPLE__
|
|
||||||
struct passwd *pw = getpwuid(getuid());
|
|
||||||
const char *homedir = pw->pw_dir;
|
|
||||||
systemFolder = std::string(homedir) + "/Library/Application Support" + "/" + folder;
|
|
||||||
#elif __linux__
|
|
||||||
struct passwd *pw = getpwuid(getuid());
|
|
||||||
const char *homedir = pw->pw_dir;
|
|
||||||
systemFolder = std::string(homedir) + "/.config/" + folder;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Intenta crear ".config", per si no existeix
|
|
||||||
std::string config_base_folder = std::string(homedir) + "/.config";
|
|
||||||
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
|
|
||||||
if (ret == -1 && errno != EEXIST) {
|
|
||||||
printf("ERROR CREATING CONFIG BASE FOLDER.");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// En Emscripten no necesitamos crear carpetas (MEMFS las crea automáticamente)
|
|
||||||
(void)folder;
|
|
||||||
#else
|
|
||||||
struct stat st = {0};
|
|
||||||
if (stat(systemFolder.c_str(), &st) == -1) {
|
|
||||||
errno = 0;
|
|
||||||
#ifdef _WIN32
|
|
||||||
int ret = mkdir(systemFolder.c_str());
|
|
||||||
#else
|
|
||||||
int ret = mkdir(systemFolder.c_str(), S_IRWXU);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret == -1) {
|
|
||||||
switch (errno) {
|
|
||||||
case EACCES:
|
|
||||||
printf("the parent directory does not allow write");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
case EEXIST:
|
|
||||||
printf("pathname already exists");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
case ENAMETOOLONG:
|
|
||||||
printf("pathname is too long");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
default:
|
|
||||||
perror("mkdir");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga el fichero de configuración
|
|
||||||
bool Director::loadConfigFile() {
|
|
||||||
// Indicador de éxito en la carga
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
// Variables para manejar el fichero
|
|
||||||
const std::string filePath = "config.txt";
|
|
||||||
std::string line;
|
|
||||||
std::ifstream file(asset->get(filePath));
|
|
||||||
|
|
||||||
// Si el fichero se puede abrir
|
|
||||||
if (file.good()) {
|
|
||||||
// Procesa el fichero linea a linea
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "Reading file " << filePath << std::endl;
|
|
||||||
}
|
|
||||||
while (std::getline(file, line)) {
|
|
||||||
// Comprueba que la linea no sea un comentario
|
|
||||||
if (line.substr(0, 1) != "#") {
|
|
||||||
// Encuentra la posición del caracter '='
|
|
||||||
int pos = line.find("=");
|
|
||||||
// Procesa las dos subcadenas
|
|
||||||
if (!setOptions(options, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "Warning: file " << filePath << std::endl;
|
|
||||||
std::cout << "Unknown parameter " << line.substr(0, pos).c_str() << std::endl;
|
|
||||||
}
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cierra el fichero
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "Closing file " << filePath << std::endl;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// El fichero no existe
|
|
||||||
else { // Crea el fichero con los valores por defecto
|
|
||||||
saveConfigFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normaliza los valores
|
|
||||||
if (options->videoMode != 0 && options->videoMode != SDL_WINDOW_FULLSCREEN) {
|
|
||||||
options->videoMode = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->windowSize < 1 || options->windowSize > 4) {
|
|
||||||
options->windowSize = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->language < 0 || options->language > MAX_LANGUAGES) {
|
|
||||||
options->language = en_UK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guarda el fichero de configuración
|
|
||||||
bool Director::saveConfigFile() {
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
// Crea y abre el fichero de texto
|
|
||||||
std::ofstream file(asset->get("config.txt"));
|
|
||||||
|
|
||||||
if (file.good()) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << asset->get("config.txt") << " open for writing" << std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << asset->get("config.txt") << " can't be opened" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opciones g´raficas
|
|
||||||
file << "## VISUAL OPTIONS\n";
|
|
||||||
if (options->videoMode == 0) {
|
|
||||||
file << "videoMode=0\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (options->videoMode == SDL_WINDOW_FULLSCREEN) {
|
|
||||||
file << "videoMode=SDL_WINDOW_FULLSCREEN\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
file << "windowSize=" + std::to_string(options->windowSize) + "\n";
|
|
||||||
|
|
||||||
if (options->filter == FILTER_NEAREST) {
|
|
||||||
file << "filter=FILTER_NEAREST\n";
|
|
||||||
} else {
|
|
||||||
file << "filter=FILTER_LINEAL\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
file << "vSync=" + boolToString(options->vSync) + "\n";
|
|
||||||
file << "integerScale=" + boolToString(options->integerScale) + "\n";
|
|
||||||
file << "keepAspect=" + boolToString(options->keepAspect) + "\n";
|
|
||||||
file << "borderEnabled=" + boolToString(options->borderEnabled) + "\n";
|
|
||||||
file << "borderWidth=" + std::to_string(options->borderWidth) + "\n";
|
|
||||||
file << "borderHeight=" + std::to_string(options->borderHeight) + "\n";
|
|
||||||
|
|
||||||
// Otras opciones del programa
|
|
||||||
file << "\n## OTHER OPTIONS\n";
|
|
||||||
file << "language=" + std::to_string(options->language) + "\n";
|
|
||||||
file << "difficulty=" + std::to_string(options->difficulty) + "\n";
|
|
||||||
file << "input0=" + std::to_string(options->input[0].deviceType) + "\n";
|
|
||||||
file << "input1=" + std::to_string(options->input[1].deviceType) + "\n";
|
|
||||||
|
|
||||||
// Cierra el fichero
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestiona las transiciones entre secciones
|
|
||||||
void Director::handleSectionTransition() {
|
|
||||||
// Determina qué sección debería estar activa
|
|
||||||
ActiveSection targetSection = ActiveSection::None;
|
|
||||||
switch (section->name) {
|
|
||||||
case SECTION_PROG_LOGO:
|
|
||||||
targetSection = ActiveSection::Logo;
|
|
||||||
break;
|
|
||||||
case SECTION_PROG_INTRO:
|
|
||||||
targetSection = ActiveSection::Intro;
|
|
||||||
break;
|
|
||||||
case SECTION_PROG_TITLE:
|
|
||||||
targetSection = ActiveSection::Title;
|
|
||||||
break;
|
|
||||||
case SECTION_PROG_GAME:
|
|
||||||
targetSection = ActiveSection::Game;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si no ha cambiado, no hay nada que hacer
|
|
||||||
if (targetSection == activeSection) return;
|
|
||||||
|
|
||||||
// Destruye la sección anterior
|
|
||||||
logo.reset();
|
|
||||||
intro.reset();
|
|
||||||
title.reset();
|
|
||||||
game.reset();
|
|
||||||
|
|
||||||
// Crea la nueva sección
|
|
||||||
activeSection = targetSection;
|
|
||||||
switch (activeSection) {
|
|
||||||
case ActiveSection::Logo:
|
|
||||||
logo = std::make_unique<Logo>(renderer, screen, asset, input, section);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Intro:
|
|
||||||
intro = std::make_unique<Intro>(renderer, screen, asset, input, lang, section);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Title:
|
|
||||||
title = std::make_unique<Title>(renderer, screen, input, asset, options, lang, section);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Game: {
|
|
||||||
const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2;
|
|
||||||
game = std::make_unique<Game>(numPlayers, 0, renderer, screen, asset, lang, input, false, options, section);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ActiveSection::None:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta un frame del juego
|
|
||||||
SDL_AppResult Director::iterate() {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// En WASM no se puede salir: reinicia al logo
|
|
||||||
if (section->name == SECTION_PROG_QUIT) {
|
|
||||||
section->name = SECTION_PROG_LOGO;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (section->name == SECTION_PROG_QUIT) {
|
|
||||||
return SDL_APP_SUCCESS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Actualiza la visibilidad del cursor del ratón
|
|
||||||
Mouse::updateCursorVisibility(options->videoMode != 0);
|
|
||||||
|
|
||||||
// Gestiona las transiciones entre secciones
|
|
||||||
handleSectionTransition();
|
|
||||||
|
|
||||||
// Ejecuta un frame de la sección activa
|
|
||||||
switch (activeSection) {
|
|
||||||
case ActiveSection::Logo:
|
|
||||||
logo->iterate();
|
|
||||||
break;
|
|
||||||
case ActiveSection::Intro:
|
|
||||||
intro->iterate();
|
|
||||||
break;
|
|
||||||
case ActiveSection::Title:
|
|
||||||
title->iterate();
|
|
||||||
break;
|
|
||||||
case ActiveSection::Game:
|
|
||||||
game->iterate();
|
|
||||||
break;
|
|
||||||
case ActiveSection::None:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Procesa un evento
|
|
||||||
SDL_AppResult Director::handleEvent(SDL_Event *event) {
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (event->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
return SDL_APP_SUCCESS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Hot-plug de mandos
|
|
||||||
if (event->type == SDL_EVENT_GAMEPAD_ADDED) {
|
|
||||||
std::string name;
|
|
||||||
if (input->handleGamepadAdded(event->gdevice.which, name)) {
|
|
||||||
screen->notify(name + " " + lang->getText(94),
|
|
||||||
color_t{0x40, 0xFF, 0x40},
|
|
||||||
color_t{0, 0, 0},
|
|
||||||
2500);
|
|
||||||
}
|
|
||||||
} else if (event->type == SDL_EVENT_GAMEPAD_REMOVED) {
|
|
||||||
std::string name;
|
|
||||||
if (input->handleGamepadRemoved(event->gdevice.which, name)) {
|
|
||||||
screen->notify(name + " " + lang->getText(95),
|
|
||||||
color_t{0xFF, 0x50, 0x50},
|
|
||||||
color_t{0, 0, 0},
|
|
||||||
2500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestiona la visibilidad del cursor según el movimiento del ratón
|
|
||||||
Mouse::handleEvent(*event, options->videoMode != 0);
|
|
||||||
|
|
||||||
// Reenvía el evento a la sección activa
|
|
||||||
switch (activeSection) {
|
|
||||||
case ActiveSection::Logo:
|
|
||||||
logo->handleEvent(event);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Intro:
|
|
||||||
intro->handleEvent(event);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Title:
|
|
||||||
title->handleEvent(event);
|
|
||||||
break;
|
|
||||||
case ActiveSection::Game:
|
|
||||||
game->handleEvent(event);
|
|
||||||
break;
|
|
||||||
case ActiveSection::None:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
|
||||||
bool Director::setOptions(options_t *options, std::string var, std::string value) {
|
|
||||||
// Indicador de éxito en la asignación
|
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
// Opciones de video
|
|
||||||
if (var == "videoMode") {
|
|
||||||
if (value == "SDL_WINDOW_FULLSCREEN" || value == "SDL_WINDOW_FULLSCREEN_DESKTOP") {
|
|
||||||
options->videoMode = SDL_WINDOW_FULLSCREEN;
|
|
||||||
} else {
|
|
||||||
options->videoMode = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "windowSize") {
|
|
||||||
options->windowSize = std::stoi(value);
|
|
||||||
if ((options->windowSize < 1) || (options->windowSize > 4)) {
|
|
||||||
options->windowSize = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "filter") {
|
|
||||||
if (value == "FILTER_LINEAL") {
|
|
||||||
options->filter = FILTER_LINEAL;
|
|
||||||
} else {
|
|
||||||
options->filter = FILTER_NEAREST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "vSync") {
|
|
||||||
options->vSync = stringToBool(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "integerScale") {
|
|
||||||
options->integerScale = stringToBool(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "keepAspect") {
|
|
||||||
options->keepAspect = stringToBool(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "borderEnabled") {
|
|
||||||
options->borderEnabled = stringToBool(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "borderWidth") {
|
|
||||||
options->borderWidth = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "borderHeight") {
|
|
||||||
options->borderHeight = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opciones varias
|
|
||||||
else if (var == "language") {
|
|
||||||
options->language = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "difficulty") {
|
|
||||||
options->difficulty = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "input0") {
|
|
||||||
options->input[0].deviceType = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (var == "input1") {
|
|
||||||
options->input[1].deviceType = std::stoi(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lineas vacias o que empiezan por comentario
|
|
||||||
else if (var == "" || var.substr(0, 1) == "#") {
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
14726
source/external/fkyaml_node.hpp
vendored
Normal file
14726
source/external/fkyaml_node.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
// Tipos de fundido
|
|
||||||
constexpr int FADE_FULLSCREEN = 0;
|
|
||||||
constexpr int FADE_CENTER = 1;
|
|
||||||
constexpr int FADE_RANDOM_SQUARE = 2;
|
|
||||||
|
|
||||||
// Clase Fade
|
|
||||||
class Fade {
|
|
||||||
private:
|
|
||||||
SDL_Renderer *mRenderer; // El renderizador de la ventana
|
|
||||||
SDL_Texture *mBackbuffer; // Textura para usar como backbuffer
|
|
||||||
Uint8 mFadeType; // Tipo de fade a realizar
|
|
||||||
Uint16 mCounter; // Contador interno
|
|
||||||
bool mEnabled; // Indica si el fade está activo
|
|
||||||
bool mFinished; // Indica si ha terminado la transición
|
|
||||||
Uint8 mR, mG, mB; // Colores para el fade
|
|
||||||
Uint8 mROriginal, mGOriginal, mBOriginal; // Colores originales para FADE_RANDOM_SQUARE
|
|
||||||
Uint32 mLastSquareTicks; // Ticks del último cuadrado dibujado (FADE_RANDOM_SQUARE)
|
|
||||||
Uint16 mSquaresDrawn; // Número de cuadrados dibujados (FADE_RANDOM_SQUARE)
|
|
||||||
bool mFullscreenDone; // Indica si el fade fullscreen ha terminado la fase de fundido
|
|
||||||
SDL_Rect mRect1; // Rectangulo usado para crear los efectos de transición
|
|
||||||
SDL_Rect mRect2; // Rectangulo usado para crear los efectos de transición
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
Fade(SDL_Renderer *renderer);
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
~Fade();
|
|
||||||
|
|
||||||
// Inicializa las variables
|
|
||||||
void init(Uint8 r, Uint8 g, Uint8 b);
|
|
||||||
|
|
||||||
// Pinta una transición en pantalla
|
|
||||||
void render();
|
|
||||||
|
|
||||||
// Actualiza las variables internas
|
|
||||||
void update();
|
|
||||||
|
|
||||||
// Activa el fade
|
|
||||||
void activateFade();
|
|
||||||
|
|
||||||
// Comprueba si ha terminado la transicion
|
|
||||||
bool hasEnded();
|
|
||||||
|
|
||||||
// Comprueba si está activo
|
|
||||||
bool isEnabled();
|
|
||||||
|
|
||||||
// Establece el tipo de fade
|
|
||||||
void setFadeType(Uint8 fadeType);
|
|
||||||
};
|
|
||||||
@@ -2,8 +2,58 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "lang.h"
|
#include "core/locale/lang.h"
|
||||||
#include "utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Defaults per a Options (alineats amb coffee_crisis_arcade_edition).
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
namespace Defaults::Window {
|
||||||
|
constexpr const char *CAPTION = "© 2020 Coffee Crisis — JailDesigner";
|
||||||
|
constexpr int ZOOM = 3;
|
||||||
|
constexpr int MAX_ZOOM = 4;
|
||||||
|
} // namespace Defaults::Window
|
||||||
|
|
||||||
|
namespace Defaults::Video {
|
||||||
|
constexpr SDL_ScaleMode SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
|
||||||
|
constexpr bool FULLSCREEN = false;
|
||||||
|
constexpr bool VSYNC = true;
|
||||||
|
constexpr bool INTEGER_SCALE = true;
|
||||||
|
constexpr bool GPU_ACCELERATION = true;
|
||||||
|
constexpr const char *GPU_PREFERRED_DRIVER = "";
|
||||||
|
constexpr bool SHADER_ENABLED = false;
|
||||||
|
constexpr bool SUPERSAMPLING = false;
|
||||||
|
constexpr bool LINEAR_UPSCALE = false;
|
||||||
|
constexpr int DOWNSCALE_ALGO = 1;
|
||||||
|
} // namespace Defaults::Video
|
||||||
|
|
||||||
|
namespace Defaults::Audio {
|
||||||
|
constexpr bool ENABLED = true;
|
||||||
|
constexpr int VOLUME = 100;
|
||||||
|
} // namespace Defaults::Audio
|
||||||
|
|
||||||
|
namespace Defaults::Music {
|
||||||
|
constexpr bool ENABLED = true;
|
||||||
|
constexpr int VOLUME = 100;
|
||||||
|
} // namespace Defaults::Music
|
||||||
|
|
||||||
|
namespace Defaults::Sound {
|
||||||
|
constexpr bool ENABLED = true;
|
||||||
|
constexpr int VOLUME = 100;
|
||||||
|
} // namespace Defaults::Sound
|
||||||
|
|
||||||
|
namespace Defaults::Loading {
|
||||||
|
constexpr bool SHOW = false;
|
||||||
|
constexpr bool SHOW_RESOURCE_NAME = true;
|
||||||
|
constexpr bool WAIT_FOR_INPUT = false;
|
||||||
|
} // namespace Defaults::Loading
|
||||||
|
|
||||||
|
namespace Defaults::Settings {
|
||||||
|
constexpr int DIFFICULTY = DIFFICULTY_NORMAL;
|
||||||
|
constexpr int LANGUAGE = ba_BA;
|
||||||
|
constexpr palette_e PALETTE = p_zxspectrum;
|
||||||
|
} // namespace Defaults::Settings
|
||||||
|
|
||||||
// Tamaño de bloque
|
// Tamaño de bloque
|
||||||
constexpr int BLOCK = 8;
|
constexpr int BLOCK = 8;
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "balloon.h"
|
#include "game/entities/balloon.h"
|
||||||
|
|
||||||
#include <math.h> // for abs
|
#include <math.h> // for abs
|
||||||
|
|
||||||
#include "animatedsprite.h" // for AnimatedSprite
|
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||||
#include "const.h" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
|
#include "core/rendering/movingsprite.h" // for MovingSprite
|
||||||
#include "movingsprite.h" // for MovingSprite
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "texture.h" // for Texture
|
#include "game/defaults.hpp" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, Texture *texture, std::vector<std::string> *animation, SDL_Renderer *renderer) {
|
Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer, Texture *texture, std::vector<std::string> *animation, SDL_Renderer *renderer) {
|
||||||
@@ -261,15 +261,13 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
|
|||||||
stoppedCounter = 0;
|
stoppedCounter = 0;
|
||||||
blinking = false;
|
blinking = false;
|
||||||
visible = true;
|
visible = true;
|
||||||
invulnerable = true;
|
|
||||||
beingCreated = true;
|
|
||||||
creationCounter = creationtimer;
|
creationCounter = creationtimer;
|
||||||
creationCounterIni = creationtimer;
|
creationCounterIni = creationtimer;
|
||||||
popping = false;
|
popping = false;
|
||||||
|
|
||||||
// Actualiza valores
|
// Valores iniciales dependentes del timer
|
||||||
beingCreated = creationCounter == 0 ? false : true;
|
beingCreated = creationCounter != 0;
|
||||||
invulnerable = beingCreated == false ? false : true;
|
invulnerable = beingCreated;
|
||||||
|
|
||||||
counter = 0;
|
counter = 0;
|
||||||
travelY = 1.0f;
|
travelY = 1.0f;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "utils.h" // for circle_t
|
#include "utils/utils.h" // for circle_t
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "bullet.h"
|
#include "game/entities/bullet.h"
|
||||||
|
|
||||||
#include "const.h" // for NO_KIND, PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_A...
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "sprite.h" // for Sprite
|
#include "game/defaults.hpp" // for NO_KIND, PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_A...
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "utils.h" // for circle_t
|
#include "utils/utils.h" // for circle_t
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "item.h"
|
#include "game/entities/item.h"
|
||||||
|
|
||||||
#include <stdlib.h> // for rand
|
#include <stdlib.h> // for rand
|
||||||
|
|
||||||
#include "animatedsprite.h" // for AnimatedSprite
|
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||||
#include "const.h" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
|
#include "game/defaults.hpp" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -107,9 +107,6 @@ void Item::move() {
|
|||||||
|
|
||||||
// Si el objeto se sale por la parte inferior
|
// Si el objeto se sale por la parte inferior
|
||||||
if (posY + height > PLAY_AREA_BOTTOM) {
|
if (posY + height > PLAY_AREA_BOTTOM) {
|
||||||
// Corrige
|
|
||||||
posY -= velY;
|
|
||||||
|
|
||||||
// Detiene el objeto
|
// Detiene el objeto
|
||||||
velY = 0;
|
velY = 0;
|
||||||
velX = 0;
|
velX = 0;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "utils.h" // for circle_t
|
#include "utils/utils.h" // for circle_t
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
#include "player.h"
|
#include "game/entities/player.h"
|
||||||
|
|
||||||
#include <stdlib.h> // for rand
|
#include <stdlib.h> // for rand
|
||||||
|
|
||||||
#include "animatedsprite.h" // for AnimatedSprite
|
#include "core/input/input.h" // for inputs_e
|
||||||
#include "const.h" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT
|
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||||
#include "input.h" // for inputs_e
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "texture.h" // for Texture
|
#include "game/defaults.hpp" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Player::Player(float x, int y, SDL_Renderer *renderer, std::vector<Texture *> texture, std::vector<std::vector<std::string> *> animations) {
|
Player::Player(float x, int y, SDL_Renderer *renderer, const std::vector<Texture *> &texture, const std::vector<std::vector<std::string> *> &animations) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
|
|
||||||
@@ -189,19 +189,12 @@ void Player::render() {
|
|||||||
|
|
||||||
// Establece el estado del jugador cuando camina
|
// Establece el estado del jugador cuando camina
|
||||||
void Player::setWalkingStatus(Uint8 status) {
|
void Player::setWalkingStatus(Uint8 status) {
|
||||||
// Si cambiamos de estado, reiniciamos la animación
|
|
||||||
if (statusWalking != status) {
|
|
||||||
statusWalking = status;
|
statusWalking = status;
|
||||||
// legsSprite->setCurrentFrame(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establece el estado del jugador cuando dispara
|
// Establece el estado del jugador cuando dispara
|
||||||
void Player::setFiringStatus(Uint8 status) {
|
void Player::setFiringStatus(Uint8 status) {
|
||||||
// Si cambiamos de estado, reiniciamos la animación
|
|
||||||
if (statusFiring != status) {
|
|
||||||
statusFiring = status;
|
statusFiring = status;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establece la animación correspondiente al estado
|
// Establece la animación correspondiente al estado
|
||||||
@@ -521,7 +514,7 @@ void Player::updatePowerUpHeadOffset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pone las texturas del jugador
|
// Pone las texturas del jugador
|
||||||
void Player::setPlayerTextures(std::vector<Texture *> texture) {
|
void Player::setPlayerTextures(const std::vector<Texture *> &texture) {
|
||||||
headSprite->setTexture(texture[0]);
|
headSprite->setTexture(texture[0]);
|
||||||
bodySprite->setTexture(texture[1]);
|
bodySprite->setTexture(texture[1]);
|
||||||
legsSprite->setTexture(texture[2]);
|
legsSprite->setTexture(texture[2]);
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "utils.h" // for circle_t
|
#include "utils/utils.h" // for circle_t
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ class Player {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Player(float x, int y, SDL_Renderer *renderer, std::vector<Texture *> texture, std::vector<std::vector<std::string> *> animations);
|
Player(float x, int y, SDL_Renderer *renderer, const std::vector<Texture *> &texture, const std::vector<std::vector<std::string> *> &animations);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Player();
|
~Player();
|
||||||
@@ -96,7 +96,7 @@ class Player {
|
|||||||
void render();
|
void render();
|
||||||
|
|
||||||
// Pone las texturas del jugador
|
// Pone las texturas del jugador
|
||||||
void setPlayerTextures(std::vector<Texture *> texture);
|
void setPlayerTextures(const std::vector<Texture *> &texture);
|
||||||
|
|
||||||
// Actua en consecuencia de la entrada recibida
|
// Actua en consecuencia de la entrada recibida
|
||||||
void setInput(Uint8 input);
|
void setInput(Uint8 input);
|
||||||
@@ -1,41 +1,40 @@
|
|||||||
#include "game.h"
|
#include "game/game.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stdlib.h> // for rand
|
#include <stdlib.h> // for rand
|
||||||
|
|
||||||
#include <algorithm> // for max, min
|
#include <algorithm> // for max, min
|
||||||
|
#include <numeric> // for accumulate
|
||||||
#include <fstream> // for basic_ifstream
|
#include <fstream> // for basic_ifstream
|
||||||
#include <iostream> // for basic_ostream, char_traits, operator<<
|
#include <iostream> // for basic_ostream, char_traits, operator<<
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/audio/jail_audio.hpp" // for JA_PlaySound, JA_DeleteSound, JA_LoadSound
|
||||||
#include "balloon.h" // for Balloon, BALLOON_VELX_NEGATIVE, BALLOON_...
|
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||||
#include "bullet.h" // for Bullet, BULLET_LEFT, BULLET_RIGHT, BULLE...
|
#include "core/input/input.h" // for inputs_e, Input, REPEAT_TRUE, REPEAT_FALSE
|
||||||
#include "const.h" // for PLAY_AREA_CENTER_X, BLOCK, PLAY_AREA_CEN...
|
#include "core/locale/lang.h" // for Lang
|
||||||
#include "fade.h" // for Fade, FADE_CENTER
|
#include "core/rendering/fade.h" // for Fade, FADE_CENTER
|
||||||
#include "input.h" // for inputs_e, Input, REPEAT_TRUE, REPEAT_FALSE
|
#include "core/rendering/movingsprite.h" // for MovingSprite
|
||||||
#include "item.h" // for Item, ITEM_COFFEE_MACHINE, ITEM_CLOCK
|
#include "core/rendering/screen.h" // for Screen
|
||||||
#include "jail_audio.hpp" // for JA_PlaySound, JA_DeleteSound, JA_LoadSound
|
#include "core/rendering/smartsprite.h" // for SmartSprite
|
||||||
#include "lang.h" // for Lang
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "menu.h" // for Menu
|
#include "core/rendering/text.h" // for Text, TXT_CENTER
|
||||||
#include "movingsprite.h" // for MovingSprite
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "player.h" // for Player, DEATH_COUNTER
|
#include "core/resources/asset.h" // for Asset
|
||||||
#include "resource.h"
|
#include "core/resources/resource.h"
|
||||||
#include "screen.h" // for Screen
|
#include "game/defaults.hpp" // for PLAY_AREA_CENTER_X, BLOCK, PLAY_AREA_CEN...
|
||||||
#include "smartsprite.h" // for SmartSprite
|
#include "game/entities/balloon.h" // for Balloon, BALLOON_VELX_NEGATIVE, BALLOON_...
|
||||||
#include "sprite.h" // for Sprite
|
#include "game/entities/bullet.h" // for Bullet, BULLET_LEFT, BULLET_RIGHT, BULLE...
|
||||||
#include "text.h" // for Text, TXT_CENTER
|
#include "game/entities/item.h" // for Item, ITEM_COFFEE_MACHINE, ITEM_CLOCK
|
||||||
#include "texture.h" // for Texture
|
#include "game/entities/player.h" // for Player, DEATH_COUNTER
|
||||||
|
#include "game/options.hpp" // for Options
|
||||||
|
#include "game/ui/menu.h" // for Menu
|
||||||
struct JA_Sound_t;
|
struct JA_Sound_t;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Game::Game(int numPlayers, int currentStage, SDL_Renderer *renderer, Screen *screen, Asset *asset, Lang *lang, Input *input, bool demo, options_t *options, section_t *section) {
|
Game::Game(int numPlayers, int currentStage, SDL_Renderer *renderer, bool demo, section_t *section)
|
||||||
|
: lastStageReached(currentStage) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->screen = screen;
|
|
||||||
this->asset = asset;
|
|
||||||
this->lang = lang;
|
|
||||||
this->input = input;
|
|
||||||
this->options = options;
|
|
||||||
this->section = section;
|
this->section = section;
|
||||||
|
|
||||||
// Pasa variables
|
// Pasa variables
|
||||||
@@ -46,12 +45,11 @@ Game::Game(int numPlayers, int currentStage, SDL_Renderer *renderer, Screen *scr
|
|||||||
#else
|
#else
|
||||||
this->currentStage = currentStage;
|
this->currentStage = currentStage;
|
||||||
#endif
|
#endif
|
||||||
lastStageReached = currentStage;
|
|
||||||
if (numPlayers == 1) { // Si solo juega un jugador, permite jugar tanto con teclado como con mando
|
if (numPlayers == 1) { // Si solo juega un jugador, permite jugar tanto con teclado como con mando
|
||||||
onePlayerControl = options->input[0].deviceType;
|
onePlayerControl = Options::inputs[0].deviceType;
|
||||||
options->input[0].deviceType = INPUT_USE_ANY;
|
Options::inputs[0].deviceType = INPUT_USE_ANY;
|
||||||
}
|
}
|
||||||
difficulty = options->difficulty;
|
difficulty = Options::settings.difficulty;
|
||||||
|
|
||||||
// Crea los objetos
|
// Crea los objetos
|
||||||
fade = new Fade(renderer);
|
fade = new Fade(renderer);
|
||||||
@@ -96,7 +94,7 @@ Game::~Game() {
|
|||||||
|
|
||||||
// Restaura el metodo de control
|
// Restaura el metodo de control
|
||||||
if (numPlayers == 1) {
|
if (numPlayers == 1) {
|
||||||
options->input[0].deviceType = onePlayerControl;
|
Options::inputs[0].deviceType = onePlayerControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elimina todos los objetos contenidos en vectores (jugadores, balas, etc.)
|
// Elimina todos los objetos contenidos en vectores (jugadores, balas, etc.)
|
||||||
@@ -143,7 +141,7 @@ void Game::init() {
|
|||||||
|
|
||||||
// Crea los jugadores
|
// Crea los jugadores
|
||||||
if (numPlayers == 1) {
|
if (numPlayers == 1) {
|
||||||
Player *player = new Player(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, renderer, playerTextures[options->playerSelected], playerAnimations);
|
Player *player = new Player(PLAY_AREA_CENTER_X - 11, PLAY_AREA_BOTTOM - 24, renderer, playerTextures[Options::settings.player_selected], playerAnimations);
|
||||||
players.push_back(player);
|
players.push_back(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +325,7 @@ void Game::init() {
|
|||||||
|
|
||||||
// Carga los recursos necesarios para la sección 'Game'
|
// Carga los recursos necesarios para la sección 'Game'
|
||||||
void Game::loadMedia() {
|
void Game::loadMedia() {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << std::endl
|
std::cout << std::endl
|
||||||
<< "** LOADING RESOURCES FOR GAME SECTION" << std::endl;
|
<< "** LOADING RESOURCES FOR GAME SECTION" << std::endl;
|
||||||
}
|
}
|
||||||
@@ -403,15 +401,15 @@ void Game::loadMedia() {
|
|||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
gameOverMenu = R->getMenu("gameover");
|
gameOverMenu = R->getMenu("gameover");
|
||||||
gameOverMenu->setItemCaption(0, lang->getText(48));
|
gameOverMenu->setItemCaption(0, Lang::get()->getText(48));
|
||||||
gameOverMenu->setItemCaption(1, lang->getText(49));
|
gameOverMenu->setItemCaption(1, Lang::get()->getText(49));
|
||||||
const int w = text->getCharacterSize() * lang->getText(45).length();
|
const int w = text->getCharacterSize() * Lang::get()->getText(45).length();
|
||||||
gameOverMenu->setRectSize(w, 0);
|
gameOverMenu->setRectSize(w, 0);
|
||||||
gameOverMenu->centerMenuOnX(199);
|
gameOverMenu->centerMenuOnX(199);
|
||||||
pauseMenu = R->getMenu("pause");
|
pauseMenu = R->getMenu("pause");
|
||||||
pauseMenu->setItemCaption(0, lang->getText(41));
|
pauseMenu->setItemCaption(0, Lang::get()->getText(41));
|
||||||
pauseMenu->setItemCaption(1, lang->getText(46));
|
pauseMenu->setItemCaption(1, Lang::get()->getText(46));
|
||||||
pauseMenu->setItemCaption(2, lang->getText(47));
|
pauseMenu->setItemCaption(2, Lang::get()->getText(47));
|
||||||
|
|
||||||
// Sonidos
|
// Sonidos
|
||||||
balloonSound = R->getSound("balloon.wav");
|
balloonSound = R->getSound("balloon.wav");
|
||||||
@@ -433,7 +431,7 @@ void Game::loadMedia() {
|
|||||||
// Musicas
|
// Musicas
|
||||||
gameMusic = R->getMusic("playing.ogg");
|
gameMusic = R->getMusic("playing.ogg");
|
||||||
|
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "** RESOURCES FOR GAME SECTION LOADED" << std::endl
|
std::cout << "** RESOURCES FOR GAME SECTION LOADED" << std::endl
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
@@ -443,20 +441,20 @@ void Game::loadMedia() {
|
|||||||
bool Game::loadScoreFile() {
|
bool Game::loadScoreFile() {
|
||||||
// Indicador de éxito en la carga
|
// Indicador de éxito en la carga
|
||||||
bool success = true;
|
bool success = true;
|
||||||
const std::string p = asset->get("score.bin");
|
const std::string p = Asset::get()->get("score.bin");
|
||||||
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
||||||
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "r+b");
|
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "r+b");
|
||||||
|
|
||||||
// El fichero no existe
|
// El fichero no existe
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl;
|
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creamos el fichero para escritura
|
// Creamos el fichero para escritura
|
||||||
file = SDL_IOFromFile(p.c_str(), "w+b");
|
file = SDL_IOFromFile(p.c_str(), "w+b");
|
||||||
if (file != nullptr) {
|
if (file != nullptr) {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "New file (" << filename.c_str() << ") created!" << std::endl;
|
std::cout << "New file (" << filename.c_str() << ") created!" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +467,7 @@ bool Game::loadScoreFile() {
|
|||||||
// Cerramos el fichero
|
// Cerramos el fichero
|
||||||
SDL_CloseIO(file);
|
SDL_CloseIO(file);
|
||||||
} else {
|
} else {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Error: Unable to create file " << filename.c_str() << std::endl;
|
std::cout << "Error: Unable to create file " << filename.c_str() << std::endl;
|
||||||
}
|
}
|
||||||
success = false;
|
success = false;
|
||||||
@@ -478,7 +476,7 @@ bool Game::loadScoreFile() {
|
|||||||
// El fichero existe
|
// El fichero existe
|
||||||
else {
|
else {
|
||||||
// Cargamos los datos
|
// Cargamos los datos
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Reading file " << filename.c_str() << std::endl;
|
std::cout << "Reading file " << filename.c_str() << std::endl;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
|
for (int i = 0; i < TOTAL_SCORE_DATA; ++i)
|
||||||
@@ -511,12 +509,12 @@ bool Game::loadDemoFile() {
|
|||||||
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
||||||
memcpy(&demo.dataFile[i], bytes.data() + i * sizeof(demoKeys_t), sizeof(demoKeys_t));
|
memcpy(&demo.dataFile[i], bytes.data() + i * sizeof(demoKeys_t), sizeof(demoKeys_t));
|
||||||
}
|
}
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Demo data loaded (" << bytes.size() << " bytes)" << std::endl;
|
std::cout << "Demo data loaded (" << bytes.size() << " bytes)" << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Si no hay datos (bytes vacíos o tamaño inválido), inicializamos a cero.
|
// Si no hay datos (bytes vacíos o tamaño inválido), inicializamos a cero.
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Warning: demo data missing or too small, initializing to zero" << std::endl;
|
std::cout << "Warning: demo data missing or too small, initializing to zero" << std::endl;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
||||||
@@ -535,7 +533,7 @@ bool Game::loadDemoFile() {
|
|||||||
// Guarda el fichero de puntos
|
// Guarda el fichero de puntos
|
||||||
bool Game::saveScoreFile() {
|
bool Game::saveScoreFile() {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
const std::string p = asset->get("score.bin");
|
const std::string p = Asset::get()->get("score.bin");
|
||||||
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
||||||
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "w+b");
|
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "w+b");
|
||||||
if (file != nullptr) {
|
if (file != nullptr) {
|
||||||
@@ -544,14 +542,14 @@ bool Game::saveScoreFile() {
|
|||||||
SDL_WriteIO(file, &scoreDataFile[i], sizeof(Uint32));
|
SDL_WriteIO(file, &scoreDataFile[i], sizeof(Uint32));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Writing file " << filename.c_str() << std::endl;
|
std::cout << "Writing file " << filename.c_str() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cerramos el fichero
|
// Cerramos el fichero
|
||||||
SDL_CloseIO(file);
|
SDL_CloseIO(file);
|
||||||
} else {
|
} else {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Error: Unable to save " << filename.c_str() << " file! " << SDL_GetError() << std::endl;
|
std::cout << "Error: Unable to save " << filename.c_str() << " file! " << SDL_GetError() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,7 +559,7 @@ bool Game::saveScoreFile() {
|
|||||||
// Guarda el fichero de datos para la demo
|
// Guarda el fichero de datos para la demo
|
||||||
bool Game::saveDemoFile() {
|
bool Game::saveDemoFile() {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
const std::string p = asset->get("demo.bin");
|
const std::string p = Asset::get()->get("demo.bin");
|
||||||
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
const std::string filename = p.substr(p.find_last_of("\\/") + 1);
|
||||||
if (demo.recording) {
|
if (demo.recording) {
|
||||||
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "w+b");
|
SDL_IOStream *file = SDL_IOFromFile(p.c_str(), "w+b");
|
||||||
@@ -571,14 +569,14 @@ bool Game::saveDemoFile() {
|
|||||||
SDL_WriteIO(file, &demo.dataFile[i], sizeof(demoKeys_t));
|
SDL_WriteIO(file, &demo.dataFile[i], sizeof(demoKeys_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Writing file " << filename.c_str() << std::endl;
|
std::cout << "Writing file " << filename.c_str() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cerramos el fichero
|
// Cerramos el fichero
|
||||||
SDL_CloseIO(file);
|
SDL_CloseIO(file);
|
||||||
} else {
|
} else {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Error: Unable to save " << filename.c_str() << " file! " << SDL_GetError() << std::endl;
|
std::cout << "Error: Unable to save " << filename.c_str() << " file! " << SDL_GetError() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -964,7 +962,6 @@ void Game::initEnemyFormations() {
|
|||||||
// #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos
|
// #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simetricos
|
||||||
j = 24;
|
j = 24;
|
||||||
enemyFormation[j].numberOfEnemies = 30;
|
enemyFormation[j].numberOfEnemies = 30;
|
||||||
incX = 0;
|
|
||||||
incTime = 5;
|
incTime = 5;
|
||||||
for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) {
|
for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) {
|
||||||
Uint8 half = enemyFormation[j].numberOfEnemies / 2;
|
Uint8 half = enemyFormation[j].numberOfEnemies / 2;
|
||||||
@@ -984,7 +981,6 @@ void Game::initEnemyFormations() {
|
|||||||
// #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos
|
// #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simetricos
|
||||||
j = 25;
|
j = 25;
|
||||||
enemyFormation[j].numberOfEnemies = 30;
|
enemyFormation[j].numberOfEnemies = 30;
|
||||||
incX = BALLOON_WIDTH_1 + 1;
|
|
||||||
incTime = 5;
|
incTime = 5;
|
||||||
for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) {
|
for (int i = 0; i < enemyFormation[j].numberOfEnemies; i++) {
|
||||||
Uint8 half = enemyFormation[j].numberOfEnemies / 2;
|
Uint8 half = enemyFormation[j].numberOfEnemies / 2;
|
||||||
@@ -1374,33 +1370,33 @@ void Game::renderScoreBoard() {
|
|||||||
const int offsetRight = PLAY_AREA_RIGHT - 45;
|
const int offsetRight = PLAY_AREA_RIGHT - 45;
|
||||||
|
|
||||||
// PLAYER1 - SCORE
|
// PLAYER1 - SCORE
|
||||||
textScoreBoard->writeCentered(offsetLeft, offset1, lang->getText(53));
|
textScoreBoard->writeCentered(offsetLeft, offset1, Lang::get()->getText(53));
|
||||||
textScoreBoard->writeCentered(offsetLeft, offset2, updateScoreText(players[0]->getScore()));
|
textScoreBoard->writeCentered(offsetLeft, offset2, updateScoreText(players[0]->getScore()));
|
||||||
|
|
||||||
// PLAYER1 - MULT
|
// PLAYER1 - MULT
|
||||||
textScoreBoard->writeCentered(offsetLeft, offset3, lang->getText(55));
|
textScoreBoard->writeCentered(offsetLeft, offset3, Lang::get()->getText(55));
|
||||||
textScoreBoard->writeCentered(offsetLeft, offset4, std::to_string(players[0]->getScoreMultiplier()).substr(0, 3));
|
textScoreBoard->writeCentered(offsetLeft, offset4, std::to_string(players[0]->getScoreMultiplier()).substr(0, 3));
|
||||||
|
|
||||||
if (numPlayers == 2) {
|
if (numPlayers == 2) {
|
||||||
// PLAYER2 - SCORE
|
// PLAYER2 - SCORE
|
||||||
textScoreBoard->writeCentered(offsetRight, offset1, lang->getText(54));
|
textScoreBoard->writeCentered(offsetRight, offset1, Lang::get()->getText(54));
|
||||||
textScoreBoard->writeCentered(offsetRight, offset2, updateScoreText(players[1]->getScore()));
|
textScoreBoard->writeCentered(offsetRight, offset2, updateScoreText(players[1]->getScore()));
|
||||||
|
|
||||||
// PLAYER2 - MULT
|
// PLAYER2 - MULT
|
||||||
textScoreBoard->writeCentered(offsetRight, offset3, lang->getText(55));
|
textScoreBoard->writeCentered(offsetRight, offset3, Lang::get()->getText(55));
|
||||||
textScoreBoard->writeCentered(offsetRight, offset4, std::to_string(players[1]->getScoreMultiplier()).substr(0, 3));
|
textScoreBoard->writeCentered(offsetRight, offset4, std::to_string(players[1]->getScoreMultiplier()).substr(0, 3));
|
||||||
} else {
|
} else {
|
||||||
// PLAYER2 - SCORE
|
// PLAYER2 - SCORE
|
||||||
textScoreBoard->writeCentered(offsetRight, offset1, lang->getText(54));
|
textScoreBoard->writeCentered(offsetRight, offset1, Lang::get()->getText(54));
|
||||||
textScoreBoard->writeCentered(offsetRight, offset2, "0000000");
|
textScoreBoard->writeCentered(offsetRight, offset2, "0000000");
|
||||||
|
|
||||||
// PLAYER2 - MULT
|
// PLAYER2 - MULT
|
||||||
textScoreBoard->writeCentered(offsetRight, offset3, lang->getText(55));
|
textScoreBoard->writeCentered(offsetRight, offset3, Lang::get()->getText(55));
|
||||||
textScoreBoard->writeCentered(offsetRight, offset4, "1.0");
|
textScoreBoard->writeCentered(offsetRight, offset4, "1.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// STAGE
|
// STAGE
|
||||||
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset1, lang->getText(57) + std::to_string(stage[currentStage].number));
|
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset1, Lang::get()->getText(57) + std::to_string(stage[currentStage].number));
|
||||||
|
|
||||||
// POWERMETER
|
// POWERMETER
|
||||||
powerMeterSprite->setPosY(offset2);
|
powerMeterSprite->setPosY(offset2);
|
||||||
@@ -1411,7 +1407,7 @@ void Game::renderScoreBoard() {
|
|||||||
powerMeterSprite->render();
|
powerMeterSprite->render();
|
||||||
|
|
||||||
// HI-SCORE
|
// HI-SCORE
|
||||||
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset3, lang->getText(56));
|
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset3, Lang::get()->getText(56));
|
||||||
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset4, hiScoreName + updateScoreText(hiScore));
|
textScoreBoard->writeCentered(PLAY_AREA_CENTER_X, offset4, hiScoreName + updateScoreText(hiScore));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1489,12 +1485,12 @@ void Game::updateStage() {
|
|||||||
// Actualiza el estado de muerte
|
// Actualiza el estado de muerte
|
||||||
void Game::updateDeath() {
|
void Game::updateDeath() {
|
||||||
// Comprueba si todos los jugadores estan muertos
|
// Comprueba si todos los jugadores estan muertos
|
||||||
bool allPlayersAreDead = true;
|
bool allDead = true;
|
||||||
for (auto player : players) {
|
for (auto player : players) {
|
||||||
allPlayersAreDead &= (!player->isAlive());
|
allDead &= (!player->isAlive());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allPlayersAreDead) {
|
if (allDead) {
|
||||||
if (deathCounter > 0) {
|
if (deathCounter > 0) {
|
||||||
deathCounter--;
|
deathCounter--;
|
||||||
|
|
||||||
@@ -1611,30 +1607,6 @@ void Game::incBalloonSpeed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrementa la velocidad de los globos
|
|
||||||
void Game::decBalloonSpeed() {
|
|
||||||
// La velocidad solo se decrementa en el modo normal
|
|
||||||
if (difficulty == DIFFICULTY_NORMAL) {
|
|
||||||
if (enemySpeed == BALLOON_SPEED_5) {
|
|
||||||
enemySpeed = BALLOON_SPEED_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (enemySpeed == BALLOON_SPEED_4) {
|
|
||||||
enemySpeed = BALLOON_SPEED_3;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (enemySpeed == BALLOON_SPEED_3) {
|
|
||||||
enemySpeed = BALLOON_SPEED_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (enemySpeed == BALLOON_SPEED_2) {
|
|
||||||
enemySpeed = BALLOON_SPEED_1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBalloonSpeed(enemySpeed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
|
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
|
||||||
void Game::updateBalloonSpeed() {
|
void Game::updateBalloonSpeed() {
|
||||||
const float percent = (float)stage[currentStage].currentPower / (float)stage[currentStage].powerToComplete;
|
const float percent = (float)stage[currentStage].currentPower / (float)stage[currentStage].powerToComplete;
|
||||||
@@ -1764,17 +1736,6 @@ void Game::destroyBalloon(Balloon *balloon) {
|
|||||||
evaluateAndSetMenace();
|
evaluateAndSetMenace();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explosiona todos los globos
|
|
||||||
void Game::popAllBalloons() {
|
|
||||||
for (auto balloon : balloons) {
|
|
||||||
if ((balloon->isEnabled()) && (!balloon->isPopping()) && (!balloon->isBeingCreated())) {
|
|
||||||
popBalloon(balloon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_PlaySound(balloonSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destruye todos los globos
|
// Destruye todos los globos
|
||||||
void Game::destroyAllBalloons() {
|
void Game::destroyAllBalloons() {
|
||||||
for (auto balloon : balloons) {
|
for (auto balloon : balloons) {
|
||||||
@@ -1811,21 +1772,6 @@ void Game::startAllBalloons() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el numero de globos activos
|
|
||||||
Uint8 Game::countBalloons() {
|
|
||||||
Uint8 num = 0;
|
|
||||||
|
|
||||||
for (auto balloon : balloons) {
|
|
||||||
if (balloon->isEnabled()) {
|
|
||||||
if (!balloon->isPopping()) {
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vacia el vector de globos
|
// Vacia el vector de globos
|
||||||
void Game::freeBalloons() {
|
void Game::freeBalloons() {
|
||||||
if (balloons.empty() == false) {
|
if (balloons.empty() == false) {
|
||||||
@@ -2093,7 +2039,7 @@ void Game::freeItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
|
// Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto
|
||||||
void Game::createItemScoreSprite(int x, int y, SmartSprite *sprite) {
|
void Game::createItemScoreSprite(int x, int y, const SmartSprite *sprite) {
|
||||||
SmartSprite *ss = new SmartSprite(nullptr, renderer);
|
SmartSprite *ss = new SmartSprite(nullptr, renderer);
|
||||||
smartSprites.push_back(ss);
|
smartSprites.push_back(ss);
|
||||||
|
|
||||||
@@ -2238,12 +2184,8 @@ void Game::updateDeathSequence() {
|
|||||||
|
|
||||||
// Calcula y establece el valor de amenaza en funcion de los globos activos
|
// Calcula y establece el valor de amenaza en funcion de los globos activos
|
||||||
void Game::evaluateAndSetMenace() {
|
void Game::evaluateAndSetMenace() {
|
||||||
menaceCurrent = 0;
|
menaceCurrent = std::accumulate(balloons.begin(), balloons.end(), Uint8(0),
|
||||||
for (auto balloon : balloons) {
|
[](Uint8 acc, Balloon *b) { return b->isEnabled() ? acc + b->getMenace() : acc; });
|
||||||
if (balloon->isEnabled()) {
|
|
||||||
menaceCurrent += balloon->getMenace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el valor de la variable
|
// Obtiene el valor de la variable
|
||||||
@@ -2457,10 +2399,10 @@ void Game::renderBackground() {
|
|||||||
// Dibuja el juego
|
// Dibuja el juego
|
||||||
void Game::render() {
|
void Game::render() {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibuja los objetos
|
// Dibuja los objetos
|
||||||
renderBackground();
|
renderBackground();
|
||||||
@@ -2483,7 +2425,7 @@ void Game::render() {
|
|||||||
renderFlashEffect();
|
renderFlashEffect();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestiona el nivel de amenaza
|
// Gestiona el nivel de amenaza
|
||||||
@@ -2517,18 +2459,8 @@ void Game::checkGameInput() {
|
|||||||
demo.keys.fireLeft = 0;
|
demo.keys.fireLeft = 0;
|
||||||
demo.keys.fireRight = 0;
|
demo.keys.fireRight = 0;
|
||||||
|
|
||||||
// Comprueba las teclas de cambiar el tamaño de la centana y el modo de video
|
// Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
|
||||||
if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
GlobalInputs::handle();
|
||||||
screen->toggleVideoMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
|
||||||
screen->decWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
|
||||||
screen->incWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modo Demo activo
|
// Modo Demo activo
|
||||||
if (demo.enabled) {
|
if (demo.enabled) {
|
||||||
@@ -2570,7 +2502,7 @@ void Game::checkGameInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Si se pulsa cualquier tecla, se sale del modo demo
|
// Si se pulsa cualquier tecla, se sale del modo demo
|
||||||
if (input->checkAnyInput()) {
|
if (Input::get()->checkAnyInput()) {
|
||||||
section->name = SECTION_PROG_TITLE;
|
section->name = SECTION_PROG_TITLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2588,12 +2520,12 @@ void Game::checkGameInput() {
|
|||||||
for (auto player : players) {
|
for (auto player : players) {
|
||||||
if (player->isAlive()) {
|
if (player->isAlive()) {
|
||||||
// Input a la izquierda
|
// Input a la izquierda
|
||||||
if (input->checkInput(input_left, REPEAT_TRUE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_left, REPEAT_TRUE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
player->setInput(input_left);
|
player->setInput(input_left);
|
||||||
demo.keys.left = 1;
|
demo.keys.left = 1;
|
||||||
} else {
|
} else {
|
||||||
// Input a la derecha
|
// Input a la derecha
|
||||||
if (input->checkInput(input_right, REPEAT_TRUE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_right, REPEAT_TRUE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
player->setInput(input_right);
|
player->setInput(input_right);
|
||||||
demo.keys.right = 1;
|
demo.keys.right = 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -2603,7 +2535,7 @@ void Game::checkGameInput() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Comprueba el input de disparar al centro
|
// Comprueba el input de disparar al centro
|
||||||
if (input->checkInput(input_fire_center, REPEAT_TRUE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_fire_center, REPEAT_TRUE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
if (player->canFire()) {
|
if (player->canFire()) {
|
||||||
player->setInput(input_fire_center);
|
player->setInput(input_fire_center);
|
||||||
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), i);
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_UP, player->isPowerUp(), i);
|
||||||
@@ -2617,7 +2549,7 @@ void Game::checkGameInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba el input de disparar a la izquierda
|
// Comprueba el input de disparar a la izquierda
|
||||||
if (input->checkInput(input_fire_left, REPEAT_TRUE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_fire_left, REPEAT_TRUE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
if (player->canFire()) {
|
if (player->canFire()) {
|
||||||
player->setInput(input_fire_left);
|
player->setInput(input_fire_left);
|
||||||
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), i);
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_LEFT, player->isPowerUp(), i);
|
||||||
@@ -2631,7 +2563,7 @@ void Game::checkGameInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba el input de disparar a la derecha
|
// Comprueba el input de disparar a la derecha
|
||||||
if (input->checkInput(input_fire_right, REPEAT_TRUE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_fire_right, REPEAT_TRUE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
if (player->canFire()) {
|
if (player->canFire()) {
|
||||||
player->setInput(input_fire_right);
|
player->setInput(input_fire_right);
|
||||||
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), i);
|
createBullet(player->getPosX() + (player->getWidth() / 2) - 4, player->getPosY() + (player->getHeight() / 2), BULLET_RIGHT, player->isPowerUp(), i);
|
||||||
@@ -2645,7 +2577,7 @@ void Game::checkGameInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba el input de pausa
|
// Comprueba el input de pausa
|
||||||
if (input->checkInput(input_pause, REPEAT_FALSE, options->input[i].deviceType, options->input[i].id)) {
|
if (Input::get()->checkInput(input_pause, REPEAT_FALSE, Options::inputs[i].deviceType, Options::inputs[i].id)) {
|
||||||
section->subsection = SUBSECTION_GAME_PAUSE;
|
section->subsection = SUBSECTION_GAME_PAUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2668,13 +2600,13 @@ void Game::checkGameInput() {
|
|||||||
void Game::renderMessages() {
|
void Game::renderMessages() {
|
||||||
// GetReady
|
// GetReady
|
||||||
if ((counter < STAGE_COUNTER) && (!demo.enabled)) {
|
if ((counter < STAGE_COUNTER) && (!demo.enabled)) {
|
||||||
textNokiaBig2->write((int)getReadyBitmapPath[counter], PLAY_AREA_CENTER_Y - 8, lang->getText(75), -2);
|
textNokiaBig2->write((int)getReadyBitmapPath[counter], PLAY_AREA_CENTER_Y - 8, Lang::get()->getText(75), -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time Stopped
|
// Time Stopped
|
||||||
if (timeStopped) {
|
if (timeStopped) {
|
||||||
if ((timeStoppedCounter > 100) || (timeStoppedCounter % 10 > 4)) {
|
if ((timeStoppedCounter > 100) || (timeStoppedCounter % 10 > 4)) {
|
||||||
textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, lang->getText(36) + std::to_string(timeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor);
|
textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, Lang::get()->getText(36) + std::to_string(timeStoppedCounter / 10), -1, noColor, 1, shdwTxtColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeStoppedCounter > 100) {
|
if (timeStoppedCounter > 100) {
|
||||||
@@ -2691,27 +2623,27 @@ void Game::renderMessages() {
|
|||||||
// D E M O
|
// D E M O
|
||||||
if (demo.enabled) {
|
if (demo.enabled) {
|
||||||
if (demo.counter % 30 > 14) {
|
if (demo.counter % 30 > 14) {
|
||||||
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, lang->getText(37), 0, noColor, 2, shdwTxtColor);
|
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, Lang::get()->getText(37), 0, noColor, 2, shdwTxtColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// STAGE NUMBER
|
// STAGE NUMBER
|
||||||
if (stageBitmapCounter < STAGE_COUNTER) {
|
if (stageBitmapCounter < STAGE_COUNTER) {
|
||||||
const int stageNum = stage[currentStage].number;
|
const int stageNum = stage[currentStage].number;
|
||||||
std::string text;
|
std::string stageText;
|
||||||
|
|
||||||
if (stageNum == 10) { // Ultima fase
|
if (stageNum == 10) { // Ultima fase
|
||||||
text = lang->getText(79);
|
stageText = Lang::get()->getText(79);
|
||||||
} else { // X fases restantes
|
} else { // X fases restantes
|
||||||
text = std::to_string(11 - stage[currentStage].number) + lang->getText(38);
|
stageText = std::to_string(11 - stage[currentStage].number) + Lang::get()->getText(38);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gameCompleted) { // Escribe el numero de fases restantes
|
if (!gameCompleted) { // Escribe el numero de fases restantes
|
||||||
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 2, shdwTxtColor);
|
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], stageText, -2, noColor, 2, shdwTxtColor);
|
||||||
} else { // Escribe el texto de juego completado
|
} else { // Escribe el texto de juego completado
|
||||||
text = lang->getText(50);
|
stageText = Lang::get()->getText(50);
|
||||||
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], text, -2, noColor, 1, shdwTxtColor);
|
textNokiaBig2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter], stageText, -2, noColor, 1, shdwTxtColor);
|
||||||
textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter] + textNokiaBig2->getCharacterSize() + 2, lang->getText(76), -1, noColor, 1, shdwTxtColor);
|
textNokia2->writeDX(TXT_CENTER, PLAY_AREA_CENTER_X, stageBitmapPath[stageBitmapCounter] + textNokiaBig2->getCharacterSize() + 2, Lang::get()->getText(76), -1, noColor, 1, shdwTxtColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2826,7 +2758,7 @@ bool Game::hasFinished() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Procesa un evento individual
|
// Procesa un evento individual
|
||||||
void Game::handleEvent(SDL_Event *event) {
|
void Game::handleEvent(const SDL_Event *event) {
|
||||||
// SDL_EVENT_QUIT ya lo maneja Director
|
// SDL_EVENT_QUIT ya lo maneja Director
|
||||||
|
|
||||||
if (event->type == SDL_EVENT_WINDOW_FOCUS_LOST) {
|
if (event->type == SDL_EVENT_WINDOW_FOCUS_LOST) {
|
||||||
@@ -2870,6 +2802,9 @@ void Game::updatePausedGame() {
|
|||||||
// Actualiza el contador de ticks
|
// Actualiza el contador de ticks
|
||||||
ticks = SDL_GetTicks();
|
ticks = SDL_GetTicks();
|
||||||
|
|
||||||
|
// Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
|
||||||
|
GlobalInputs::handle();
|
||||||
|
|
||||||
if (leavingPauseMenu) {
|
if (leavingPauseMenu) {
|
||||||
if (pauseCounter > 0) { // El contador está descendiendo
|
if (pauseCounter > 0) { // El contador está descendiendo
|
||||||
const bool a = pauseCounter == 90;
|
const bool a = pauseCounter == 90;
|
||||||
@@ -2922,10 +2857,10 @@ void Game::updatePausedGame() {
|
|||||||
// Dibuja el menu de pausa del juego
|
// Dibuja el menu de pausa del juego
|
||||||
void Game::renderPausedGame() {
|
void Game::renderPausedGame() {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Pinta el escenario
|
// Pinta el escenario
|
||||||
{
|
{
|
||||||
@@ -2957,7 +2892,7 @@ void Game::renderPausedGame() {
|
|||||||
fade->render();
|
fade->render();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa el estado de pausa del juego
|
// Inicializa el estado de pausa del juego
|
||||||
@@ -2983,6 +2918,9 @@ void Game::updateGameOverScreen() {
|
|||||||
// Actualiza el contador de ticks
|
// Actualiza el contador de ticks
|
||||||
ticks = SDL_GetTicks();
|
ticks = SDL_GetTicks();
|
||||||
|
|
||||||
|
// Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
|
||||||
|
GlobalInputs::handle();
|
||||||
|
|
||||||
// Actualiza la lógica del menu
|
// Actualiza la lógica del menu
|
||||||
gameOverMenu->update();
|
gameOverMenu->update();
|
||||||
|
|
||||||
@@ -3032,30 +2970,13 @@ void Game::updateGameOverScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos de la pantalla de game over
|
|
||||||
void Game::checkGameOverEvents() {
|
|
||||||
while (SDL_PollEvent(eventHandler) != 0) {
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (eventHandler->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
break;
|
|
||||||
} else if (eventHandler->type == SDL_EVENT_KEY_DOWN && eventHandler->key.repeat == 0) {
|
|
||||||
if (gameCompleted) {
|
|
||||||
gameOverPostFade = 1;
|
|
||||||
fade->activateFade();
|
|
||||||
JA_PlaySound(itemPickUpSound);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dibuja los elementos de la pantalla de game over
|
// Dibuja los elementos de la pantalla de game over
|
||||||
void Game::renderGameOverScreen() {
|
void Game::renderGameOverScreen() {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibujo
|
// Dibujo
|
||||||
if (!gameCompleted) { // Dibujo de haber perdido la partida
|
if (!gameCompleted) { // Dibujo de haber perdido la partida
|
||||||
@@ -3068,33 +2989,33 @@ void Game::renderGameOverScreen() {
|
|||||||
if (numPlayers == 1) {
|
if (numPlayers == 1) {
|
||||||
// Congratulations!!
|
// Congratulations!!
|
||||||
if (gameCompleted) {
|
if (gameCompleted) {
|
||||||
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 8), lang->getText(50));
|
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 8), Lang::get()->getText(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game Over
|
// Game Over
|
||||||
textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 6), lang->getText(43));
|
textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 6), Lang::get()->getText(43));
|
||||||
|
|
||||||
// Your Score
|
// Your Score
|
||||||
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 3), lang->getText(44) + std::to_string(players[0]->getScore()));
|
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 3), Lang::get()->getText(44) + std::to_string(players[0]->getScore()));
|
||||||
} else {
|
} else {
|
||||||
// Congratulations!!
|
// Congratulations!!
|
||||||
if (gameCompleted) {
|
if (gameCompleted) {
|
||||||
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 9), lang->getText(50));
|
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 9), Lang::get()->getText(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game Over
|
// Game Over
|
||||||
textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 7), lang->getText(43));
|
textBig->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 7), Lang::get()->getText(43));
|
||||||
|
|
||||||
// Player1 Score
|
// Player1 Score
|
||||||
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), lang->getText(77) + std::to_string(players[0]->getScore()));
|
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 4), Lang::get()->getText(77) + std::to_string(players[0]->getScore()));
|
||||||
|
|
||||||
// Player2 Score
|
// Player2 Score
|
||||||
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 2), lang->getText(78) + std::to_string(players[1]->getScore()));
|
text->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_CENTER_Y - (BLOCK * 2), Lang::get()->getText(78) + std::to_string(players[1]->getScore()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue?
|
// Continue?
|
||||||
if (!gameCompleted) { // Solo dibuja el menu de continuar en el caso de no haber completado la partida
|
if (!gameCompleted) { // Solo dibuja el menu de continuar en el caso de no haber completado la partida
|
||||||
text->writeCentered(199, PLAY_AREA_CENTER_Y + BLOCK * 3, lang->getText(45));
|
text->writeCentered(199, PLAY_AREA_CENTER_Y + BLOCK * 3, Lang::get()->getText(45));
|
||||||
gameOverMenu->render();
|
gameOverMenu->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3102,7 +3023,7 @@ void Game::renderGameOverScreen() {
|
|||||||
fade->render();
|
fade->render();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa el estado de game over
|
// Inicializa el estado de game over
|
||||||
@@ -3127,15 +3048,8 @@ bool Game::canPowerBallBeCreated() {
|
|||||||
|
|
||||||
// Calcula el poder actual de los globos en pantalla
|
// Calcula el poder actual de los globos en pantalla
|
||||||
int Game::calculateScreenPower() {
|
int Game::calculateScreenPower() {
|
||||||
int power = 0;
|
return std::accumulate(balloons.begin(), balloons.end(), 0,
|
||||||
|
[](int acc, Balloon *b) { return b->isEnabled() ? acc + b->getPower() : acc; });
|
||||||
for (auto balloon : balloons) {
|
|
||||||
if (balloon->isEnabled()) {
|
|
||||||
power += balloon->getPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return power;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inicializa las variables que contienen puntos de ruta para mover objetos
|
// Inicializa las variables que contienen puntos de ruta para mover objetos
|
||||||
@@ -3167,7 +3081,7 @@ void Game::initPaths() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Letrero de GetReady
|
// Letrero de GetReady
|
||||||
const int size = textNokiaBig2->lenght(lang->getText(75), -2);
|
const int size = textNokiaBig2->lenght(Lang::get()->getText(75), -2);
|
||||||
|
|
||||||
const float start1 = PLAY_AREA_LEFT - size;
|
const float start1 = PLAY_AREA_LEFT - size;
|
||||||
const float finish1 = PLAY_AREA_CENTER_X - (size / 2);
|
const float finish1 = PLAY_AREA_CENTER_X - (size / 2);
|
||||||
@@ -3241,28 +3155,6 @@ bool Game::allPlayersAreDead() {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos que hay en cola
|
|
||||||
void Game::checkEvents() {
|
|
||||||
while (SDL_PollEvent(eventHandler) != 0) {
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (eventHandler->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (eventHandler->type == SDL_EVENT_WINDOW_FOCUS_LOST) {
|
|
||||||
section->subsection = SUBSECTION_GAME_PAUSE;
|
|
||||||
}
|
|
||||||
#ifdef PAUSE
|
|
||||||
else if (eventHandler->type == SDL_EVENT_KEY_DOWN) {
|
|
||||||
if (eventHandler->key.scancode == SDL_SCANCODE_P) {
|
|
||||||
pause = !pause;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elimina todos los objetos contenidos en vectores
|
// Elimina todos los objetos contenidos en vectores
|
||||||
void Game::deleteAllVectorObjects() {
|
void Game::deleteAllVectorObjects() {
|
||||||
for (auto player : players) {
|
for (auto player : players) {
|
||||||
@@ -3291,33 +3183,6 @@ void Game::deleteAllVectorObjects() {
|
|||||||
smartSprites.clear();
|
smartSprites.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recarga las texturas
|
|
||||||
void Game::reloadTextures() {
|
|
||||||
for (auto texture : itemTextures) {
|
|
||||||
texture->reLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto texture : balloonTextures) {
|
|
||||||
texture->reLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto texture : player1Textures) {
|
|
||||||
texture->reLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto texture : player2Textures) {
|
|
||||||
texture->reLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
bulletTexture->reLoad();
|
|
||||||
gameBuildingsTexture->reLoad();
|
|
||||||
gameCloudsTexture->reLoad();
|
|
||||||
gameGrassTexture->reLoad();
|
|
||||||
gamePowerMeterTexture->reLoad();
|
|
||||||
gameSkyColorsTexture->reLoad();
|
|
||||||
gameTextTexture->reLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece la máxima puntuación desde fichero o desde las puntuaciones online
|
// Establece la máxima puntuación desde fichero o desde las puntuaciones online
|
||||||
void Game::setHiScore() {
|
void Game::setHiScore() {
|
||||||
// Carga el fichero de puntos
|
// Carga el fichero de puntos
|
||||||
@@ -5,18 +5,14 @@
|
|||||||
#include <string> // for string, basic_string
|
#include <string> // for string, basic_string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "utils.h" // for demoKeys_t, color_t
|
#include "utils/utils.h" // for demoKeys_t, color_t
|
||||||
class Asset;
|
|
||||||
class Balloon;
|
class Balloon;
|
||||||
class Bullet;
|
class Bullet;
|
||||||
class Fade;
|
class Fade;
|
||||||
class Input;
|
|
||||||
class Item;
|
class Item;
|
||||||
class Lang;
|
|
||||||
class Menu;
|
class Menu;
|
||||||
class MovingSprite;
|
class MovingSprite;
|
||||||
class Player;
|
class Player;
|
||||||
class Screen;
|
|
||||||
class SmartSprite;
|
class SmartSprite;
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Text;
|
class Text;
|
||||||
@@ -131,10 +127,6 @@ class Game {
|
|||||||
|
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
Lang *lang; // Objeto para gestionar los textos en diferentes idiomas
|
|
||||||
Input *input; // Manejador de entrada
|
|
||||||
section_t *section; // Seccion actual dentro del juego
|
section_t *section; // Seccion actual dentro del juego
|
||||||
|
|
||||||
std::vector<Player *> players; // Vector con los jugadores
|
std::vector<Player *> players; // Vector con los jugadores
|
||||||
@@ -245,7 +237,6 @@ class Game {
|
|||||||
Uint8 difficulty; // Dificultad del juego
|
Uint8 difficulty; // Dificultad del juego
|
||||||
float difficultyScoreMultiplier; // Multiplicador de puntos en función de la dificultad
|
float difficultyScoreMultiplier; // Multiplicador de puntos en función de la dificultad
|
||||||
color_t difficultyColor; // Color asociado a la dificultad
|
color_t difficultyColor; // Color asociado a la dificultad
|
||||||
struct options_t *options; // Variable con todas las variables de las opciones del programa
|
|
||||||
Uint8 onePlayerControl; // Variable para almacenar el valor de las opciones
|
Uint8 onePlayerControl; // Variable para almacenar el valor de las opciones
|
||||||
enemyFormation_t enemyFormation[NUMBER_OF_ENEMY_FORMATIONS]; // Vector con todas las formaciones enemigas
|
enemyFormation_t enemyFormation[NUMBER_OF_ENEMY_FORMATIONS]; // Vector con todas las formaciones enemigas
|
||||||
enemyPool_t enemyPool[10]; // Variable con los diferentes conjuntos de formaciones enemigas
|
enemyPool_t enemyPool[10]; // Variable con los diferentes conjuntos de formaciones enemigas
|
||||||
@@ -268,9 +259,6 @@ class Game {
|
|||||||
// Dibuja el juego
|
// Dibuja el juego
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
// Comprueba los eventos que hay en cola
|
|
||||||
void checkEvents();
|
|
||||||
|
|
||||||
// Inicializa las variables necesarias para la sección 'Game'
|
// Inicializa las variables necesarias para la sección 'Game'
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
@@ -349,9 +337,6 @@ class Game {
|
|||||||
// Incrementa la velocidad de los globos
|
// Incrementa la velocidad de los globos
|
||||||
void incBalloonSpeed();
|
void incBalloonSpeed();
|
||||||
|
|
||||||
// Decrementa la velocidad de los globos
|
|
||||||
void decBalloonSpeed();
|
|
||||||
|
|
||||||
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
|
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
|
||||||
void updateBalloonSpeed();
|
void updateBalloonSpeed();
|
||||||
|
|
||||||
@@ -361,9 +346,6 @@ class Game {
|
|||||||
// Explosiona un globo. Lo destruye
|
// Explosiona un globo. Lo destruye
|
||||||
void destroyBalloon(Balloon *balloon);
|
void destroyBalloon(Balloon *balloon);
|
||||||
|
|
||||||
// Explosiona todos los globos
|
|
||||||
void popAllBalloons();
|
|
||||||
|
|
||||||
// Destruye todos los globos
|
// Destruye todos los globos
|
||||||
void destroyAllBalloons();
|
void destroyAllBalloons();
|
||||||
|
|
||||||
@@ -373,9 +355,6 @@ class Game {
|
|||||||
// Pone en marcha todos los globos
|
// Pone en marcha todos los globos
|
||||||
void startAllBalloons();
|
void startAllBalloons();
|
||||||
|
|
||||||
// Obtiene el numero de globos activos
|
|
||||||
Uint8 countBalloons();
|
|
||||||
|
|
||||||
// Vacia el vector de globos
|
// Vacia el vector de globos
|
||||||
void freeBalloons();
|
void freeBalloons();
|
||||||
|
|
||||||
@@ -416,7 +395,7 @@ class Game {
|
|||||||
void freeItems();
|
void freeItems();
|
||||||
|
|
||||||
// Crea un objeto SmartSprite
|
// Crea un objeto SmartSprite
|
||||||
void createItemScoreSprite(int x, int y, SmartSprite *sprite);
|
void createItemScoreSprite(int x, int y, const SmartSprite *sprite);
|
||||||
|
|
||||||
// Vacia el vector de smartsprites
|
// Vacia el vector de smartsprites
|
||||||
void freeSmartSprites();
|
void freeSmartSprites();
|
||||||
@@ -514,9 +493,6 @@ class Game {
|
|||||||
// Inicializa el estado de game over
|
// Inicializa el estado de game over
|
||||||
void enterGameOverScreen();
|
void enterGameOverScreen();
|
||||||
|
|
||||||
// Comprueba los eventos de la pantalla de game over
|
|
||||||
void checkGameOverEvents();
|
|
||||||
|
|
||||||
// Indica si se puede crear una powerball
|
// Indica si se puede crear una powerball
|
||||||
bool canPowerBallBeCreated();
|
bool canPowerBallBeCreated();
|
||||||
|
|
||||||
@@ -538,15 +514,12 @@ class Game {
|
|||||||
// Elimina todos los objetos contenidos en vectores
|
// Elimina todos los objetos contenidos en vectores
|
||||||
void deleteAllVectorObjects();
|
void deleteAllVectorObjects();
|
||||||
|
|
||||||
// Recarga las texturas
|
|
||||||
void reloadTextures();
|
|
||||||
|
|
||||||
// Establece la máxima puntuación desde fichero o desde las puntuaciones online
|
// Establece la máxima puntuación desde fichero o desde las puntuaciones online
|
||||||
void setHiScore();
|
void setHiScore();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Game(int numPlayers, int currentStage, SDL_Renderer *renderer, Screen *screen, Asset *asset, Lang *lang, Input *input, bool demo, options_t *options, section_t *section);
|
Game(int numPlayers, int currentStage, SDL_Renderer *renderer, bool demo, section_t *section);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Game();
|
~Game();
|
||||||
@@ -561,5 +534,5 @@ class Game {
|
|||||||
bool hasFinished() const;
|
bool hasFinished() const;
|
||||||
|
|
||||||
// Procesa un evento
|
// Procesa un evento
|
||||||
void handleEvent(SDL_Event *event);
|
void handleEvent(const SDL_Event *event);
|
||||||
};
|
};
|
||||||
574
source/game/options.cpp
Normal file
574
source/game/options.cpp
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
#include "game/options.hpp"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/input/input.h" // for INPUT_USE_KEYBOARD, INPUT_USE_GAMECONTROLLER
|
||||||
|
#include "core/locale/lang.h" // for MAX_LANGUAGES, en_UK
|
||||||
|
#include "external/fkyaml_node.hpp" // for fkyaml::node
|
||||||
|
#include "utils/utils.h" // for boolToString
|
||||||
|
|
||||||
|
namespace Options {
|
||||||
|
|
||||||
|
// --- Variables globales ---
|
||||||
|
Window window;
|
||||||
|
Video video;
|
||||||
|
Audio audio;
|
||||||
|
Loading loading;
|
||||||
|
Settings settings;
|
||||||
|
std::vector<input_t> inputs;
|
||||||
|
|
||||||
|
std::vector<PostFXPreset> postfx_presets;
|
||||||
|
std::string postfx_file_path;
|
||||||
|
int current_postfx_preset = 0;
|
||||||
|
|
||||||
|
std::vector<CrtPiPreset> crtpi_presets;
|
||||||
|
std::string crtpi_file_path;
|
||||||
|
int current_crtpi_preset = 0;
|
||||||
|
|
||||||
|
// --- Helpers locals ---
|
||||||
|
namespace {
|
||||||
|
void parseBoolField(const fkyaml::node &node, const std::string &key, bool &target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseIntField(const fkyaml::node &node, const std::string &key, int &target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseStringField(const fkyaml::node &node, const std::string &key, std::string &target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadWindowFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("window")) { return; }
|
||||||
|
const auto &win = yaml["window"];
|
||||||
|
parseIntField(win, "zoom", window.zoom);
|
||||||
|
if (window.zoom < 1 || window.zoom > window.max_zoom) {
|
||||||
|
window.zoom = Defaults::Window::ZOOM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadVideoFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("video")) { return; }
|
||||||
|
const auto &vid = yaml["video"];
|
||||||
|
|
||||||
|
parseBoolField(vid, "fullscreen", video.fullscreen);
|
||||||
|
parseBoolField(vid, "vsync", video.vsync);
|
||||||
|
parseBoolField(vid, "integer_scale", video.integer_scale);
|
||||||
|
if (vid.contains("scale_mode")) {
|
||||||
|
try {
|
||||||
|
video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>());
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid.contains("gpu")) {
|
||||||
|
const auto &gpu = vid["gpu"];
|
||||||
|
parseBoolField(gpu, "acceleration", video.gpu.acceleration);
|
||||||
|
parseStringField(gpu, "preferred_driver", video.gpu.preferred_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid.contains("supersampling")) {
|
||||||
|
const auto &ss = vid["supersampling"];
|
||||||
|
parseBoolField(ss, "enabled", video.supersampling.enabled);
|
||||||
|
parseBoolField(ss, "linear_upscale", video.supersampling.linear_upscale);
|
||||||
|
parseIntField(ss, "downscale_algo", video.supersampling.downscale_algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid.contains("shader")) {
|
||||||
|
const auto &sh = vid["shader"];
|
||||||
|
parseBoolField(sh, "enabled", video.shader.enabled);
|
||||||
|
if (sh.contains("current_shader")) {
|
||||||
|
try {
|
||||||
|
auto s = sh["current_shader"].get_value<std::string>();
|
||||||
|
video.shader.current_shader = (s == "crtpi" || s == "CRTPI")
|
||||||
|
? Rendering::ShaderType::CRTPI
|
||||||
|
: Rendering::ShaderType::POSTFX;
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
parseStringField(sh, "current_postfx_preset", video.shader.current_postfx_preset_name);
|
||||||
|
parseStringField(sh, "current_crtpi_preset", video.shader.current_crtpi_preset_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAudioFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("audio")) { return; }
|
||||||
|
const auto &aud = yaml["audio"];
|
||||||
|
|
||||||
|
parseBoolField(aud, "enabled", audio.enabled);
|
||||||
|
if (aud.contains("volume")) {
|
||||||
|
try {
|
||||||
|
audio.volume = std::clamp(aud["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (aud.contains("music")) {
|
||||||
|
const auto &mus = aud["music"];
|
||||||
|
parseBoolField(mus, "enabled", audio.music.enabled);
|
||||||
|
if (mus.contains("volume")) {
|
||||||
|
try {
|
||||||
|
audio.music.volume = std::clamp(mus["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aud.contains("sound")) {
|
||||||
|
const auto &snd = aud["sound"];
|
||||||
|
parseBoolField(snd, "enabled", audio.sound.enabled);
|
||||||
|
if (snd.contains("volume")) {
|
||||||
|
try {
|
||||||
|
audio.sound.volume = std::clamp(snd["volume"].get_value<int>(), 0, 100);
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadLoadingFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("loading")) { return; }
|
||||||
|
const auto &ld = yaml["loading"];
|
||||||
|
parseBoolField(ld, "show", loading.show);
|
||||||
|
parseBoolField(ld, "show_resource_name", loading.show_resource_name);
|
||||||
|
parseBoolField(ld, "wait_for_input", loading.wait_for_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadSettingsFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("settings")) { return; }
|
||||||
|
const auto &st = yaml["settings"];
|
||||||
|
parseIntField(st, "difficulty", settings.difficulty);
|
||||||
|
parseIntField(st, "language", settings.language);
|
||||||
|
if (settings.language < 0 || settings.language > MAX_LANGUAGES) {
|
||||||
|
settings.language = en_UK;
|
||||||
|
}
|
||||||
|
if (st.contains("palette")) {
|
||||||
|
try {
|
||||||
|
settings.palette = static_cast<palette_e>(st["palette"].get_value<int>());
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
parseIntField(st, "player_selected", settings.player_selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadInputsFromYaml(const fkyaml::node &yaml) {
|
||||||
|
if (!yaml.contains("input") || inputs.size() < 2) { return; }
|
||||||
|
const auto &ins = yaml["input"];
|
||||||
|
size_t i = 0;
|
||||||
|
for (const auto &entry : ins) {
|
||||||
|
if (i >= inputs.size()) { break; }
|
||||||
|
if (entry.contains("device_type")) {
|
||||||
|
try {
|
||||||
|
inputs[i].deviceType = static_cast<Uint8>(entry["device_type"].get_value<int>());
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// --- Funciones públiques ---
|
||||||
|
|
||||||
|
void setConfigFile(const std::string &file_path) {
|
||||||
|
settings.config_file = file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
// Reinicia structs a defaults (els member-initializers ho fan sols).
|
||||||
|
window = Window{};
|
||||||
|
video = Video{};
|
||||||
|
audio = Audio{};
|
||||||
|
loading = Loading{};
|
||||||
|
|
||||||
|
// Preserva config_file si ja s'ha establert abans.
|
||||||
|
const std::string PREV_CONFIG_FILE = settings.config_file;
|
||||||
|
settings = Settings{};
|
||||||
|
settings.config_file = PREV_CONFIG_FILE;
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En Emscripten la ventana la gestiona el navegador
|
||||||
|
window.zoom = 4;
|
||||||
|
video.fullscreen = false;
|
||||||
|
video.integer_scale = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dispositius d'entrada per defecte
|
||||||
|
inputs.clear();
|
||||||
|
input_t kb;
|
||||||
|
kb.id = 0;
|
||||||
|
kb.name = "KEYBOARD";
|
||||||
|
kb.deviceType = INPUT_USE_KEYBOARD;
|
||||||
|
inputs.push_back(kb);
|
||||||
|
|
||||||
|
input_t gc;
|
||||||
|
gc.id = 0;
|
||||||
|
gc.name = "GAME CONTROLLER";
|
||||||
|
gc.deviceType = INPUT_USE_GAMECONTROLLER;
|
||||||
|
inputs.push_back(gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadFromFile() -> bool {
|
||||||
|
init();
|
||||||
|
|
||||||
|
std::ifstream file(settings.config_file);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
// Primera execució: crea el YAML amb defaults.
|
||||||
|
return saveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string CONTENT((std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(CONTENT);
|
||||||
|
|
||||||
|
int file_version = 0;
|
||||||
|
if (yaml.contains("config_version")) {
|
||||||
|
try {
|
||||||
|
file_version = yaml["config_version"].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (file_version != Settings::CURRENT_CONFIG_VERSION) {
|
||||||
|
std::cout << "Config version " << file_version
|
||||||
|
<< " != expected " << Settings::CURRENT_CONFIG_VERSION
|
||||||
|
<< ". Recreating defaults.\n";
|
||||||
|
init();
|
||||||
|
return saveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadWindowFromYaml(yaml);
|
||||||
|
loadVideoFromYaml(yaml);
|
||||||
|
loadAudioFromYaml(yaml);
|
||||||
|
loadLoadingFromYaml(yaml);
|
||||||
|
loadSettingsFromYaml(yaml);
|
||||||
|
loadInputsFromYaml(yaml);
|
||||||
|
|
||||||
|
} catch (const fkyaml::exception &e) {
|
||||||
|
std::cout << "Error parsing YAML config: " << e.what() << ". Using defaults.\n";
|
||||||
|
init();
|
||||||
|
return saveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto saveToFile() -> bool {
|
||||||
|
if (settings.config_file.empty()) { return false; }
|
||||||
|
std::ofstream file(settings.config_file);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cout << "Error: " << settings.config_file << " can't be opened for writing\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "# Coffee Crisis - Configuration file\n";
|
||||||
|
file << "# Auto-generated, managed by the game.\n\n";
|
||||||
|
|
||||||
|
file << "config_version: " << settings.config_version << "\n\n";
|
||||||
|
|
||||||
|
// WINDOW
|
||||||
|
file << "# WINDOW\n";
|
||||||
|
file << "window:\n";
|
||||||
|
file << " zoom: " << window.zoom << "\n";
|
||||||
|
file << " max_zoom: " << window.max_zoom << "\n\n";
|
||||||
|
|
||||||
|
// VIDEO
|
||||||
|
file << "# VIDEO\n";
|
||||||
|
file << "video:\n";
|
||||||
|
file << " fullscreen: " << boolToString(video.fullscreen) << "\n";
|
||||||
|
file << " vsync: " << boolToString(video.vsync) << "\n";
|
||||||
|
file << " integer_scale: " << boolToString(video.integer_scale) << "\n";
|
||||||
|
file << " scale_mode: " << static_cast<int>(video.scale_mode)
|
||||||
|
<< " # " << static_cast<int>(SDL_SCALEMODE_NEAREST) << ": nearest, "
|
||||||
|
<< static_cast<int>(SDL_SCALEMODE_LINEAR) << ": linear\n";
|
||||||
|
file << " gpu:\n";
|
||||||
|
file << " acceleration: " << boolToString(video.gpu.acceleration) << "\n";
|
||||||
|
file << " preferred_driver: \"" << video.gpu.preferred_driver << "\"\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 << " shader:\n";
|
||||||
|
file << " enabled: " << boolToString(video.shader.enabled) << "\n";
|
||||||
|
file << " current_shader: "
|
||||||
|
<< (video.shader.current_shader == Rendering::ShaderType::CRTPI ? "crtpi" : "postfx")
|
||||||
|
<< "\n";
|
||||||
|
file << " current_postfx_preset: \"" << video.shader.current_postfx_preset_name << "\"\n";
|
||||||
|
file << " current_crtpi_preset: \"" << video.shader.current_crtpi_preset_name << "\"\n\n";
|
||||||
|
|
||||||
|
// AUDIO
|
||||||
|
file << "# AUDIO (volume range: 0..100)\n";
|
||||||
|
file << "audio:\n";
|
||||||
|
file << " enabled: " << boolToString(audio.enabled) << "\n";
|
||||||
|
file << " volume: " << audio.volume << "\n";
|
||||||
|
file << " music:\n";
|
||||||
|
file << " enabled: " << boolToString(audio.music.enabled) << "\n";
|
||||||
|
file << " volume: " << audio.music.volume << "\n";
|
||||||
|
file << " sound:\n";
|
||||||
|
file << " enabled: " << boolToString(audio.sound.enabled) << "\n";
|
||||||
|
file << " volume: " << audio.sound.volume << "\n\n";
|
||||||
|
|
||||||
|
// LOADING
|
||||||
|
file << "# LOADING SCREEN\n";
|
||||||
|
file << "loading:\n";
|
||||||
|
file << " show: " << boolToString(loading.show) << "\n";
|
||||||
|
file << " show_resource_name: " << boolToString(loading.show_resource_name) << "\n";
|
||||||
|
file << " wait_for_input: " << boolToString(loading.wait_for_input) << "\n\n";
|
||||||
|
|
||||||
|
// SETTINGS
|
||||||
|
file << "# SETTINGS\n";
|
||||||
|
file << "settings:\n";
|
||||||
|
file << " difficulty: " << settings.difficulty << "\n";
|
||||||
|
file << " language: " << settings.language << "\n";
|
||||||
|
file << " palette: " << static_cast<int>(settings.palette) << "\n";
|
||||||
|
file << " player_selected: " << settings.player_selected << "\n\n";
|
||||||
|
|
||||||
|
// INPUT
|
||||||
|
file << "# INPUT DEVICES (device_type: "
|
||||||
|
<< static_cast<int>(INPUT_USE_KEYBOARD) << "=KEYBOARD, "
|
||||||
|
<< static_cast<int>(INPUT_USE_GAMECONTROLLER) << "=GAMECONTROLLER)\n";
|
||||||
|
file << "input:\n";
|
||||||
|
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||||
|
file << " - slot: " << i << "\n";
|
||||||
|
file << " device_type: " << static_cast<int>(inputs[i].deviceType) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Presets de shaders (postfx.yaml / crtpi.yaml)
|
||||||
|
//
|
||||||
|
// Els defaults viuen en una única font (defaultPostFXPresets / defaultCrtPiPresets).
|
||||||
|
// Generem el YAML a partir d'ells el primer cop i els usem també com a
|
||||||
|
// fallback si el YAML és absent o corrupte. Si algú toca els valors, ho fa
|
||||||
|
// en un sol lloc.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void parseFloatField(const fkyaml::node &node, const std::string &key, float &target) {
|
||||||
|
if (node.contains(key)) {
|
||||||
|
try {
|
||||||
|
target = node[key].get_value<float>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultPostFXPresets() -> const std::vector<PostFXPreset> & {
|
||||||
|
static const std::vector<PostFXPreset> DEFAULTS = {
|
||||||
|
{"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F},
|
||||||
|
{"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F},
|
||||||
|
{"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F},
|
||||||
|
{"SCANLINES", 0.0F, 0.8F},
|
||||||
|
{"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F},
|
||||||
|
{"CRT LIVE", 0.5F, 0.6F, 0.3F, 0.3F, 0.4F, 0.3F, 0.4F, 0.8F},
|
||||||
|
};
|
||||||
|
return DEFAULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto defaultCrtPiPresets() -> const std::vector<CrtPiPreset> & {
|
||||||
|
static const std::vector<CrtPiPreset> DEFAULTS = {
|
||||||
|
{"Default", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, false, false},
|
||||||
|
{"Curved", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, true, true, true, false},
|
||||||
|
{"Sharp", 6.0F, 0.12F, 3.5F, 2.4F, 2.2F, 0.80F, 0.05F, 0.10F, 2, true, false, true, false, true},
|
||||||
|
{"Minimal", 8.0F, 0.05F, 2.0F, 2.4F, 2.2F, 1.00F, 0.0F, 0.0F, 0, true, false, false, false, false},
|
||||||
|
};
|
||||||
|
return DEFAULTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePostFXDefaults(std::ostream &out) {
|
||||||
|
out << "# Coffee Crisis - PostFX Shader Presets\n\n";
|
||||||
|
out << "presets:\n";
|
||||||
|
for (const auto &p : defaultPostFXPresets()) {
|
||||||
|
out << " - name: \"" << p.name << "\"\n";
|
||||||
|
out << " vignette: " << p.vignette << "\n";
|
||||||
|
out << " scanlines: " << p.scanlines << "\n";
|
||||||
|
out << " chroma: " << p.chroma << "\n";
|
||||||
|
out << " mask: " << p.mask << "\n";
|
||||||
|
out << " gamma: " << p.gamma << "\n";
|
||||||
|
out << " curvature: " << p.curvature << "\n";
|
||||||
|
out << " bleeding: " << p.bleeding << "\n";
|
||||||
|
out << " flicker: " << p.flicker << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCrtPiDefaults(std::ostream &out) {
|
||||||
|
out << "# Coffee Crisis - CrtPi Shader Presets\n\n";
|
||||||
|
out << "presets:\n";
|
||||||
|
for (const auto &p : defaultCrtPiPresets()) {
|
||||||
|
out << " - name: \"" << p.name << "\"\n";
|
||||||
|
out << " scanline_weight: " << p.scanline_weight << "\n";
|
||||||
|
out << " scanline_gap_brightness: " << p.scanline_gap_brightness << "\n";
|
||||||
|
out << " bloom_factor: " << p.bloom_factor << "\n";
|
||||||
|
out << " input_gamma: " << p.input_gamma << "\n";
|
||||||
|
out << " output_gamma: " << p.output_gamma << "\n";
|
||||||
|
out << " mask_brightness: " << p.mask_brightness << "\n";
|
||||||
|
out << " curvature_x: " << p.curvature_x << "\n";
|
||||||
|
out << " curvature_y: " << p.curvature_y << "\n";
|
||||||
|
out << " mask_type: " << p.mask_type << "\n";
|
||||||
|
out << " enable_scanlines: " << boolToString(p.enable_scanlines) << "\n";
|
||||||
|
out << " enable_multisample: " << boolToString(p.enable_multisample) << "\n";
|
||||||
|
out << " enable_gamma: " << boolToString(p.enable_gamma) << "\n";
|
||||||
|
out << " enable_curvature: " << boolToString(p.enable_curvature) << "\n";
|
||||||
|
out << " enable_sharper: " << boolToString(p.enable_sharper) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void setPostFXFile(const std::string &path) {
|
||||||
|
postfx_file_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadPostFXFromFile() -> bool {
|
||||||
|
postfx_presets.clear();
|
||||||
|
current_postfx_preset = 0;
|
||||||
|
|
||||||
|
std::ifstream file(postfx_file_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
// No existeix: escriu el YAML a partir dels defaults i copia'ls a memòria.
|
||||||
|
std::ofstream out(postfx_file_path);
|
||||||
|
if (out.is_open()) {
|
||||||
|
writePostFXDefaults(out);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
postfx_presets = defaultPostFXPresets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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")) {
|
||||||
|
for (const auto &p : yaml["presets"]) {
|
||||||
|
PostFXPreset preset;
|
||||||
|
if (p.contains("name")) {
|
||||||
|
try {
|
||||||
|
preset.name = p["name"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
parseFloatField(p, "vignette", preset.vignette);
|
||||||
|
parseFloatField(p, "scanlines", preset.scanlines);
|
||||||
|
parseFloatField(p, "chroma", preset.chroma);
|
||||||
|
parseFloatField(p, "mask", preset.mask);
|
||||||
|
parseFloatField(p, "gamma", preset.gamma);
|
||||||
|
parseFloatField(p, "curvature", preset.curvature);
|
||||||
|
parseFloatField(p, "bleeding", preset.bleeding);
|
||||||
|
parseFloatField(p, "flicker", preset.flicker);
|
||||||
|
postfx_presets.push_back(preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "PostFX loaded: " << postfx_presets.size() << " preset(s)\n";
|
||||||
|
} catch (const fkyaml::exception &e) {
|
||||||
|
std::cout << "Error parsing PostFX YAML: " << e.what() << ". Using defaults.\n";
|
||||||
|
postfx_presets = defaultPostFXPresets();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postfx_presets.empty()) {
|
||||||
|
postfx_presets = defaultPostFXPresets();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCrtPiFile(const std::string &path) {
|
||||||
|
crtpi_file_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadCrtPiFromFile() -> bool {
|
||||||
|
crtpi_presets.clear();
|
||||||
|
current_crtpi_preset = 0;
|
||||||
|
|
||||||
|
std::ifstream file(crtpi_file_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::ofstream out(crtpi_file_path);
|
||||||
|
if (out.is_open()) {
|
||||||
|
writeCrtPiDefaults(out);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
crtpi_presets = defaultCrtPiPresets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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")) {
|
||||||
|
for (const auto &p : yaml["presets"]) {
|
||||||
|
CrtPiPreset preset;
|
||||||
|
if (p.contains("name")) {
|
||||||
|
try {
|
||||||
|
preset.name = p["name"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if (p.contains("mask_type")) {
|
||||||
|
try {
|
||||||
|
preset.mask_type = p["mask_type"].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("enable_scanlines")) {
|
||||||
|
try {
|
||||||
|
preset.enable_scanlines = p["enable_scanlines"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("enable_multisample")) {
|
||||||
|
try {
|
||||||
|
preset.enable_multisample = p["enable_multisample"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("enable_gamma")) {
|
||||||
|
try {
|
||||||
|
preset.enable_gamma = p["enable_gamma"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("enable_curvature")) {
|
||||||
|
try {
|
||||||
|
preset.enable_curvature = p["enable_curvature"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("enable_sharper")) {
|
||||||
|
try {
|
||||||
|
preset.enable_sharper = p["enable_sharper"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
crtpi_presets.push_back(preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "CrtPi loaded: " << crtpi_presets.size() << " preset(s)\n";
|
||||||
|
} catch (const fkyaml::exception &e) {
|
||||||
|
std::cout << "Error parsing CrtPi YAML: " << e.what() << ". Using defaults.\n";
|
||||||
|
crtpi_presets = defaultCrtPiPresets();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crtpi_presets.empty()) {
|
||||||
|
crtpi_presets = defaultCrtPiPresets();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Options
|
||||||
153
source/game/options.hpp
Normal file
153
source/game/options.hpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/rendering/shader_backend.hpp" // for Rendering::ShaderType
|
||||||
|
#include "game/defaults.hpp"
|
||||||
|
#include "utils/utils.h" // for input_t, palette_e
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Opciones del programa, alineades amb coffee_crisis_arcade_edition.
|
||||||
|
// L'estat viu en globals dins el namespace Options:: (window, video, audio,
|
||||||
|
// loading, settings, inputs). La persistència usa fkyaml i es guarda a
|
||||||
|
// config.yaml dins la carpeta de configuració del sistema.
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
namespace Options {
|
||||||
|
|
||||||
|
struct Window {
|
||||||
|
std::string caption = Defaults::Window::CAPTION;
|
||||||
|
int zoom = Defaults::Window::ZOOM;
|
||||||
|
int max_zoom = Defaults::Window::MAX_ZOOM;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPU {
|
||||||
|
bool acceleration = Defaults::Video::GPU_ACCELERATION;
|
||||||
|
std::string preferred_driver = Defaults::Video::GPU_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 = "CRT";
|
||||||
|
std::string current_crtpi_preset_name = "Default";
|
||||||
|
int current_postfx_preset = 0;
|
||||||
|
int current_crtpi_preset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Video {
|
||||||
|
SDL_ScaleMode scale_mode = Defaults::Video::SCALE_MODE;
|
||||||
|
bool fullscreen = Defaults::Video::FULLSCREEN;
|
||||||
|
bool vsync = Defaults::Video::VSYNC;
|
||||||
|
bool integer_scale = Defaults::Video::INTEGER_SCALE;
|
||||||
|
GPU gpu;
|
||||||
|
Supersampling supersampling;
|
||||||
|
ShaderConfig shader;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Music {
|
||||||
|
bool enabled = Defaults::Music::ENABLED;
|
||||||
|
int volume = Defaults::Music::VOLUME;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sound {
|
||||||
|
bool enabled = Defaults::Sound::ENABLED;
|
||||||
|
int volume = Defaults::Sound::VOLUME;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Audio {
|
||||||
|
bool enabled = Defaults::Audio::ENABLED;
|
||||||
|
int volume = Defaults::Audio::VOLUME;
|
||||||
|
Music music;
|
||||||
|
Sound sound;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Loading {
|
||||||
|
bool show = Defaults::Loading::SHOW;
|
||||||
|
bool show_resource_name = Defaults::Loading::SHOW_RESOURCE_NAME;
|
||||||
|
bool wait_for_input = Defaults::Loading::WAIT_FOR_INPUT;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
static constexpr int CURRENT_CONFIG_VERSION = 1;
|
||||||
|
int config_version = CURRENT_CONFIG_VERSION;
|
||||||
|
int difficulty = Defaults::Settings::DIFFICULTY;
|
||||||
|
int language = Defaults::Settings::LANGUAGE;
|
||||||
|
palette_e palette = Defaults::Settings::PALETTE;
|
||||||
|
bool console = false;
|
||||||
|
int player_selected = 0;
|
||||||
|
std::string config_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preset PostFX
|
||||||
|
struct PostFXPreset {
|
||||||
|
std::string name;
|
||||||
|
float vignette{0.0F};
|
||||||
|
float scanlines{0.0F};
|
||||||
|
float chroma{0.0F};
|
||||||
|
float mask{0.0F};
|
||||||
|
float gamma{0.0F};
|
||||||
|
float curvature{0.0F};
|
||||||
|
float bleeding{0.0F};
|
||||||
|
float flicker{0.0F};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preset CrtPi
|
||||||
|
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};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Variables globales ---
|
||||||
|
extern Window window;
|
||||||
|
extern Video video;
|
||||||
|
extern Audio audio;
|
||||||
|
extern Loading loading;
|
||||||
|
extern Settings settings;
|
||||||
|
extern std::vector<input_t> inputs; // [0]=KEYBOARD, [1]=GAMECONTROLLER per defecte
|
||||||
|
|
||||||
|
// Presets de shaders (carregats de postfx.yaml / crtpi.yaml al config folder)
|
||||||
|
extern std::vector<PostFXPreset> postfx_presets;
|
||||||
|
extern std::string postfx_file_path;
|
||||||
|
extern int current_postfx_preset; // Índex dins `postfx_presets`
|
||||||
|
|
||||||
|
extern std::vector<CrtPiPreset> crtpi_presets;
|
||||||
|
extern std::string crtpi_file_path;
|
||||||
|
extern int current_crtpi_preset; // Índex dins `crtpi_presets`
|
||||||
|
|
||||||
|
// --- Funciones ---
|
||||||
|
void init(); // Reinicia a defaults i omple `inputs`
|
||||||
|
void setConfigFile(const std::string &file_path); // Ruta del config.yaml
|
||||||
|
auto loadFromFile() -> bool; // Carrega el YAML; si no existeix, crea'l amb defaults
|
||||||
|
auto saveToFile() -> bool; // Guarda el YAML
|
||||||
|
|
||||||
|
// Presets de shaders. Si el fitxer no existeix, l'escriu amb els defaults
|
||||||
|
// i deixa els presets carregats en memòria.
|
||||||
|
void setPostFXFile(const std::string &path);
|
||||||
|
auto loadPostFXFromFile() -> bool;
|
||||||
|
void setCrtPiFile(const std::string &path);
|
||||||
|
auto loadCrtPiFromFile() -> bool;
|
||||||
|
|
||||||
|
} // namespace Options
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "instructions.h"
|
#include "game/scenes/instructions.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
@@ -6,26 +6,22 @@
|
|||||||
#include <iostream> // for char_traits, basic_ostream, operator<<
|
#include <iostream> // for char_traits, basic_ostream, operator<<
|
||||||
#include <string> // for basic_string
|
#include <string> // for basic_string
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/audio/jail_audio.hpp" // for JA_StopMusic
|
||||||
#include "const.h" // for shdwTxtColor, GAMECANVAS_CENTER_X, GAME...
|
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||||
#include "input.h" // for Input, REPEAT_FALSE, inputs_e
|
#include "core/input/input.h" // for Input, REPEAT_FALSE, inputs_e
|
||||||
#include "jail_audio.hpp" // for JA_StopMusic
|
#include "core/locale/lang.h" // for Lang
|
||||||
#include "lang.h" // for Lang
|
#include "core/rendering/screen.h" // for Screen
|
||||||
#include "resource.h"
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "screen.h" // for Screen
|
#include "core/rendering/text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_SHADOW
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_SHADOW
|
#include "core/resources/resource.h"
|
||||||
#include "texture.h" // for Texture
|
#include "game/defaults.hpp" // for shdwTxtColor, GAMECANVAS_CENTER_X, GAME...
|
||||||
#include "utils.h" // for color_t, section_t
|
#include "utils/utils.h" // for color_t, section_t
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Instructions::Instructions(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, Lang *lang, section_t *section) {
|
Instructions::Instructions(SDL_Renderer *renderer, section_t *section) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->screen = screen;
|
|
||||||
this->asset = asset;
|
|
||||||
this->input = input;
|
|
||||||
this->lang = lang;
|
|
||||||
this->section = section;
|
this->section = section;
|
||||||
|
|
||||||
// Texturas (handles compartidos de Resource)
|
// Texturas (handles compartidos de Resource)
|
||||||
@@ -121,21 +117,21 @@ void Instructions::render() {
|
|||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
// Escribe el texto
|
// Escribe el texto
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 8, lang->getText(11), 1, orangeColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 8, Lang::get()->getText(11), 1, orangeColor, 1, shdwTxtColor);
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 24, lang->getText(12), 1, noColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 24, Lang::get()->getText(12), 1, noColor, 1, shdwTxtColor);
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 34, lang->getText(13), 1, noColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 34, Lang::get()->getText(13), 1, noColor, 1, shdwTxtColor);
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 48, lang->getText(14), 1, noColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 48, Lang::get()->getText(14), 1, noColor, 1, shdwTxtColor);
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 58, lang->getText(15), 1, noColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 58, Lang::get()->getText(15), 1, noColor, 1, shdwTxtColor);
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 75, lang->getText(16), 1, orangeColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 75, Lang::get()->getText(16), 1, orangeColor, 1, shdwTxtColor);
|
||||||
|
|
||||||
text->writeShadowed(84, 92, lang->getText(17), shdwTxtColor);
|
text->writeShadowed(84, 92, Lang::get()->getText(17), shdwTxtColor);
|
||||||
text->writeShadowed(84, 108, lang->getText(18), shdwTxtColor);
|
text->writeShadowed(84, 108, Lang::get()->getText(18), shdwTxtColor);
|
||||||
text->writeShadowed(84, 124, lang->getText(19), shdwTxtColor);
|
text->writeShadowed(84, 124, Lang::get()->getText(19), shdwTxtColor);
|
||||||
text->writeShadowed(84, 140, lang->getText(20), shdwTxtColor);
|
text->writeShadowed(84, 140, Lang::get()->getText(20), shdwTxtColor);
|
||||||
text->writeShadowed(84, 156, lang->getText(21), shdwTxtColor);
|
text->writeShadowed(84, 156, Lang::get()->getText(21), shdwTxtColor);
|
||||||
|
|
||||||
if ((mode == m_manual) && (counter % 50 > 14)) {
|
if ((mode == m_manual) && (counter % 50 > 14)) {
|
||||||
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - 12, lang->getText(22), 1, orangeColor, 1, shdwTxtColor);
|
text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - 12, Lang::get()->getText(22), 1, orangeColor, 1, shdwTxtColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disquito
|
// Disquito
|
||||||
@@ -177,10 +173,10 @@ void Instructions::render() {
|
|||||||
SDL_SetRenderTarget(renderer, nullptr);
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Establece la ventana del backbuffer
|
// Establece la ventana del backbuffer
|
||||||
if (mode == m_auto) {
|
if (mode == m_auto) {
|
||||||
@@ -194,7 +190,7 @@ void Instructions::render() {
|
|||||||
SDL_RenderTexture(renderer, backbuffer, nullptr, &fWindow);
|
SDL_RenderTexture(renderer, backbuffer, nullptr, &fWindow);
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos
|
// Comprueba los eventos
|
||||||
@@ -215,24 +211,15 @@ void Instructions::checkEvents() {
|
|||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void Instructions::checkInput() {
|
void Instructions::checkInput() {
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
if (input->checkInput(input_exit, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) {
|
||||||
quitRequested = true;
|
quitRequested = true;
|
||||||
finished = true;
|
finished = true;
|
||||||
} else
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
if (GlobalInputs::handle()) { return; }
|
||||||
screen->toggleVideoMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_pause, REPEAT_FALSE) || Input::get()->checkInput(input_accept, REPEAT_FALSE) || Input::get()->checkInput(input_fire_left, REPEAT_FALSE) || Input::get()->checkInput(input_fire_center, REPEAT_FALSE) || Input::get()->checkInput(input_fire_right, REPEAT_FALSE)) {
|
||||||
screen->decWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
|
||||||
screen->incWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_pause, REPEAT_FALSE) || input->checkInput(input_accept, REPEAT_FALSE) || input->checkInput(input_fire_left, REPEAT_FALSE) || input->checkInput(input_fire_center, REPEAT_FALSE) || input->checkInput(input_fire_right, REPEAT_FALSE)) {
|
|
||||||
if (mode == m_auto) {
|
if (mode == m_auto) {
|
||||||
finished = true;
|
finished = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -3,10 +3,6 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
class Asset;
|
|
||||||
class Input;
|
|
||||||
class Lang;
|
|
||||||
class Screen;
|
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Text;
|
class Text;
|
||||||
class Texture;
|
class Texture;
|
||||||
@@ -22,14 +18,10 @@ class Instructions {
|
|||||||
private:
|
private:
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
std::vector<Texture *> itemTextures; // Vector con las texturas de los items
|
std::vector<Texture *> itemTextures; // Vector con las texturas de los items
|
||||||
SDL_Event *eventHandler; // Manejador de eventos
|
SDL_Event *eventHandler; // Manejador de eventos
|
||||||
SDL_Texture *backbuffer; // Textura para usar como backbuffer
|
SDL_Texture *backbuffer; // Textura para usar como backbuffer
|
||||||
Sprite *sprite; // Sprite con la textura de las instrucciones
|
Sprite *sprite; // Sprite con la textura de las instrucciones
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
Input *input; // Objeto pata gestionar la entrada
|
|
||||||
Lang *lang; // Objeto para gestionar los textos en diferentes idiomas
|
|
||||||
Text *text; // Objeto para escribir texto
|
Text *text; // Objeto para escribir texto
|
||||||
section_t *section; // Estado del bucle principal para saber si continua o se sale
|
section_t *section; // Estado del bucle principal para saber si continua o se sale
|
||||||
|
|
||||||
@@ -48,7 +40,7 @@ class Instructions {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Instructions(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, Lang *lang, section_t *section);
|
Instructions(SDL_Renderer *renderer, section_t *section);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Instructions();
|
~Instructions();
|
||||||
@@ -1,30 +1,26 @@
|
|||||||
#include "intro.h"
|
#include "game/scenes/intro.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <string> // for basic_string
|
#include <string> // for basic_string
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/audio/jail_audio.hpp" // for JA_StopMusic, JA_DeleteMusic, JA_LoadMusic
|
||||||
#include "const.h" // for GAMECANVAS_CENTER_X, GAMECANVAS_FIRST_QU...
|
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||||
#include "input.h" // for Input, REPEAT_FALSE, inputs_e
|
#include "core/input/input.h" // for Input, REPEAT_FALSE, inputs_e
|
||||||
#include "jail_audio.hpp" // for JA_StopMusic, JA_DeleteMusic, JA_LoadMusic
|
#include "core/locale/lang.h" // for Lang
|
||||||
#include "lang.h" // for Lang
|
#include "core/rendering/screen.h" // for Screen
|
||||||
#include "resource.h"
|
#include "core/rendering/smartsprite.h" // for SmartSprite
|
||||||
#include "screen.h" // for Screen
|
#include "core/rendering/text.h" // for Text
|
||||||
#include "smartsprite.h" // for SmartSprite
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "text.h" // for Text
|
#include "core/rendering/writer.h" // for Writer
|
||||||
#include "texture.h" // for Texture
|
#include "core/resources/resource.h"
|
||||||
#include "utils.h" // for section_t, color_t
|
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, GAMECANVAS_FIRST_QU...
|
||||||
#include "writer.h" // for Writer
|
#include "utils/utils.h" // for section_t, color_t
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Intro::Intro(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, Lang *lang, section_t *section) {
|
Intro::Intro(SDL_Renderer *renderer, section_t *section) {
|
||||||
// Copia los punteros
|
// Copia los punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->screen = screen;
|
|
||||||
this->lang = lang;
|
|
||||||
this->asset = asset;
|
|
||||||
this->input = input;
|
|
||||||
this->section = section;
|
this->section = section;
|
||||||
|
|
||||||
// Reserva memoria para los objetos
|
// Reserva memoria para los objetos
|
||||||
@@ -114,43 +110,43 @@ Intro::Intro(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Un dia qualsevol de l'any 2000
|
// Un dia qualsevol de l'any 2000
|
||||||
texts[0]->setCaption(lang->getText(27));
|
texts[0]->setCaption(Lang::get()->getText(27));
|
||||||
texts[0]->setSpeed(8);
|
texts[0]->setSpeed(8);
|
||||||
|
|
||||||
// Tot esta tranquil a la UPV
|
// Tot esta tranquil a la UPV
|
||||||
texts[1]->setCaption(lang->getText(28));
|
texts[1]->setCaption(Lang::get()->getText(28));
|
||||||
texts[1]->setSpeed(8);
|
texts[1]->setSpeed(8);
|
||||||
|
|
||||||
// Fins que un desaprensiu...
|
// Fins que un desaprensiu...
|
||||||
texts[2]->setCaption(lang->getText(29));
|
texts[2]->setCaption(Lang::get()->getText(29));
|
||||||
texts[2]->setSpeed(12);
|
texts[2]->setSpeed(12);
|
||||||
|
|
||||||
// HEY! ME ANE A FERME UN CORTAET...
|
// HEY! ME ANE A FERME UN CORTAET...
|
||||||
texts[3]->setCaption(lang->getText(30));
|
texts[3]->setCaption(Lang::get()->getText(30));
|
||||||
texts[3]->setSpeed(8);
|
texts[3]->setSpeed(8);
|
||||||
|
|
||||||
// UAAAAAAAAAAAAA!!!
|
// UAAAAAAAAAAAAA!!!
|
||||||
texts[4]->setCaption(lang->getText(31));
|
texts[4]->setCaption(Lang::get()->getText(31));
|
||||||
texts[4]->setSpeed(1);
|
texts[4]->setSpeed(1);
|
||||||
|
|
||||||
// Espera un moment...
|
// Espera un moment...
|
||||||
texts[5]->setCaption(lang->getText(32));
|
texts[5]->setCaption(Lang::get()->getText(32));
|
||||||
texts[5]->setSpeed(16);
|
texts[5]->setSpeed(16);
|
||||||
|
|
||||||
// Si resulta que no tinc solt!
|
// Si resulta que no tinc solt!
|
||||||
texts[6]->setCaption(lang->getText(33));
|
texts[6]->setCaption(Lang::get()->getText(33));
|
||||||
texts[6]->setSpeed(2);
|
texts[6]->setSpeed(2);
|
||||||
|
|
||||||
// MERDA DE MAQUINA!
|
// MERDA DE MAQUINA!
|
||||||
texts[7]->setCaption(lang->getText(34));
|
texts[7]->setCaption(Lang::get()->getText(34));
|
||||||
texts[7]->setSpeed(3);
|
texts[7]->setSpeed(3);
|
||||||
|
|
||||||
// Blop... blop... blop...
|
// Blop... blop... blop...
|
||||||
texts[8]->setCaption(lang->getText(35));
|
texts[8]->setCaption(Lang::get()->getText(35));
|
||||||
texts[8]->setSpeed(16);
|
texts[8]->setSpeed(16);
|
||||||
|
|
||||||
for (auto text : texts) {
|
for (auto *t : texts) {
|
||||||
text->center(GAMECANVAS_CENTER_X);
|
t->center(GAMECANVAS_CENTER_X);
|
||||||
}
|
}
|
||||||
|
|
||||||
JA_PlayMusic(music, 0);
|
JA_PlayMusic(music, 0);
|
||||||
@@ -170,45 +166,17 @@ Intro::~Intro() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los recursos (ya no carga nada, se mantiene por si hay callers)
|
|
||||||
bool Intro::loadMedia() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void Intro::checkEvents() {
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
// Comprueba los eventos que hay en la cola
|
|
||||||
while (SDL_PollEvent(eventHandler) != 0) {
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (eventHandler->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void Intro::checkInput() {
|
void Intro::checkInput() {
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
if (input->checkInput(input_exit, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) {
|
||||||
section->name = SECTION_PROG_QUIT;
|
section->name = SECTION_PROG_QUIT;
|
||||||
} else
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
if (GlobalInputs::handle()) { return; }
|
||||||
screen->toggleVideoMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_pause, REPEAT_FALSE) || Input::get()->checkInput(input_accept, REPEAT_FALSE) || Input::get()->checkInput(input_fire_left, REPEAT_FALSE) || Input::get()->checkInput(input_fire_center, REPEAT_FALSE) || Input::get()->checkInput(input_fire_right, REPEAT_FALSE)) {
|
||||||
screen->decWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
|
||||||
screen->incWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_pause, REPEAT_FALSE) || input->checkInput(input_accept, REPEAT_FALSE) || input->checkInput(input_fire_left, REPEAT_FALSE) || input->checkInput(input_fire_center, REPEAT_FALSE) || input->checkInput(input_fire_right, REPEAT_FALSE)) {
|
|
||||||
JA_StopMusic();
|
JA_StopMusic();
|
||||||
section->name = SECTION_PROG_TITLE;
|
section->name = SECTION_PROG_TITLE;
|
||||||
section->subsection = SUBSECTION_TITLE_1;
|
section->subsection = SUBSECTION_TITLE_1;
|
||||||
@@ -365,8 +333,8 @@ void Intro::update() {
|
|||||||
bitmap->update();
|
bitmap->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto text : texts) {
|
for (auto *t : texts) {
|
||||||
text->update();
|
t->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las escenas de la intro
|
// Actualiza las escenas de la intro
|
||||||
@@ -377,22 +345,22 @@ void Intro::update() {
|
|||||||
// Dibuja el objeto en pantalla
|
// Dibuja el objeto en pantalla
|
||||||
void Intro::render() {
|
void Intro::render() {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibuja los objetos
|
// Dibuja los objetos
|
||||||
for (auto bitmap : bitmaps) {
|
for (auto bitmap : bitmaps) {
|
||||||
bitmap->render();
|
bitmap->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto text : texts) {
|
for (auto *t : texts) {
|
||||||
text->render();
|
t->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucle principal
|
// Bucle principal
|
||||||
@@ -411,6 +379,6 @@ void Intro::iterate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Procesa un evento individual
|
// Procesa un evento individual
|
||||||
void Intro::handleEvent(SDL_Event *event) {
|
void Intro::handleEvent(const SDL_Event *event) {
|
||||||
// SDL_EVENT_QUIT ya lo maneja Director
|
// SDL_EVENT_QUIT ya lo maneja Director
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,6 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
class Asset;
|
|
||||||
class Input;
|
|
||||||
class Lang;
|
|
||||||
class Screen;
|
|
||||||
class SmartSprite;
|
class SmartSprite;
|
||||||
class Text;
|
class Text;
|
||||||
class Texture;
|
class Texture;
|
||||||
@@ -19,12 +15,8 @@ class Intro {
|
|||||||
private:
|
private:
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
Texture *texture; // Textura con los graficos
|
Texture *texture; // Textura con los graficos
|
||||||
SDL_Event *eventHandler; // Manejador de eventos
|
SDL_Event *eventHandler; // Manejador de eventos
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
Lang *lang; // Objeto para gestionar los textos en diferentes idiomas
|
|
||||||
Input *input; // Objeto pata gestionar la entrada
|
|
||||||
std::vector<SmartSprite *> bitmaps; // Vector con los sprites inteligentes para los dibujos de la intro
|
std::vector<SmartSprite *> bitmaps; // Vector con los sprites inteligentes para los dibujos de la intro
|
||||||
std::vector<Writer *> texts; // Textos de la intro
|
std::vector<Writer *> texts; // Textos de la intro
|
||||||
Text *text; // Textos de la intro
|
Text *text; // Textos de la intro
|
||||||
@@ -42,12 +34,6 @@ class Intro {
|
|||||||
// Dibuja el objeto en pantalla
|
// Dibuja el objeto en pantalla
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
// Carga los recursos
|
|
||||||
bool loadMedia();
|
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void checkEvents();
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void checkInput();
|
void checkInput();
|
||||||
|
|
||||||
@@ -56,7 +42,7 @@ class Intro {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Intro(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, Lang *lang, section_t *section);
|
Intro(SDL_Renderer *renderer, section_t *section);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Intro();
|
~Intro();
|
||||||
@@ -68,5 +54,5 @@ class Intro {
|
|||||||
void iterate();
|
void iterate();
|
||||||
|
|
||||||
// Procesa un evento
|
// Procesa un evento
|
||||||
void handleEvent(SDL_Event *event);
|
void handleEvent(const SDL_Event *event);
|
||||||
};
|
};
|
||||||
@@ -1,31 +1,29 @@
|
|||||||
#include "logo.h"
|
#include "game/scenes/logo.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <algorithm> // for min
|
#include <algorithm> // for min
|
||||||
#include <string> // for basic_string
|
#include <string> // for basic_string
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/audio/jail_audio.hpp" // for JA_StopMusic
|
||||||
#include "const.h" // for bgColor, SECTION_PROG_LOGO, SECTION_PROG...
|
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||||
#include "input.h" // for Input, REPEAT_FALSE, inputs_e
|
#include "core/input/input.h" // for Input, REPEAT_FALSE, inputs_e
|
||||||
#include "jail_audio.hpp" // for JA_StopMusic
|
#include "core/rendering/screen.h" // for Screen
|
||||||
#include "resource.h"
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "screen.h" // for Screen
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/resources/asset.h" // for Asset
|
||||||
#include "texture.h" // for Texture
|
#include "core/resources/resource.h"
|
||||||
#include "utils.h" // for section_t, color_t
|
#include "game/defaults.hpp" // for bgColor, SECTION_PROG_LOGO, SECTION_PROG...
|
||||||
|
#include "utils/utils.h" // for section_t, color_t
|
||||||
|
|
||||||
// Valores de inicialización y fin
|
// Valores de inicialización y fin
|
||||||
constexpr int INIT_FADE = 100;
|
constexpr int INIT_FADE = 100;
|
||||||
constexpr int END_LOGO = 200;
|
constexpr int END_LOGO = 200;
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Logo::Logo(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, section_t *section) {
|
Logo::Logo(SDL_Renderer *renderer, section_t *section) {
|
||||||
// Copia la dirección de los objetos
|
// Copia la dirección de los objetos
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->screen = screen;
|
|
||||||
this->asset = asset;
|
|
||||||
this->input = input;
|
|
||||||
this->section = section;
|
this->section = section;
|
||||||
|
|
||||||
// Reserva memoria para los punteros
|
// Reserva memoria para los punteros
|
||||||
@@ -58,40 +56,17 @@ void Logo::checkLogoEnd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void Logo::checkEvents() {
|
|
||||||
#ifndef __EMSCRIPTEN__
|
|
||||||
// Comprueba los eventos que hay en la cola
|
|
||||||
while (SDL_PollEvent(eventHandler) != 0) {
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (eventHandler->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void Logo::checkInput() {
|
void Logo::checkInput() {
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
if (input->checkInput(input_exit, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) {
|
||||||
section->name = SECTION_PROG_QUIT;
|
section->name = SECTION_PROG_QUIT;
|
||||||
} else
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
if (GlobalInputs::handle()) { return; }
|
||||||
screen->toggleVideoMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_pause, REPEAT_FALSE) || Input::get()->checkInput(input_accept, REPEAT_FALSE) || Input::get()->checkInput(input_fire_left, REPEAT_FALSE) || Input::get()->checkInput(input_fire_center, REPEAT_FALSE) || Input::get()->checkInput(input_fire_right, REPEAT_FALSE)) {
|
||||||
screen->decWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
|
||||||
screen->incWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_pause, REPEAT_FALSE) || input->checkInput(input_accept, REPEAT_FALSE) || input->checkInput(input_fire_left, REPEAT_FALSE) || input->checkInput(input_fire_center, REPEAT_FALSE) || input->checkInput(input_fire_right, REPEAT_FALSE)) {
|
|
||||||
section->name = SECTION_PROG_TITLE;
|
section->name = SECTION_PROG_TITLE;
|
||||||
section->subsection = SUBSECTION_TITLE_1;
|
section->subsection = SUBSECTION_TITLE_1;
|
||||||
}
|
}
|
||||||
@@ -128,10 +103,10 @@ void Logo::update() {
|
|||||||
// Dibuja el objeto en pantalla
|
// Dibuja el objeto en pantalla
|
||||||
void Logo::render() {
|
void Logo::render() {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean({238, 238, 238});
|
Screen::get()->clean({238, 238, 238});
|
||||||
|
|
||||||
// Dibuja los objetos
|
// Dibuja los objetos
|
||||||
sprite->render();
|
sprite->render();
|
||||||
@@ -140,7 +115,7 @@ void Logo::render() {
|
|||||||
renderFade();
|
renderFade();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucle para el logo del juego
|
// Bucle para el logo del juego
|
||||||
@@ -159,6 +134,6 @@ void Logo::iterate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Procesa un evento individual
|
// Procesa un evento individual
|
||||||
void Logo::handleEvent(SDL_Event *event) {
|
void Logo::handleEvent(const SDL_Event *event) {
|
||||||
// SDL_EVENT_QUIT ya lo maneja Director
|
// SDL_EVENT_QUIT ya lo maneja Director
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
class Asset;
|
|
||||||
class Input;
|
|
||||||
class Screen;
|
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Texture;
|
class Texture;
|
||||||
struct section_t;
|
struct section_t;
|
||||||
@@ -13,9 +10,6 @@ class Logo {
|
|||||||
private:
|
private:
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
Input *input; // Objeto pata gestionar la entrada
|
|
||||||
Texture *texture; // Textura con los graficos
|
Texture *texture; // Textura con los graficos
|
||||||
SDL_Event *eventHandler; // Manejador de eventos
|
SDL_Event *eventHandler; // Manejador de eventos
|
||||||
Sprite *sprite; // Sprite con la textura del logo
|
Sprite *sprite; // Sprite con la textura del logo
|
||||||
@@ -35,9 +29,6 @@ class Logo {
|
|||||||
// Comprueba si ha terminado el logo
|
// Comprueba si ha terminado el logo
|
||||||
void checkLogoEnd();
|
void checkLogoEnd();
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void checkEvents();
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void checkInput();
|
void checkInput();
|
||||||
|
|
||||||
@@ -46,7 +37,7 @@ class Logo {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Logo(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input, section_t *section);
|
Logo(SDL_Renderer *renderer, section_t *section);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Logo();
|
~Logo();
|
||||||
@@ -58,5 +49,5 @@ class Logo {
|
|||||||
void iterate();
|
void iterate();
|
||||||
|
|
||||||
// Procesa un evento
|
// Procesa un evento
|
||||||
void handleEvent(SDL_Event *event);
|
void handleEvent(const SDL_Event *event);
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "title.h"
|
#include "game/scenes/title.h"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stdlib.h> // for rand
|
#include <stdlib.h> // for rand
|
||||||
@@ -6,31 +6,28 @@
|
|||||||
#include <iostream> // for basic_ostream, operator<<, basic_ostrea...
|
#include <iostream> // for basic_ostream, operator<<, basic_ostrea...
|
||||||
#include <string> // for basic_string, operator+, char_traits
|
#include <string> // for basic_string, operator+, char_traits
|
||||||
|
|
||||||
#include "animatedsprite.h" // for AnimatedSprite
|
#include "core/audio/jail_audio.hpp" // for JA_StopMusic, JA_GetMusicState, JA_Play...
|
||||||
#include "asset.h" // for Asset
|
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||||
#include "const.h" // for GAMECANVAS_CENTER_X, SECTION_PROG_QUIT
|
#include "core/input/input.h" // for Input, INPUT_USE_GAMECONTROLLER, INPUT_...
|
||||||
#include "fade.h" // for Fade
|
#include "core/locale/lang.h" // for Lang, ba_BA, en_UK, es_ES
|
||||||
#include "game.h" // for Game
|
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||||
#include "input.h" // for Input, INPUT_USE_GAMECONTROLLER, INPUT_...
|
#include "core/rendering/fade.h" // for Fade
|
||||||
#include "jail_audio.hpp" // for JA_StopMusic, JA_GetMusicState, JA_Play...
|
#include "core/rendering/screen.h" // for Screen
|
||||||
#include "lang.h" // for Lang, ba_BA, en_UK, es_ES
|
#include "core/rendering/smartsprite.h" // for SmartSprite
|
||||||
#include "menu.h" // for Menu
|
#include "core/rendering/sprite.h" // for Sprite
|
||||||
#include "resource.h"
|
#include "core/rendering/text.h" // for Text, TXT_CENTER, TXT_SHADOW
|
||||||
#include "screen.h" // for Screen, FILTER_LINEAL, FILTER_NEAREST
|
#include "core/rendering/texture.h" // for Texture
|
||||||
#include "smartsprite.h" // for SmartSprite
|
#include "core/resources/asset.h" // for Asset
|
||||||
#include "sprite.h" // for Sprite
|
#include "core/resources/resource.h"
|
||||||
#include "text.h" // for Text, TXT_CENTER, TXT_SHADOW
|
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, SECTION_PROG_QUIT
|
||||||
#include "texture.h" // for Texture
|
#include "game/game.h" // for Game
|
||||||
|
#include "game/options.hpp" // for Options
|
||||||
|
#include "game/ui/menu.h" // for Menu
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Title::Title(SDL_Renderer *renderer, Screen *screen, Input *input, Asset *asset, options_t *options, Lang *lang, section_t *section) {
|
Title::Title(SDL_Renderer *renderer, section_t *section) {
|
||||||
// Copia las direcciones de los punteros
|
// Copia las direcciones de los punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->screen = screen;
|
|
||||||
this->input = input;
|
|
||||||
this->asset = asset;
|
|
||||||
this->options = options;
|
|
||||||
this->lang = lang;
|
|
||||||
this->section = section;
|
this->section = section;
|
||||||
|
|
||||||
// Reserva memoria para los punteros
|
// Reserva memoria para los punteros
|
||||||
@@ -113,18 +110,18 @@ void Title::init() {
|
|||||||
demoThenInstructions = false;
|
demoThenInstructions = false;
|
||||||
|
|
||||||
// Pone valores por defecto a las opciones de control
|
// Pone valores por defecto a las opciones de control
|
||||||
options->input.clear();
|
Options::inputs.clear();
|
||||||
|
|
||||||
input_t i;
|
input_t inp;
|
||||||
i.id = 0;
|
inp.id = 0;
|
||||||
i.name = "KEYBOARD";
|
inp.name = "KEYBOARD";
|
||||||
i.deviceType = INPUT_USE_KEYBOARD;
|
inp.deviceType = INPUT_USE_KEYBOARD;
|
||||||
options->input.push_back(i);
|
Options::inputs.push_back(inp);
|
||||||
|
|
||||||
i.id = 0;
|
inp.id = 0;
|
||||||
i.name = "GAME CONTROLLER";
|
inp.name = "GAME CONTROLLER";
|
||||||
i.deviceType = INPUT_USE_GAMECONTROLLER;
|
inp.deviceType = INPUT_USE_GAMECONTROLLER;
|
||||||
options->input.push_back(i);
|
Options::inputs.push_back(inp);
|
||||||
|
|
||||||
// Comprueba si hay mandos conectados
|
// Comprueba si hay mandos conectados
|
||||||
checkInputDevices();
|
checkInputDevices();
|
||||||
@@ -135,10 +132,10 @@ void Title::init() {
|
|||||||
deviceIndex.push_back(0); // El primer mando encontrado. Si no ha encontrado ninguno es el teclado
|
deviceIndex.push_back(0); // El primer mando encontrado. Si no ha encontrado ninguno es el teclado
|
||||||
|
|
||||||
// Si ha encontrado un mando se lo asigna al segundo jugador
|
// Si ha encontrado un mando se lo asigna al segundo jugador
|
||||||
if (input->gameControllerFound()) {
|
if (Input::get()->gameControllerFound()) {
|
||||||
options->input[1].id = availableInputDevices[deviceIndex[1]].id;
|
Options::inputs[1].id = availableInputDevices[deviceIndex[1]].id;
|
||||||
options->input[1].name = availableInputDevices[deviceIndex[1]].name;
|
Options::inputs[1].name = availableInputDevices[deviceIndex[1]].name;
|
||||||
options->input[1].deviceType = availableInputDevices[deviceIndex[1]].deviceType;
|
Options::inputs[1].deviceType = availableInputDevices[deviceIndex[1]].deviceType;
|
||||||
} else { // Si no ha encontrado un mando, deshabilita la opción de jugar a 2 jugadores
|
} else { // Si no ha encontrado un mando, deshabilita la opción de jugar a 2 jugadores
|
||||||
menu.title->setSelectable(1, false);
|
menu.title->setSelectable(1, false);
|
||||||
menu.title->setGreyed(1, true);
|
menu.title->setGreyed(1, true);
|
||||||
@@ -236,10 +233,14 @@ void Title::update() {
|
|||||||
if (coffeeBitmap->hasFinished() && crisisBitmap->hasFinished()) {
|
if (coffeeBitmap->hasFinished() && crisisBitmap->hasFinished()) {
|
||||||
section->subsection = SUBSECTION_TITLE_2;
|
section->subsection = SUBSECTION_TITLE_2;
|
||||||
|
|
||||||
// Pantallazo blanco
|
// Pantallazo blanco: pintar sobre el gameCanvas y dejar
|
||||||
|
// que Screen::blit() presente por la ruta activa (GPU o
|
||||||
|
// SDL_Renderer). Un `SDL_RenderPresent(renderer)` directe
|
||||||
|
// crasheja quan el SDL3 GPU ha reclamat la ventana.
|
||||||
|
Screen::get()->start();
|
||||||
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
SDL_RenderPresent(renderer);
|
Screen::get()->blit();
|
||||||
|
|
||||||
// Reproduce el efecto sonoro
|
// Reproduce el efecto sonoro
|
||||||
JA_PlaySound(crashSound);
|
JA_PlaySound(crashSound);
|
||||||
@@ -343,7 +344,10 @@ void Title::update() {
|
|||||||
|
|
||||||
case 2: // OPTIONS
|
case 2: // OPTIONS
|
||||||
menu.active = menu.options;
|
menu.active = menu.options;
|
||||||
optionsPrevious = *options;
|
prevVideo = Options::video;
|
||||||
|
prevWindow = Options::window;
|
||||||
|
prevSettings = Options::settings;
|
||||||
|
prevInputs = Options::inputs;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: // QUIT
|
case 3: // QUIT
|
||||||
@@ -365,13 +369,13 @@ void Title::update() {
|
|||||||
|
|
||||||
case 1: // BAL1
|
case 1: // BAL1
|
||||||
postFade = 0;
|
postFade = 0;
|
||||||
options->playerSelected = 0;
|
Options::settings.player_selected = 0;
|
||||||
fade->activateFade();
|
fade->activateFade();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // AROUNDER
|
case 2: // AROUNDER
|
||||||
postFade = 0;
|
postFade = 0;
|
||||||
options->playerSelected = 1;
|
Options::settings.player_selected = 1;
|
||||||
fade->activateFade();
|
fade->activateFade();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -389,12 +393,12 @@ void Title::update() {
|
|||||||
if (menu.active->getName() == "OPTIONS") {
|
if (menu.active->getName() == "OPTIONS") {
|
||||||
switch (menu.active->getItemSelected()) {
|
switch (menu.active->getItemSelected()) {
|
||||||
case 0: // Difficulty
|
case 0: // Difficulty
|
||||||
if (options->difficulty == DIFFICULTY_EASY)
|
if (Options::settings.difficulty == DIFFICULTY_EASY)
|
||||||
options->difficulty = DIFFICULTY_NORMAL;
|
Options::settings.difficulty = DIFFICULTY_NORMAL;
|
||||||
else if (options->difficulty == DIFFICULTY_NORMAL)
|
else if (Options::settings.difficulty == DIFFICULTY_NORMAL)
|
||||||
options->difficulty = DIFFICULTY_HARD;
|
Options::settings.difficulty = DIFFICULTY_HARD;
|
||||||
else
|
else
|
||||||
options->difficulty = DIFFICULTY_EASY;
|
Options::settings.difficulty = DIFFICULTY_EASY;
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -409,15 +413,15 @@ void Title::update() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // Language
|
case 5: // Language
|
||||||
options->language++;
|
Options::settings.language++;
|
||||||
if (options->language == 3)
|
if (Options::settings.language == 3)
|
||||||
options->language = 0;
|
Options::settings.language = 0;
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6: // Display mode
|
case 6: // Display mode
|
||||||
switchFullScreenModeVar();
|
switchFullScreenModeVar();
|
||||||
if (options->videoMode != 0) {
|
if (Options::video.fullscreen) {
|
||||||
menu.options->setSelectable(8, false);
|
menu.options->setSelectable(8, false);
|
||||||
menu.options->setGreyed(8, true);
|
menu.options->setGreyed(8, true);
|
||||||
} else {
|
} else {
|
||||||
@@ -428,27 +432,23 @@ void Title::update() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: // Windows size
|
case 8: // Windows size
|
||||||
options->windowSize++;
|
Options::window.zoom++;
|
||||||
if (options->windowSize == 5)
|
if (Options::window.zoom > Options::window.max_zoom)
|
||||||
options->windowSize = 1;
|
Options::window.zoom = 1;
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 9: // FILTER
|
case 9: // Scale mode
|
||||||
if (options->filter == FILTER_LINEAL)
|
Options::video.scale_mode = (Options::video.scale_mode == SDL_SCALEMODE_NEAREST)
|
||||||
options->filter = FILTER_NEAREST;
|
? SDL_SCALEMODE_LINEAR
|
||||||
else
|
: SDL_SCALEMODE_NEAREST;
|
||||||
options->filter = FILTER_LINEAL;
|
Texture::setGlobalScaleMode(Options::video.scale_mode);
|
||||||
Texture::setGlobalScaleMode(options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
|
|
||||||
reLoadTextures();
|
reLoadTextures();
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 10: // VSYNC
|
case 10: // VSYNC
|
||||||
if (options->vSync)
|
Options::video.vsync = !Options::video.vsync;
|
||||||
options->vSync = false;
|
|
||||||
else
|
|
||||||
options->vSync = true;
|
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -463,7 +463,10 @@ void Title::update() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 13: // CANCEL
|
case 13: // CANCEL
|
||||||
options = &optionsPrevious;
|
Options::video = prevVideo;
|
||||||
|
Options::window = prevWindow;
|
||||||
|
Options::settings = prevSettings;
|
||||||
|
Options::inputs = prevInputs;
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
menu.active->reset();
|
menu.active->reset();
|
||||||
menu.active = menu.title;
|
menu.active = menu.title;
|
||||||
@@ -506,10 +509,10 @@ void Title::render() {
|
|||||||
// Sección 1 - Titulo desplazandose
|
// Sección 1 - Titulo desplazandose
|
||||||
case SUBSECTION_TITLE_1: {
|
case SUBSECTION_TITLE_1: {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibuja el tileado de fondo
|
// Dibuja el tileado de fondo
|
||||||
{
|
{
|
||||||
@@ -525,16 +528,16 @@ void Title::render() {
|
|||||||
crisisBitmap->render();
|
crisisBitmap->render();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
// Sección 2 - Titulo vibrando
|
// Sección 2 - Titulo vibrando
|
||||||
case SUBSECTION_TITLE_2: {
|
case SUBSECTION_TITLE_2: {
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
// Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibuja el tileado de fondo
|
// Dibuja el tileado de fondo
|
||||||
{
|
{
|
||||||
@@ -553,15 +556,15 @@ void Title::render() {
|
|||||||
dustBitmapL->render();
|
dustBitmapL->render();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
// Sección 3 - La pantalla de titulo con el menú y la música
|
// Sección 3 - La pantalla de titulo con el menú y la música
|
||||||
case SUBSECTION_TITLE_3: { // Prepara para empezar a dibujar en la textura de juego
|
case SUBSECTION_TITLE_3: { // Prepara para empezar a dibujar en la textura de juego
|
||||||
screen->start();
|
Screen::get()->start();
|
||||||
|
|
||||||
// Limpia la pantalla
|
// Limpia la pantalla
|
||||||
screen->clean(bgColor);
|
Screen::get()->clean(bgColor);
|
||||||
|
|
||||||
// Dibuja el tileado de fondo
|
// Dibuja el tileado de fondo
|
||||||
{
|
{
|
||||||
@@ -591,14 +594,14 @@ void Title::render() {
|
|||||||
|
|
||||||
// PRESS ANY KEY!
|
// PRESS ANY KEY!
|
||||||
if ((counter % 50 > 14) && (menuVisible == false)) {
|
if ((counter % 50 > 14) && (menuVisible == false)) {
|
||||||
text1->writeDX(TXT_CENTER | TXT_SHADOW, GAMECANVAS_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, lang->getText(23), 1, noColor, 1, shdwTxtColor);
|
text1->writeDX(TXT_CENTER | TXT_SHADOW, GAMECANVAS_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, Lang::get()->getText(23), 1, noColor, 1, shdwTxtColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade
|
// Fade
|
||||||
fade->render();
|
fade->render();
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
screen->blit();
|
Screen::get()->blit();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -606,50 +609,15 @@ void Title::render() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void Title::checkEvents() {
|
|
||||||
// Comprueba los eventos que hay en la cola
|
|
||||||
while (SDL_PollEvent(eventHandler) != 0) {
|
|
||||||
// Evento de salida de la aplicación
|
|
||||||
if (eventHandler->type == SDL_EVENT_QUIT) {
|
|
||||||
section->name = SECTION_PROG_QUIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (eventHandler->type == SDL_EVENT_RENDER_DEVICE_RESET || eventHandler->type == SDL_EVENT_RENDER_TARGETS_RESET) {
|
|
||||||
reLoadTextures();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (section->subsection == SUBSECTION_TITLE_3) { // Si se pulsa alguna tecla durante la tercera sección del titulo
|
|
||||||
if ((eventHandler->type == SDL_EVENT_KEY_UP) || (eventHandler->type == SDL_EVENT_JOYSTICK_BUTTON_UP)) {
|
|
||||||
// Muestra el menu
|
|
||||||
menuVisible = true;
|
|
||||||
|
|
||||||
// Reinicia el contador
|
|
||||||
counter = TITLE_COUNTER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void Title::checkInput() {
|
void Title::checkInput() {
|
||||||
#ifndef __EMSCRIPTEN__
|
#ifndef __EMSCRIPTEN__
|
||||||
if (input->checkInput(input_exit, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) {
|
||||||
section->name = SECTION_PROG_QUIT;
|
section->name = SECTION_PROG_QUIT;
|
||||||
} else
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (input->checkInput(input_window_fullscreen, REPEAT_FALSE)) {
|
GlobalInputs::handle();
|
||||||
screen->toggleVideoMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_dec_size, REPEAT_FALSE)) {
|
|
||||||
screen->decWindowZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (input->checkInput(input_window_inc_size, REPEAT_FALSE)) {
|
|
||||||
screen->incWindowZoom();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza el tileado de fondo
|
// Actualiza el tileado de fondo
|
||||||
@@ -666,165 +634,142 @@ void Title::updateBG() {
|
|||||||
|
|
||||||
// Cambia el valor de la variable de modo de pantalla completa
|
// Cambia el valor de la variable de modo de pantalla completa
|
||||||
void Title::switchFullScreenModeVar() {
|
void Title::switchFullScreenModeVar() {
|
||||||
switch (options->videoMode) {
|
Options::video.fullscreen = !Options::video.fullscreen;
|
||||||
case 0:
|
|
||||||
options->videoMode = SDL_WINDOW_FULLSCREEN;
|
|
||||||
break;
|
|
||||||
case SDL_WINDOW_FULLSCREEN:
|
|
||||||
options->videoMode = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
options->videoMode = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los elementos de los menus
|
// Actualiza los elementos de los menus
|
||||||
void Title::updateMenuLabels() {
|
void Title::updateMenuLabels() {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
// DIFFICULTY
|
// DIFFICULTY
|
||||||
switch (options->difficulty) {
|
switch (Options::settings.difficulty) {
|
||||||
case DIFFICULTY_EASY:
|
case DIFFICULTY_EASY:
|
||||||
menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(66)); // EASY
|
menu.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(66)); // EASY
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DIFFICULTY_NORMAL:
|
case DIFFICULTY_NORMAL:
|
||||||
menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(67)); // NORMAL
|
menu.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(67)); // NORMAL
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DIFFICULTY_HARD:
|
case DIFFICULTY_HARD:
|
||||||
menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(68)); // HARD
|
menu.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(68)); // HARD
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
menu.options->setItemCaption(i, lang->getText(59) + ": " + lang->getText(67)); // NORMAL
|
menu.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(67)); // NORMAL
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// PLAYER 1 CONTROLS
|
// PLAYER 1 CONTROLS
|
||||||
menu.options->setItemCaption(i, lang->getText(62));
|
menu.options->setItemCaption(i, Lang::get()->getText(62));
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// PLAYER 1 CONTROLS - OPTIONS
|
// PLAYER 1 CONTROLS - OPTIONS
|
||||||
switch (options->input[0].deviceType) {
|
switch (Options::inputs[0].deviceType) {
|
||||||
case INPUT_USE_KEYBOARD:
|
case INPUT_USE_KEYBOARD:
|
||||||
menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD
|
menu.options->setItemCaption(i, Lang::get()->getText(69)); // KEYBOARD
|
||||||
menu.options->setGreyed(i, false);
|
menu.options->setGreyed(i, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_USE_GAMECONTROLLER:
|
case INPUT_USE_GAMECONTROLLER:
|
||||||
menu.options->setItemCaption(i, lang->getText(70)); // GAME CONTROLLER
|
menu.options->setItemCaption(i, Lang::get()->getText(70)); // GAME CONTROLLER
|
||||||
if (!input->gameControllerFound())
|
if (!Input::get()->gameControllerFound())
|
||||||
menu.options->setGreyed(i, true);
|
menu.options->setGreyed(i, true);
|
||||||
else {
|
else {
|
||||||
menu.options->setGreyed(i, false);
|
menu.options->setGreyed(i, false);
|
||||||
menu.options->setItemCaption(i, options->input[0].name);
|
menu.options->setItemCaption(i, Options::inputs[0].name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD
|
menu.options->setItemCaption(i, Lang::get()->getText(69)); // KEYBOARD
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// PLAYER 2 CONTROLS
|
// PLAYER 2 CONTROLS
|
||||||
menu.options->setItemCaption(i, lang->getText(63));
|
menu.options->setItemCaption(i, Lang::get()->getText(63));
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// PLAYER 2 CONTROLS - OPTIONS
|
// PLAYER 2 CONTROLS - OPTIONS
|
||||||
switch (options->input[1].deviceType) {
|
switch (Options::inputs[1].deviceType) {
|
||||||
case INPUT_USE_KEYBOARD:
|
case INPUT_USE_KEYBOARD:
|
||||||
menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD
|
menu.options->setItemCaption(i, Lang::get()->getText(69)); // KEYBOARD
|
||||||
menu.options->setGreyed(i, false);
|
menu.options->setGreyed(i, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_USE_GAMECONTROLLER:
|
case INPUT_USE_GAMECONTROLLER:
|
||||||
menu.options->setItemCaption(i, lang->getText(70)); // GAME CONTROLLER
|
menu.options->setItemCaption(i, Lang::get()->getText(70)); // GAME CONTROLLER
|
||||||
if (!input->gameControllerFound())
|
if (!Input::get()->gameControllerFound())
|
||||||
menu.options->setGreyed(i, true);
|
menu.options->setGreyed(i, true);
|
||||||
else {
|
else {
|
||||||
menu.options->setGreyed(i, false);
|
menu.options->setGreyed(i, false);
|
||||||
menu.options->setItemCaption(i, options->input[1].name);
|
menu.options->setItemCaption(i, Options::inputs[1].name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
menu.options->setItemCaption(i, lang->getText(69)); // KEYBOARD
|
menu.options->setItemCaption(i, Lang::get()->getText(69)); // KEYBOARD
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// LANGUAGE
|
// LANGUAGE
|
||||||
switch (options->language) {
|
switch (Options::settings.language) {
|
||||||
case es_ES:
|
case es_ES:
|
||||||
menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(24)); // SPANISH
|
menu.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(24)); // SPANISH
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ba_BA:
|
case ba_BA:
|
||||||
menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(25)); // VALENCIAN
|
menu.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(25)); // VALENCIAN
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case en_UK:
|
case en_UK:
|
||||||
menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(26)); // ENGLISH
|
menu.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(26)); // ENGLISH
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
menu.options->setItemCaption(i, lang->getText(8) + ": " + lang->getText(26)); // ENGLISH
|
menu.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(26)); // ENGLISH
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// DISPLAY MODE
|
// DISPLAY MODE
|
||||||
menu.options->setItemCaption(i, lang->getText(58));
|
menu.options->setItemCaption(i, Lang::get()->getText(58));
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// DISPLAY MODE - OPTIONS
|
// DISPLAY MODE - OPTIONS
|
||||||
switch (options->videoMode) {
|
menu.options->setItemCaption(i, Options::video.fullscreen ? Lang::get()->getText(5) : Lang::get()->getText(4));
|
||||||
case 0:
|
|
||||||
menu.options->setItemCaption(i, lang->getText(4)); // WINDOW
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_WINDOW_FULLSCREEN:
|
|
||||||
menu.options->setItemCaption(i, lang->getText(5)); // FULLSCREEN
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
menu.options->setItemCaption(i, lang->getText(4)); // WINDOW
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// WINDOW SIZE
|
// WINDOW SIZE
|
||||||
menu.options->setItemCaption(i, lang->getText(7) + " x" + std::to_string(options->windowSize)); // WINDOW SIZE
|
menu.options->setItemCaption(i, Lang::get()->getText(7) + " x" + std::to_string(Options::window.zoom)); // WINDOW SIZE
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// FILTER
|
// SCALE MODE
|
||||||
if (options->filter == FILTER_LINEAL)
|
if (Options::video.scale_mode == SDL_SCALEMODE_LINEAR)
|
||||||
menu.options->setItemCaption(i, lang->getText(60) + ": " + lang->getText(71)); // BILINEAL
|
menu.options->setItemCaption(i, Lang::get()->getText(60) + ": " + Lang::get()->getText(71)); // BILINEAL
|
||||||
else
|
else
|
||||||
menu.options->setItemCaption(i, lang->getText(60) + ": " + lang->getText(72)); // LINEAL
|
menu.options->setItemCaption(i, Lang::get()->getText(60) + ": " + Lang::get()->getText(72)); // LINEAL
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// VSYNC
|
// VSYNC
|
||||||
if (options->vSync)
|
if (Options::video.vsync)
|
||||||
menu.options->setItemCaption(i, lang->getText(61) + ": " + lang->getText(73)); // ON
|
menu.options->setItemCaption(i, Lang::get()->getText(61) + ": " + Lang::get()->getText(73)); // ON
|
||||||
else
|
else
|
||||||
menu.options->setItemCaption(i, lang->getText(61) + ": " + lang->getText(74)); // OFF
|
menu.options->setItemCaption(i, Lang::get()->getText(61) + ": " + Lang::get()->getText(74)); // OFF
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// HOW TO PLAY
|
// HOW TO PLAY
|
||||||
menu.options->setItemCaption(i, lang->getText(2));
|
menu.options->setItemCaption(i, Lang::get()->getText(2));
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// ACCEPT
|
// ACCEPT
|
||||||
menu.options->setItemCaption(i, lang->getText(9)); // ACCEPT
|
menu.options->setItemCaption(i, Lang::get()->getText(9)); // ACCEPT
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
// CANCEL
|
// CANCEL
|
||||||
menu.options->setItemCaption(i, lang->getText(10)); // CANCEL
|
menu.options->setItemCaption(i, Lang::get()->getText(10)); // CANCEL
|
||||||
|
|
||||||
// Recoloca el menu de opciones
|
// Recoloca el menu de opciones
|
||||||
menu.options->centerMenuOnX(GAMECANVAS_CENTER_X);
|
menu.options->centerMenuOnX(GAMECANVAS_CENTER_X);
|
||||||
@@ -833,13 +778,13 @@ void Title::updateMenuLabels() {
|
|||||||
|
|
||||||
// Establece las etiquetas del menu de titulo
|
// Establece las etiquetas del menu de titulo
|
||||||
#ifdef GAME_CONSOLE
|
#ifdef GAME_CONSOLE
|
||||||
menu.title->setItemCaption(0, lang->getText(0)); // PLAY
|
menu.title->setItemCaption(0, Lang::get()->getText(0)); // PLAY
|
||||||
#else
|
#else
|
||||||
menu.title->setItemCaption(0, lang->getText(51)); // 1 PLAYER
|
menu.title->setItemCaption(0, Lang::get()->getText(51)); // 1 PLAYER
|
||||||
#endif
|
#endif
|
||||||
menu.title->setItemCaption(1, lang->getText(52)); // 2 PLAYERS
|
menu.title->setItemCaption(1, Lang::get()->getText(52)); // 2 PLAYERS
|
||||||
menu.title->setItemCaption(2, lang->getText(1)); // OPTIONS
|
menu.title->setItemCaption(2, Lang::get()->getText(1)); // OPTIONS
|
||||||
menu.title->setItemCaption(3, lang->getText(3)); // QUIT
|
menu.title->setItemCaption(3, Lang::get()->getText(3)); // QUIT
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
menu.title->setVisible(3, false);
|
menu.title->setVisible(3, false);
|
||||||
menu.title->setSelectable(3, false);
|
menu.title->setSelectable(3, false);
|
||||||
@@ -850,8 +795,8 @@ void Title::updateMenuLabels() {
|
|||||||
menu.title->centerMenuElementsOnX();
|
menu.title->centerMenuElementsOnX();
|
||||||
|
|
||||||
// Establece las etiquetas del menu de seleccion de jugador
|
// Establece las etiquetas del menu de seleccion de jugador
|
||||||
menu.playerSelect->setItemCaption(0, lang->getText(39)); // SELECT PLAYER
|
menu.playerSelect->setItemCaption(0, Lang::get()->getText(39)); // SELECT PLAYER
|
||||||
menu.playerSelect->setItemCaption(3, lang->getText(40)); // BACK
|
menu.playerSelect->setItemCaption(3, Lang::get()->getText(40)); // BACK
|
||||||
|
|
||||||
// Recoloca el menu de selección de jugador
|
// Recoloca el menu de selección de jugador
|
||||||
menu.playerSelect->centerMenuOnX(GAMECANVAS_CENTER_X);
|
menu.playerSelect->centerMenuOnX(GAMECANVAS_CENTER_X);
|
||||||
@@ -871,9 +816,11 @@ void Title::updateMenuLabels() {
|
|||||||
|
|
||||||
// Aplica las opciones de menu seleccionadas
|
// Aplica las opciones de menu seleccionadas
|
||||||
void Title::applyOptions() {
|
void Title::applyOptions() {
|
||||||
screen->setVideoMode(options->videoMode != 0);
|
Screen::get()->setVideoMode(Options::video.fullscreen);
|
||||||
|
Screen::get()->setWindowZoom(Options::window.zoom);
|
||||||
|
Screen::get()->setVSync(Options::video.vsync);
|
||||||
|
|
||||||
lang->setLang(options->language);
|
Lang::get()->setLang(Options::settings.language);
|
||||||
|
|
||||||
updateMenuLabels();
|
updateMenuLabels();
|
||||||
createTiledBackground();
|
createTiledBackground();
|
||||||
@@ -942,7 +889,7 @@ void Title::iterate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Procesa un evento individual
|
// Procesa un evento individual
|
||||||
void Title::handleEvent(SDL_Event *event) {
|
void Title::handleEvent(const SDL_Event *event) {
|
||||||
// Si hay un sub-estado activo, delega el evento
|
// Si hay un sub-estado activo, delega el evento
|
||||||
if (instructionsActive && instructions) {
|
if (instructionsActive && instructions) {
|
||||||
// SDL_EVENT_QUIT ya lo maneja Director
|
// SDL_EVENT_QUIT ya lo maneja Director
|
||||||
@@ -977,7 +924,7 @@ void Title::run() {
|
|||||||
|
|
||||||
// Inicia la parte donde se muestran las instrucciones
|
// Inicia la parte donde se muestran las instrucciones
|
||||||
void Title::runInstructions(mode_e mode) {
|
void Title::runInstructions(mode_e mode) {
|
||||||
instructions = new Instructions(renderer, screen, asset, input, lang, section);
|
instructions = new Instructions(renderer, section);
|
||||||
instructions->start(mode);
|
instructions->start(mode);
|
||||||
instructionsActive = true;
|
instructionsActive = true;
|
||||||
instructionsMode = mode;
|
instructionsMode = mode;
|
||||||
@@ -988,7 +935,7 @@ void Title::runDemoGame() {
|
|||||||
// Temporalmente ponemos section para que el constructor de Game funcione
|
// Temporalmente ponemos section para que el constructor de Game funcione
|
||||||
section->name = SECTION_PROG_GAME;
|
section->name = SECTION_PROG_GAME;
|
||||||
section->subsection = SUBSECTION_GAME_PLAY_1P;
|
section->subsection = SUBSECTION_GAME_PLAY_1P;
|
||||||
demoGame = new Game(1, 0, renderer, screen, asset, lang, input, true, options, section);
|
demoGame = new Game(1, 0, renderer, true, section);
|
||||||
demoGameActive = true;
|
demoGameActive = true;
|
||||||
// Restauramos section para que Director no transicione fuera de Title
|
// Restauramos section para que Director no transicione fuera de Title
|
||||||
section->name = SECTION_PROG_TITLE;
|
section->name = SECTION_PROG_TITLE;
|
||||||
@@ -998,21 +945,21 @@ void Title::runDemoGame() {
|
|||||||
bool Title::updatePlayerInputs(int numPlayer) {
|
bool Title::updatePlayerInputs(int numPlayer) {
|
||||||
const int numDevices = availableInputDevices.size();
|
const int numDevices = availableInputDevices.size();
|
||||||
|
|
||||||
if (!input->gameControllerFound()) { // Si no hay mandos se deja todo de manera prefijada
|
if (!Input::get()->gameControllerFound()) { // Si no hay mandos se deja todo de manera prefijada
|
||||||
deviceIndex[0] = 0;
|
deviceIndex[0] = 0;
|
||||||
deviceIndex[1] = 0;
|
deviceIndex[1] = 0;
|
||||||
|
|
||||||
options->input[0].id = -1;
|
Options::inputs[0].id = -1;
|
||||||
options->input[0].name = "KEYBOARD";
|
Options::inputs[0].name = "KEYBOARD";
|
||||||
options->input[0].deviceType = INPUT_USE_KEYBOARD;
|
Options::inputs[0].deviceType = INPUT_USE_KEYBOARD;
|
||||||
|
|
||||||
options->input[1].id = 0;
|
Options::inputs[1].id = 0;
|
||||||
options->input[1].name = "GAME CONTROLLER";
|
Options::inputs[1].name = "GAME CONTROLLER";
|
||||||
options->input[1].deviceType = INPUT_USE_GAMECONTROLLER;
|
Options::inputs[1].deviceType = INPUT_USE_GAMECONTROLLER;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else { // Si hay mas de un dispositivo, se recorre el vector
|
} else { // Si hay mas de un dispositivo, se recorre el vector
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "numplayer:" << numPlayer << std::endl;
|
std::cout << "numplayer:" << numPlayer << std::endl;
|
||||||
std::cout << "deviceindex:" << deviceIndex[numPlayer] << std::endl;
|
std::cout << "deviceindex:" << deviceIndex[numPlayer] << std::endl;
|
||||||
}
|
}
|
||||||
@@ -1023,7 +970,7 @@ bool Title::updatePlayerInputs(int numPlayer) {
|
|||||||
} else {
|
} else {
|
||||||
deviceIndex[numPlayer] = 0;
|
deviceIndex[numPlayer] = 0;
|
||||||
}
|
}
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "deviceindex:" << deviceIndex[numPlayer] << std::endl;
|
std::cout << "deviceindex:" << deviceIndex[numPlayer] << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1037,8 +984,8 @@ bool Title::updatePlayerInputs(int numPlayer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copia el dispositivo marcado por el indice a la variable de opciones de cada jugador
|
// Copia el dispositivo marcado por el indice a la variable de opciones de cada jugador
|
||||||
options->input[0] = availableInputDevices[deviceIndex[0]];
|
Options::inputs[0] = availableInputDevices[deviceIndex[0]];
|
||||||
options->input[1] = availableInputDevices[deviceIndex[1]];
|
Options::inputs[1] = availableInputDevices[deviceIndex[1]];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1052,13 +999,13 @@ void Title::createTiledBackground() {
|
|||||||
SDL_SetTextureScaleMode(background, Texture::currentScaleMode);
|
SDL_SetTextureScaleMode(background, Texture::currentScaleMode);
|
||||||
}
|
}
|
||||||
if (background == nullptr) {
|
if (background == nullptr) {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "TitleSurface could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
std::cout << "TitleSurface could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea los objetos para pintar en la textura de fondo
|
// Crea los objetos para pintar en la textura de fondo
|
||||||
Texture *bgTileTexture = new Texture(renderer, asset->get("title_bg_tile.png"));
|
Texture *bgTileTexture = new Texture(renderer, Asset::get()->get("title_bg_tile.png"));
|
||||||
Sprite *tile = new Sprite({0, 0, 64, 64}, bgTileTexture, renderer);
|
Sprite *tile = new Sprite({0, 0, 64, 64}, bgTileTexture, renderer);
|
||||||
|
|
||||||
// Prepara para dibujar sobre la textura
|
// Prepara para dibujar sobre la textura
|
||||||
@@ -1089,10 +1036,10 @@ void Title::createTiledBackground() {
|
|||||||
// El estado de Input lo mantiene al día Director via eventos SDL_EVENT_GAMEPAD_ADDED/REMOVED,
|
// El estado de Input lo mantiene al día Director via eventos SDL_EVENT_GAMEPAD_ADDED/REMOVED,
|
||||||
// así que aquí solo leemos la lista actual sin reescanear.
|
// así que aquí solo leemos la lista actual sin reescanear.
|
||||||
void Title::checkInputDevices() {
|
void Title::checkInputDevices() {
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Filling devices for options menu..." << std::endl;
|
std::cout << "Filling devices for options menu..." << std::endl;
|
||||||
}
|
}
|
||||||
const int numControllers = input->getNumControllers();
|
const int numControllers = Input::get()->getNumControllers();
|
||||||
availableInputDevices.clear();
|
availableInputDevices.clear();
|
||||||
input_t temp;
|
input_t temp;
|
||||||
|
|
||||||
@@ -1100,10 +1047,10 @@ void Title::checkInputDevices() {
|
|||||||
if (numControllers > 0)
|
if (numControllers > 0)
|
||||||
for (int i = 0; i < numControllers; ++i) {
|
for (int i = 0; i < numControllers; ++i) {
|
||||||
temp.id = i;
|
temp.id = i;
|
||||||
temp.name = input->getControllerName(i);
|
temp.name = Input::get()->getControllerName(i);
|
||||||
temp.deviceType = INPUT_USE_GAMECONTROLLER;
|
temp.deviceType = INPUT_USE_GAMECONTROLLER;
|
||||||
availableInputDevices.push_back(temp);
|
availableInputDevices.push_back(temp);
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Device " << (int)availableInputDevices.size() << " - " << temp.name.c_str() << std::endl;
|
std::cout << "Device " << (int)availableInputDevices.size() << " - " << temp.name.c_str() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1113,7 +1060,7 @@ void Title::checkInputDevices() {
|
|||||||
temp.name = "KEYBOARD";
|
temp.name = "KEYBOARD";
|
||||||
temp.deviceType = INPUT_USE_KEYBOARD;
|
temp.deviceType = INPUT_USE_KEYBOARD;
|
||||||
availableInputDevices.push_back(temp);
|
availableInputDevices.push_back(temp);
|
||||||
if (options->console) {
|
if (Options::settings.console) {
|
||||||
std::cout << "Device " << (int)availableInputDevices.size() << " - " << temp.name.c_str() << std::endl;
|
std::cout << "Device " << (int)availableInputDevices.size() << " - " << temp.name.c_str() << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
@@ -4,16 +4,13 @@
|
|||||||
|
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "instructions.h" // for mode_e
|
#include "game/options.hpp" // for Options::Video, Options::Window (per snapshot cancel)
|
||||||
#include "utils.h" // for input_t, options_t, section_t
|
#include "game/scenes/instructions.h" // for mode_e
|
||||||
|
#include "utils/utils.h" // for input_t, section_t
|
||||||
class AnimatedSprite;
|
class AnimatedSprite;
|
||||||
class Asset;
|
|
||||||
class Fade;
|
class Fade;
|
||||||
class Game;
|
class Game;
|
||||||
class Input;
|
|
||||||
class Lang;
|
|
||||||
class Menu;
|
class Menu;
|
||||||
class Screen;
|
|
||||||
class SmartSprite;
|
class SmartSprite;
|
||||||
class Sprite;
|
class Sprite;
|
||||||
class Text;
|
class Text;
|
||||||
@@ -42,10 +39,6 @@ class Title {
|
|||||||
|
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // El renderizador de la ventana
|
SDL_Renderer *renderer; // El renderizador de la ventana
|
||||||
Screen *screen; // Objeto encargado de dibujar en pantalla
|
|
||||||
Asset *asset; // Objeto que gestiona todos los ficheros de recursos
|
|
||||||
Input *input; // Objeto para leer las entradas de teclado o mando
|
|
||||||
Lang *lang; // Objeto para gestionar los textos en diferentes idiomas
|
|
||||||
Instructions *instructions; // Objeto para la sección de las instrucciones
|
Instructions *instructions; // Objeto para la sección de las instrucciones
|
||||||
Game *demoGame; // Objeto para lanzar la demo del juego
|
Game *demoGame; // Objeto para lanzar la demo del juego
|
||||||
SDL_Event *eventHandler; // Manejador de eventos
|
SDL_Event *eventHandler; // Manejador de eventos
|
||||||
@@ -85,8 +78,11 @@ class Title {
|
|||||||
Uint32 ticksSpeed; // Velocidad a la que se repiten los bucles del programa
|
Uint32 ticksSpeed; // Velocidad a la que se repiten los bucles del programa
|
||||||
Uint8 postFade; // Opción a realizar cuando termina el fundido
|
Uint8 postFade; // Opción a realizar cuando termina el fundido
|
||||||
menu_t menu; // Variable con todos los objetos menus y sus variables
|
menu_t menu; // Variable con todos los objetos menus y sus variables
|
||||||
struct options_t *options; // Variable con todas las variables de las opciones del programa
|
// Snapshot per a permetre CANCEL al menú d'opcions.
|
||||||
options_t optionsPrevious; // Variable de respaldo para las opciones
|
Options::Video prevVideo;
|
||||||
|
Options::Window prevWindow;
|
||||||
|
Options::Settings prevSettings;
|
||||||
|
std::vector<input_t> prevInputs;
|
||||||
std::vector<input_t> availableInputDevices; // Vector con todos los metodos de control disponibles
|
std::vector<input_t> availableInputDevices; // Vector con todos los metodos de control disponibles
|
||||||
std::vector<int> deviceIndex; // Indice para el jugador [i] del vector de dispositivos de entrada disponibles
|
std::vector<int> deviceIndex; // Indice para el jugador [i] del vector de dispositivos de entrada disponibles
|
||||||
|
|
||||||
@@ -111,9 +107,6 @@ class Title {
|
|||||||
// Dibuja el objeto en pantalla
|
// Dibuja el objeto en pantalla
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
// Comprueba los eventos
|
|
||||||
void checkEvents();
|
|
||||||
|
|
||||||
// Comprueba las entradas
|
// Comprueba las entradas
|
||||||
void checkInput();
|
void checkInput();
|
||||||
|
|
||||||
@@ -149,7 +142,7 @@ class Title {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Title(SDL_Renderer *renderer, Screen *screen, Input *input, Asset *asset, options_t *options, Lang *lang, section_t *section);
|
Title(SDL_Renderer *renderer, section_t *section);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Title();
|
~Title();
|
||||||
@@ -161,5 +154,5 @@ class Title {
|
|||||||
void iterate();
|
void iterate();
|
||||||
|
|
||||||
// Procesa un evento
|
// Procesa un evento
|
||||||
void handleEvent(SDL_Event *event);
|
void handleEvent(const SDL_Event *event);
|
||||||
};
|
};
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
#include "menu.h"
|
#include "game/ui/menu.h"
|
||||||
|
|
||||||
#include <algorithm> // for max, min
|
#include <algorithm> // for max, min
|
||||||
#include <fstream> // for char_traits, basic_ifstream, basic_istream
|
#include <fstream> // for char_traits, basic_ifstream, basic_istream
|
||||||
|
#include <numeric> // for accumulate
|
||||||
#include <sstream> // for basic_stringstream
|
#include <sstream> // for basic_stringstream
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
#include "core/audio/jail_audio.hpp" // for JA_LoadSound, JA_PlaySound, JA_DeleteSound
|
||||||
#include "input.h" // for Input, REPEAT_FALSE, inputs_e
|
#include "core/input/input.h" // for Input, REPEAT_FALSE, inputs_e
|
||||||
#include "jail_audio.hpp" // for JA_LoadSound, JA_PlaySound, JA_DeleteSound
|
#include "core/rendering/text.h" // for Text
|
||||||
#include "resource_helper.h"
|
#include "core/resources/asset.h" // for Asset
|
||||||
#include "text.h" // for Text
|
#include "core/resources/resource_helper.h"
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Menu::Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file) {
|
Menu::Menu(SDL_Renderer *renderer, const std::string &file)
|
||||||
|
: colorGreyed{128, 128, 128},
|
||||||
|
font_png(""),
|
||||||
|
font_txt("") {
|
||||||
// Copia punteros
|
// Copia punteros
|
||||||
this->renderer = renderer;
|
this->renderer = renderer;
|
||||||
this->asset = asset;
|
|
||||||
this->input = input;
|
|
||||||
|
|
||||||
// Inicializa punteros
|
// Inicializa punteros
|
||||||
soundMove = nullptr;
|
soundMove = nullptr;
|
||||||
@@ -23,7 +25,6 @@ Menu::Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file)
|
|||||||
soundCancel = nullptr;
|
soundCancel = nullptr;
|
||||||
|
|
||||||
// Inicializa variables
|
// Inicializa variables
|
||||||
name = "";
|
|
||||||
selector.index = 0;
|
selector.index = 0;
|
||||||
selector.previousIndex = 0;
|
selector.previousIndex = 0;
|
||||||
itemSelected = MENU_NO_OPTION;
|
itemSelected = MENU_NO_OPTION;
|
||||||
@@ -40,10 +41,7 @@ Menu::Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file)
|
|||||||
centerX = 0;
|
centerX = 0;
|
||||||
centerY = 0;
|
centerY = 0;
|
||||||
widestItem = 0;
|
widestItem = 0;
|
||||||
colorGreyed = {128, 128, 128};
|
|
||||||
defaultActionWhenCancel = 0;
|
defaultActionWhenCancel = 0;
|
||||||
font_png = "";
|
|
||||||
font_txt = "";
|
|
||||||
|
|
||||||
// Selector
|
// Selector
|
||||||
selector.originY = 0;
|
selector.originY = 0;
|
||||||
@@ -65,7 +63,7 @@ Menu::Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file)
|
|||||||
// Inicializa las variables desde un fichero. Si no se pasa fichero, el
|
// Inicializa las variables desde un fichero. Si no se pasa fichero, el
|
||||||
// llamante (p.ej. Resource::preloadAll) usará loadFromBytes después —
|
// llamante (p.ej. Resource::preloadAll) usará loadFromBytes después —
|
||||||
// y ese método ya llama a setSelectorItemColors() y reset() al final.
|
// y ese método ya llama a setSelectorItemColors() y reset() al final.
|
||||||
if (file != "") {
|
if (!file.empty()) {
|
||||||
load(file);
|
load(file);
|
||||||
setSelectorItemColors();
|
setSelectorItemColors();
|
||||||
reset();
|
reset();
|
||||||
@@ -74,8 +72,6 @@ Menu::Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file)
|
|||||||
|
|
||||||
Menu::~Menu() {
|
Menu::~Menu() {
|
||||||
renderer = nullptr;
|
renderer = nullptr;
|
||||||
asset = nullptr;
|
|
||||||
input = nullptr;
|
|
||||||
|
|
||||||
if (soundMove) {
|
if (soundMove) {
|
||||||
JA_DeleteSound(soundMove);
|
JA_DeleteSound(soundMove);
|
||||||
@@ -103,24 +99,24 @@ bool Menu::parseFromStream(std::istream &file, const std::string &filename) {
|
|||||||
|
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
if (line == "[item]") {
|
if (line == "[item]") {
|
||||||
item_t item;
|
item_t newItem;
|
||||||
item.label = "";
|
newItem.label = "";
|
||||||
item.hPaddingDown = 1;
|
newItem.hPaddingDown = 1;
|
||||||
item.selectable = true;
|
newItem.selectable = true;
|
||||||
item.greyed = false;
|
newItem.greyed = false;
|
||||||
item.linkedDown = false;
|
newItem.linkedDown = false;
|
||||||
item.visible = true;
|
newItem.visible = true;
|
||||||
item.line = false;
|
newItem.line = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
std::getline(file, line);
|
std::getline(file, line);
|
||||||
int pos = line.find("=");
|
int pos = line.find("=");
|
||||||
if (!setItem(&item, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
|
if (!setItem(&newItem, line.substr(0, pos), line.substr(pos + 1, line.length()))) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
} while (line != "[/item]");
|
} while (line != "[/item]");
|
||||||
|
|
||||||
addItem(item);
|
addItem(newItem);
|
||||||
} else {
|
} else {
|
||||||
int pos = line.find("=");
|
int pos = line.find("=");
|
||||||
if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
|
if (!setVars(line.substr(0, pos), line.substr(pos + 1, line.length()))) {
|
||||||
@@ -130,8 +126,8 @@ bool Menu::parseFromStream(std::istream &file, const std::string &filename) {
|
|||||||
// Crea el objeto text tan pronto como se pueda. Necesario para añadir items.
|
// Crea el objeto text tan pronto como se pueda. Necesario para añadir items.
|
||||||
// Carga via ResourceHelper para que funcione tanto con pack como con filesystem.
|
// Carga via ResourceHelper para que funcione tanto con pack como con filesystem.
|
||||||
if (font_png != "" && font_txt != "" && !textAllocated) {
|
if (font_png != "" && font_txt != "" && !textAllocated) {
|
||||||
auto pngBytes = ResourceHelper::loadFile(asset->get(font_png));
|
auto pngBytes = ResourceHelper::loadFile(Asset::get()->get(font_png));
|
||||||
auto txtBytes = ResourceHelper::loadFile(asset->get(font_txt));
|
auto txtBytes = ResourceHelper::loadFile(Asset::get()->get(font_txt));
|
||||||
text = new Text(pngBytes, txtBytes, renderer);
|
text = new Text(pngBytes, txtBytes, renderer);
|
||||||
textAllocated = true;
|
textAllocated = true;
|
||||||
}
|
}
|
||||||
@@ -141,7 +137,7 @@ bool Menu::parseFromStream(std::istream &file, const std::string &filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga la configuración del menu desde un archivo de texto
|
// Carga la configuración del menu desde un archivo de texto
|
||||||
bool Menu::load(std::string file_path) {
|
bool Menu::load(const std::string &file_path) {
|
||||||
const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
|
const std::string filename = file_path.substr(file_path.find_last_of("\\/") + 1);
|
||||||
std::ifstream file(file_path);
|
std::ifstream file(file_path);
|
||||||
if (!file.good()) {
|
if (!file.good()) {
|
||||||
@@ -162,7 +158,7 @@ bool Menu::loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
// Asigna variables a partir de dos cadenas
|
||||||
bool Menu::setItem(item_t *item, std::string var, std::string value) {
|
bool Menu::setItem(item_t *item, const std::string &var, const std::string &value) {
|
||||||
// Indicador de éxito en la asignación
|
// Indicador de éxito en la asignación
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
@@ -205,7 +201,7 @@ bool Menu::setItem(item_t *item, std::string var, std::string value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
// Asigna variables a partir de dos cadenas
|
||||||
bool Menu::setVars(std::string var, std::string value) {
|
bool Menu::setVars(const std::string &var, const std::string &value) {
|
||||||
// Indicador de éxito en la asignación
|
// Indicador de éxito en la asignación
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
@@ -218,17 +214,17 @@ bool Menu::setVars(std::string var, std::string value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (var == "sound_cancel") {
|
else if (var == "sound_cancel") {
|
||||||
auto bytes = ResourceHelper::loadFile(asset->get(value));
|
auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
|
||||||
if (!bytes.empty()) soundCancel = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
if (!bytes.empty()) soundCancel = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (var == "sound_accept") {
|
else if (var == "sound_accept") {
|
||||||
auto bytes = ResourceHelper::loadFile(asset->get(value));
|
auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
|
||||||
if (!bytes.empty()) soundAccept = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
if (!bytes.empty()) soundAccept = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (var == "sound_move") {
|
else if (var == "sound_move") {
|
||||||
auto bytes = ResourceHelper::loadFile(asset->get(value));
|
auto bytes = ResourceHelper::loadFile(Asset::get()->get(value));
|
||||||
if (!bytes.empty()) soundMove = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
if (!bytes.empty()) soundMove = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +319,7 @@ bool Menu::setVars(std::string var, std::string value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga los ficheros de audio
|
// Carga los ficheros de audio
|
||||||
void Menu::loadAudioFile(std::string file, int sound) {
|
void Menu::loadAudioFile(const std::string &file, int sound) {
|
||||||
switch (sound) {
|
switch (sound) {
|
||||||
case SOUND_ACCEPT:
|
case SOUND_ACCEPT:
|
||||||
soundAccept = JA_LoadSound(file.c_str());
|
soundAccept = JA_LoadSound(file.c_str());
|
||||||
@@ -343,7 +339,7 @@ void Menu::loadAudioFile(std::string file, int sound) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el nombre del menu
|
// Obtiene el nombre del menu
|
||||||
std::string Menu::getName() {
|
const std::string &Menu::getName() const {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,14 +430,8 @@ void Menu::setSelectorPos(int index) {
|
|||||||
|
|
||||||
// Obtiene la anchura del elemento más ancho del menu
|
// Obtiene la anchura del elemento más ancho del menu
|
||||||
int Menu::getWidestItem() {
|
int Menu::getWidestItem() {
|
||||||
int result = 0;
|
return std::accumulate(item.begin(), item.end(), 0,
|
||||||
|
[](int acc, const item_t &i) { return std::max(acc, i.rect.w); });
|
||||||
// Obtenemos la anchura del item mas ancho
|
|
||||||
for (auto &i : item) {
|
|
||||||
result = std::max(result, i.rect.w);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deja el menu apuntando al primer elemento
|
// Deja el menu apuntando al primer elemento
|
||||||
@@ -478,7 +468,7 @@ void Menu::reorganize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deja el menu apuntando al siguiente elemento
|
// Deja el menu apuntando al siguiente elemento
|
||||||
bool Menu::increaseSelectorIndex() {
|
void Menu::increaseSelectorIndex() {
|
||||||
// Guarda el indice actual antes de modificarlo
|
// Guarda el indice actual antes de modificarlo
|
||||||
selector.previousIndex = selector.index;
|
selector.previousIndex = selector.index;
|
||||||
|
|
||||||
@@ -503,12 +493,10 @@ bool Menu::increaseSelectorIndex() {
|
|||||||
if (selector.incH != 0) {
|
if (selector.incH != 0) {
|
||||||
selector.resizing = true;
|
selector.resizing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deja el menu apuntando al elemento anterior
|
// Deja el menu apuntando al elemento anterior
|
||||||
bool Menu::decreaseSelectorIndex() {
|
void Menu::decreaseSelectorIndex() {
|
||||||
// Guarda el indice actual antes de modificarlo
|
// Guarda el indice actual antes de modificarlo
|
||||||
selector.previousIndex = selector.index;
|
selector.previousIndex = selector.index;
|
||||||
|
|
||||||
@@ -542,8 +530,6 @@ bool Menu::decreaseSelectorIndex() {
|
|||||||
if (selector.incH != 0) {
|
if (selector.incH != 0) {
|
||||||
selector.resizing = true;
|
selector.resizing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la logica del menu
|
// Actualiza la logica del menu
|
||||||
@@ -756,7 +742,7 @@ void Menu::addItem(item_t temp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cambia el texto de un item
|
// Cambia el texto de un item
|
||||||
void Menu::setItemCaption(int index, std::string text) {
|
void Menu::setItemCaption(int index, const std::string &text) {
|
||||||
item[index].label = text;
|
item[index].label = text;
|
||||||
item[index].rect.w = this->text->lenght(item[index].label);
|
item[index].rect.w = this->text->lenght(item[index].label);
|
||||||
item[index].rect.h = this->text->getCharacterSize();
|
item[index].rect.h = this->text->getCharacterSize();
|
||||||
@@ -770,30 +756,28 @@ void Menu::setDefaultActionWhenCancel(int item) {
|
|||||||
|
|
||||||
// Gestiona la entrada de teclado y mando durante el menu
|
// Gestiona la entrada de teclado y mando durante el menu
|
||||||
void Menu::checkInput() {
|
void Menu::checkInput() {
|
||||||
if (input->checkInput(input_up, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_up, REPEAT_FALSE)) {
|
||||||
if (decreaseSelectorIndex()) {
|
decreaseSelectorIndex();
|
||||||
if (soundMove) {
|
if (soundMove) {
|
||||||
JA_PlaySound(soundMove);
|
JA_PlaySound(soundMove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (input->checkInput(input_down, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_down, REPEAT_FALSE)) {
|
||||||
if (increaseSelectorIndex()) {
|
increaseSelectorIndex();
|
||||||
if (soundMove) {
|
if (soundMove) {
|
||||||
JA_PlaySound(soundMove);
|
JA_PlaySound(soundMove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (input->checkInput(input_accept, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_accept, REPEAT_FALSE)) {
|
||||||
itemSelected = selector.index;
|
itemSelected = selector.index;
|
||||||
if (soundAccept) {
|
if (soundAccept) {
|
||||||
JA_PlaySound(soundAccept);
|
JA_PlaySound(soundAccept);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input->checkInput(input_cancel, REPEAT_FALSE)) {
|
if (Input::get()->checkInput(input_cancel, REPEAT_FALSE)) {
|
||||||
itemSelected = defaultActionWhenCancel;
|
itemSelected = defaultActionWhenCancel;
|
||||||
if (soundCancel) {
|
if (soundCancel) {
|
||||||
JA_PlaySound(soundCancel);
|
JA_PlaySound(soundCancel);
|
||||||
@@ -808,12 +792,8 @@ int Menu::findWidth() {
|
|||||||
|
|
||||||
// Calcula el alto del menu
|
// Calcula el alto del menu
|
||||||
int Menu::findHeight() {
|
int Menu::findHeight() {
|
||||||
int height = 0;
|
const int height = std::accumulate(item.begin(), item.end(), 0,
|
||||||
|
[](int acc, const item_t &i) { return acc + i.rect.h + i.hPaddingDown; });
|
||||||
// Obtenemos la altura de la suma de alturas de los items
|
|
||||||
for (auto &i : item) {
|
|
||||||
height += i.rect.h + i.hPaddingDown;
|
|
||||||
}
|
|
||||||
|
|
||||||
return height - item.back().hPaddingDown;
|
return height - item.back().hPaddingDown;
|
||||||
}
|
}
|
||||||
@@ -857,7 +837,7 @@ int Menu::getSelectorHeight(int value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece el nombre del menu
|
// Establece el nombre del menu
|
||||||
void Menu::setName(std::string name) {
|
void Menu::setName(const std::string &name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,9 +853,9 @@ void Menu::setBackgroundType(int value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Establece la fuente de texto que se utilizará
|
// Establece la fuente de texto que se utilizará
|
||||||
void Menu::setText(std::string font_png, std::string font_txt) {
|
void Menu::setText(const std::string &font_png, const std::string &font_txt) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
text = new Text(asset->get(font_png), asset->get(font_txt), renderer);
|
text = new Text(Asset::get()->get(font_png), Asset::get()->get(font_txt), renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6,9 +6,7 @@
|
|||||||
#include <string> // for string, basic_string
|
#include <string> // for string, basic_string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "utils.h" // for color_t
|
#include "utils/utils.h" // for color_t
|
||||||
class Asset;
|
|
||||||
class Input;
|
|
||||||
class Text;
|
class Text;
|
||||||
struct JA_Sound_t;
|
struct JA_Sound_t;
|
||||||
|
|
||||||
@@ -70,9 +68,7 @@ class Menu {
|
|||||||
|
|
||||||
// Objetos y punteros
|
// Objetos y punteros
|
||||||
SDL_Renderer *renderer; // Puntero al renderizador de la ventana
|
SDL_Renderer *renderer; // Puntero al renderizador de la ventana
|
||||||
Asset *asset; // Objeto para gestionar los ficheros de recursos
|
|
||||||
Text *text; // Texto para poder escribir los items del menu
|
Text *text; // Texto para poder escribir los items del menu
|
||||||
Input *input; // Gestor de eventos de entrada de teclado o gamepad
|
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
std::string name; // Nombre del menu
|
std::string name; // Nombre del menu
|
||||||
@@ -100,25 +96,25 @@ class Menu {
|
|||||||
std::string font_txt;
|
std::string font_txt;
|
||||||
|
|
||||||
// Carga la configuración del menu desde un archivo de texto
|
// Carga la configuración del menu desde un archivo de texto
|
||||||
bool load(std::string file_path);
|
bool load(const std::string &file_path);
|
||||||
|
|
||||||
// Parser compartido (recibe cualquier istream)
|
// Parser compartido (recibe cualquier istream)
|
||||||
bool parseFromStream(std::istream &file, const std::string &filename);
|
bool parseFromStream(std::istream &file, const std::string &filename);
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
// Asigna variables a partir de dos cadenas
|
||||||
bool setVars(std::string var, std::string value);
|
bool setVars(const std::string &var, const std::string &value);
|
||||||
|
|
||||||
// Asigna variables a partir de dos cadenas
|
// Asigna variables a partir de dos cadenas
|
||||||
bool setItem(item_t *item, std::string var, std::string value);
|
bool setItem(item_t *item, const std::string &var, const std::string &value);
|
||||||
|
|
||||||
// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
|
// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
|
||||||
void reorganize();
|
void reorganize();
|
||||||
|
|
||||||
// Deja el menu apuntando al siguiente elemento
|
// Deja el menu apuntando al siguiente elemento
|
||||||
bool increaseSelectorIndex();
|
void increaseSelectorIndex();
|
||||||
|
|
||||||
// Deja el menu apuntando al elemento anterior
|
// Deja el menu apuntando al elemento anterior
|
||||||
bool decreaseSelectorIndex();
|
void decreaseSelectorIndex();
|
||||||
|
|
||||||
// Actualiza la posicion y el estado del selector
|
// Actualiza la posicion y el estado del selector
|
||||||
void updateSelector();
|
void updateSelector();
|
||||||
@@ -146,7 +142,7 @@ class Menu {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
Menu(SDL_Renderer *renderer, Asset *asset, Input *input, std::string file = "");
|
explicit Menu(SDL_Renderer *renderer, const std::string &file = "");
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Menu();
|
~Menu();
|
||||||
@@ -155,10 +151,10 @@ class Menu {
|
|||||||
bool loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "");
|
bool loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "");
|
||||||
|
|
||||||
// Carga los ficheros de audio
|
// Carga los ficheros de audio
|
||||||
void loadAudioFile(std::string file, int sound);
|
void loadAudioFile(const std::string &file, int sound);
|
||||||
|
|
||||||
// Obtiene el nombre del menu
|
// Obtiene el nombre del menu
|
||||||
std::string getName();
|
const std::string &getName() const;
|
||||||
|
|
||||||
// Obtiene el valor de la variable
|
// Obtiene el valor de la variable
|
||||||
int getItemSelected();
|
int getItemSelected();
|
||||||
@@ -197,7 +193,7 @@ class Menu {
|
|||||||
void addItem(item_t item);
|
void addItem(item_t item);
|
||||||
|
|
||||||
// Cambia el texto de un item
|
// Cambia el texto de un item
|
||||||
void setItemCaption(int index, std::string text);
|
void setItemCaption(int index, const std::string &text);
|
||||||
|
|
||||||
// Establece el indice del item que se usará por defecto al cancelar el menu
|
// Establece el indice del item que se usará por defecto al cancelar el menu
|
||||||
void setDefaultActionWhenCancel(int item);
|
void setDefaultActionWhenCancel(int item);
|
||||||
@@ -218,7 +214,7 @@ class Menu {
|
|||||||
void setVisible(int index, bool value);
|
void setVisible(int index, bool value);
|
||||||
|
|
||||||
// Establece el nombre del menu
|
// Establece el nombre del menu
|
||||||
void setName(std::string name);
|
void setName(const std::string &name);
|
||||||
|
|
||||||
// Establece la posición del menu
|
// Establece la posición del menu
|
||||||
void setPos(int x, int y);
|
void setPos(int x, int y);
|
||||||
@@ -227,7 +223,7 @@ class Menu {
|
|||||||
void setBackgroundType(int value);
|
void setBackgroundType(int value);
|
||||||
|
|
||||||
// Establece la fuente de texto que se utilizará
|
// Establece la fuente de texto que se utilizará
|
||||||
void setText(std::string font_png, std::string font_txt);
|
void setText(const std::string &font_png, const std::string &font_txt);
|
||||||
|
|
||||||
// Establece el rectangulo de fondo del menu
|
// Establece el rectangulo de fondo del menu
|
||||||
void setRectSize(int w = 0, int h = 0);
|
void setRectSize(int w = 0, int h = 0);
|
||||||
@@ -42,7 +42,7 @@ Reescribiendo el código el 27/09/2022
|
|||||||
#define SDL_MAIN_USE_CALLBACKS 1
|
#define SDL_MAIN_USE_CALLBACKS 1
|
||||||
#include <SDL3/SDL_main.h>
|
#include <SDL3/SDL_main.h>
|
||||||
|
|
||||||
#include "director.h"
|
#include "core/system/director.h"
|
||||||
#include "external/stb_vorbis.c"
|
#include "external/stb_vorbis.c"
|
||||||
|
|
||||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
||||||
|
|||||||
@@ -1,369 +0,0 @@
|
|||||||
#include "screen.h"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#include <algorithm> // for max, min
|
|
||||||
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
|
||||||
#include <string> // for basic_string, char_traits, string
|
|
||||||
|
|
||||||
#include "asset.h" // for Asset
|
|
||||||
#include "mouse.hpp" // for Mouse::cursorVisible, Mouse::lastMouseMoveTime
|
|
||||||
#include "resource.h"
|
|
||||||
#include "text.h" // for Text, TXT_CENTER, TXT_COLOR, TXT_STROKE
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
#include <emscripten.h>
|
|
||||||
#include <emscripten/html5.h>
|
|
||||||
|
|
||||||
// --- Fix per a fullscreen/resize en Emscripten ---
|
|
||||||
//
|
|
||||||
// SDL3 + Emscripten no emet de forma fiable SDL_EVENT_WINDOW_LEAVE_FULLSCREEN
|
|
||||||
// (libsdl-org/SDL#13300) ni SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED /
|
|
||||||
// SDL_EVENT_DISPLAY_ORIENTATION (libsdl-org/SDL#11389). Quan l'usuari ix de
|
|
||||||
// fullscreen amb Esc o rota el mòbil, el canvas HTML torna al tamany correcte
|
|
||||||
// però l'estat intern de SDL creu que segueix en fullscreen amb la resolució
|
|
||||||
// anterior i el viewport queda desencuadrat.
|
|
||||||
//
|
|
||||||
// Solució: registrar callbacks natius d'Emscripten, diferir la feina un tick
|
|
||||||
// del event loop (el canvas encara no està estable en el moment del callback)
|
|
||||||
// i cridar setVideoMode() amb el flag de fullscreen actualitzat. La crida
|
|
||||||
// interna a SDL_SetWindowFullscreen(false) és la peça que realment fa eixir
|
|
||||||
// SDL del seu estat intern de fullscreen — sense això res més funciona.
|
|
||||||
namespace {
|
|
||||||
Screen *g_screen_instance = nullptr;
|
|
||||||
|
|
||||||
void deferredCanvasResize(void * /*userData*/) {
|
|
||||||
if (g_screen_instance) {
|
|
||||||
g_screen_instance->handleCanvasResized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EM_BOOL onEmFullscreenChange(int /*eventType*/, const EmscriptenFullscreenChangeEvent *event, void * /*userData*/) {
|
|
||||||
if (g_screen_instance && event) {
|
|
||||||
g_screen_instance->syncFullscreenFlagFromBrowser(event->isFullscreen != 0);
|
|
||||||
}
|
|
||||||
emscripten_async_call(deferredCanvasResize, nullptr, 0);
|
|
||||||
return EM_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
EM_BOOL onEmOrientationChange(int /*eventType*/, const EmscriptenOrientationChangeEvent * /*event*/, void * /*userData*/) {
|
|
||||||
emscripten_async_call(deferredCanvasResize, nullptr, 0);
|
|
||||||
return EM_FALSE;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
#endif // __EMSCRIPTEN__
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, options_t *options) {
|
|
||||||
// Inicializa variables
|
|
||||||
this->window = window;
|
|
||||||
this->renderer = renderer;
|
|
||||||
this->options = options;
|
|
||||||
this->asset = asset;
|
|
||||||
|
|
||||||
gameCanvasWidth = options->gameWidth;
|
|
||||||
gameCanvasHeight = options->gameHeight;
|
|
||||||
borderWidth = options->borderWidth * 2;
|
|
||||||
borderHeight = options->borderHeight * 2;
|
|
||||||
|
|
||||||
// Define el color del borde para el modo de pantalla completa
|
|
||||||
borderColor = {0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
// Crea la textura donde se dibujan los graficos del juego
|
|
||||||
gameCanvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, gameCanvasWidth, gameCanvasHeight);
|
|
||||||
if (gameCanvas != nullptr) {
|
|
||||||
SDL_SetTextureScaleMode(gameCanvas, options->filter == FILTER_NEAREST ? SDL_SCALEMODE_NEAREST : SDL_SCALEMODE_LINEAR);
|
|
||||||
}
|
|
||||||
if (gameCanvas == nullptr) {
|
|
||||||
if (options->console) {
|
|
||||||
std::cout << "gameCanvas could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece el modo de video
|
|
||||||
setVideoMode(options->videoMode != 0);
|
|
||||||
|
|
||||||
// Inicializa el sistema de notificaciones (Text compartido de Resource)
|
|
||||||
notificationText = Resource::get()->getText("8bithud");
|
|
||||||
notificationMessage = "";
|
|
||||||
notificationTextColor = {0xFF, 0xFF, 0xFF};
|
|
||||||
notificationOutlineColor = {0x00, 0x00, 0x00};
|
|
||||||
notificationEndTime = 0;
|
|
||||||
notificationY = 2;
|
|
||||||
|
|
||||||
// Registra callbacks natius d'Emscripten per a fullscreen/orientation
|
|
||||||
registerEmscriptenEventCallbacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
Screen::~Screen() {
|
|
||||||
// notificationText es propiedad de Resource — no liberar.
|
|
||||||
SDL_DestroyTexture(gameCanvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limpia la pantalla
|
|
||||||
void Screen::clean(color_t color) {
|
|
||||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, 0xFF);
|
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepara para empezar a dibujar en la textura de juego
|
|
||||||
void Screen::start() {
|
|
||||||
SDL_SetRenderTarget(renderer, gameCanvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
|
||||||
void Screen::blit() {
|
|
||||||
// Dibuja la notificación activa sobre el gameCanvas antes de presentar
|
|
||||||
SDL_SetRenderTarget(renderer, gameCanvas);
|
|
||||||
renderNotification();
|
|
||||||
|
|
||||||
// Vuelve a dejar el renderizador en modo normal
|
|
||||||
SDL_SetRenderTarget(renderer, nullptr);
|
|
||||||
|
|
||||||
// Borra el contenido previo
|
|
||||||
SDL_SetRenderDrawColor(renderer, borderColor.r, borderColor.g, borderColor.b, 0xFF);
|
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
|
|
||||||
// Copia la textura de juego en el renderizador en la posición adecuada
|
|
||||||
SDL_FRect fdest = {(float)dest.x, (float)dest.y, (float)dest.w, (float)dest.h};
|
|
||||||
SDL_RenderTexture(renderer, gameCanvas, nullptr, &fdest);
|
|
||||||
|
|
||||||
// Muestra por pantalla el renderizador
|
|
||||||
SDL_RenderPresent(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Video y ventana
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Establece el modo de video
|
|
||||||
void Screen::setVideoMode(bool fullscreen) {
|
|
||||||
applyFullscreen(fullscreen);
|
|
||||||
if (fullscreen) {
|
|
||||||
applyFullscreenLayout();
|
|
||||||
} else {
|
|
||||||
applyWindowedLayout();
|
|
||||||
}
|
|
||||||
applyLogicalPresentation(fullscreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cambia entre pantalla completa y ventana
|
|
||||||
void Screen::toggleVideoMode() {
|
|
||||||
setVideoMode(options->videoMode == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reduce el zoom de la ventana
|
|
||||||
auto Screen::decWindowZoom() -> bool {
|
|
||||||
if (options->videoMode != 0) { return false; }
|
|
||||||
const int PREV = options->windowSize;
|
|
||||||
options->windowSize = std::max(options->windowSize - 1, WINDOW_ZOOM_MIN);
|
|
||||||
if (options->windowSize == PREV) { return false; }
|
|
||||||
setVideoMode(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aumenta el zoom de la ventana
|
|
||||||
auto Screen::incWindowZoom() -> bool {
|
|
||||||
if (options->videoMode != 0) { return false; }
|
|
||||||
const int PREV = options->windowSize;
|
|
||||||
options->windowSize = std::min(options->windowSize + 1, WINDOW_ZOOM_MAX);
|
|
||||||
if (options->windowSize == PREV) { return false; }
|
|
||||||
setVideoMode(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece el zoom de la ventana directamente
|
|
||||||
auto Screen::setWindowZoom(int zoom) -> bool {
|
|
||||||
if (options->videoMode != 0) { return false; }
|
|
||||||
if (zoom < WINDOW_ZOOM_MIN || zoom > WINDOW_ZOOM_MAX) { return false; }
|
|
||||||
if (zoom == options->windowSize) { return false; }
|
|
||||||
options->windowSize = zoom;
|
|
||||||
setVideoMode(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece el escalado entero
|
|
||||||
void Screen::setIntegerScale(bool enabled) {
|
|
||||||
if (options->integerScale == enabled) { return; }
|
|
||||||
options->integerScale = enabled;
|
|
||||||
setVideoMode(options->videoMode != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alterna el escalado entero
|
|
||||||
void Screen::toggleIntegerScale() {
|
|
||||||
setIntegerScale(!options->integerScale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establece el V-Sync
|
|
||||||
void Screen::setVSync(bool enabled) {
|
|
||||||
options->vSync = enabled;
|
|
||||||
SDL_SetRenderVSync(renderer, enabled ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alterna el V-Sync
|
|
||||||
void Screen::toggleVSync() {
|
|
||||||
setVSync(!options->vSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cambia el color del borde
|
|
||||||
void Screen::setBorderColor(color_t color) {
|
|
||||||
borderColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Helpers privados de setVideoMode
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// SDL_SetWindowFullscreen + visibilidad del cursor
|
|
||||||
void Screen::applyFullscreen(bool fullscreen) {
|
|
||||||
SDL_SetWindowFullscreen(window, fullscreen);
|
|
||||||
if (fullscreen) {
|
|
||||||
SDL_HideCursor();
|
|
||||||
Mouse::cursorVisible = false;
|
|
||||||
} else {
|
|
||||||
SDL_ShowCursor();
|
|
||||||
Mouse::cursorVisible = true;
|
|
||||||
Mouse::lastMouseMoveTime = SDL_GetTicks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcula windowWidth/Height/dest para el modo ventana y aplica SDL_SetWindowSize
|
|
||||||
void Screen::applyWindowedLayout() {
|
|
||||||
if (options->borderEnabled) {
|
|
||||||
windowWidth = gameCanvasWidth + borderWidth;
|
|
||||||
windowHeight = gameCanvasHeight + borderHeight;
|
|
||||||
dest = {0 + (borderWidth / 2), 0 + (borderHeight / 2), gameCanvasWidth, gameCanvasHeight};
|
|
||||||
} else {
|
|
||||||
windowWidth = gameCanvasWidth;
|
|
||||||
windowHeight = gameCanvasHeight;
|
|
||||||
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
windowWidth *= WASM_RENDER_SCALE;
|
|
||||||
windowHeight *= WASM_RENDER_SCALE;
|
|
||||||
dest.w *= WASM_RENDER_SCALE;
|
|
||||||
dest.h *= WASM_RENDER_SCALE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Modifica el tamaño de la ventana
|
|
||||||
SDL_SetWindowSize(window, windowWidth * options->windowSize, windowHeight * options->windowSize);
|
|
||||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtiene el tamaño de la ventana en fullscreen y calcula el rect del juego
|
|
||||||
void Screen::applyFullscreenLayout() {
|
|
||||||
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
|
||||||
computeFullscreenGameRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcula el rectángulo dest para fullscreen: integerScale / keepAspect / stretched
|
|
||||||
void Screen::computeFullscreenGameRect() {
|
|
||||||
if (options->integerScale) {
|
|
||||||
// Calcula el tamaño de la escala máxima
|
|
||||||
int scale = 0;
|
|
||||||
while (((gameCanvasWidth * (scale + 1)) <= windowWidth) && ((gameCanvasHeight * (scale + 1)) <= windowHeight)) {
|
|
||||||
scale++;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest.w = gameCanvasWidth * scale;
|
|
||||||
dest.h = gameCanvasHeight * scale;
|
|
||||||
dest.x = (windowWidth - dest.w) / 2;
|
|
||||||
dest.y = (windowHeight - dest.h) / 2;
|
|
||||||
} else if (options->keepAspect) {
|
|
||||||
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
|
|
||||||
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight)) {
|
|
||||||
dest.h = windowHeight;
|
|
||||||
dest.w = (int)((windowHeight * ratio) + 0.5f);
|
|
||||||
dest.x = (windowWidth - dest.w) / 2;
|
|
||||||
dest.y = (windowHeight - dest.h) / 2;
|
|
||||||
} else {
|
|
||||||
dest.w = windowWidth;
|
|
||||||
dest.h = (int)((windowWidth / ratio) + 0.5f);
|
|
||||||
dest.x = (windowWidth - dest.w) / 2;
|
|
||||||
dest.y = (windowHeight - dest.h) / 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dest.w = windowWidth;
|
|
||||||
dest.h = windowHeight;
|
|
||||||
dest.x = dest.y = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aplica la logical presentation y persiste el estado en options
|
|
||||||
void Screen::applyLogicalPresentation(bool fullscreen) {
|
|
||||||
SDL_SetRenderLogicalPresentation(renderer, windowWidth, windowHeight, SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
|
||||||
|
|
||||||
// Actualiza las opciones
|
|
||||||
options->videoMode = fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
|
|
||||||
options->screen.windowWidth = windowWidth;
|
|
||||||
options->screen.windowHeight = windowHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Notificaciones
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Muestra una notificación en la línea superior durante durationMs
|
|
||||||
void Screen::notify(const std::string &text, color_t textColor, color_t outlineColor, Uint32 durationMs) {
|
|
||||||
notificationMessage = text;
|
|
||||||
notificationTextColor = textColor;
|
|
||||||
notificationOutlineColor = outlineColor;
|
|
||||||
notificationEndTime = SDL_GetTicks() + durationMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limpia la notificación actual
|
|
||||||
void Screen::clearNotification() {
|
|
||||||
notificationEndTime = 0;
|
|
||||||
notificationMessage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dibuja la notificación activa (si la hay) sobre el gameCanvas
|
|
||||||
void Screen::renderNotification() {
|
|
||||||
if (SDL_GetTicks() >= notificationEndTime) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
notificationText->writeDX(TXT_CENTER | TXT_COLOR | TXT_STROKE,
|
|
||||||
gameCanvasWidth / 2,
|
|
||||||
notificationY,
|
|
||||||
notificationMessage,
|
|
||||||
1,
|
|
||||||
notificationTextColor,
|
|
||||||
1,
|
|
||||||
notificationOutlineColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Emscripten — fix per a fullscreen/resize (veure el bloc de comentaris al
|
|
||||||
// principi del fitxer i l'anonymous namespace amb els callbacks natius).
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
void Screen::handleCanvasResized() {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// La crida a SDL_SetWindowFullscreen + SDL_SetRenderLogicalPresentation
|
|
||||||
// que fa setVideoMode és l'única manera de resincronitzar l'estat intern
|
|
||||||
// de SDL amb el canvas HTML real.
|
|
||||||
setVideoMode(options->videoMode != 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Screen::syncFullscreenFlagFromBrowser(bool isFullscreen) {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
options->videoMode = isFullscreen ? SDL_WINDOW_FULLSCREEN : 0;
|
|
||||||
#else
|
|
||||||
(void)isFullscreen;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Screen::registerEmscriptenEventCallbacks() {
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
// IMPORTANT: NO registrem resize callback. En mòbil, fer scroll fa que el
|
|
||||||
// navegador oculti/mostri la barra d'URL, disparant un resize del DOM per
|
|
||||||
// cada scroll. Això portava a cridar setVideoMode per cada scroll, que
|
|
||||||
// re-aplicava la logical presentation i corrompia el viewport intern de SDL.
|
|
||||||
g_screen_instance = this;
|
|
||||||
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, nullptr, EM_TRUE, onEmFullscreenChange);
|
|
||||||
emscripten_set_orientationchange_callback(nullptr, EM_TRUE, onEmOrientationChange);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "utils.h"
|
#include "utils/utils.h"
|
||||||
|
|
||||||
#include <stdlib.h> // for abs, free, malloc
|
|
||||||
|
|
||||||
|
#include <algorithm> // for transform
|
||||||
|
#include <cctype> // for tolower
|
||||||
#include <cmath> // for round, abs
|
#include <cmath> // for round, abs
|
||||||
|
#include <cstdlib> // for abs
|
||||||
|
|
||||||
// Calcula el cuadrado de la distancia entre dos puntos
|
// Calcula el cuadrado de la distancia entre dos puntos
|
||||||
double distanceSquared(int x1, int y1, int x2, int y2) {
|
double distanceSquared(int x1, int y1, int x2, int y2) {
|
||||||
@@ -12,7 +13,7 @@ double distanceSquared(int x1, int y1, int x2, int y2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre dos circulos
|
// Detector de colisiones entre dos circulos
|
||||||
bool checkCollision(circle_t &a, circle_t &b) {
|
bool checkCollision(const circle_t &a, const circle_t &b) {
|
||||||
// Calcula el radio total al cuadrado
|
// Calcula el radio total al cuadrado
|
||||||
int totalRadiusSquared = a.r + b.r;
|
int totalRadiusSquared = a.r + b.r;
|
||||||
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
|
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
|
||||||
@@ -28,7 +29,7 @@ bool checkCollision(circle_t &a, circle_t &b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre un circulo y un rectangulo
|
// Detector de colisiones entre un circulo y un rectangulo
|
||||||
bool checkCollision(circle_t &a, SDL_Rect &b) {
|
bool checkCollision(const circle_t &a, const SDL_Rect &b) {
|
||||||
// Closest point on collision box
|
// Closest point on collision box
|
||||||
int cX, cY;
|
int cX, cY;
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ bool checkCollision(circle_t &a, SDL_Rect &b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre dos rectangulos
|
// Detector de colisiones entre dos rectangulos
|
||||||
bool checkCollision(SDL_Rect &a, SDL_Rect &b) {
|
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b) {
|
||||||
// Calcula las caras del rectangulo a
|
// Calcula las caras del rectangulo a
|
||||||
const int leftA = a.x;
|
const int leftA = a.x;
|
||||||
const int rightA = a.x + a.w;
|
const int rightA = a.x + a.w;
|
||||||
@@ -96,7 +97,7 @@ bool checkCollision(SDL_Rect &a, SDL_Rect &b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre un punto y un rectangulo
|
// Detector de colisiones entre un punto y un rectangulo
|
||||||
bool checkCollision(SDL_Point &p, SDL_Rect &r) {
|
bool checkCollision(const SDL_Point &p, const SDL_Rect &r) {
|
||||||
// Comprueba si el punto está a la izquierda del rectangulo
|
// Comprueba si el punto está a la izquierda del rectangulo
|
||||||
if (p.x < r.x) {
|
if (p.x < r.x) {
|
||||||
return false;
|
return false;
|
||||||
@@ -122,7 +123,7 @@ bool checkCollision(SDL_Point &p, SDL_Rect &r) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre una linea horizontal y un rectangulo
|
// Detector de colisiones entre una linea horizontal y un rectangulo
|
||||||
bool checkCollision(h_line_t &l, SDL_Rect &r) {
|
bool checkCollision(const h_line_t &l, const SDL_Rect &r) {
|
||||||
// Comprueba si la linea esta por encima del rectangulo
|
// Comprueba si la linea esta por encima del rectangulo
|
||||||
if (l.y < r.y) {
|
if (l.y < r.y) {
|
||||||
return false;
|
return false;
|
||||||
@@ -148,7 +149,7 @@ bool checkCollision(h_line_t &l, SDL_Rect &r) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre una linea vertical y un rectangulo
|
// Detector de colisiones entre una linea vertical y un rectangulo
|
||||||
bool checkCollision(v_line_t &l, SDL_Rect &r) {
|
bool checkCollision(const v_line_t &l, const SDL_Rect &r) {
|
||||||
// Comprueba si la linea esta por la izquierda del rectangulo
|
// Comprueba si la linea esta por la izquierda del rectangulo
|
||||||
if (l.x < r.x) {
|
if (l.x < r.x) {
|
||||||
return false;
|
return false;
|
||||||
@@ -174,7 +175,7 @@ bool checkCollision(v_line_t &l, SDL_Rect &r) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre una linea horizontal y un punto
|
// Detector de colisiones entre una linea horizontal y un punto
|
||||||
bool checkCollision(h_line_t &l, SDL_Point &p) {
|
bool checkCollision(const h_line_t &l, const SDL_Point &p) {
|
||||||
// Comprueba si el punto esta sobre la linea
|
// Comprueba si el punto esta sobre la linea
|
||||||
if (p.y > l.y) {
|
if (p.y > l.y) {
|
||||||
return false;
|
return false;
|
||||||
@@ -200,7 +201,7 @@ bool checkCollision(h_line_t &l, SDL_Point &p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre dos lineas
|
// Detector de colisiones entre dos lineas
|
||||||
SDL_Point checkCollision(line_t &l1, line_t &l2) {
|
SDL_Point checkCollision(const line_t &l1, const line_t &l2) {
|
||||||
const float x1 = l1.x1;
|
const float x1 = l1.x1;
|
||||||
const float y1 = l1.y1;
|
const float y1 = l1.y1;
|
||||||
const float x2 = l1.x2;
|
const float x2 = l1.x2;
|
||||||
@@ -227,7 +228,7 @@ SDL_Point checkCollision(line_t &l1, line_t &l2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre dos lineas
|
// Detector de colisiones entre dos lineas
|
||||||
SDL_Point checkCollision(d_line_t &l1, v_line_t &l2) {
|
SDL_Point checkCollision(const d_line_t &l1, const v_line_t &l2) {
|
||||||
const float x1 = l1.x1;
|
const float x1 = l1.x1;
|
||||||
const float y1 = l1.y1;
|
const float y1 = l1.y1;
|
||||||
const float x2 = l1.x2;
|
const float x2 = l1.x2;
|
||||||
@@ -290,7 +291,7 @@ void normalizeLine(d_line_t &l) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Detector de colisiones entre un punto y una linea diagonal
|
// Detector de colisiones entre un punto y una linea diagonal
|
||||||
bool checkCollision(SDL_Point &p, d_line_t &l) {
|
bool checkCollision(const SDL_Point &p, const d_line_t &l) {
|
||||||
// Comprueba si el punto está en alineado con la linea
|
// Comprueba si el punto está en alineado con la linea
|
||||||
if (abs(p.x - l.x1) != abs(p.y - l.y1)) {
|
if (abs(p.x - l.x1) != abs(p.y - l.y1)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -330,7 +331,7 @@ bool checkCollision(SDL_Point &p, d_line_t &l) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Devuelve un color_t a partir de un string
|
// Devuelve un color_t a partir de un string
|
||||||
color_t stringToColor(palette_e pal, std::string str) {
|
color_t stringToColor(palette_e pal, const std::string &str) {
|
||||||
if (pal == p_zxspectrum) {
|
if (pal == p_zxspectrum) {
|
||||||
if (str == "black") {
|
if (str == "black") {
|
||||||
return {0x00, 0x00, 0x00};
|
return {0x00, 0x00, 0x00};
|
||||||
@@ -467,7 +468,7 @@ color_t stringToColor(palette_e pal, std::string str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convierte una cadena en un valor booleano
|
// Convierte una cadena en un valor booleano
|
||||||
bool stringToBool(std::string str) {
|
bool stringToBool(const std::string &str) {
|
||||||
if (str == "true") {
|
if (str == "true") {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -485,15 +486,9 @@ std::string boolToString(bool value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convierte una cadena a minusculas
|
// Convierte una cadena a minusculas
|
||||||
std::string toLower(std::string str) {
|
std::string toLower(const std::string &str) {
|
||||||
const char *original = str.c_str();
|
std::string result;
|
||||||
char *lower = (char *)malloc(str.size() + 1);
|
result.reserve(str.size());
|
||||||
for (int i = 0; i < (int)str.size(); ++i) {
|
std::transform(str.begin(), str.end(), std::back_inserter(result), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||||
char c = original[i];
|
return result;
|
||||||
lower[i] = (c >= 65 && c <= 90) ? c + 32 : c;
|
|
||||||
}
|
|
||||||
lower[str.size()] = 0;
|
|
||||||
std::string nova(lower);
|
|
||||||
free(lower);
|
|
||||||
return nova;
|
|
||||||
}
|
}
|
||||||
@@ -82,80 +82,50 @@ struct input_t {
|
|||||||
Uint8 deviceType; // Tipo de dispositivo (teclado o mando)
|
Uint8 deviceType; // Tipo de dispositivo (teclado o mando)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Estructura con opciones de la pantalla
|
|
||||||
struct op_screen_t {
|
|
||||||
int windowWidth; // Ancho de la ventana
|
|
||||||
int windowHeight; // Alto de la ventana
|
|
||||||
};
|
|
||||||
|
|
||||||
// Estructura con todas las opciones de configuración del programa
|
|
||||||
struct options_t {
|
|
||||||
Uint8 difficulty; // Dificultad del juego
|
|
||||||
Uint8 playerSelected; // Jugador seleccionado para el modo 1P
|
|
||||||
std::vector<input_t> input; // Modo de control (teclado o mando)
|
|
||||||
Uint8 language; // Idioma usado en el juego
|
|
||||||
|
|
||||||
Uint32 videoMode; // Contiene el valor del modo de pantalla completa
|
|
||||||
int windowSize; // Contiene el valor por el que se multiplica el tamaño de la ventana
|
|
||||||
Uint32 filter; // Filtro usado para el escalado de la imagen
|
|
||||||
bool vSync; // Indica si se quiere usar vsync o no
|
|
||||||
int gameWidth; // Ancho de la resolucion nativa del juego
|
|
||||||
int gameHeight; // Alto de la resolucion nativa del juego
|
|
||||||
bool integerScale; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
|
|
||||||
bool keepAspect; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
|
|
||||||
bool borderEnabled; // Indica si ha de mostrar el borde en el modo de ventana
|
|
||||||
int borderWidth; // Cantidad de pixels que se añade en el borde de la ventana
|
|
||||||
int borderHeight; // Cantidad de pixels que se añade en el borde de la ventana
|
|
||||||
palette_e palette; // Paleta de colores a usar en el juego
|
|
||||||
bool console; // Indica si ha de mostrar información por la consola de texto
|
|
||||||
|
|
||||||
op_screen_t screen; // Opciones relativas a la clase screen
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calcula el cuadrado de la distancia entre dos puntos
|
// Calcula el cuadrado de la distancia entre dos puntos
|
||||||
double distanceSquared(int x1, int y1, int x2, int y2);
|
double distanceSquared(int x1, int y1, int x2, int y2);
|
||||||
|
|
||||||
// Detector de colisiones entre dos circulos
|
// Detector de colisiones entre dos circulos
|
||||||
bool checkCollision(circle_t &a, circle_t &b);
|
bool checkCollision(const circle_t &a, const circle_t &b);
|
||||||
|
|
||||||
// Detector de colisiones entre un circulo y un rectangulo
|
// Detector de colisiones entre un circulo y un rectangulo
|
||||||
bool checkCollision(circle_t &a, SDL_Rect &b);
|
bool checkCollision(const circle_t &a, const SDL_Rect &b);
|
||||||
|
|
||||||
// Detector de colisiones entre un dos rectangulos
|
// Detector de colisiones entre un dos rectangulos
|
||||||
bool checkCollision(SDL_Rect &a, SDL_Rect &b);
|
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b);
|
||||||
|
|
||||||
// Detector de colisiones entre un punto y un rectangulo
|
// Detector de colisiones entre un punto y un rectangulo
|
||||||
bool checkCollision(SDL_Point &p, SDL_Rect &r);
|
bool checkCollision(const SDL_Point &p, const SDL_Rect &r);
|
||||||
|
|
||||||
// Detector de colisiones entre una linea horizontal y un rectangulo
|
// Detector de colisiones entre una linea horizontal y un rectangulo
|
||||||
bool checkCollision(h_line_t &l, SDL_Rect &r);
|
bool checkCollision(const h_line_t &l, const SDL_Rect &r);
|
||||||
|
|
||||||
// Detector de colisiones entre una linea vertical y un rectangulo
|
// Detector de colisiones entre una linea vertical y un rectangulo
|
||||||
bool checkCollision(v_line_t &l, SDL_Rect &r);
|
bool checkCollision(const v_line_t &l, const SDL_Rect &r);
|
||||||
|
|
||||||
// Detector de colisiones entre una linea horizontal y un punto
|
// Detector de colisiones entre una linea horizontal y un punto
|
||||||
bool checkCollision(h_line_t &l, SDL_Point &p);
|
bool checkCollision(const h_line_t &l, const SDL_Point &p);
|
||||||
|
|
||||||
// Detector de colisiones entre dos lineas
|
// Detector de colisiones entre dos lineas
|
||||||
SDL_Point checkCollision(line_t &l1, line_t &l2);
|
SDL_Point checkCollision(const line_t &l1, const line_t &l2);
|
||||||
|
|
||||||
// Detector de colisiones entre dos lineas
|
// Detector de colisiones entre dos lineas
|
||||||
SDL_Point checkCollision(d_line_t &l1, v_line_t &l2);
|
SDL_Point checkCollision(const d_line_t &l1, const v_line_t &l2);
|
||||||
|
|
||||||
// Detector de colisiones entre un punto y una linea diagonal
|
// Detector de colisiones entre un punto y una linea diagonal
|
||||||
bool checkCollision(SDL_Point &p, d_line_t &l);
|
bool checkCollision(const SDL_Point &p, const d_line_t &l);
|
||||||
|
|
||||||
// Normaliza una linea diagonal
|
// Normaliza una linea diagonal
|
||||||
void normalizeLine(d_line_t &l);
|
void normalizeLine(d_line_t &l);
|
||||||
|
|
||||||
// Devuelve un color_t a partir de un string
|
// Devuelve un color_t a partir de un string
|
||||||
color_t stringToColor(palette_e pal, std::string str);
|
color_t stringToColor(palette_e pal, const std::string &str);
|
||||||
|
|
||||||
// Convierte una cadena en un valor booleano
|
// Convierte una cadena en un valor booleano
|
||||||
bool stringToBool(std::string str);
|
bool stringToBool(const std::string &str);
|
||||||
|
|
||||||
// Convierte un valor booleano en una cadena
|
// Convierte un valor booleano en una cadena
|
||||||
std::string boolToString(bool value);
|
std::string boolToString(bool value);
|
||||||
|
|
||||||
// Convierte una cadena a minusculas
|
// Convierte una cadena a minusculas
|
||||||
std::string toLower(std::string str);
|
std::string toLower(const std::string &str);
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "resource_pack.h"
|
#include "core/resources/resource_pack.h"
|
||||||
|
|
||||||
static constexpr const char* APP_NAME = "Coffee Crisis";
|
static constexpr const char* APP_NAME = "Coffee Crisis";
|
||||||
|
|
||||||
|
|||||||
57
tools/shaders/compile_shader.cmake
Normal file
57
tools/shaders/compile_shader.cmake
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# compile_shader.cmake
|
||||||
|
# Compila un shader GLSL a header C++ embebible con datos SPIR-V.
|
||||||
|
# Funciona en Windows, Linux y macOS sin bash ni herramientas Unix.
|
||||||
|
#
|
||||||
|
# Variables requeridas (pasar con -D al invocar cmake -P):
|
||||||
|
# GLSLC - ruta al ejecutable glslc
|
||||||
|
# SRC - archivo fuente GLSL
|
||||||
|
# OUT_H - archivo header de salida
|
||||||
|
# VAR - nombre base de la variable C++ (e.g. "postfx_vert_spv")
|
||||||
|
# STAGE - (opcional) stage explícito para glslc (e.g. "fragment")
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
get_filename_component(SRC_NAME "${SRC}" NAME_WE)
|
||||||
|
get_filename_component(OUT_DIR "${OUT_H}" DIRECTORY)
|
||||||
|
set(OUT_SPV "${OUT_DIR}/${SRC_NAME}.spv")
|
||||||
|
|
||||||
|
# Compilar GLSL → SPIR-V
|
||||||
|
if(DEFINED STAGE AND NOT STAGE STREQUAL "")
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GLSLC}" "-fshader-stage=${STAGE}" "${SRC}" -o "${OUT_SPV}"
|
||||||
|
RESULT_VARIABLE RESULT
|
||||||
|
ERROR_VARIABLE ERROR_MSG
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GLSLC}" "${SRC}" -o "${OUT_SPV}"
|
||||||
|
RESULT_VARIABLE RESULT
|
||||||
|
ERROR_VARIABLE ERROR_MSG
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "glslc falló compilando ${SRC}:\n${ERROR_MSG}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Leer binario SPIR-V como cadena hexadecimal
|
||||||
|
file(READ "${OUT_SPV}" SPV_HEX HEX)
|
||||||
|
file(REMOVE "${OUT_SPV}")
|
||||||
|
|
||||||
|
string(LENGTH "${SPV_HEX}" HEX_LEN)
|
||||||
|
math(EXPR BYTE_COUNT "${HEX_LEN} / 2")
|
||||||
|
|
||||||
|
# Convertir a array C++ con formato " 0xXX,\n" por byte
|
||||||
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" " 0x\\1,\n" SPV_BYTES "${SPV_HEX}")
|
||||||
|
# Eliminar la última coma y sustituir por el "}" de cierre
|
||||||
|
string(REGEX REPLACE ",\n$" "}" SPV_BYTES "${SPV_BYTES}")
|
||||||
|
|
||||||
|
# Escribir el header
|
||||||
|
file(WRITE "${OUT_H}"
|
||||||
|
"#pragma once\n"
|
||||||
|
"#include <cstddef>\n"
|
||||||
|
"#include <cstdint>\n"
|
||||||
|
"static const uint8_t k${VAR}[] = {\n"
|
||||||
|
"${SPV_BYTES};\n"
|
||||||
|
"static const size_t k${VAR}_size = ${BYTE_COUNT};\n"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user