Compare commits
14 Commits
2026-04-03
...
8706b2c7fb
| Author | SHA1 | Date | |
|---|---|---|---|
| 8706b2c7fb | |||
| d493ebf4f0 | |||
| 3e795998d1 | |||
| c694781f38 | |||
| 1a2298963d | |||
| 4c1ed1cf9b | |||
| f80d0a656e | |||
| 4429cd92c1 | |||
| 4ac34b8583 | |||
| cf7ea6cc9c | |||
| f5da35bfb2 | |||
| c0accd25e2 | |||
| ad8ad7e756 | |||
| 673587230e |
37
.clang-tidy
37
.clang-tidy
@@ -1,28 +1,25 @@
|
|||||||
Checks: >
|
Checks:
|
||||||
readability-*,
|
- readability-*
|
||||||
modernize-*,
|
- modernize-*
|
||||||
performance-*,
|
- performance-*
|
||||||
bugprone-unchecked-optional-access,
|
- bugprone-*
|
||||||
bugprone-sizeof-expression,
|
- -readability-identifier-length
|
||||||
bugprone-suspicious-missing-comma,
|
- -readability-magic-numbers
|
||||||
bugprone-suspicious-index,
|
- -bugprone-integer-division
|
||||||
bugprone-undefined-memory-manipulation,
|
- -bugprone-easily-swappable-parameters
|
||||||
bugprone-use-after-move,
|
- -bugprone-narrowing-conversions
|
||||||
bugprone-out-of-bound-access,
|
- -modernize-avoid-c-arrays,-warnings-as-errors
|
||||||
-readability-identifier-length,
|
|
||||||
-readability-magic-numbers,
|
|
||||||
-bugprone-narrowing-conversions,
|
|
||||||
-performance-enum-size,
|
|
||||||
-performance-inefficient-string-concatenation,
|
|
||||||
-bugprone-integer-division,
|
|
||||||
-bugprone-easily-swappable-parameters,
|
|
||||||
|
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
# Solo incluir archivos de tu código fuente
|
# Solo incluir archivos de tu código fuente (external tiene su propio .clang-tidy)
|
||||||
HeaderFilterRegex: '^source/(sections|ui)/.*'
|
# Excluye los headers SPIR-V generados en rendering/sdl3gpu/
|
||||||
|
HeaderFilterRegex: 'source/(?!external/|rendering/sdl3gpu/.*_spv\.h).*'
|
||||||
FormatStyle: file
|
FormatStyle: file
|
||||||
|
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
|
# bugprone-empty-catch: aceptar catches vacíos marcados con @INTENTIONAL en un comentario
|
||||||
|
- { key: bugprone-empty-catch.IgnoreCatchWithKeywords, value: '@INTENTIONAL' }
|
||||||
|
|
||||||
# Variables locales en snake_case
|
# Variables locales en snake_case
|
||||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
|||||||
.vscode/
|
|
||||||
.claude/
|
.claude/
|
||||||
.cache/
|
.cache/
|
||||||
build/
|
build/
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json"
|
||||||
|
}
|
||||||
38
CLAUDE.md
38
CLAUDE.md
@@ -44,24 +44,38 @@ make tidy-fix # run clang-tidy with auto-fix
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Singletons (core systems)
|
### Source layout
|
||||||
- **Director** (`source/director.hpp`) — Application state machine, orchestrates scene transitions (Logo → Intro → Title → Game → Credits/HiScore → Title)
|
The `source/` tree is organised in the same style as the sibling projects `projecte_2026` and `jaildoctors_dilemma`:
|
||||||
- **Screen** (`source/screen.hpp`) — Window management, SDL3 GPU rendering pipeline, post-processing effects
|
|
||||||
- **Resource** (`source/resource.hpp`) — Asset loading/caching with PRELOAD and LAZY_LOAD modes, reads from `resources.pack`
|
|
||||||
- **Audio** (`source/audio.hpp`) — Music and SFX management
|
|
||||||
- **Input** (`source/input.hpp`) — Keyboard and gamepad input handling
|
|
||||||
|
|
||||||
### Scenes (source/sections/)
|
```
|
||||||
|
source/
|
||||||
|
├── core/ # engine: audio, input, locale, rendering (+ sdl3gpu, sprite), resources, system
|
||||||
|
├── game/ # gameplay: entities, gameplay, scenes, ui, options
|
||||||
|
├── utils/ # color, param, utils
|
||||||
|
├── external/ # vendored third-party headers (json, fkyaml, stb_*)
|
||||||
|
└── main.cpp
|
||||||
|
```
|
||||||
|
|
||||||
|
`#include` paths are absolute relative to `source/` (e.g. `#include "core/audio/audio.hpp"`, `#include "game/scenes/logo.hpp"`). The CMake build adds a single `-I${CMAKE_SOURCE_DIR}/source`.
|
||||||
|
|
||||||
|
### Singletons (core systems)
|
||||||
|
- **Director** (`source/core/system/director.hpp`) — Application state machine, orchestrates scene transitions (Logo → Intro → Title → Game → Credits/HiScore → Title)
|
||||||
|
- **Screen** (`source/core/rendering/screen.hpp`) — Window management, SDL3 GPU rendering pipeline, post-processing effects
|
||||||
|
- **Resource** (`source/core/resources/resource.hpp`) — Asset loading/caching with PRELOAD and LAZY_LOAD modes, reads from `resources.pack`
|
||||||
|
- **Audio** (`source/core/audio/audio.hpp`) — Music and SFX management
|
||||||
|
- **Input** (`source/core/input/input.hpp`) — Keyboard and gamepad input handling
|
||||||
|
|
||||||
|
### Scenes (source/game/scenes/)
|
||||||
Each scene is a self-contained class with update/render lifecycle. Scene flow is managed by Director.
|
Each scene is a self-contained class with update/render lifecycle. Scene flow is managed by Director.
|
||||||
|
|
||||||
### Entity Managers
|
### Entity Managers
|
||||||
- `BalloonManager` / `BulletManager` — Object pool-based entity management
|
- `BalloonManager` / `BulletManager` — Object pool-based entity management (`source/game/gameplay/`)
|
||||||
- `Player` — Two-player support (player 1: keyboard, player 2: gamepad)
|
- `Player` — Two-player support (player 1: keyboard, player 2: gamepad) (`source/game/entities/`)
|
||||||
|
|
||||||
### Rendering Pipeline
|
### Rendering Pipeline
|
||||||
- SDL3 GPU API (Vulkan/Metal/D3D12 backends)
|
- SDL3 GPU API (Vulkan/Metal/D3D12 backends)
|
||||||
- SPIR-V shaders compiled offline from GLSL (`data/shaders/`) via `glslc`
|
- SPIR-V shaders compiled offline from GLSL (`data/shaders/`) via `glslc`
|
||||||
- Compiled shader headers embedded in `source/rendering/sdl3gpu/postfx_*_spv.h`
|
- Compiled shader headers embedded in `source/core/rendering/sdl3gpu/postfx_*_spv.h`
|
||||||
- macOS uses Metal (no SPIR-V compilation needed)
|
- macOS uses Metal (no SPIR-V compilation needed)
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
@@ -72,7 +86,9 @@ Each scene is a self-contained class with update/render lifecycle. Scene flow is
|
|||||||
- Gamepad mappings: `config/gamecontrollerdb.txt`
|
- Gamepad mappings: `config/gamecontrollerdb.txt`
|
||||||
|
|
||||||
### External Libraries (header-only/vendored in source/external/)
|
### External Libraries (header-only/vendored in source/external/)
|
||||||
- nlohmann/json, fkyaml (YAML), stb_image, stb_vorbis, jail_audio
|
- nlohmann/json, fkyaml (YAML), stb_image, stb_vorbis
|
||||||
|
|
||||||
|
`jail_audio` lives in `source/core/audio/` and `gif.{hpp,cpp}` in `source/core/rendering/` — these are first-party, not third-party.
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
|
|||||||
294
CMakeLists.txt
294
CMakeLists.txt
@@ -28,100 +28,121 @@ configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/versi
|
|||||||
|
|
||||||
# --- 1. LISTA EXPLÍCITA DE FUENTES ---
|
# --- 1. LISTA EXPLÍCITA DE FUENTES ---
|
||||||
set(APP_SOURCES
|
set(APP_SOURCES
|
||||||
# --- Archivos Principales del Sistema ---
|
|
||||||
source/asset.cpp
|
|
||||||
source/audio.cpp
|
|
||||||
source/director.cpp
|
|
||||||
source/global_events.cpp
|
|
||||||
source/global_inputs.cpp
|
|
||||||
source/input.cpp
|
|
||||||
source/lang.cpp
|
|
||||||
source/main.cpp
|
source/main.cpp
|
||||||
source/param.cpp
|
|
||||||
source/resource.cpp
|
|
||||||
source/resource_helper.cpp
|
|
||||||
source/resource_loader.cpp
|
|
||||||
source/resource_pack.cpp
|
|
||||||
source/screen.cpp
|
|
||||||
source/text.cpp
|
|
||||||
source/writer.cpp
|
|
||||||
|
|
||||||
# --- UI (User Interface) ---
|
# --- core/audio ---
|
||||||
source/ui/menu_option.cpp
|
source/core/audio/audio.cpp
|
||||||
source/ui/menu_renderer.cpp
|
|
||||||
source/ui/notifier.cpp
|
|
||||||
source/ui/service_menu.cpp
|
|
||||||
source/ui/ui_message.cpp
|
|
||||||
source/ui/window_message.cpp
|
|
||||||
|
|
||||||
# --- Lógica del Juego ---
|
# --- core/input ---
|
||||||
source/balloon_formations.cpp
|
source/core/input/define_buttons.cpp
|
||||||
source/balloon_manager.cpp
|
source/core/input/global_inputs.cpp
|
||||||
source/balloon.cpp
|
source/core/input/input.cpp
|
||||||
source/bullet.cpp
|
source/core/input/input_types.cpp
|
||||||
source/bullet_manager.cpp
|
source/core/input/mouse.cpp
|
||||||
source/enter_name.cpp
|
|
||||||
source/explosions.cpp
|
|
||||||
source/game_logo.cpp
|
|
||||||
source/item.cpp
|
|
||||||
source/manage_hiscore_table.cpp
|
|
||||||
source/player.cpp
|
|
||||||
source/scoreboard.cpp
|
|
||||||
source/tabe.cpp
|
|
||||||
|
|
||||||
# --- Escenas ---
|
# --- core/locale ---
|
||||||
source/sections/credits.cpp
|
source/core/locale/lang.cpp
|
||||||
source/sections/game.cpp
|
|
||||||
source/sections/hiscore_table.cpp
|
|
||||||
source/sections/instructions.cpp
|
|
||||||
source/sections/intro.cpp
|
|
||||||
source/sections/logo.cpp
|
|
||||||
source/sections/title.cpp
|
|
||||||
|
|
||||||
# --- Sprites y Gráficos ---
|
# --- core/rendering ---
|
||||||
source/animated_sprite.cpp
|
source/core/rendering/background.cpp
|
||||||
source/background.cpp
|
source/core/rendering/fade.cpp
|
||||||
source/card_sprite.cpp
|
source/core/rendering/gif.cpp
|
||||||
source/fade.cpp
|
source/core/rendering/screen.cpp
|
||||||
source/moving_sprite.cpp
|
source/core/rendering/text.cpp
|
||||||
source/path_sprite.cpp
|
source/core/rendering/texture.cpp
|
||||||
source/smart_sprite.cpp
|
source/core/rendering/tiled_bg.cpp
|
||||||
source/sprite.cpp
|
source/core/rendering/writer.cpp
|
||||||
source/texture.cpp
|
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
||||||
source/tiled_bg.cpp
|
source/core/rendering/sprite/animated_sprite.cpp
|
||||||
|
source/core/rendering/sprite/card_sprite.cpp
|
||||||
|
source/core/rendering/sprite/moving_sprite.cpp
|
||||||
|
source/core/rendering/sprite/path_sprite.cpp
|
||||||
|
source/core/rendering/sprite/smart_sprite.cpp
|
||||||
|
source/core/rendering/sprite/sprite.cpp
|
||||||
|
|
||||||
# --- Otros ---
|
# --- core/resources ---
|
||||||
source/color.cpp
|
source/core/resources/asset.cpp
|
||||||
source/demo.cpp
|
source/core/resources/asset_integrated.cpp
|
||||||
source/define_buttons.cpp
|
source/core/resources/resource.cpp
|
||||||
source/difficulty.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
source/input_types.cpp
|
source/core/resources/resource_loader.cpp
|
||||||
source/mouse.cpp
|
source/core/resources/resource_pack.cpp
|
||||||
source/options.cpp
|
|
||||||
source/shutdown.cpp
|
|
||||||
source/stage.cpp
|
|
||||||
source/system_utils.cpp
|
|
||||||
source/utils.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fuentes de librerías de terceros
|
# --- core/system ---
|
||||||
set(EXTERNAL_SOURCES
|
source/core/system/demo.cpp
|
||||||
source/external/jail_audio.cpp
|
source/core/system/director.cpp
|
||||||
source/external/json.hpp
|
source/core/system/global_events.cpp
|
||||||
source/external/gif.cpp
|
source/core/system/shutdown.cpp
|
||||||
)
|
source/core/system/system_utils.cpp
|
||||||
|
|
||||||
# Fuentes del sistema de renderizado
|
# --- game ---
|
||||||
set(RENDERING_SOURCES
|
source/game/options.cpp
|
||||||
source/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
|
||||||
|
# --- game/entities ---
|
||||||
|
source/game/entities/balloon.cpp
|
||||||
|
source/game/entities/bullet.cpp
|
||||||
|
source/game/entities/explosions.cpp
|
||||||
|
source/game/entities/item.cpp
|
||||||
|
source/game/entities/player.cpp
|
||||||
|
source/game/entities/tabe.cpp
|
||||||
|
|
||||||
|
# --- game/gameplay ---
|
||||||
|
source/game/gameplay/balloon_formations.cpp
|
||||||
|
source/game/gameplay/balloon_manager.cpp
|
||||||
|
source/game/gameplay/bullet_manager.cpp
|
||||||
|
source/game/gameplay/difficulty.cpp
|
||||||
|
source/game/gameplay/enter_name.cpp
|
||||||
|
source/game/gameplay/game_logo.cpp
|
||||||
|
source/game/gameplay/manage_hiscore_table.cpp
|
||||||
|
source/game/gameplay/scoreboard.cpp
|
||||||
|
source/game/gameplay/stage.cpp
|
||||||
|
|
||||||
|
# --- game/scenes ---
|
||||||
|
source/game/scenes/credits.cpp
|
||||||
|
source/game/scenes/game.cpp
|
||||||
|
source/game/scenes/hiscore_table.cpp
|
||||||
|
source/game/scenes/instructions.cpp
|
||||||
|
source/game/scenes/intro.cpp
|
||||||
|
source/game/scenes/logo.cpp
|
||||||
|
source/game/scenes/preload.cpp
|
||||||
|
source/game/scenes/title.cpp
|
||||||
|
|
||||||
|
# --- game/ui ---
|
||||||
|
source/game/ui/menu_option.cpp
|
||||||
|
source/game/ui/menu_renderer.cpp
|
||||||
|
source/game/ui/notifier.cpp
|
||||||
|
source/game/ui/service_menu.cpp
|
||||||
|
source/game/ui/ui_message.cpp
|
||||||
|
source/game/ui/window_message.cpp
|
||||||
|
|
||||||
|
# --- utils ---
|
||||||
|
source/utils/color.cpp
|
||||||
|
source/utils/param.cpp
|
||||||
|
source/utils/utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Configuración de SDL3
|
# Configuración de SDL3
|
||||||
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
|
if(EMSCRIPTEN)
|
||||||
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
|
# En Emscripten, SDL3 se compila desde source con FetchContent
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
SDL3
|
||||||
|
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
|
||||||
|
GIT_TAG release-3.2.12
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||||
|
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||||
|
set(SDL_TEST_LIBRARY OFF CACHE BOOL "" FORCE)
|
||||||
|
FetchContent_MakeAvailable(SDL3)
|
||||||
|
message(STATUS "SDL3 compilado desde source para Emscripten")
|
||||||
|
else()
|
||||||
|
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
|
||||||
|
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- SHADER COMPILATION (Linux/Windows only - macOS uses Metal) ---
|
# --- SHADER COMPILATION (Linux/Windows only - macOS uses Metal, Emscripten no soporta SDL3 GPU) ---
|
||||||
if(NOT APPLE)
|
if(NOT APPLE AND NOT EMSCRIPTEN)
|
||||||
find_program(GLSLC_EXE NAMES glslc)
|
find_program(GLSLC_EXE NAMES glslc)
|
||||||
|
|
||||||
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
set(SHADER_VERT_SRC "${CMAKE_SOURCE_DIR}/data/shaders/postfx.vert")
|
||||||
@@ -130,23 +151,40 @@ if(NOT APPLE)
|
|||||||
set(SHADER_UPSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/upscale.frag")
|
set(SHADER_UPSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/upscale.frag")
|
||||||
set(SHADER_DOWNSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/downscale.frag")
|
set(SHADER_DOWNSCALE_SRC "${CMAKE_SOURCE_DIR}/data/shaders/downscale.frag")
|
||||||
|
|
||||||
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_vert_spv.h")
|
set(SHADER_VERT_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_vert_spv.h")
|
||||||
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/postfx_frag_spv.h")
|
set(SHADER_FRAG_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/postfx_frag_spv.h")
|
||||||
set(SHADER_CRTPI_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/crtpi_frag_spv.h")
|
set(SHADER_CRTPI_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/crtpi_frag_spv.h")
|
||||||
set(SHADER_UPSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/upscale_frag_spv.h")
|
set(SHADER_UPSCALE_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/upscale_frag_spv.h")
|
||||||
set(SHADER_DOWNSCALE_H "${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu/downscale_frag_spv.h")
|
set(SHADER_DOWNSCALE_H "${CMAKE_SOURCE_DIR}/source/core/rendering/sdl3gpu/downscale_frag_spv.h")
|
||||||
|
|
||||||
set(ALL_SHADER_SOURCES "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" "${SHADER_CRTPI_SRC}" "${SHADER_UPSCALE_SRC}" "${SHADER_DOWNSCALE_SRC}")
|
set(ALL_SHADER_SOURCES "${SHADER_VERT_SRC}" "${SHADER_FRAG_SRC}" "${SHADER_CRTPI_SRC}" "${SHADER_UPSCALE_SRC}" "${SHADER_DOWNSCALE_SRC}")
|
||||||
set(ALL_SHADER_HEADERS "${SHADER_VERT_H}" "${SHADER_FRAG_H}" "${SHADER_CRTPI_H}" "${SHADER_UPSCALE_H}" "${SHADER_DOWNSCALE_H}")
|
set(ALL_SHADER_HEADERS "${SHADER_VERT_H}" "${SHADER_FRAG_H}" "${SHADER_CRTPI_H}" "${SHADER_UPSCALE_H}" "${SHADER_DOWNSCALE_H}")
|
||||||
|
|
||||||
if(GLSLC_EXE)
|
if(GLSLC_EXE)
|
||||||
|
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(
|
add_custom_command(
|
||||||
OUTPUT ${ALL_SHADER_HEADERS}
|
OUTPUT "${OUT_H}"
|
||||||
COMMAND "${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.sh"
|
COMMAND ${CMAKE_COMMAND}
|
||||||
DEPENDS ${ALL_SHADER_SOURCES}
|
"-DGLSLC=${GLSLC_EXE}"
|
||||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
"-DSRC=${SRC_FILE}"
|
||||||
COMMENT "Compilando shaders SPIR-V..."
|
"-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})
|
add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
|
||||||
message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
|
message(STATUS "glslc encontrado: shaders se compilarán automáticamente")
|
||||||
else()
|
else()
|
||||||
@@ -163,22 +201,30 @@ if(NOT APPLE)
|
|||||||
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
message(STATUS "glslc no encontrado - usando headers SPIR-V precompilados")
|
||||||
endif()
|
endif()
|
||||||
else()
|
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)")
|
message(STATUS "macOS: shaders SPIR-V omitidos (usa Metal)")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --- 2. AÑADIR EJECUTABLE ---
|
# --- 2. AÑADIR EJECUTABLE ---
|
||||||
add_executable(${PROJECT_NAME} ${APP_SOURCES} ${EXTERNAL_SOURCES} ${RENDERING_SOURCES})
|
if(EMSCRIPTEN)
|
||||||
|
# En Emscripten no compilamos sdl3gpu_shader (SDL3 GPU no está soportado en WebGL2)
|
||||||
|
set(APP_SOURCES_WASM ${APP_SOURCES})
|
||||||
|
list(REMOVE_ITEM APP_SOURCES_WASM source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp)
|
||||||
|
add_executable(${PROJECT_NAME} ${APP_SOURCES_WASM})
|
||||||
|
else()
|
||||||
|
add_executable(${PROJECT_NAME} ${APP_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT APPLE AND GLSLC_EXE)
|
if(NOT APPLE AND NOT EMSCRIPTEN AND GLSLC_EXE)
|
||||||
add_dependencies(${PROJECT_NAME} shaders)
|
add_dependencies(${PROJECT_NAME} shaders)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --- 3. DIRECTORIOS DE INCLUSIÓN ---
|
# --- 3. DIRECTORIOS DE INCLUSIÓN ---
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
"${CMAKE_SOURCE_DIR}/source"
|
"${CMAKE_SOURCE_DIR}/source"
|
||||||
"${CMAKE_SOURCE_DIR}/source/external"
|
|
||||||
"${CMAKE_SOURCE_DIR}/source/rendering"
|
|
||||||
"${CMAKE_SOURCE_DIR}/source/rendering/sdl3gpu"
|
|
||||||
"${CMAKE_BINARY_DIR}"
|
"${CMAKE_BINARY_DIR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -215,17 +261,39 @@ elseif(APPLE)
|
|||||||
-rpath @executable_path/../Frameworks/
|
-rpath @executable_path/../Frameworks/
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
elseif(EMSCRIPTEN)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE EMSCRIPTEN_BUILD NO_SHADERS)
|
||||||
|
# -fexceptions: habilita excepciones C++ (fkyaml, std::runtime_error...) — sin esto cualquier throw llama a abort()
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -fexceptions)
|
||||||
|
target_link_options(${PROJECT_NAME} PRIVATE
|
||||||
|
"SHELL:--preload-file ${CMAKE_SOURCE_DIR}/data@/data"
|
||||||
|
"SHELL:--preload-file ${CMAKE_SOURCE_DIR}/config@/config"
|
||||||
|
-fexceptions
|
||||||
|
-sALLOW_MEMORY_GROWTH=1
|
||||||
|
-sMAX_WEBGL_VERSION=2
|
||||||
|
-sINITIAL_MEMORY=67108864
|
||||||
|
-sASSERTIONS=1
|
||||||
|
# ASYNCIFY solo para permitir emscripten_sleep(0) durante la precarga de recursos
|
||||||
|
# (el bucle principal del juego ya usa SDL3 Callback API, no depende de ASYNCIFY).
|
||||||
|
-sASYNCIFY=1
|
||||||
|
)
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html")
|
||||||
elseif(UNIX AND NOT APPLE)
|
elseif(UNIX AND NOT APPLE)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Especificar la ubicación del ejecutable
|
# Especificar la ubicación del ejecutable
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
|
if(EMSCRIPTEN)
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
else()
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- 5. STATIC ANALYSIS TARGETS ---
|
# --- 5. STATIC ANALYSIS TARGETS ---
|
||||||
|
|
||||||
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
|
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
|
||||||
find_program(CLANG_FORMAT_EXE NAMES clang-format)
|
find_program(CLANG_FORMAT_EXE NAMES clang-format)
|
||||||
|
find_program(CPPCHECK_EXE NAMES cppcheck)
|
||||||
|
|
||||||
# Recopilar todos los archivos fuente, excluyendo external/
|
# Recopilar todos los archivos fuente, excluyendo external/
|
||||||
file(GLOB_RECURSE ALL_SOURCE_FILES
|
file(GLOB_RECURSE ALL_SOURCE_FILES
|
||||||
@@ -235,6 +303,14 @@ file(GLOB_RECURSE ALL_SOURCE_FILES
|
|||||||
)
|
)
|
||||||
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX ".*/external/.*")
|
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX ".*/external/.*")
|
||||||
|
|
||||||
|
# Para cppcheck, pasar solo .cpp (los headers se procesan transitivamente).
|
||||||
|
# Si pasamos .hpp como TUs independientes, cppcheck reporta falsos positivos de
|
||||||
|
# 'unusedStructMember' porque no hace análisis cross-TU y ve miembros de clase
|
||||||
|
# cuyo uso vive en un .cpp distinto.
|
||||||
|
set(CPPCHECK_SOURCES ${ALL_SOURCE_FILES})
|
||||||
|
list(FILTER CPPCHECK_SOURCES INCLUDE REGEX ".*\\.cpp$")
|
||||||
|
list(FILTER CPPCHECK_SOURCES EXCLUDE REGEX ".*_spv\\.h$")
|
||||||
|
|
||||||
# Targets de clang-tidy
|
# Targets de clang-tidy
|
||||||
if(CLANG_TIDY_EXE)
|
if(CLANG_TIDY_EXE)
|
||||||
# En macOS con clang-tidy de Homebrew LLVM, es necesario pasar el sysroot
|
# En macOS con clang-tidy de Homebrew LLVM, es necesario pasar el sysroot
|
||||||
@@ -291,3 +367,25 @@ if(CLANG_FORMAT_EXE)
|
|||||||
else()
|
else()
|
||||||
message(STATUS "clang-format no encontrado - targets 'format' y 'format-check' no disponibles")
|
message(STATUS "clang-format no encontrado - targets 'format' y 'format-check' no disponibles")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Targets de cppcheck
|
||||||
|
if(CPPCHECK_EXE)
|
||||||
|
add_custom_target(cppcheck
|
||||||
|
COMMAND ${CPPCHECK_EXE}
|
||||||
|
--enable=warning,style,performance,portability
|
||||||
|
--std=c++20
|
||||||
|
--language=c++
|
||||||
|
--inline-suppr
|
||||||
|
--suppress=missingIncludeSystem
|
||||||
|
--suppress=toomanyconfigs
|
||||||
|
-D_DEBUG
|
||||||
|
-DLINUX_BUILD
|
||||||
|
--quiet
|
||||||
|
-I ${CMAKE_SOURCE_DIR}/source
|
||||||
|
${CPPCHECK_SOURCES}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
COMMENT "Running cppcheck..."
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "cppcheck no encontrado - target 'cppcheck' no disponible")
|
||||||
|
endif()
|
||||||
|
|||||||
34
Makefile
34
Makefile
@@ -8,7 +8,11 @@ DIR_TOOLS := $(addsuffix /, $(DIR_ROOT)tools)
|
|||||||
# TARGET NAMES
|
# TARGET NAMES
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
TARGET_NAME := coffee_crisis_arcade_edition
|
TARGET_NAME := coffee_crisis_arcade_edition
|
||||||
TARGET_FILE := $(DIR_ROOT)$(TARGET_NAME)
|
ifeq ($(OS),Windows_NT)
|
||||||
|
TARGET_FILE := $(DIR_ROOT)$(TARGET_NAME).exe
|
||||||
|
else
|
||||||
|
TARGET_FILE := $(DIR_ROOT)$(TARGET_NAME)
|
||||||
|
endif
|
||||||
APP_NAME := Coffee Crisis Arcade Edition
|
APP_NAME := Coffee Crisis Arcade Edition
|
||||||
DIST_DIR := dist
|
DIST_DIR := dist
|
||||||
RELEASE_FOLDER := dist/_tmp
|
RELEASE_FOLDER := dist/_tmp
|
||||||
@@ -22,12 +26,12 @@ DIR_PACK_TOOL := $(DIR_TOOLS)pack_resources
|
|||||||
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
|
SHADER_SCRIPT := $(DIR_ROOT)tools/shaders/compile_spirv.sh
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# VERSION (fecha actual)
|
# VERSION (extraída de defines.hpp)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
VERSION := $(shell powershell -Command "Get-Date -Format 'yyyy-MM-dd'")
|
VERSION := $(shell powershell -Command "(Select-String -Path 'source/utils/defines.hpp' -Pattern 'constexpr const char\* VERSION = \"(.+?)\"').Matches.Groups[1].Value")
|
||||||
else
|
else
|
||||||
VERSION := $(shell date +%Y-%m-%d)
|
VERSION := $(shell grep 'constexpr const char\* VERSION' source/utils/defines.hpp | sed -E 's/.*VERSION = "([^"]+)".*/\1/')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -138,7 +142,7 @@ windows_release:
|
|||||||
@powershell -Command "Copy-Item 'LICENSE' -Destination '$(RELEASE_FOLDER)'"
|
@powershell -Command "Copy-Item 'LICENSE' -Destination '$(RELEASE_FOLDER)'"
|
||||||
@powershell -Command "Copy-Item 'README.md' -Destination '$(RELEASE_FOLDER)'"
|
@powershell -Command "Copy-Item 'README.md' -Destination '$(RELEASE_FOLDER)'"
|
||||||
@powershell -Command "Copy-Item 'release\windows\dll\*.dll' -Destination '$(RELEASE_FOLDER)'"
|
@powershell -Command "Copy-Item 'release\windows\dll\*.dll' -Destination '$(RELEASE_FOLDER)'"
|
||||||
@powershell -Command "Copy-Item -Path '$(TARGET_FILE)' -Destination '\"$(WIN_RELEASE_FILE).exe\"'"
|
@powershell -Command "Copy-Item -Path '$(TARGET_FILE)' -Destination '$(WIN_RELEASE_FILE).exe'"
|
||||||
strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
|
strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
|
||||||
|
|
||||||
# Crea el fichero .zip
|
# Crea el fichero .zip
|
||||||
@@ -403,6 +407,23 @@ raspi_release:
|
|||||||
# Elimina la carpeta temporal
|
# Elimina la carpeta temporal
|
||||||
$(RMDIR) "$(RELEASE_FOLDER)"
|
$(RMDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# COMPILACIÓN PARA WEBASSEMBLY (requiere Docker)
|
||||||
|
# ==============================================================================
|
||||||
|
wasm:
|
||||||
|
@echo "Compilando para WebAssembly - Version: $(VERSION)"
|
||||||
|
docker run --rm \
|
||||||
|
-v $(DIR_ROOT):/src \
|
||||||
|
-w /src \
|
||||||
|
emscripten/emsdk:latest \
|
||||||
|
bash -c "emcmake cmake -S . -B build/wasm -DCMAKE_BUILD_TYPE=Release && cmake --build build/wasm"
|
||||||
|
$(MKDIR) "$(DIST_DIR)/wasm"
|
||||||
|
cp build/wasm/$(TARGET_NAME).html $(DIST_DIR)/wasm/
|
||||||
|
cp build/wasm/$(TARGET_NAME).js $(DIST_DIR)/wasm/
|
||||||
|
cp build/wasm/$(TARGET_NAME).wasm $(DIST_DIR)/wasm/
|
||||||
|
cp build/wasm/$(TARGET_NAME).data $(DIST_DIR)/wasm/
|
||||||
|
@echo "Output: $(DIST_DIR)/wasm/"
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# CODE QUALITY (delegados a cmake)
|
# CODE QUALITY (delegados a cmake)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -443,6 +464,7 @@ help:
|
|||||||
@echo " make linux_release_desktop - Crear release con integracion desktop para Linux"
|
@echo " make linux_release_desktop - Crear release con integracion desktop para Linux"
|
||||||
@echo " make macos_release - Crear release para macOS"
|
@echo " make macos_release - Crear release para macOS"
|
||||||
@echo " make raspi_release - Crear release para Raspberry Pi"
|
@echo " make raspi_release - Crear release para Raspberry Pi"
|
||||||
|
@echo " make wasm - Crear build WebAssembly (requiere Docker) en dist/wasm"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " Herramientas:"
|
@echo " Herramientas:"
|
||||||
@echo " make spirv - Compilar shaders SPIR-V"
|
@echo " make spirv - Compilar shaders SPIR-V"
|
||||||
@@ -459,4 +481,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 linux_release_desktop raspi_release pack_tool resources.pack spirv format format-check tidy tidy-fix show_version help
|
.PHONY: all debug release windows_release macos_release linux_release linux_release_desktop raspi_release wasm pack_tool resources.pack spirv format format-check tidy tidy-fix show_version help
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "desconectat",
|
"[NOTIFICATIONS] DISCONNECTED": "desconectat",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Carregant",
|
"[RESOURCE] LOADING": "Carregant",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Prem una tecla per continuar",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Menu de servei",
|
"[SERVICE_MENU] TITLE": "Menu de servei",
|
||||||
"[SERVICE_MENU] RESET": "Reiniciar",
|
"[SERVICE_MENU] RESET": "Reiniciar",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "disconnected",
|
"[NOTIFICATIONS] DISCONNECTED": "disconnected",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Loading",
|
"[RESOURCE] LOADING": "Loading",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Press any key to continue",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Service Menu",
|
"[SERVICE_MENU] TITLE": "Service Menu",
|
||||||
"[SERVICE_MENU] RESET": "Reset",
|
"[SERVICE_MENU] RESET": "Reset",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"[NOTIFICATIONS] DISCONNECTED": "desconectado",
|
"[NOTIFICATIONS] DISCONNECTED": "desconectado",
|
||||||
|
|
||||||
"[RESOURCE] LOADING": "Cargando",
|
"[RESOURCE] LOADING": "Cargando",
|
||||||
|
"[RESOURCE] PRESS_TO_CONTINUE": "Pulsa una tecla para continuar",
|
||||||
|
|
||||||
"[SERVICE_MENU] TITLE": "Menu de servicio",
|
"[SERVICE_MENU] TITLE": "Menu de servicio",
|
||||||
"[SERVICE_MENU] RESET": "Reiniciar",
|
"[SERVICE_MENU] RESET": "Reiniciar",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -1,13 +1,27 @@
|
|||||||
#include "audio.hpp"
|
#include "core/audio/audio.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_G...
|
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_G...
|
||||||
|
|
||||||
#include <algorithm> // Para clamp
|
#include <algorithm> // Para clamp
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
|
|
||||||
#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
|
// Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp).
|
||||||
#include "options.hpp" // Para AudioOptions, audio, MusicOptions
|
// clang-format off
|
||||||
#include "resource.hpp" // Para Resource
|
#undef STB_VORBIS_HEADER_ONLY
|
||||||
#include "ui/logger.hpp" // Para logger
|
#include "external/stb_vorbis.h"
|
||||||
|
// stb_vorbis.h filtra les macros L, C i R (i PLAYBACK_*) al TU. Les netegem
|
||||||
|
// perquè xocarien amb noms de paràmetres de plantilla en json.hpp i altres.
|
||||||
|
#undef L
|
||||||
|
#undef C
|
||||||
|
#undef R
|
||||||
|
#undef PLAYBACK_MONO
|
||||||
|
#undef PLAYBACK_LEFT
|
||||||
|
#undef PLAYBACK_RIGHT
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#include "core/audio/jail_audio.hpp" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
|
||||||
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
|
#include "game/options.hpp" // Para AudioOptions, audio, MusicOptions
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Audio* Audio::instance = nullptr;
|
Audio* Audio::instance = nullptr;
|
||||||
@@ -140,11 +154,9 @@ void Audio::enable(bool value) {
|
|||||||
// Inicializa SDL Audio
|
// Inicializa SDL Audio
|
||||||
void Audio::initSDLAudio() {
|
void Audio::initSDLAudio() {
|
||||||
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError());
|
std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n';
|
||||||
} else {
|
} else {
|
||||||
JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2);
|
JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2);
|
||||||
enable(Options::audio.enabled);
|
enable(Options::audio.enabled);
|
||||||
|
|
||||||
Logger::info("Audio system initialized successfully");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
557
source/core/audio/jail_audio.hpp
Normal file
557
source/core/audio/jail_audio.hpp
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// --- Includes ---
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <stdint.h> // Para uint32_t, uint8_t
|
||||||
|
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
|
||||||
|
#include <stdlib.h> // Para free, malloc
|
||||||
|
#include <string.h> // Para strcpy, strlen
|
||||||
|
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
|
|
||||||
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
#include "external/stb_vorbis.h" // Para stb_vorbis_decode_memory
|
||||||
|
|
||||||
|
// --- Public Enums ---
|
||||||
|
enum JA_Channel_state { JA_CHANNEL_INVALID,
|
||||||
|
JA_CHANNEL_FREE,
|
||||||
|
JA_CHANNEL_PLAYING,
|
||||||
|
JA_CHANNEL_PAUSED,
|
||||||
|
JA_SOUND_DISABLED };
|
||||||
|
enum JA_Music_state { JA_MUSIC_INVALID,
|
||||||
|
JA_MUSIC_PLAYING,
|
||||||
|
JA_MUSIC_PAUSED,
|
||||||
|
JA_MUSIC_STOPPED,
|
||||||
|
JA_MUSIC_DISABLED };
|
||||||
|
|
||||||
|
// --- Struct Definitions ---
|
||||||
|
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
|
||||||
|
#define JA_MAX_GROUPS 2
|
||||||
|
|
||||||
|
struct JA_Sound_t {
|
||||||
|
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
|
||||||
|
Uint32 length{0};
|
||||||
|
Uint8* buffer{NULL};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JA_Channel_t {
|
||||||
|
JA_Sound_t* sound{nullptr};
|
||||||
|
int pos{0};
|
||||||
|
int times{0};
|
||||||
|
int group{0};
|
||||||
|
SDL_AudioStream* stream{nullptr};
|
||||||
|
JA_Channel_state state{JA_CHANNEL_FREE};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JA_Music_t {
|
||||||
|
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
|
||||||
|
|
||||||
|
// OGG comprimit en memòria. Propietat nostra; es copia des del fitxer una
|
||||||
|
// sola vegada en JA_LoadMusic i es descomprimix en chunks per streaming.
|
||||||
|
Uint8* ogg_data{nullptr};
|
||||||
|
Uint32 ogg_length{0};
|
||||||
|
stb_vorbis* vorbis{nullptr}; // Handle del decoder, viu tot el cicle del JA_Music_t
|
||||||
|
|
||||||
|
char* filename{nullptr};
|
||||||
|
|
||||||
|
int times{0}; // Loops restants (-1 = infinit, 0 = un sol play)
|
||||||
|
SDL_AudioStream* stream{nullptr};
|
||||||
|
JA_Music_state state{JA_MUSIC_INVALID};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Internal Global State ---
|
||||||
|
// Marcado 'inline' (C++17) para asegurar una única instancia.
|
||||||
|
|
||||||
|
inline JA_Music_t* current_music{nullptr};
|
||||||
|
inline JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
|
||||||
|
|
||||||
|
inline SDL_AudioSpec JA_audioSpec{SDL_AUDIO_S16, 2, 48000};
|
||||||
|
inline float JA_musicVolume{1.0f};
|
||||||
|
inline float JA_soundVolume[JA_MAX_GROUPS];
|
||||||
|
inline bool JA_musicEnabled{true};
|
||||||
|
inline bool JA_soundEnabled{true};
|
||||||
|
inline SDL_AudioDeviceID sdlAudioDevice{0};
|
||||||
|
|
||||||
|
inline bool fading{false};
|
||||||
|
inline int fade_start_time{0};
|
||||||
|
inline int fade_duration{0};
|
||||||
|
inline float fade_initial_volume{0.0f}; // Corregido de 'int' a 'float'
|
||||||
|
|
||||||
|
// --- Forward Declarations ---
|
||||||
|
inline void JA_StopMusic();
|
||||||
|
inline void JA_StopChannel(const int channel);
|
||||||
|
inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop = 0, const int group = 0);
|
||||||
|
|
||||||
|
// --- Music streaming internals ---
|
||||||
|
// Bytes-per-sample per canal (sempre s16)
|
||||||
|
static constexpr int JA_MUSIC_BYTES_PER_SAMPLE = 2;
|
||||||
|
// Quants shorts decodifiquem per crida a get_samples_short_interleaved.
|
||||||
|
// 8192 shorts = 4096 samples/channel en estèreo ≈ 85ms de so a 48kHz.
|
||||||
|
static constexpr int JA_MUSIC_CHUNK_SHORTS = 8192;
|
||||||
|
// Umbral d'audio per davant del cursor de reproducció. Mantenim ≥ 0.5 s a
|
||||||
|
// l'SDL_AudioStream per absorbir jitter de frame i evitar underruns.
|
||||||
|
static constexpr float JA_MUSIC_LOW_WATER_SECONDS = 0.5f;
|
||||||
|
|
||||||
|
// Decodifica un chunk del vorbis i el volca a l'stream. Retorna samples
|
||||||
|
// decodificats per canal (0 = EOF de l'stream vorbis).
|
||||||
|
inline int JA_FeedMusicChunk(JA_Music_t* music) {
|
||||||
|
if (!music || !music->vorbis || !music->stream) return 0;
|
||||||
|
|
||||||
|
short chunk[JA_MUSIC_CHUNK_SHORTS];
|
||||||
|
const int channels = music->spec.channels;
|
||||||
|
const int samples_per_channel = stb_vorbis_get_samples_short_interleaved(
|
||||||
|
music->vorbis,
|
||||||
|
channels,
|
||||||
|
chunk,
|
||||||
|
JA_MUSIC_CHUNK_SHORTS);
|
||||||
|
if (samples_per_channel <= 0) return 0;
|
||||||
|
|
||||||
|
const int bytes = samples_per_channel * channels * JA_MUSIC_BYTES_PER_SAMPLE;
|
||||||
|
SDL_PutAudioStreamData(music->stream, chunk, bytes);
|
||||||
|
return samples_per_channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reompli l'stream fins que tinga ≥ JA_MUSIC_LOW_WATER_SECONDS bufferats.
|
||||||
|
// En arribar a EOF del vorbis, aplica el loop (times) o deixa drenar.
|
||||||
|
inline void JA_PumpMusic(JA_Music_t* music) {
|
||||||
|
if (!music || !music->vorbis || !music->stream) return;
|
||||||
|
|
||||||
|
const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE;
|
||||||
|
const int low_water_bytes = static_cast<int>(JA_MUSIC_LOW_WATER_SECONDS * static_cast<float>(bytes_per_second));
|
||||||
|
|
||||||
|
while (SDL_GetAudioStreamAvailable(music->stream) < low_water_bytes) {
|
||||||
|
const int decoded = JA_FeedMusicChunk(music);
|
||||||
|
if (decoded > 0) continue;
|
||||||
|
|
||||||
|
// EOF: si queden loops, rebobinar; si no, tallar i deixar drenar.
|
||||||
|
if (music->times != 0) {
|
||||||
|
stb_vorbis_seek_start(music->vorbis);
|
||||||
|
if (music->times > 0) music->times--;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Core Functions ---
|
||||||
|
|
||||||
|
inline void JA_Update() {
|
||||||
|
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) {
|
||||||
|
if (fading) {
|
||||||
|
int time = SDL_GetTicks();
|
||||||
|
if (time > (fade_start_time + fade_duration)) {
|
||||||
|
fading = false;
|
||||||
|
JA_StopMusic();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const int time_passed = time - fade_start_time;
|
||||||
|
const float percent = (float)time_passed / (float)fade_duration;
|
||||||
|
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * (1.0 - percent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming: rellenem l'stream fins al low-water-mark i parem si el
|
||||||
|
// vorbis s'ha esgotat i no queden loops.
|
||||||
|
JA_PumpMusic(current_music);
|
||||||
|
if (current_music->times == 0 && SDL_GetAudioStreamAvailable(current_music->stream) == 0) {
|
||||||
|
JA_StopMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JA_soundEnabled) {
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
|
||||||
|
if (channels[i].state == JA_CHANNEL_PLAYING) {
|
||||||
|
if (channels[i].times != 0) {
|
||||||
|
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) {
|
||||||
|
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
|
||||||
|
if (channels[i].times > 0) channels[i].times--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JA_audioSpec = {format, num_channels, freq};
|
||||||
|
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice
|
||||||
|
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
|
||||||
|
if (sdlAudioDevice == 0) std::cout << "Failed to initialize SDL audio!" << '\n';
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
|
||||||
|
for (int i = 0; i < JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_Quit() {
|
||||||
|
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice
|
||||||
|
sdlAudioDevice = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Music Functions ---
|
||||||
|
|
||||||
|
inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
|
||||||
|
if (!buffer || length == 0) return nullptr;
|
||||||
|
|
||||||
|
// Còpia del OGG comprimit: stb_vorbis llig de forma persistent aquesta
|
||||||
|
// memòria mentre el handle estiga viu, així que hem de posseir-la nosaltres.
|
||||||
|
Uint8* ogg_copy = static_cast<Uint8*>(SDL_malloc(length));
|
||||||
|
if (!ogg_copy) return nullptr;
|
||||||
|
SDL_memcpy(ogg_copy, buffer, length);
|
||||||
|
|
||||||
|
int error = 0;
|
||||||
|
stb_vorbis* vorbis = stb_vorbis_open_memory(ogg_copy, static_cast<int>(length), &error, nullptr);
|
||||||
|
if (!vorbis) {
|
||||||
|
SDL_free(ogg_copy);
|
||||||
|
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << error << ")" << '\n';
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* music = new JA_Music_t();
|
||||||
|
music->ogg_data = ogg_copy;
|
||||||
|
music->ogg_length = length;
|
||||||
|
music->vorbis = vorbis;
|
||||||
|
|
||||||
|
const stb_vorbis_info info = stb_vorbis_get_info(vorbis);
|
||||||
|
music->spec.channels = info.channels;
|
||||||
|
music->spec.freq = static_cast<int>(info.sample_rate);
|
||||||
|
music->spec.format = SDL_AUDIO_S16;
|
||||||
|
music->state = JA_MUSIC_STOPPED;
|
||||||
|
|
||||||
|
return music;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JA_Music_t* JA_LoadMusic(const char* filename) {
|
||||||
|
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
|
||||||
|
FILE* f = fopen(filename, "rb");
|
||||||
|
if (!f) return NULL; // Añadida comprobación de apertura
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long fsize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
auto* buffer = static_cast<Uint8*>(malloc(fsize + 1));
|
||||||
|
if (!buffer) { // Añadida comprobación de malloc
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (fread(buffer, fsize, 1, f) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
JA_Music_t* music = JA_LoadMusic(buffer, fsize);
|
||||||
|
if (music) { // Comprobar que JA_LoadMusic tuvo éxito
|
||||||
|
music->filename = static_cast<char*>(malloc(strlen(filename) + 1));
|
||||||
|
if (music->filename) {
|
||||||
|
strcpy(music->filename, filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return music;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
|
||||||
|
if (!JA_musicEnabled || !music || !music->vorbis) return;
|
||||||
|
|
||||||
|
JA_StopMusic();
|
||||||
|
|
||||||
|
current_music = music;
|
||||||
|
current_music->state = JA_MUSIC_PLAYING;
|
||||||
|
current_music->times = loop;
|
||||||
|
|
||||||
|
// Rebobinem l'stream de vorbis al principi. Cobreix tant play-per-primera-
|
||||||
|
// vegada com replays/canvis de track que tornen a la mateixa pista.
|
||||||
|
stb_vorbis_seek_start(current_music->vorbis);
|
||||||
|
|
||||||
|
current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec);
|
||||||
|
if (!current_music->stream) {
|
||||||
|
std::cout << "Failed to create audio stream!" << '\n';
|
||||||
|
current_music->state = JA_MUSIC_STOPPED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||||
|
|
||||||
|
// Pre-cargem el buffer abans de bindejar per evitar un underrun inicial.
|
||||||
|
JA_PumpMusic(current_music);
|
||||||
|
|
||||||
|
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char* JA_GetMusicFilename(const JA_Music_t* music = nullptr) {
|
||||||
|
if (!music) music = current_music;
|
||||||
|
if (!music) return nullptr; // Añadida comprobación
|
||||||
|
return music->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_PauseMusic() {
|
||||||
|
if (!JA_musicEnabled) return;
|
||||||
|
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return; // Comprobación mejorada
|
||||||
|
|
||||||
|
current_music->state = JA_MUSIC_PAUSED;
|
||||||
|
SDL_UnbindAudioStream(current_music->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_ResumeMusic() {
|
||||||
|
if (!JA_musicEnabled) return;
|
||||||
|
if (!current_music || current_music->state != JA_MUSIC_PAUSED) return; // Comprobación mejorada
|
||||||
|
|
||||||
|
current_music->state = JA_MUSIC_PLAYING;
|
||||||
|
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_StopMusic() {
|
||||||
|
if (!current_music || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) return;
|
||||||
|
|
||||||
|
current_music->state = JA_MUSIC_STOPPED;
|
||||||
|
if (current_music->stream) {
|
||||||
|
SDL_DestroyAudioStream(current_music->stream);
|
||||||
|
current_music->stream = nullptr;
|
||||||
|
}
|
||||||
|
// Deixem el handle de vorbis viu — es tanca en JA_DeleteMusic.
|
||||||
|
// Rebobinem perquè un futur JA_PlayMusic comence des del principi.
|
||||||
|
if (current_music->vorbis) {
|
||||||
|
stb_vorbis_seek_start(current_music->vorbis);
|
||||||
|
}
|
||||||
|
// No liberem filename aquí; es fa en JA_DeleteMusic.
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_FadeOutMusic(const int milliseconds) {
|
||||||
|
if (!JA_musicEnabled) return;
|
||||||
|
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return;
|
||||||
|
|
||||||
|
fading = true;
|
||||||
|
fade_start_time = SDL_GetTicks();
|
||||||
|
fade_duration = milliseconds;
|
||||||
|
fade_initial_volume = JA_musicVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JA_Music_state JA_GetMusicState() {
|
||||||
|
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
|
||||||
|
if (!current_music) return JA_MUSIC_INVALID;
|
||||||
|
|
||||||
|
return current_music->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_DeleteMusic(JA_Music_t* music) {
|
||||||
|
if (!music) return;
|
||||||
|
if (current_music == music) {
|
||||||
|
JA_StopMusic();
|
||||||
|
current_music = nullptr;
|
||||||
|
}
|
||||||
|
if (music->stream) SDL_DestroyAudioStream(music->stream);
|
||||||
|
if (music->vorbis) stb_vorbis_close(music->vorbis);
|
||||||
|
SDL_free(music->ogg_data);
|
||||||
|
free(music->filename); // filename es libera aquí
|
||||||
|
delete music;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float JA_SetMusicVolume(float volume) {
|
||||||
|
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
|
||||||
|
if (current_music && current_music->stream) {
|
||||||
|
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||||
|
}
|
||||||
|
return JA_musicVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_SetMusicPosition(float /*value*/) {
|
||||||
|
// No implementat amb el backend de streaming. Mai va arribar a usar-se
|
||||||
|
// en el codi existent, així que es manté com a stub.
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float JA_GetMusicPosition() {
|
||||||
|
// Veure nota a JA_SetMusicPosition.
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_EnableMusic(const bool value) {
|
||||||
|
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic();
|
||||||
|
|
||||||
|
JA_musicEnabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Sound Functions ---
|
||||||
|
|
||||||
|
inline JA_Sound_t* JA_NewSound(Uint8* buffer, Uint32 length) {
|
||||||
|
JA_Sound_t* sound = new JA_Sound_t();
|
||||||
|
sound->buffer = buffer;
|
||||||
|
sound->length = length;
|
||||||
|
// Nota: spec se queda con los valores por defecto.
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) {
|
||||||
|
JA_Sound_t* sound = new JA_Sound_t();
|
||||||
|
if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length)) {
|
||||||
|
std::cout << "Failed to load WAV from memory: " << SDL_GetError() << '\n';
|
||||||
|
delete sound;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JA_Sound_t* JA_LoadSound(const char* filename) {
|
||||||
|
JA_Sound_t* sound = new JA_Sound_t();
|
||||||
|
if (!SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length)) {
|
||||||
|
std::cout << "Failed to load WAV file: " << SDL_GetError() << '\n';
|
||||||
|
delete sound;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) {
|
||||||
|
if (!JA_soundEnabled || !sound) return -1;
|
||||||
|
|
||||||
|
int channel = 0;
|
||||||
|
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
|
||||||
|
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) {
|
||||||
|
// No hay canal libre, reemplazamos el primero
|
||||||
|
channel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JA_PlaySoundOnChannel(sound, channel, loop, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop, const int group) {
|
||||||
|
if (!JA_soundEnabled || !sound) return -1;
|
||||||
|
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
|
||||||
|
|
||||||
|
JA_StopChannel(channel); // Detiene y limpia el canal si estaba en uso
|
||||||
|
|
||||||
|
channels[channel].sound = sound;
|
||||||
|
channels[channel].times = loop;
|
||||||
|
channels[channel].pos = 0;
|
||||||
|
channels[channel].group = group; // Asignar grupo
|
||||||
|
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||||
|
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
||||||
|
|
||||||
|
if (!channels[channel].stream) {
|
||||||
|
std::cout << "Failed to create audio stream for sound!" << '\n';
|
||||||
|
channels[channel].state = JA_CHANNEL_FREE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||||
|
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
|
||||||
|
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_DeleteSound(JA_Sound_t* sound) {
|
||||||
|
if (!sound) return;
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||||
|
if (channels[i].sound == sound) JA_StopChannel(i);
|
||||||
|
}
|
||||||
|
SDL_free(sound->buffer);
|
||||||
|
delete sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_PauseChannel(const int channel) {
|
||||||
|
if (!JA_soundEnabled) return;
|
||||||
|
|
||||||
|
if (channel == -1) {
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||||
|
if (channels[i].state == JA_CHANNEL_PLAYING) {
|
||||||
|
channels[i].state = JA_CHANNEL_PAUSED;
|
||||||
|
SDL_UnbindAudioStream(channels[i].stream);
|
||||||
|
}
|
||||||
|
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
|
||||||
|
if (channels[channel].state == JA_CHANNEL_PLAYING) {
|
||||||
|
channels[channel].state = JA_CHANNEL_PAUSED;
|
||||||
|
SDL_UnbindAudioStream(channels[channel].stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_ResumeChannel(const int channel) {
|
||||||
|
if (!JA_soundEnabled) return;
|
||||||
|
|
||||||
|
if (channel == -1) {
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||||
|
if (channels[i].state == JA_CHANNEL_PAUSED) {
|
||||||
|
channels[i].state = JA_CHANNEL_PLAYING;
|
||||||
|
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
|
||||||
|
}
|
||||||
|
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
|
||||||
|
if (channels[channel].state == JA_CHANNEL_PAUSED) {
|
||||||
|
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||||
|
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_StopChannel(const int channel) {
|
||||||
|
if (channel == -1) {
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||||
|
if (channels[i].state != JA_CHANNEL_FREE) {
|
||||||
|
if (channels[i].stream) SDL_DestroyAudioStream(channels[i].stream);
|
||||||
|
channels[i].stream = nullptr;
|
||||||
|
channels[i].state = JA_CHANNEL_FREE;
|
||||||
|
channels[i].pos = 0;
|
||||||
|
channels[i].sound = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
|
||||||
|
if (channels[channel].state != JA_CHANNEL_FREE) {
|
||||||
|
if (channels[channel].stream) SDL_DestroyAudioStream(channels[channel].stream);
|
||||||
|
channels[channel].stream = nullptr;
|
||||||
|
channels[channel].state = JA_CHANNEL_FREE;
|
||||||
|
channels[channel].pos = 0;
|
||||||
|
channels[channel].sound = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JA_Channel_state JA_GetChannelState(const int channel) {
|
||||||
|
if (!JA_soundEnabled) return JA_SOUND_DISABLED;
|
||||||
|
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID;
|
||||||
|
|
||||||
|
return channels[channel].state;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float JA_SetSoundVolume(float volume, const int group = -1) // -1 para todos los grupos
|
||||||
|
{
|
||||||
|
const float v = SDL_clamp(volume, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
if (group == -1) {
|
||||||
|
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
|
||||||
|
JA_soundVolume[i] = v;
|
||||||
|
}
|
||||||
|
} else if (group >= 0 && group < JA_MAX_GROUPS) {
|
||||||
|
JA_soundVolume[group] = v;
|
||||||
|
} else {
|
||||||
|
return v; // Grupo inválido
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplicar volumen a canales activos
|
||||||
|
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||||
|
if ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) {
|
||||||
|
if (group == -1 || channels[i].group == group) {
|
||||||
|
if (channels[i].stream) {
|
||||||
|
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[channels[i].group]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void JA_EnableSound(const bool value) {
|
||||||
|
if (!value) {
|
||||||
|
JA_StopChannel(-1); // Detener todos los canales
|
||||||
|
}
|
||||||
|
JA_soundEnabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float JA_SetVolume(float volume) {
|
||||||
|
float v = JA_SetMusicVolume(volume);
|
||||||
|
JA_SetSoundVolume(v, -1); // Aplicar a todos los grupos de sonido
|
||||||
|
return v;
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
#include "define_buttons.hpp"
|
#include "core/input/define_buttons.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para __all_of_fn, all_of
|
#include <algorithm> // Para __all_of_fn, all_of
|
||||||
#include <memory> // Para unique_ptr, allocator, shared_ptr, operator==, make_unique
|
#include <memory> // Para unique_ptr, allocator, shared_ptr, operator==, make_unique
|
||||||
|
|
||||||
#include "input.hpp" // Para Input
|
#include "core/input/input.hpp" // Para Input
|
||||||
#include "input_types.hpp" // Para InputAction
|
#include "core/input/input_types.hpp" // Para InputAction
|
||||||
#include "lang.hpp" // Para getText
|
#include "core/locale/lang.hpp" // Para getText
|
||||||
#include "options.hpp" // Para Gamepad
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
#include "param.hpp" // Para Param, param, ParamGame, ParamServiceMenu
|
#include "game/options.hpp" // Para Gamepad
|
||||||
#include "resource.hpp" // Para Resource
|
#include "game/ui/window_message.hpp" // Para WindowMessage
|
||||||
#include "ui/window_message.hpp" // Para WindowMessage
|
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamServiceMenu
|
||||||
#include "utils.hpp" // Para Zone
|
#include "utils/utils.hpp" // Para Zone
|
||||||
|
|
||||||
DefineButtons::DefineButtons()
|
DefineButtons::DefineButtons()
|
||||||
: input_(Input::get()) {
|
: input_(Input::get()) {
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "input.hpp"
|
#include "core/input/input.hpp"
|
||||||
#include "ui/window_message.hpp"
|
#include "game/ui/window_message.hpp"
|
||||||
|
|
||||||
namespace Options {
|
namespace Options {
|
||||||
struct Gamepad;
|
struct Gamepad;
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/input/input_types.hpp" // Solo incluimos los tipos compartidos
|
||||||
#include "external/json.hpp"
|
#include "external/json.hpp"
|
||||||
#include "input_types.hpp" // Solo incluimos los tipos compartidos
|
|
||||||
|
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct GamepadConfig {
|
struct GamepadConfig {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "global_inputs.hpp"
|
#include "core/input/global_inputs.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para __any_of_fn, any_of
|
#include <algorithm> // Para __any_of_fn, any_of
|
||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
@@ -7,16 +7,16 @@
|
|||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "audio.hpp" // Para Audio
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
#include "input.hpp" // Para Input
|
#include "core/input/input.hpp" // Para Input
|
||||||
#include "input_types.hpp" // Para InputAction
|
#include "core/input/input_types.hpp" // Para InputAction
|
||||||
#include "lang.hpp" // Para getText, getLangFile, getLangName, getNextLangCode, loadFromFile
|
#include "core/locale/lang.hpp" // Para getText, getLangFile, getLangName, getNextLangCode, loadFromFile
|
||||||
#include "options.hpp" // Para Video, video, Settings, settings, Audio, audio, Window, window
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "screen.hpp" // Para Screen
|
#include "core/system/section.hpp" // Para Name, name, Options, options, AttractMode, attract_mode
|
||||||
#include "section.hpp" // Para Name, name, Options, options, AttractMode, attract_mode
|
#include "game/options.hpp" // Para Video, video, Settings, settings, Audio, audio, Window, window
|
||||||
#include "ui/notifier.hpp" // Para Notifier
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
#include "utils.hpp" // Para boolToOnOff
|
#include "utils/utils.hpp" // Para boolToOnOff
|
||||||
|
|
||||||
namespace GlobalInputs {
|
namespace GlobalInputs {
|
||||||
// Termina
|
// Termina
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "input.hpp"
|
#include "core/input/input.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_GetGamepadAxis, SDL_GamepadAxis, SDL_GamepadButton, SDL_GetError, SDL_JoystickID, SDL_AddGamepadMappingsFromFile, SDL_Event, SDL_EventType, SDL_GetGamepadButton, SDL_GetKeyboardState, SDL_INIT_GAMEPAD, SDL_InitSubSystem, SDL_LogError, SDL_OpenGamepad, SDL_PollEvent, SDL_WasInit, Sint16, SDL_Gamepad, SDL_LogCategory, SDL_Scancode
|
#include <SDL3/SDL.h> // Para SDL_GetGamepadAxis, SDL_GamepadAxis, SDL_GamepadButton, SDL_GetError, SDL_JoystickID, SDL_AddGamepadMappingsFromFile, SDL_Event, SDL_EventType, SDL_GetGamepadButton, SDL_GetKeyboardState, SDL_INIT_GAMEPAD, SDL_InitSubSystem, SDL_LogError, SDL_OpenGamepad, SDL_PollEvent, SDL_WasInit, Sint16, SDL_Gamepad, SDL_LogCategory, SDL_Scancode
|
||||||
|
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
#include <unordered_map> // Para unordered_map, _Node_iterator, operator==, _Node_iterator_base, _Node_const_iterator
|
#include <unordered_map> // Para unordered_map, _Node_iterator, operator==, _Node_iterator_base, _Node_const_iterator
|
||||||
#include <utility> // Para pair, move
|
#include <utility> // Para pair, move
|
||||||
|
|
||||||
#include "ui/logger.hpp" // Para info
|
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Input* Input::instance = nullptr;
|
Input* Input::instance = nullptr;
|
||||||
|
|
||||||
@@ -305,22 +303,16 @@ void Input::addGamepadMappingsFromFile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::discoverGamepads() {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
handleEvent(event); // Comprueba mandos conectados
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Input::initSDLGamePad() {
|
void Input::initSDLGamePad() {
|
||||||
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) {
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GAMEPAD could not initialize! SDL Error: %s", SDL_GetError());
|
std::cout << "SDL_GAMEPAD could not initialize! SDL Error: " << SDL_GetError() << '\n';
|
||||||
} else {
|
} else {
|
||||||
addGamepadMappingsFromFile();
|
addGamepadMappingsFromFile();
|
||||||
loadGamepadConfigs();
|
loadGamepadConfigs();
|
||||||
discoverGamepads();
|
// Los mandos ya conectados llegan como SDL_EVENT_GAMEPAD_ADDED en el
|
||||||
Logger::info("Input System initialized successfully");
|
// primer pase del pump de eventos (antes del primer SDL_AppIterate),
|
||||||
|
// por lo que no hace falta enumerarlos aquí a mano.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,6 +432,27 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
|||||||
return config.path == gamepad->path;
|
return config.path == gamepad->path;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fallback por nombre: si el mismo dispositivo se enchufa a otro puerto, su
|
||||||
|
// path cambia pero el nombre suele mantenerse. Recuperamos su configuración
|
||||||
|
// y actualizamos el path guardado al actual. Solo se acepta un match cuyo
|
||||||
|
// path guardado NO corresponda a otro mando ya conectado, para no arruinar
|
||||||
|
// setups con varios mandos idénticos en puertos distintos (cabinet arcade).
|
||||||
|
if (config_it == gamepad_configs_.end() && !gamepad->name.empty()) {
|
||||||
|
auto is_path_active = [this](const std::string& query_path) -> bool {
|
||||||
|
return std::ranges::any_of(gamepads_, [&query_path](const std::shared_ptr<Gamepad>& g) -> bool {
|
||||||
|
return g && g->path == query_path;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
config_it = std::ranges::find_if(gamepad_configs_, [&gamepad, &is_path_active](const GamepadConfig& config) -> bool {
|
||||||
|
return config.name == gamepad->name && !is_path_active(config.path);
|
||||||
|
});
|
||||||
|
if (config_it != gamepad_configs_.end()) {
|
||||||
|
std::cout << "Gamepad '" << gamepad->name << "' found by name, refreshing path to: " << gamepad->path << '\n';
|
||||||
|
config_it->path = gamepad->path;
|
||||||
|
saveGamepadConfigs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config_it != gamepad_configs_.end()) {
|
if (config_it != gamepad_configs_.end()) {
|
||||||
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
// Se encontró una configuración específica para este puerto/dispositivo. La aplicamos.
|
||||||
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
|
std::cout << "Applying custom config for gamepad at path: " << gamepad->path << '\n';
|
||||||
@@ -449,7 +462,6 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Opcional: Podrías añadir un fallback para buscar por nombre si no se encuentra por ruta.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "gamepad_config_manager.hpp" // for GamepadConfig (ptr only), GamepadConfigs
|
#include "core/input/gamepad_config_manager.hpp" // for GamepadConfig (ptr only), GamepadConfigs
|
||||||
#include "input_types.hpp" // for InputAction
|
#include "core/input/input_types.hpp" // for InputAction
|
||||||
|
|
||||||
// --- Clase Input: gestiona la entrada de teclado y mandos (singleton) ---
|
// --- Clase Input: gestiona la entrada de teclado y mandos (singleton) ---
|
||||||
class Input {
|
class Input {
|
||||||
@@ -206,7 +206,6 @@ class Input {
|
|||||||
auto addGamepad(int device_index) -> std::string;
|
auto addGamepad(int device_index) -> std::string;
|
||||||
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
auto removeGamepad(SDL_JoystickID id) -> std::string;
|
||||||
void addGamepadMappingsFromFile();
|
void addGamepadMappingsFromFile();
|
||||||
void discoverGamepads();
|
|
||||||
|
|
||||||
// --- Métodos para integración con GamepadConfigManager ---
|
// --- Métodos para integración con GamepadConfigManager ---
|
||||||
void loadGamepadConfigs();
|
void loadGamepadConfigs();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "input_types.hpp"
|
#include "core/input/input_types.hpp"
|
||||||
|
|
||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "mouse.hpp"
|
#include "core/input/mouse.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show...
|
#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show...
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "lang.hpp"
|
#include "core/locale/lang.hpp"
|
||||||
|
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
#include <exception> // Para exception
|
#include <exception> // Para exception
|
||||||
@@ -7,11 +7,11 @@
|
|||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
#include "difficulty.hpp" // Para Difficulty
|
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
|
||||||
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
||||||
#include "options.hpp" // Para SettingsOpt...
|
#include "game/gameplay/difficulty.hpp" // Para Difficulty
|
||||||
#include "resource_helper.hpp" // Para ResourceHelper
|
#include "game/options.hpp" // Para SettingsOpt...
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "background.hpp"
|
#include "core/rendering/background.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_GetRenderTarget, SDL_RenderTexture, SDL_SetTextureAlphaMod, SDL_SetTextureBlendMode, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_TextureAccess, SDL_FPoint
|
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_GetRenderTarget, SDL_RenderTexture, SDL_SetTextureAlphaMod, SDL_SetTextureBlendMode, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_TextureAccess, SDL_FPoint
|
||||||
|
|
||||||
@@ -8,14 +8,14 @@
|
|||||||
#include <string> // Para basic_string
|
#include <string> // Para basic_string
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "animated_sprite.hpp" // Para AnimatedSprite
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "moving_sprite.hpp" // Para MovingSprite
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||||
#include "param.hpp" // Para Param, ParamBackground, param
|
#include "core/rendering/sprite/moving_sprite.hpp" // Para MovingSprite
|
||||||
#include "resource.hpp" // Para Resource
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
#include "screen.hpp" // Para Screen
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
#include "texture.hpp" // Para Texture
|
#include "utils/param.hpp" // Para Param, ParamBackground, param
|
||||||
#include "utils.hpp" // Para easeOutCubic
|
#include "utils/utils.hpp" // Para easeOutCubic
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Background::Background(float total_progress_to_complete)
|
Background::Background(float total_progress_to_complete)
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
#include <memory> // Para unique_ptr, shared_ptr
|
#include <memory> // Para unique_ptr, shared_ptr
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "utils/color.hpp" // Para Color
|
||||||
|
|
||||||
class MovingSprite;
|
class MovingSprite;
|
||||||
class Sprite;
|
class Sprite;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "fade.hpp"
|
#include "core/rendering/fade.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "color.hpp"
|
#include "core/rendering/screen.hpp"
|
||||||
#include "param.hpp"
|
#include "utils/color.hpp"
|
||||||
#include "screen.hpp"
|
#include "utils/param.hpp"
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Fade::Fade()
|
Fade::Fade()
|
||||||
253
source/core/rendering/gif.cpp
Normal file
253
source/core/rendering/gif.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#include "core/rendering/gif.hpp"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo
|
||||||
|
|
||||||
|
#include <cstring> // Para memcpy, size_t
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
|
#include <stdexcept> // Para runtime_error
|
||||||
|
#include <string> // Para char_traits, operator==, basic_string, string
|
||||||
|
|
||||||
|
namespace GIF {
|
||||||
|
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) {
|
||||||
|
std::memcpy(dst, buffer, size);
|
||||||
|
buffer += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) {
|
||||||
|
if (code_length < 2 || code_length > 12) {
|
||||||
|
std::cout << "Invalid LZW code length: " << code_length << '\n';
|
||||||
|
throw std::runtime_error("Invalid LZW code length");
|
||||||
|
}
|
||||||
|
|
||||||
|
int i, bit;
|
||||||
|
int prev = -1;
|
||||||
|
std::vector<DictionaryEntry> dictionary;
|
||||||
|
int dictionary_ind;
|
||||||
|
unsigned int mask = 0x01;
|
||||||
|
int reset_code_length = code_length;
|
||||||
|
int clear_code = 1 << code_length;
|
||||||
|
int stop_code = clear_code + 1;
|
||||||
|
int match_len = 0;
|
||||||
|
|
||||||
|
dictionary.resize(1 << (code_length + 1));
|
||||||
|
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
|
||||||
|
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
||||||
|
dictionary[dictionary_ind].prev = -1;
|
||||||
|
dictionary[dictionary_ind].len = 1;
|
||||||
|
}
|
||||||
|
dictionary_ind += 2;
|
||||||
|
|
||||||
|
while (input_length > 0) {
|
||||||
|
int code = 0;
|
||||||
|
for (i = 0; i < (code_length + 1); i++) {
|
||||||
|
if (input_length <= 0) {
|
||||||
|
std::cout << "Unexpected end of input in decompress" << '\n';
|
||||||
|
throw std::runtime_error("Unexpected end of input in decompress");
|
||||||
|
}
|
||||||
|
bit = ((*input & mask) != 0) ? 1 : 0;
|
||||||
|
mask <<= 1;
|
||||||
|
if (mask == 0x100) {
|
||||||
|
mask = 0x01;
|
||||||
|
input++;
|
||||||
|
input_length--;
|
||||||
|
}
|
||||||
|
code |= (bit << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == clear_code) {
|
||||||
|
code_length = reset_code_length;
|
||||||
|
dictionary.resize(1 << (code_length + 1));
|
||||||
|
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
|
||||||
|
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
||||||
|
dictionary[dictionary_ind].prev = -1;
|
||||||
|
dictionary[dictionary_ind].len = 1;
|
||||||
|
}
|
||||||
|
dictionary_ind += 2;
|
||||||
|
prev = -1;
|
||||||
|
continue;
|
||||||
|
} else if (code == stop_code) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev > -1 && code_length < 12) {
|
||||||
|
if (code > dictionary_ind) {
|
||||||
|
std::cout << "LZW error: code (" << code << ") exceeds dictionary_ind (" << dictionary_ind << ")" << '\n';
|
||||||
|
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int ptr;
|
||||||
|
if (code == dictionary_ind) {
|
||||||
|
ptr = prev;
|
||||||
|
while (dictionary[ptr].prev != -1)
|
||||||
|
ptr = dictionary[ptr].prev;
|
||||||
|
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
||||||
|
} else {
|
||||||
|
ptr = code;
|
||||||
|
while (dictionary[ptr].prev != -1)
|
||||||
|
ptr = dictionary[ptr].prev;
|
||||||
|
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
||||||
|
}
|
||||||
|
dictionary[dictionary_ind].prev = prev;
|
||||||
|
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
|
||||||
|
dictionary_ind++;
|
||||||
|
|
||||||
|
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
|
||||||
|
code_length++;
|
||||||
|
dictionary.resize(1 << (code_length + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = code;
|
||||||
|
|
||||||
|
if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) {
|
||||||
|
std::cout << "Invalid LZW code " << code << ", dictionary size " << static_cast<unsigned long>(dictionary.size()) << '\n';
|
||||||
|
throw std::runtime_error("LZW error: invalid code encountered");
|
||||||
|
}
|
||||||
|
|
||||||
|
int curCode = code;
|
||||||
|
match_len = dictionary[curCode].len;
|
||||||
|
while (curCode != -1) {
|
||||||
|
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
|
||||||
|
if (dictionary[curCode].prev == curCode) {
|
||||||
|
std::cout << "Internal error; self-reference detected." << '\n';
|
||||||
|
throw std::runtime_error("Internal error in decompress: self-reference");
|
||||||
|
}
|
||||||
|
curCode = dictionary[curCode].prev;
|
||||||
|
}
|
||||||
|
out += match_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
uint8_t block_size = *buffer;
|
||||||
|
buffer++;
|
||||||
|
while (block_size != 0) {
|
||||||
|
data.insert(data.end(), buffer, buffer + block_size);
|
||||||
|
buffer += block_size;
|
||||||
|
block_size = *buffer;
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) {
|
||||||
|
ImageDescriptor image_descriptor;
|
||||||
|
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
|
||||||
|
|
||||||
|
uint8_t lzw_code_size;
|
||||||
|
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
|
||||||
|
|
||||||
|
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
|
||||||
|
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
|
||||||
|
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
|
||||||
|
|
||||||
|
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
|
||||||
|
return uncompressed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) {
|
||||||
|
uint8_t header[6];
|
||||||
|
std::memcpy(header, buffer, 6);
|
||||||
|
buffer += 6;
|
||||||
|
|
||||||
|
ScreenDescriptor screen_descriptor;
|
||||||
|
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
|
||||||
|
buffer += sizeof(ScreenDescriptor);
|
||||||
|
|
||||||
|
std::vector<uint32_t> global_color_table;
|
||||||
|
if (screen_descriptor.fields & 0x80) {
|
||||||
|
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
||||||
|
global_color_table.resize(global_color_table_size);
|
||||||
|
for (int i = 0; i < global_color_table_size; ++i) {
|
||||||
|
uint8_t r = buffer[0];
|
||||||
|
uint8_t g = buffer[1];
|
||||||
|
uint8_t b = buffer[2];
|
||||||
|
global_color_table[i] = (r << 16) | (g << 8) | b;
|
||||||
|
buffer += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return global_color_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
|
||||||
|
uint8_t header[6];
|
||||||
|
std::memcpy(header, buffer, 6);
|
||||||
|
buffer += 6;
|
||||||
|
|
||||||
|
std::string headerStr(reinterpret_cast<char *>(header), 6);
|
||||||
|
if (headerStr != "GIF87a" && headerStr != "GIF89a") {
|
||||||
|
std::cout << "Formato de archivo GIF inválido: " << headerStr << '\n';
|
||||||
|
throw std::runtime_error("Formato de archivo GIF inválido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenDescriptor screen_descriptor;
|
||||||
|
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
|
||||||
|
|
||||||
|
w = screen_descriptor.width;
|
||||||
|
h = screen_descriptor.height;
|
||||||
|
|
||||||
|
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
|
||||||
|
std::vector<RGB> global_color_table;
|
||||||
|
if (screen_descriptor.fields & 0x80) {
|
||||||
|
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
||||||
|
global_color_table.resize(global_color_table_size);
|
||||||
|
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
|
||||||
|
buffer += 3 * global_color_table_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t block_type = *buffer++;
|
||||||
|
while (block_type != TRAILER) {
|
||||||
|
if (block_type == EXTENSION_INTRODUCER) {
|
||||||
|
uint8_t extension_label = *buffer++;
|
||||||
|
switch (extension_label) {
|
||||||
|
case GRAPHIC_CONTROL: {
|
||||||
|
uint8_t blockSize = *buffer++;
|
||||||
|
buffer += blockSize;
|
||||||
|
uint8_t subBlockSize = *buffer++;
|
||||||
|
while (subBlockSize != 0) {
|
||||||
|
buffer += subBlockSize;
|
||||||
|
subBlockSize = *buffer++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case APPLICATION_EXTENSION:
|
||||||
|
case COMMENT_EXTENSION:
|
||||||
|
case PLAINTEXT_EXTENSION: {
|
||||||
|
uint8_t blockSize = *buffer++;
|
||||||
|
buffer += blockSize;
|
||||||
|
uint8_t subBlockSize = *buffer++;
|
||||||
|
while (subBlockSize != 0) {
|
||||||
|
buffer += subBlockSize;
|
||||||
|
subBlockSize = *buffer++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
uint8_t blockSize = *buffer++;
|
||||||
|
buffer += blockSize;
|
||||||
|
uint8_t subBlockSize = *buffer++;
|
||||||
|
while (subBlockSize != 0) {
|
||||||
|
buffer += subBlockSize;
|
||||||
|
subBlockSize = *buffer++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (block_type == IMAGE_DESCRIPTOR) {
|
||||||
|
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
|
||||||
|
} else {
|
||||||
|
std::cout << "Unrecognized block type: 0x" << std::hex << static_cast<int>(block_type) << std::dec << '\n';
|
||||||
|
return std::vector<uint8_t>{};
|
||||||
|
}
|
||||||
|
block_type = *buffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<uint8_t>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
|
||||||
|
return processGifStream(buffer, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GIF
|
||||||
92
source/core/rendering/gif.hpp
Normal file
92
source/core/rendering/gif.hpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
namespace GIF {
|
||||||
|
|
||||||
|
// Constantes definidas con constexpr, en lugar de macros
|
||||||
|
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
|
||||||
|
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
|
||||||
|
constexpr uint8_t TRAILER = 0x3B;
|
||||||
|
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
|
||||||
|
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
|
||||||
|
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
|
||||||
|
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct ScreenDescriptor {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint8_t fields;
|
||||||
|
uint8_t background_color_index;
|
||||||
|
uint8_t pixel_aspect_ratio;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RGB {
|
||||||
|
uint8_t r, g, b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImageDescriptor {
|
||||||
|
uint16_t image_left_position;
|
||||||
|
uint16_t image_top_position;
|
||||||
|
uint16_t image_width;
|
||||||
|
uint16_t image_height;
|
||||||
|
uint8_t fields;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct DictionaryEntry {
|
||||||
|
uint8_t byte;
|
||||||
|
int prev;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Extension {
|
||||||
|
uint8_t extension_code;
|
||||||
|
uint8_t block_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GraphicControlExtension {
|
||||||
|
uint8_t fields;
|
||||||
|
uint16_t delay_time;
|
||||||
|
uint8_t transparent_color_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApplicationExtension {
|
||||||
|
uint8_t application_id[8];
|
||||||
|
uint8_t version[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlaintextExtension {
|
||||||
|
uint16_t left, top, width, height;
|
||||||
|
uint8_t cell_width, cell_height;
|
||||||
|
uint8_t foreground_color, background_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Gif {
|
||||||
|
public:
|
||||||
|
// Descompone (uncompress) el bloque comprimido usando LZW.
|
||||||
|
// Este método puede lanzar std::runtime_error en caso de error.
|
||||||
|
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
|
||||||
|
|
||||||
|
// Carga la paleta (global color table) a partir de un buffer,
|
||||||
|
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
|
||||||
|
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
|
||||||
|
|
||||||
|
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
|
||||||
|
// asigna el ancho y alto mediante referencias.
|
||||||
|
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
|
||||||
|
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
|
||||||
|
|
||||||
|
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
|
||||||
|
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
|
||||||
|
|
||||||
|
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
|
||||||
|
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GIF
|
||||||
@@ -1,26 +1,28 @@
|
|||||||
#include "screen.hpp"
|
#include "core/rendering/screen.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_RenderTexture, SDL_SetRenderDrawColor, SDL_SetRenderVSync, SDL_LogCategory, SDL_GetError, SDL_LogError, SDL_LogInfo, SDL_RendererLogicalPresentation, SDL_SetRenderLogicalPresentation, SDL_CreateTexture, SDL_DestroyTexture, SDL_DestroyWindow, SDL_GetDisplayName, SDL_GetTicks, SDL_Quit, SDL_RENDERER_VSYNC_DISABLED, SDL_RenderClear, SDL_CreateRenderer, SDL_CreateWindow, SDL_DestroyRenderer, SDL_DisplayID, SDL_FRect, SDL_GetCurrentDisplayMode, SDL_GetDisplays, SDL_GetRenderTarget, SDL_GetWindowPosition, SDL_GetWindowSize, SDL_Init, SDL_LogWarn, SDL_PixelFormat, SDL_RenderFillRect, SDL_RenderPresent, SDL_SetHint, SDL_SetRenderDrawBlendMode, SDL_SetTextureScaleMode, SDL_SetWindowFullscreen, SDL_SetWindowPosition, SDL_SetWindowSize, SDL_TextureAccess, SDL_free, SDL_BLENDMODE_BLEND, SDL_HINT_RENDER_DRIVER, SDL_INIT_VIDEO, SDL_ScaleMode, SDL_WINDOW_FULLSCREEN, SDL_WindowFlags
|
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_RenderTexture, SDL_SetRenderDrawColor, SDL_SetRenderVSync, SDL_LogCategory, SDL_GetError, SDL_LogError, SDL_LogInfo, SDL_RendererLogicalPresentation, SDL_SetRenderLogicalPresentation, SDL_CreateTexture, SDL_DestroyTexture, SDL_DestroyWindow, SDL_GetDisplayName, SDL_GetTicks, SDL_Quit, SDL_RENDERER_VSYNC_DISABLED, SDL_RenderClear, SDL_CreateRenderer, SDL_CreateWindow, SDL_DestroyRenderer, SDL_DisplayID, SDL_FRect, SDL_GetCurrentDisplayMode, SDL_GetDisplays, SDL_GetRenderTarget, SDL_GetWindowPosition, SDL_GetWindowSize, SDL_Init, SDL_LogWarn, SDL_PixelFormat, SDL_RenderFillRect, SDL_RenderPresent, SDL_SetHint, SDL_SetRenderDrawBlendMode, SDL_SetTextureScaleMode, SDL_SetWindowFullscreen, SDL_SetWindowPosition, SDL_SetWindowSize, SDL_TextureAccess, SDL_free, SDL_BLENDMODE_BLEND, SDL_HINT_RENDER_DRIVER, SDL_INIT_VIDEO, SDL_ScaleMode, SDL_WINDOW_FULLSCREEN, SDL_WindowFlags
|
||||||
|
|
||||||
#include <algorithm> // Para min, max
|
#include <algorithm> // Para min, max
|
||||||
#include <cstring> // Para memcpy
|
#include <cstring> // Para memcpy
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
#include <memory> // Para allocator, shared_ptr, unique_ptr, __shared_ptr_access, make_shared, make_unique
|
#include <memory> // Para allocator, shared_ptr, unique_ptr, __shared_ptr_access, make_shared, make_unique
|
||||||
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
#include <string> // Para basic_string, operator+, char_traits, to_string, string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
||||||
#include "director.hpp" // Para Director::debug_config
|
#ifndef NO_SHADERS
|
||||||
#include "mouse.hpp" // Para updateCursorVisibility
|
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||||
#include "options.hpp" // Para Video, video, Window, window
|
#endif
|
||||||
#include "param.hpp" // Para Param, param, ParamGame, ParamDebug
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
#include "rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "resource.hpp" // Para Resource
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
#include "text.hpp" // Para Text
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/system/director.hpp" // Para Director::debug_config
|
||||||
#include "ui/logger.hpp" // Para info
|
#include "game/options.hpp" // Para Video, video, Window, window
|
||||||
#include "ui/notifier.hpp" // Para Notifier
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
#include "utils.hpp" // Para toLower
|
#include "utils/param.hpp" // Para Param, param, ParamGame, ParamDebug
|
||||||
|
#include "utils/utils.hpp" // Para toLower
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Screen* Screen::instance = nullptr;
|
Screen* Screen::instance = nullptr;
|
||||||
@@ -290,7 +292,7 @@ void Screen::initShaders() {
|
|||||||
#ifndef NO_SHADERS
|
#ifndef NO_SHADERS
|
||||||
auto* self = Screen::get();
|
auto* self = Screen::get();
|
||||||
if (self == nullptr) {
|
if (self == nullptr) {
|
||||||
SDL_Log("Screen::initShaders: instance is null, skipping");
|
std::cout << "Screen::initShaders: instance is null, skipping" << '\n';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!self->shader_backend_) {
|
if (!self->shader_backend_) {
|
||||||
@@ -301,7 +303,7 @@ void Screen::initShaders() {
|
|||||||
}
|
}
|
||||||
if (!self->shader_backend_->isHardwareAccelerated()) {
|
if (!self->shader_backend_->isHardwareAccelerated()) {
|
||||||
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
|
||||||
SDL_Log("Screen::initShaders: SDL3GPUShader::init() = %s", ok ? "OK" : "FAILED");
|
std::cout << "Screen::initShaders: SDL3GPUShader::init() = " << (ok ? "OK" : "FAILED") << '\n';
|
||||||
}
|
}
|
||||||
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
|
||||||
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
|
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
|
||||||
@@ -371,9 +373,7 @@ void Screen::renderAttenuate() {
|
|||||||
auto Screen::initSDLVideo() -> bool {
|
auto Screen::initSDLVideo() -> bool {
|
||||||
// Inicializar SDL
|
// Inicializar SDL
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "FATAL: Failed to initialize SDL_VIDEO! SDL Error: " << SDL_GetError() << '\n';
|
||||||
"FATAL: Failed to initialize SDL_VIDEO! SDL Error: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,8 +383,7 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
// Configurar hint para renderizado
|
// Configurar hint para renderizado
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Warning: Failed to set Metal hint!" << '\n';
|
||||||
"Warning: Failed to set Metal hint!");
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -405,9 +404,7 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
window_flags);
|
window_flags);
|
||||||
|
|
||||||
if (window_ == nullptr) {
|
if (window_ == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "FATAL: Failed to create window! SDL Error: " << SDL_GetError() << '\n';
|
||||||
"FATAL: Failed to create window! SDL Error: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -415,9 +412,7 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
// Crear renderer
|
// Crear renderer
|
||||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||||
if (renderer_ == nullptr) {
|
if (renderer_ == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "FATAL: Failed to create renderer! SDL Error: " << SDL_GetError() << '\n';
|
||||||
"FATAL: Failed to create renderer! SDL Error: %s",
|
|
||||||
SDL_GetError());
|
|
||||||
SDL_DestroyWindow(window_);
|
SDL_DestroyWindow(window_);
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
@@ -429,24 +424,14 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
||||||
SDL_SetRenderVSync(renderer_, Options::video.vsync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
SDL_SetRenderVSync(renderer_, Options::video.vsync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
||||||
|
|
||||||
Logger::info("Video system initialized successfully");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene información sobre la pantalla
|
// Obtiene información sobre la pantalla
|
||||||
void Screen::getDisplayInfo() {
|
void Screen::getDisplayInfo() {
|
||||||
int i;
|
|
||||||
int num_displays = 0;
|
int num_displays = 0;
|
||||||
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
|
||||||
if (displays != nullptr) {
|
if (displays != nullptr) {
|
||||||
for (i = 0; i < num_displays; ++i) {
|
|
||||||
SDL_DisplayID instance_id = displays[i];
|
|
||||||
const char* name = SDL_GetDisplayName(instance_id);
|
|
||||||
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, (name != nullptr) ? name : "Unknown");
|
|
||||||
Logger::info(std::string("Display ") + std::to_string(instance_id) + ": " + (name != nullptr ? name : "Unknown"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
|
||||||
|
|
||||||
// Guarda información del monitor en display_monitor_
|
// Guarda información del monitor en display_monitor_
|
||||||
@@ -465,13 +450,6 @@ void Screen::getDisplayInfo() {
|
|||||||
std::to_string(dm->h) + " @ " +
|
std::to_string(dm->h) + " @ " +
|
||||||
std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
|
std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
|
||||||
|
|
||||||
// Muestra información sobre el tamaño de la pantalla y de la ventana de juego
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz", static_cast<int>(dm->w), static_cast<int>(dm->h), static_cast<int>(dm->refresh_rate));
|
|
||||||
Logger::info("Current display mode: " + Options::video.info);
|
|
||||||
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast<int>(param.game.width), static_cast<int>(param.game.height), Options::window.zoom);
|
|
||||||
Logger::info("Window resolution: " + std::to_string(static_cast<int>(param.game.width)) + "x" + std::to_string(static_cast<int>(param.game.height)) + "x" + std::to_string(Options::window.zoom));
|
|
||||||
|
|
||||||
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
||||||
const int MAX_ZOOM = std::min(dm->w / param.game.width, (dm->h - WINDOWS_DECORATIONS) / param.game.height);
|
const int MAX_ZOOM = std::min(dm->w / param.game.width, (dm->h - WINDOWS_DECORATIONS) / param.game.height);
|
||||||
|
|
||||||
@@ -563,11 +541,7 @@ void Screen::applyCurrentPostFXPreset() {
|
|||||||
p.curvature = preset.curvature;
|
p.curvature = preset.curvature;
|
||||||
p.bleeding = preset.bleeding;
|
p.bleeding = preset.bleeding;
|
||||||
p.flicker = preset.flicker;
|
p.flicker = preset.flicker;
|
||||||
SDL_Log("Screen::applyCurrentPostFXPreset: preset='%s' scan=%.2f vign=%.2f chroma=%.2f",
|
std::cout << "Screen::applyCurrentPostFXPreset: preset='" << preset.name << "' scan=" << p.scanlines << " vign=" << p.vignette << " chroma=" << p.chroma << '\n';
|
||||||
preset.name.c_str(),
|
|
||||||
p.scanlines,
|
|
||||||
p.vignette,
|
|
||||||
p.chroma);
|
|
||||||
}
|
}
|
||||||
shader_backend_->setPostFXParams(p);
|
shader_backend_->setPostFXParams(p);
|
||||||
}
|
}
|
||||||
@@ -594,7 +568,7 @@ void Screen::applyCurrentCrtPiPreset() {
|
|||||||
.enable_sharper = preset.enable_sharper,
|
.enable_sharper = preset.enable_sharper,
|
||||||
};
|
};
|
||||||
shader_backend_->setCrtPiParams(p);
|
shader_backend_->setCrtPiParams(p);
|
||||||
SDL_Log("Screen::applyCurrentCrtPiPreset: preset='%s'", preset.name.c_str());
|
std::cout << "Screen::applyCurrentCrtPiPreset: preset='" << preset.name << "'" << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "core/rendering/shader_backend.hpp" // Para Rendering::ShaderType
|
||||||
#include "options.hpp" // Para VideoOptions, video
|
#include "game/options.hpp" // Para VideoOptions, video
|
||||||
#include "rendering/shader_backend.hpp" // Para Rendering::ShaderType
|
#include "utils/color.hpp" // Para Color
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class Notifier;
|
class Notifier;
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
#include "rendering/sdl3gpu/sdl3gpu_shader.hpp"
|
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL_log.h>
|
#include <SDL3/SDL_log.h>
|
||||||
|
|
||||||
#include <algorithm> // std::min, std::max, std::floor
|
#include <algorithm> // std::min, std::max, std::floor
|
||||||
#include <cmath> // std::floor, std::ceil
|
#include <cmath> // std::floor, std::ceil
|
||||||
#include <cstring> // memcpy, strlen
|
#include <cstring> // memcpy, strlen
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
#include "rendering/sdl3gpu/crtpi_frag_spv.h"
|
#include "core/rendering/sdl3gpu/crtpi_frag_spv.h"
|
||||||
#include "rendering/sdl3gpu/downscale_frag_spv.h"
|
#include "core/rendering/sdl3gpu/downscale_frag_spv.h"
|
||||||
#include "rendering/sdl3gpu/postfx_frag_spv.h"
|
#include "core/rendering/sdl3gpu/postfx_frag_spv.h"
|
||||||
#include "rendering/sdl3gpu/postfx_vert_spv.h"
|
#include "core/rendering/sdl3gpu/postfx_vert_spv.h"
|
||||||
#include "rendering/sdl3gpu/upscale_frag_spv.h"
|
#include "core/rendering/sdl3gpu/upscale_frag_spv.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@@ -377,7 +378,7 @@ namespace Rendering {
|
|||||||
|
|
||||||
// 1. GPU disabled by config
|
// 1. GPU disabled by config
|
||||||
if (preferred_driver_ == "none") {
|
if (preferred_driver_ == "none") {
|
||||||
SDL_Log("SDL3GPUShader: GPU disabled by config, using SDL_Renderer fallback");
|
std::cout << "SDL3GPUShader: GPU disabled by config, using SDL_Renderer fallback" << '\n';
|
||||||
driver_name_ = "";
|
driver_name_ = "";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -392,18 +393,18 @@ namespace Rendering {
|
|||||||
const char* preferred = preferred_driver_.empty() ? nullptr : preferred_driver_.c_str();
|
const char* preferred = preferred_driver_.empty() ? nullptr : preferred_driver_.c_str();
|
||||||
device_ = SDL_CreateGPUDevice(PREFERRED, false, preferred);
|
device_ = SDL_CreateGPUDevice(PREFERRED, false, preferred);
|
||||||
if (device_ == nullptr && preferred != nullptr) {
|
if (device_ == nullptr && preferred != nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: driver '%s' not available, falling back to auto", preferred);
|
std::cout << "SDL3GPUShader: driver '" << preferred << "' not available, falling back to auto" << '\n';
|
||||||
device_ = SDL_CreateGPUDevice(PREFERRED, false, nullptr);
|
device_ = SDL_CreateGPUDevice(PREFERRED, false, nullptr);
|
||||||
}
|
}
|
||||||
if (device_ == nullptr) {
|
if (device_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_CreateGPUDevice failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: SDL_CreateGPUDevice failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
driver_name_ = SDL_GetGPUDeviceDriver(device_);
|
driver_name_ = SDL_GetGPUDeviceDriver(device_);
|
||||||
SDL_Log("SDL3GPUShader: driver = %s", driver_name_.c_str());
|
std::cout << "SDL3GPUShader: driver = " << driver_name_ << '\n';
|
||||||
|
|
||||||
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_ClaimWindowForGPUDevice failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: SDL_ClaimWindowForGPUDevice failed: " << SDL_GetError() << '\n';
|
||||||
SDL_DestroyGPUDevice(device_);
|
SDL_DestroyGPUDevice(device_);
|
||||||
device_ = nullptr;
|
device_ = nullptr;
|
||||||
return false;
|
return false;
|
||||||
@@ -422,7 +423,7 @@ namespace Rendering {
|
|||||||
tex_info.num_levels = 1;
|
tex_info.num_levels = 1;
|
||||||
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
||||||
if (scene_texture_ == nullptr) {
|
if (scene_texture_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create scene texture: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create scene texture: " << SDL_GetError() << '\n';
|
||||||
cleanup();
|
cleanup();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -435,7 +436,7 @@ namespace Rendering {
|
|||||||
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
||||||
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
||||||
if (upload_buffer_ == nullptr) {
|
if (upload_buffer_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create upload buffer: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create upload buffer: " << SDL_GetError() << '\n';
|
||||||
cleanup();
|
cleanup();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -450,7 +451,7 @@ namespace Rendering {
|
|||||||
samp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
samp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
||||||
sampler_ = SDL_CreateGPUSampler(device_, &samp_info);
|
sampler_ = SDL_CreateGPUSampler(device_, &samp_info);
|
||||||
if (sampler_ == nullptr) {
|
if (sampler_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create sampler: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create sampler: " << SDL_GetError() << '\n';
|
||||||
cleanup();
|
cleanup();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -464,7 +465,7 @@ namespace Rendering {
|
|||||||
lsamp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
lsamp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
|
||||||
linear_sampler_ = SDL_CreateGPUSampler(device_, &lsamp_info);
|
linear_sampler_ = SDL_CreateGPUSampler(device_, &lsamp_info);
|
||||||
if (linear_sampler_ == nullptr) {
|
if (linear_sampler_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create linear sampler: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create linear sampler: " << SDL_GetError() << '\n';
|
||||||
cleanup();
|
cleanup();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -480,7 +481,7 @@ namespace Rendering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_initialized_ = true;
|
is_initialized_ = true;
|
||||||
SDL_Log("SDL3GPUShader: initialized OK (%dx%d)", game_width_, game_height_);
|
std::cout << "SDL3GPUShader: initialized OK (" << game_width_ << "x" << game_height_ << ")" << '\n';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +501,7 @@ namespace Rendering {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((vert == nullptr) || (frag == nullptr)) {
|
if ((vert == nullptr) || (frag == nullptr)) {
|
||||||
SDL_Log("SDL3GPUShader: failed to compile PostFX shaders");
|
std::cout << "SDL3GPUShader: failed to compile PostFX shaders" << '\n';
|
||||||
if (vert != nullptr) { SDL_ReleaseGPUShader(device_, vert); }
|
if (vert != nullptr) { SDL_ReleaseGPUShader(device_, vert); }
|
||||||
if (frag != nullptr) { SDL_ReleaseGPUShader(device_, frag); }
|
if (frag != nullptr) { SDL_ReleaseGPUShader(device_, frag); }
|
||||||
return false;
|
return false;
|
||||||
@@ -529,7 +530,7 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, frag);
|
SDL_ReleaseGPUShader(device_, frag);
|
||||||
|
|
||||||
if (pipeline_ == nullptr) {
|
if (pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: PostFX pipeline creation failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: PostFX pipeline creation failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,7 +566,7 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, ufrag);
|
SDL_ReleaseGPUShader(device_, ufrag);
|
||||||
|
|
||||||
if (upscale_pipeline_ == nullptr) {
|
if (upscale_pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: upscale pipeline creation failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: upscale pipeline creation failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +602,7 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, offrag);
|
SDL_ReleaseGPUShader(device_, offrag);
|
||||||
|
|
||||||
if (postfx_offscreen_pipeline_ == nullptr) {
|
if (postfx_offscreen_pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: PostFX offscreen pipeline creation failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: PostFX offscreen pipeline creation failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +638,7 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, dfrag);
|
SDL_ReleaseGPUShader(device_, dfrag);
|
||||||
|
|
||||||
if (downscale_pipeline_ == nullptr) {
|
if (downscale_pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: downscale pipeline creation failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: downscale pipeline creation failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,7 +684,7 @@ namespace Rendering {
|
|||||||
SDL_ReleaseGPUShader(device_, frag);
|
SDL_ReleaseGPUShader(device_, frag);
|
||||||
|
|
||||||
if (crtpi_pipeline_ == nullptr) {
|
if (crtpi_pipeline_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: CrtPi pipeline creation failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: CrtPi pipeline creation failed: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -697,7 +698,7 @@ namespace Rendering {
|
|||||||
|
|
||||||
void* mapped = SDL_MapGPUTransferBuffer(device_, upload_buffer_, false);
|
void* mapped = SDL_MapGPUTransferBuffer(device_, upload_buffer_, false);
|
||||||
if (mapped == nullptr) {
|
if (mapped == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_MapGPUTransferBuffer failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: SDL_MapGPUTransferBuffer failed: " << SDL_GetError() << '\n';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +727,7 @@ namespace Rendering {
|
|||||||
|
|
||||||
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
|
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device_);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: SDL_AcquireGPUCommandBuffer failed: " << SDL_GetError() << '\n';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +774,7 @@ namespace Rendering {
|
|||||||
Uint32 sw = 0;
|
Uint32 sw = 0;
|
||||||
Uint32 sh = 0;
|
Uint32 sh = 0;
|
||||||
if (!SDL_AcquireGPUSwapchainTexture(cmd, window_, &swapchain, &sw, &sh)) {
|
if (!SDL_AcquireGPUSwapchainTexture(cmd, window_, &swapchain, &sw, &sh)) {
|
||||||
SDL_Log("SDL3GPUShader: SDL_AcquireGPUSwapchainTexture failed: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: SDL_AcquireGPUSwapchainTexture failed: " << SDL_GetError() << '\n';
|
||||||
SDL_SubmitGPUCommandBuffer(cmd);
|
SDL_SubmitGPUCommandBuffer(cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -998,7 +999,7 @@ namespace Rendering {
|
|||||||
info.num_uniform_buffers = num_uniform_buffers;
|
info.num_uniform_buffers = num_uniform_buffers;
|
||||||
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
|
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
|
||||||
if (shader == nullptr) {
|
if (shader == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: MSL shader '%s' failed: %s", entrypoint, SDL_GetError());
|
std::cout << "SDL3GPUShader: MSL shader '" << entrypoint << "' failed: " << SDL_GetError() << '\n';
|
||||||
}
|
}
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
@@ -1020,7 +1021,7 @@ namespace Rendering {
|
|||||||
info.num_uniform_buffers = num_uniform_buffers;
|
info.num_uniform_buffers = num_uniform_buffers;
|
||||||
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
|
SDL_GPUShader* shader = SDL_CreateGPUShader(device, &info);
|
||||||
if (shader == nullptr) {
|
if (shader == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: SPIRV shader '%s' failed: %s", entrypoint, SDL_GetError());
|
std::cout << "SDL3GPUShader: SPIRV shader '" << entrypoint << "' failed: " << SDL_GetError() << '\n';
|
||||||
}
|
}
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
@@ -1135,7 +1136,7 @@ namespace Rendering {
|
|||||||
tex_info.num_levels = 1;
|
tex_info.num_levels = 1;
|
||||||
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info);
|
||||||
if (scene_texture_ == nullptr) {
|
if (scene_texture_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: reinit — failed to create scene texture: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: reinit — failed to create scene texture: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1144,13 +1145,13 @@ namespace Rendering {
|
|||||||
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
tb_info.size = static_cast<Uint32>(game_width_ * game_height_ * 4);
|
||||||
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info);
|
||||||
if (upload_buffer_ == nullptr) {
|
if (upload_buffer_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: reinit — failed to create upload buffer: %s", SDL_GetError());
|
std::cout << "SDL3GPUShader: reinit — failed to create upload buffer: " << SDL_GetError() << '\n';
|
||||||
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
SDL_ReleaseGPUTexture(device_, scene_texture_);
|
||||||
scene_texture_ = nullptr;
|
scene_texture_ = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Log("SDL3GPUShader: reinit — scene %dx%d, SS %s", game_width_, game_height_, oversample_ > 1 ? "on" : "off");
|
std::cout << "SDL3GPUShader: reinit — scene " << game_width_ << "x" << game_height_ << ", SS " << (oversample_ > 1 ? "on" : "off") << '\n';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1185,20 +1186,20 @@ namespace Rendering {
|
|||||||
|
|
||||||
scaled_texture_ = SDL_CreateGPUTexture(device_, &info);
|
scaled_texture_ = SDL_CreateGPUTexture(device_, &info);
|
||||||
if (scaled_texture_ == nullptr) {
|
if (scaled_texture_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create scaled texture %dx%d: %s", W, H, SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create scaled texture " << W << "x" << H << ": " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
postfx_texture_ = SDL_CreateGPUTexture(device_, &info);
|
postfx_texture_ = SDL_CreateGPUTexture(device_, &info);
|
||||||
if (postfx_texture_ == nullptr) {
|
if (postfx_texture_ == nullptr) {
|
||||||
SDL_Log("SDL3GPUShader: failed to create postfx texture %dx%d: %s", W, H, SDL_GetError());
|
std::cout << "SDL3GPUShader: failed to create postfx texture " << W << "x" << H << ": " << SDL_GetError() << '\n';
|
||||||
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
SDL_ReleaseGPUTexture(device_, scaled_texture_);
|
||||||
scaled_texture_ = nullptr;
|
scaled_texture_ = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss_factor_ = factor;
|
ss_factor_ = factor;
|
||||||
SDL_Log("SDL3GPUShader: scaled+postfx textures %dx%d (factor %dx)", W, H, factor);
|
std::cout << "SDL3GPUShader: scaled+postfx textures " << W << "x" << H << " (factor " << factor << "x)" << '\n';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "rendering/shader_backend.hpp"
|
#include "core/rendering/shader_backend.hpp"
|
||||||
|
|
||||||
// PostFX uniforms pushed to fragment stage each frame.
|
// PostFX uniforms pushed to fragment stage each frame.
|
||||||
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
// 12 floats = 48 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
#include "animated_sprite.hpp"
|
#include "core/rendering/sprite/animated_sprite.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError, SDL_FRect
|
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError, SDL_FRect
|
||||||
|
|
||||||
#include <algorithm> // Para min
|
#include <algorithm> // Para min
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
#include <fstream> // Para basic_istream, basic_ifstream, istream, basic_ios, ifstream, istringstream, stringstream
|
#include <fstream> // Para basic_istream, basic_ifstream, istream, basic_ios, ifstream, istringstream, stringstream
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
#include <sstream> // Para basic_istringstream, basic_stringstream
|
#include <sstream> // Para basic_istringstream, basic_stringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <utility> // Para move, pair
|
#include <utility> // Para move, pair
|
||||||
|
|
||||||
#include "resource_helper.hpp" // Para loadFile
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/resources/resource_helper.hpp" // Para loadFile
|
||||||
#include "ui/logger.hpp" // Para dots
|
|
||||||
|
|
||||||
// Carga las animaciones en un vector(Animations) desde un fichero
|
// Carga las animaciones en un vector(Animations) desde un fichero
|
||||||
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {
|
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {
|
||||||
@@ -31,15 +31,13 @@ auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffe
|
|||||||
if (!using_resource_data) {
|
if (!using_resource_data) {
|
||||||
file.open(file_path);
|
file.open(file_path);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file);
|
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file);
|
||||||
|
|
||||||
Logger::dots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
|
||||||
|
|
||||||
std::vector<std::string> buffer;
|
std::vector<std::string> buffer;
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(input_stream, line)) {
|
while (std::getline(input_stream, line)) {
|
||||||
@@ -82,7 +80,7 @@ auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Si no se encuentra, muestra una advertencia y devuelve -1
|
// Si no se encuentra, muestra una advertencia y devuelve -1
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str());
|
std::cout << "** Warning: could not find \"" << name << "\" animation" << '\n';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +217,7 @@ void AnimatedSprite::processConfigLine(const std::string& line, AnimationConfig&
|
|||||||
config.frame_height = value;
|
config.frame_height = value;
|
||||||
updateFrameCalculations(config);
|
updateFrameCalculations(config);
|
||||||
} else {
|
} else {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
|
std::cout << "Warning: unknown parameter " << key << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +273,7 @@ void AnimatedSprite::processAnimationParameter(const std::string& line, Animatio
|
|||||||
} else if (key == "frames") {
|
} else if (key == "frames") {
|
||||||
parseFramesParameter(value, animation, config);
|
parseFramesParameter(value, animation, config);
|
||||||
} else {
|
} else {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str());
|
std::cout << "Warning: unknown parameter " << key << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "moving_sprite.hpp" // for MovingSprite
|
#include "core/rendering/sprite/moving_sprite.hpp" // for MovingSprite
|
||||||
|
|
||||||
// Declaración adelantada
|
// Declaración adelantada
|
||||||
class Texture;
|
class Texture;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "card_sprite.hpp"
|
#include "core/rendering/sprite/card_sprite.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para std::clamp
|
#include <algorithm> // Para std::clamp
|
||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "utils.hpp" // Para easeOutBounce, easeOutCubic
|
#include "utils/utils.hpp" // Para easeOutBounce, easeOutCubic
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
|
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
|
|
||||||
#include "moving_sprite.hpp" // Para MovingSprite
|
#include "core/rendering/sprite/moving_sprite.hpp" // Para MovingSprite
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "moving_sprite.hpp"
|
#include "core/rendering/sprite/moving_sprite.hpp"
|
||||||
|
|
||||||
#include <cmath> // Para std::abs
|
#include <cmath> // Para std::abs
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip)
|
MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float horizontal_zoom, float vertical_zoom, SDL_FlipMode flip)
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
|
|
||||||
#include "sprite.hpp" // for Sprite
|
#include "core/rendering/sprite/sprite.hpp" // for Sprite
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// IWYU pragma: no_include <bits/std_abs.h>
|
// IWYU pragma: no_include <bits/std_abs.h>
|
||||||
#include "path_sprite.hpp"
|
#include "core/rendering/sprite/path_sprite.hpp"
|
||||||
|
|
||||||
#include <cmath> // Para abs
|
#include <cmath> // Para abs
|
||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "smart_sprite.hpp"
|
#include "core/rendering/sprite/smart_sprite.hpp"
|
||||||
|
|
||||||
#include "moving_sprite.hpp" // Para MovingSprite
|
#include "core/rendering/sprite/moving_sprite.hpp" // Para MovingSprite
|
||||||
|
|
||||||
// Actualiza la posición y comprueba si ha llegado a su destino (time-based)
|
// Actualiza la posición y comprueba si ha llegado a su destino (time-based)
|
||||||
void SmartSprite::update(float delta_time) {
|
void SmartSprite::update(float delta_time) {
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <memory> // Para shared_ptr
|
#include <memory> // Para shared_ptr
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "animated_sprite.hpp" // Para AnimatedSprite
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "sprite.hpp"
|
#include "core/rendering/sprite/sprite.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Sprite::Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height)
|
Sprite::Sprite(std::shared_ptr<Texture> texture, float pos_x, float pos_y, float width, float height)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "text.hpp"
|
#include "core/rendering/text.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_FRect, Uint8, SDL_GetRenderTarget, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_TextureAccess, SDL_GetTextureAlphaMod
|
#include <SDL3/SDL.h> // Para SDL_FRect, Uint8, SDL_GetRenderTarget, SDL_RenderClear, SDL_SetRenderDrawColor, SDL_SetRenderTarget, SDL_BLENDMODE_BLEND, SDL_PixelFormat, SDL_TextureAccess, SDL_GetTextureAlphaMod
|
||||||
|
|
||||||
@@ -10,13 +10,12 @@
|
|||||||
#include <utility> // Para std::cmp_less_equal
|
#include <utility> // Para std::cmp_less_equal
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "resource_helper.hpp" // Para loadFile
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
#include "screen.hpp" // Para Screen
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "core/resources/resource_helper.hpp" // Para loadFile
|
||||||
#include "texture.hpp" // Para Texture
|
#include "utils/color.hpp" // Para Color
|
||||||
#include "ui/logger.hpp" // Para dots
|
#include "utils/utils.hpp" // Para getFileName
|
||||||
#include "utils.hpp" // Para getFileName
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Text::Text(const std::shared_ptr<Texture>& texture, const std::string& text_file) {
|
Text::Text(const std::shared_ptr<Texture>& texture, const std::string& text_file) {
|
||||||
@@ -432,7 +431,6 @@ auto Text::loadFile(const std::string& file_path) -> std::shared_ptr<Text::File>
|
|||||||
line_read++;
|
line_read++;
|
||||||
};
|
};
|
||||||
|
|
||||||
Logger::dots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
|
||||||
// Cierra el fichero si se usó
|
// Cierra el fichero si se usó
|
||||||
if (!using_resource_data && file.is_open()) {
|
if (!using_resource_data && file.is_open()) {
|
||||||
file.close();
|
file.close();
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
#include <memory> // Para shared_ptr, unique_ptr
|
#include <memory> // Para shared_ptr, unique_ptr
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "utils/color.hpp" // Para Color
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "texture.hpp"
|
#include "core/rendering/texture.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_...
|
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_...
|
||||||
|
|
||||||
#include <cstdint> // Para uint32_t
|
#include <cstdint> // Para uint32_t
|
||||||
#include <cstring> // Para memcpy
|
#include <cstring> // Para memcpy
|
||||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ios
|
#include <fstream> // Para basic_ifstream, basic_istream, basic_ios
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string> // Para basic_string, char_traits, operator+, string
|
#include <string> // Para basic_string, char_traits, operator+, string
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.hpp" // Para getFileName, Color, printWithDots
|
#include "core/rendering/gif.hpp" // Para Gif
|
||||||
#include "external/gif.hpp" // Para Gif
|
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
|
||||||
#include "resource_helper.hpp" // Para ResourceHelper
|
#include "external/stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
||||||
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
#include "utils/color.hpp" // Para getFileName, Color
|
||||||
#include "ui/logger.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Texture::Texture(SDL_Renderer* renderer, std::string path)
|
Texture::Texture(SDL_Renderer* renderer, std::string path)
|
||||||
@@ -39,7 +39,7 @@ Texture::Texture(SDL_Renderer* renderer, std::string path)
|
|||||||
surface_ = loadSurface(path_);
|
surface_ = loadSurface(path_);
|
||||||
|
|
||||||
// Añade la propia paleta del fichero a la lista
|
// Añade la propia paleta del fichero a la lista
|
||||||
addPaletteFromGifFile(path_, true); // Usar modo silencioso
|
addPaletteFromGifFile(path_);
|
||||||
|
|
||||||
// Crea la textura, establece el BlendMode y copia la surface a la textura
|
// Crea la textura, establece el BlendMode y copia la surface a la textura
|
||||||
createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING);
|
createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING);
|
||||||
@@ -80,10 +80,9 @@ auto Texture::loadFromFile(const std::string& file_path) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
|
std::cout << "Error: Fichero no encontrado " << getFileName(file_path) << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
||||||
}
|
}
|
||||||
Logger::dots("Texture : ", getFileName(file_path), "[ LOADED ]");
|
|
||||||
|
|
||||||
int pitch;
|
int pitch;
|
||||||
SDL_PixelFormat pixel_format;
|
SDL_PixelFormat pixel_format;
|
||||||
@@ -99,12 +98,12 @@ auto Texture::loadFromFile(const std::string& file_path) -> bool {
|
|||||||
// Carga la imagen desde una ruta específica
|
// Carga la imagen desde una ruta específica
|
||||||
auto* loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void*>(data), pitch);
|
auto* loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void*>(data), pitch);
|
||||||
if (loaded_surface == nullptr) {
|
if (loaded_surface == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load image %s", file_path.c_str());
|
std::cout << "Unable to load image " << file_path << '\n';
|
||||||
} else {
|
} else {
|
||||||
// Crea la textura desde los pixels de la surface
|
// Crea la textura desde los pixels de la surface
|
||||||
new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
|
new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface);
|
||||||
if (new_texture == nullptr) {
|
if (new_texture == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture from %s! SDL Error: %s", file_path.c_str(), SDL_GetError());
|
std::cout << "Unable to create texture from " << file_path << "! SDL Error: " << SDL_GetError() << '\n';
|
||||||
} else {
|
} else {
|
||||||
// Obtiene las dimensiones de la imagen
|
// Obtiene las dimensiones de la imagen
|
||||||
width_ = loaded_surface->w;
|
width_ = loaded_surface->w;
|
||||||
@@ -126,7 +125,7 @@ auto Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_Tex
|
|||||||
// Crea una textura sin inicializar
|
// Crea una textura sin inicializar
|
||||||
texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
|
texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
|
||||||
if (texture_ == nullptr) {
|
if (texture_ == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError());
|
std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << '\n';
|
||||||
} else {
|
} else {
|
||||||
width_ = width;
|
width_ = width;
|
||||||
height_ = height;
|
height_ = height;
|
||||||
@@ -236,7 +235,7 @@ auto Texture::loadSurface(const std::string& file_path) -> std::shared_ptr<Surfa
|
|||||||
// Fallback a filesystem directo
|
// Fallback a filesystem directo
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +246,7 @@ auto Texture::loadSurface(const std::string& file_path) -> std::shared_ptr<Surfa
|
|||||||
// Leer el contenido del archivo en un buffer
|
// Leer el contenido del archivo en un buffer
|
||||||
buffer.resize(size);
|
buffer.resize(size);
|
||||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
|
std::cout << "Error al leer el fichero " << file_path << '\n';
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,7 +257,7 @@ auto Texture::loadSurface(const std::string& file_path) -> std::shared_ptr<Surfa
|
|||||||
Uint16 h = 0;
|
Uint16 h = 0;
|
||||||
std::vector<Uint8> raw_pixels = gif.loadGif(buffer.data(), w, h);
|
std::vector<Uint8> raw_pixels = gif.loadGif(buffer.data(), w, h);
|
||||||
if (raw_pixels.empty()) {
|
if (raw_pixels.empty()) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo cargar el GIF %s", file_path.c_str());
|
std::cout << "Error: No se pudo cargar el GIF " << file_path << '\n';
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +272,6 @@ auto Texture::loadSurface(const std::string& file_path) -> std::shared_ptr<Surfa
|
|||||||
width_ = w;
|
width_ = w;
|
||||||
height_ = h;
|
height_ = h;
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF %s cargado correctamente.", file_path.c_str());
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +300,7 @@ void Texture::setPaletteColor(int palette, int index, Uint32 color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carga una paleta desde un fichero
|
// Carga una paleta desde un fichero
|
||||||
auto Texture::loadPaletteFromFile(const std::string& file_path, bool quiet) -> Palette {
|
auto Texture::loadPaletteFromFile(const std::string& file_path) -> Palette {
|
||||||
Palette palette;
|
Palette palette;
|
||||||
|
|
||||||
std::vector<Uint8> buffer;
|
std::vector<Uint8> buffer;
|
||||||
@@ -315,7 +313,7 @@ auto Texture::loadPaletteFromFile(const std::string& file_path, bool quiet) -> P
|
|||||||
// Fallback a filesystem directo
|
// Fallback a filesystem directo
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,20 +323,16 @@ auto Texture::loadPaletteFromFile(const std::string& file_path, bool quiet) -> P
|
|||||||
|
|
||||||
buffer.resize(size);
|
buffer.resize(size);
|
||||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str());
|
std::cout << "Error: No se pudo leer completamente el fichero " << file_path << '\n';
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quiet) {
|
|
||||||
Logger::dots("Palette : ", getFileName(file_path), "[ LOADED ]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
|
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
|
||||||
GIF::Gif gif;
|
GIF::Gif gif;
|
||||||
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
|
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
|
||||||
if (pal.empty()) {
|
if (pal.empty()) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Advertencia: No se encontró paleta en el archivo %s", file_path.c_str());
|
std::cout << "Advertencia: No se encontró paleta en el archivo " << file_path << '\n';
|
||||||
return palette; // Devuelve un vector vacío si no hay paleta
|
return palette; // Devuelve un vector vacío si no hay paleta
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,19 +341,18 @@ auto Texture::loadPaletteFromFile(const std::string& file_path, bool quiet) -> P
|
|||||||
palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA
|
palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Paleta cargada correctamente desde %s", file_path.c_str());
|
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Añade una paleta a la lista
|
// Añade una paleta a la lista
|
||||||
void Texture::addPaletteFromGifFile(const std::string& path, bool quiet) {
|
void Texture::addPaletteFromGifFile(const std::string& path) {
|
||||||
palettes_.emplace_back(loadPaletteFromFile(path, quiet));
|
palettes_.emplace_back(loadPaletteFromFile(path));
|
||||||
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
|
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Añade una paleta a la lista
|
// Añade una paleta a la lista
|
||||||
void Texture::addPaletteFromPalFile(const std::string& path) {
|
void Texture::addPaletteFromPalFile(const std::string& path) {
|
||||||
palettes_.emplace_back(readPalFile(path, true)); // Usar modo silencioso
|
palettes_.emplace_back(readPalFile(path));
|
||||||
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
|
setPaletteColor(palettes_.size() - 1, 0, 0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +368,7 @@ void Texture::setPalette(size_t palette) {
|
|||||||
auto Texture::getRenderer() -> SDL_Renderer* { return renderer_; }
|
auto Texture::getRenderer() -> SDL_Renderer* { return renderer_; }
|
||||||
|
|
||||||
// Carga una paleta desde un archivo .pal
|
// Carga una paleta desde un archivo .pal
|
||||||
auto Texture::readPalFile(const std::string& file_path, bool quiet) -> Palette {
|
auto Texture::readPalFile(const std::string& file_path) -> Palette {
|
||||||
Palette palette{};
|
Palette palette{};
|
||||||
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
|
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class Texture {
|
|||||||
void setAlpha(Uint8 alpha); // Establece el alpha para la modulación
|
void setAlpha(Uint8 alpha); // Establece el alpha para la modulación
|
||||||
|
|
||||||
// --- Paletas ---
|
// --- Paletas ---
|
||||||
void addPaletteFromGifFile(const std::string& path, bool quiet = false); // Añade una paleta a la lista
|
void addPaletteFromGifFile(const std::string& path); // Añade una paleta a la lista
|
||||||
void addPaletteFromPalFile(const std::string& path); // Añade una paleta a la lista
|
void addPaletteFromPalFile(const std::string& path); // Añade una paleta a la lista
|
||||||
void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta
|
void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta
|
||||||
void setPalette(size_t palette); // Cambia la paleta de la textura
|
void setPalette(size_t palette); // Cambia la paleta de la textura
|
||||||
@@ -76,8 +76,8 @@ class Texture {
|
|||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
auto loadSurface(const std::string& file_path) -> std::shared_ptr<Surface>; // Crea una surface desde un fichero .gif
|
auto loadSurface(const std::string& file_path) -> std::shared_ptr<Surface>; // Crea una surface desde un fichero .gif
|
||||||
void flipSurface(); // Vuelca la surface en la textura
|
void flipSurface(); // Vuelca la surface en la textura
|
||||||
static auto loadPaletteFromFile(const std::string& file_path, bool quiet = false) -> Palette; // Carga una paleta desde un fichero
|
static auto loadPaletteFromFile(const std::string& file_path) -> Palette; // Carga una paleta desde un fichero
|
||||||
void unloadTexture(); // Libera la memoria de la textura
|
void unloadTexture(); // Libera la memoria de la textura
|
||||||
void unloadSurface(); // Libera la surface actual
|
void unloadSurface(); // Libera la surface actual
|
||||||
static auto readPalFile(const std::string& file_path, bool quiet = false) -> Palette; // Carga una paleta desde un archivo .pal
|
static auto readPalFile(const std::string& file_path) -> Palette; // Carga una paleta desde un archivo .pal
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "tiled_bg.hpp"
|
#include "core/rendering/tiled_bg.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess
|
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess
|
||||||
|
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
#include <numbers> // Para pi
|
#include <numbers> // Para pi
|
||||||
#include <string> // Para basic_string
|
#include <string> // Para basic_string
|
||||||
|
|
||||||
#include "resource.hpp" // Para Resource
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "screen.hpp" // Para Screen
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode)
|
TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetTextureColorMod, SDL_Renderer, SDL_Texture
|
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetTextureColorMod, SDL_Renderer, SDL_Texture
|
||||||
|
|
||||||
#include "color.hpp" // Para Color
|
#include "utils/color.hpp" // Para Color
|
||||||
|
|
||||||
// --- Enums ---
|
// --- Enums ---
|
||||||
enum class TiledBGMode : int { // Modos de funcionamiento para el tileado de fondo
|
enum class TiledBGMode : int { // Modos de funcionamiento para el tileado de fondo
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "writer.hpp"
|
#include "core/rendering/writer.hpp"
|
||||||
|
|
||||||
#include "text.hpp" // Para Text
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
|
|
||||||
// Actualiza el objeto (delta_time en ms)
|
// Actualiza el objeto (delta_time en ms)
|
||||||
void Writer::update(float delta_time) {
|
void Writer::update(float delta_time) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "asset.hpp"
|
#include "core/resources/asset.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
|
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
|
||||||
|
|
||||||
@@ -11,9 +11,8 @@
|
|||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
|
|
||||||
#include "resource_helper.hpp" // Para loadFile
|
#include "core/resources/resource_helper.hpp" // Para loadFile
|
||||||
#include "ui/logger.hpp" // Para info, section, CYAN
|
#include "utils/utils.hpp" // Para getFileName
|
||||||
#include "utils.hpp" // Para getFileName
|
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Asset* Asset::instance = nullptr;
|
Asset* Asset::instance = nullptr;
|
||||||
@@ -37,9 +36,7 @@ void Asset::addToMap(const std::string& file_path, Type type, bool required, boo
|
|||||||
|
|
||||||
// Verificar si ya existe el archivo
|
// Verificar si ya existe el archivo
|
||||||
if (file_list_.contains(filename)) {
|
if (file_list_.contains(filename)) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Warning: Asset '" << filename << "' already exists, overwriting" << '\n';
|
||||||
"Warning: Asset '%s' already exists, overwriting",
|
|
||||||
filename.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file_list_.emplace(filename, Item{std::move(full_path), type, required});
|
file_list_.emplace(filename, Item{std::move(full_path), type, required});
|
||||||
@@ -54,9 +51,7 @@ void Asset::add(const std::string& file_path, Type type, bool required, bool abs
|
|||||||
void Asset::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) {
|
void Asset::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) {
|
||||||
std::ifstream file(config_file_path);
|
std::ifstream file(config_file_path);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Error: Cannot open config file: " << config_file_path << '\n';
|
||||||
"Error: Cannot open config file: %s",
|
|
||||||
config_file_path.c_str());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +84,7 @@ void Asset::loadFromFile(const std::string& config_file_path, const std::string&
|
|||||||
|
|
||||||
// Verificar que tenemos al menos tipo y ruta
|
// Verificar que tenemos al menos tipo y ruta
|
||||||
if (parts.size() < 2) {
|
if (parts.size() < 2) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Warning: Malformed line " << line_number << " in config file (insufficient fields)" << '\n';
|
||||||
"Warning: Malformed line %d in config file (insufficient fields)",
|
|
||||||
line_number);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,14 +111,10 @@ void Asset::loadFromFile(const std::string& config_file_path, const std::string&
|
|||||||
addToMap(path, type, required, absolute);
|
addToMap(path, type, required, absolute);
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Error parsing line " << line_number << " in config file: " << e.what() << '\n';
|
||||||
"Error parsing line %d in config file: %s",
|
|
||||||
line_number,
|
|
||||||
e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Loaded " << file_list_.size() << " assets from config file" << '\n';
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +125,7 @@ auto Asset::getPath(const std::string& filename) const -> std::string {
|
|||||||
return it->second.file;
|
return it->second.file;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found", filename.c_str());
|
std::cout << "Warning: file " << filename << " not found" << '\n';
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +136,7 @@ auto Asset::loadData(const std::string& filename) const -> std::vector<uint8_t>
|
|||||||
return ResourceHelper::loadFile(it->second.file);
|
return ResourceHelper::loadFile(it->second.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found for data loading", filename.c_str());
|
std::cout << "Warning: file " << filename << " not found for data loading" << '\n';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +149,6 @@ auto Asset::exists(const std::string& filename) const -> bool {
|
|||||||
auto Asset::check() const -> bool {
|
auto Asset::check() const -> bool {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES");
|
|
||||||
Logger::section("CHECKING FILES", Logger::CYAN);
|
|
||||||
|
|
||||||
// Agrupar por tipo para mostrar organizado
|
// Agrupar por tipo para mostrar organizado
|
||||||
std::unordered_map<Type, std::vector<const Item*>> by_type;
|
std::unordered_map<Type, std::vector<const Item*>> by_type;
|
||||||
|
|
||||||
@@ -177,19 +163,11 @@ auto Asset::check() const -> bool {
|
|||||||
Type asset_type = static_cast<Type>(type);
|
Type asset_type = static_cast<Type>(type);
|
||||||
|
|
||||||
if (by_type.contains(asset_type)) {
|
if (by_type.contains(asset_type)) {
|
||||||
Logger::info(getTypeName(asset_type) + " FILES");
|
|
||||||
|
|
||||||
bool type_success = true;
|
|
||||||
for (const auto* item : by_type[asset_type]) {
|
for (const auto* item : by_type[asset_type]) {
|
||||||
if (!checkFile(item->file)) {
|
if (!checkFile(item->file)) {
|
||||||
success = false;
|
success = false;
|
||||||
type_success = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type_success) {
|
|
||||||
Logger::info("All files are OK.\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
@@ -215,9 +193,7 @@ auto Asset::checkFile(const std::string& path) const -> bool {
|
|||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
std::cout << "Error: Could not open file: " << path << '\n';
|
||||||
"Error: Could not open file: %s",
|
|
||||||
path.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "asset_integrated.hpp"
|
#include "core/resources/asset_integrated.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "asset.hpp"
|
#include "core/resources/asset.hpp"
|
||||||
#include "resource_loader.hpp"
|
#include "core/resources/resource_loader.hpp"
|
||||||
|
|
||||||
// Extensión de Asset que integra ResourceLoader
|
// Extensión de Asset que integra ResourceLoader
|
||||||
class AssetIntegrated : public Asset {
|
class AssetIntegrated : public Asset {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "resource.hpp"
|
#include "core/resources/resource.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
|
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
|
||||||
|
|
||||||
@@ -7,20 +7,22 @@
|
|||||||
#include <exception> // Para exception
|
#include <exception> // Para exception
|
||||||
#include <filesystem> // Para exists, path, remove
|
#include <filesystem> // Para exists, path, remove
|
||||||
#include <fstream> // Para basic_ofstream, basic_ios, basic_ostream::write, ios, ofstream
|
#include <fstream> // Para basic_ofstream, basic_ios, basic_ostream::write, ios, ofstream
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
#include <ranges> // Para __find_if_fn, find_if, __find_fn, find
|
#include <ranges> // Para __find_if_fn, find_if, __find_fn, find
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
#include "core/audio/jail_audio.hpp" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
||||||
#include "color.hpp" // Para Color, NO_COLOR_MOD
|
#include "core/locale/lang.hpp" // Para getText
|
||||||
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "lang.hpp" // Para getText
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
#include "param.hpp" // Para Param, param, ParamPlayer, ParamResource, ParamGame
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
#include "resource_helper.hpp" // Para loadFile
|
#include "core/resources/resource_helper.hpp" // Para loadFile
|
||||||
#include "screen.hpp" // Para Screen
|
#include "game/options.hpp" // Para Options::loading
|
||||||
#include "text.hpp" // Para Text
|
#include "utils/color.hpp" // Para Color, NO_COLOR_MOD
|
||||||
#include "ui/logger.hpp" // Para info, CR, dots
|
#include "utils/defines.hpp" // Para Texts::VERSION
|
||||||
#include "utils.hpp" // Para getFileName
|
#include "utils/param.hpp" // Para Param, param, ParamPlayer, ParamResource, ParamGame
|
||||||
|
#include "utils/utils.hpp" // Para getFileName
|
||||||
#include "version.h" // Para APP_NAME, GIT_HASH
|
#include "version.h" // Para APP_NAME, GIT_HASH
|
||||||
|
|
||||||
struct JA_Music_t; // lines 11-11
|
struct JA_Music_t; // lines 11-11
|
||||||
@@ -65,7 +67,8 @@ Resource::Resource(LoadingMode mode)
|
|||||||
Screen::get()->show();
|
Screen::get()->show();
|
||||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||||
loading_text_ = Screen::get()->getText();
|
loading_text_ = Screen::get()->getText();
|
||||||
load();
|
// Ya NO llamamos load() aquí: Director bombea beginLoad() + loadStep()
|
||||||
|
// desde iterate() para mantener vivo el bucle SDL3 durante la carga.
|
||||||
} else {
|
} else {
|
||||||
// En modo lazy, cargamos lo mínimo indispensable
|
// En modo lazy, cargamos lo mínimo indispensable
|
||||||
initResourceLists();
|
initResourceLists();
|
||||||
@@ -80,21 +83,16 @@ Resource::~Resource() {
|
|||||||
|
|
||||||
// Carga los recursos esenciales que siempre se necesitan (modo lazy)
|
// Carga los recursos esenciales que siempre se necesitan (modo lazy)
|
||||||
void Resource::loadEssentialResources() {
|
void Resource::loadEssentialResources() {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading essential resources for lazy mode");
|
|
||||||
|
|
||||||
// Cargar recursos de texto básicos que se usan para crear texturas
|
// Cargar recursos de texto básicos que se usan para crear texturas
|
||||||
loadTextFilesQuiet(); // <- VERSIÓN SILENCIOSA
|
loadTextFilesQuiet(); // <- VERSIÓN SILENCIOSA
|
||||||
loadEssentialTextures(); // Ya es silenciosa
|
loadEssentialTextures(); // Ya es silenciosa
|
||||||
createText(); // Crear objetos de texto
|
createText(); // Crear objetos de texto
|
||||||
createTextTextures(); // Crear texturas generadas (game_text_xxx)
|
createTextTextures(); // Crear texturas generadas (game_text_xxx)
|
||||||
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Essential resources loaded");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los ficheros de texto del juego (versión silenciosa)
|
// Carga los ficheros de texto del juego (versión silenciosa)
|
||||||
void Resource::loadTextFilesQuiet() {
|
void Resource::loadTextFilesQuiet() {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXT FILES (quiet load)");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
|
|
||||||
for (const auto& l : list) {
|
for (const auto& l : list) {
|
||||||
@@ -103,7 +101,6 @@ void Resource::loadTextFilesQuiet() {
|
|||||||
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> auto { return t.name == name; });
|
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||||
if (it != text_files_.end()) {
|
if (it != text_files_.end()) {
|
||||||
it->text_file = Text::loadFile(l);
|
it->text_file = Text::loadFile(l);
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Text file loaded: %s", name.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,8 +138,6 @@ void Resource::loadEssentialTextures() {
|
|||||||
|
|
||||||
// Inicializa las listas de recursos sin cargar el contenido (modo lazy)
|
// Inicializa las listas de recursos sin cargar el contenido (modo lazy)
|
||||||
void Resource::initResourceLists() {
|
void Resource::initResourceLists() {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Initializing resource lists for lazy loading");
|
|
||||||
|
|
||||||
// Inicializa lista de sonidos
|
// Inicializa lista de sonidos
|
||||||
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
sounds_.clear();
|
sounds_.clear();
|
||||||
@@ -200,8 +195,6 @@ void Resource::initResourceLists() {
|
|||||||
for (const auto& text_name : TEXT_OBJECTS) {
|
for (const auto& text_name : TEXT_OBJECTS) {
|
||||||
texts_.emplace_back(text_name); // Constructor con nullptr por defecto
|
texts_.emplace_back(text_name); // Constructor con nullptr por defecto
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Resource lists initialized for lazy loading");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el sonido a partir de un nombre (con carga perezosa)
|
// Obtiene el sonido a partir de un nombre (con carga perezosa)
|
||||||
@@ -216,7 +209,7 @@ auto Resource::getSound(const std::string& name) -> JA_Sound_t* {
|
|||||||
return it->sound;
|
return it->sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Sonido no encontrado %s", name.c_str());
|
std::cout << "Error: Sonido no encontrado " << name << '\n';
|
||||||
throw std::runtime_error("Sonido no encontrado: " + name);
|
throw std::runtime_error("Sonido no encontrado: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +225,7 @@ auto Resource::getMusic(const std::string& name) -> JA_Music_t* {
|
|||||||
return it->music;
|
return it->music;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Música no encontrada %s", name.c_str());
|
std::cout << "Error: Música no encontrada " << name << '\n';
|
||||||
throw std::runtime_error("Música no encontrada: " + name);
|
throw std::runtime_error("Música no encontrada: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +241,7 @@ auto Resource::getTexture(const std::string& name) -> std::shared_ptr<Texture> {
|
|||||||
return it->texture;
|
return it->texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Imagen no encontrada %s", name.c_str());
|
std::cout << "Error: Imagen no encontrada " << name << '\n';
|
||||||
throw std::runtime_error("Imagen no encontrada: " + name);
|
throw std::runtime_error("Imagen no encontrada: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +257,7 @@ auto Resource::getTextFile(const std::string& name) -> std::shared_ptr<Text::Fil
|
|||||||
return it->text_file;
|
return it->text_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: TextFile no encontrado %s", name.c_str());
|
std::cout << "Error: TextFile no encontrado " << name << '\n';
|
||||||
throw std::runtime_error("TextFile no encontrado: " + name);
|
throw std::runtime_error("TextFile no encontrado: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +273,7 @@ auto Resource::getText(const std::string& name) -> std::shared_ptr<Text> {
|
|||||||
return it->text;
|
return it->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Text no encontrado %s", name.c_str());
|
std::cout << "Error: Text no encontrado " << name << '\n';
|
||||||
throw std::runtime_error("Text no encontrado: " + name);
|
throw std::runtime_error("Text no encontrado: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,14 +289,14 @@ auto Resource::getAnimation(const std::string& name) -> AnimationsFileBuffer& {
|
|||||||
return it->animation;
|
return it->animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Animación no encontrada %s", name.c_str());
|
std::cout << "Error: Animación no encontrada " << name << '\n';
|
||||||
throw std::runtime_error("Animación no encontrada: " + name);
|
throw std::runtime_error("Animación no encontrada: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene el fichero con los datos para el modo demostración a partir de un índice
|
// Obtiene el fichero con los datos para el modo demostración a partir de un índice
|
||||||
auto Resource::getDemoData(int index) -> DemoData& {
|
auto Resource::getDemoData(int index) -> DemoData& {
|
||||||
if (index < 0 || std::cmp_greater_equal(index, demos_.size())) {
|
if (index < 0 || std::cmp_greater_equal(index, demos_.size())) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Index %d out of range for demo data (size: %d)", index, static_cast<int>(demos_.size()));
|
std::cout << "Index " << index << " out of range for demo data (size: " << static_cast<int>(demos_.size()) << ")" << '\n';
|
||||||
static DemoData empty_demo_;
|
static DemoData empty_demo_;
|
||||||
return empty_demo_;
|
return empty_demo_;
|
||||||
}
|
}
|
||||||
@@ -313,7 +306,6 @@ auto Resource::getDemoData(int index) -> DemoData& {
|
|||||||
// --- Métodos de carga perezosa ---
|
// --- Métodos de carga perezosa ---
|
||||||
|
|
||||||
auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
|
auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading sound lazily: %s", name.c_str());
|
|
||||||
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
for (const auto& file : sound_list) {
|
for (const auto& file : sound_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
@@ -329,7 +321,6 @@ auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* {
|
auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading music lazily: %s", name.c_str());
|
|
||||||
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
for (const auto& file : music_list) {
|
for (const auto& file : music_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
@@ -345,7 +336,6 @@ auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture> {
|
auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture> {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading texture lazily: %s", name.c_str());
|
|
||||||
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||||
for (const auto& file : texture_list) {
|
for (const auto& file : texture_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
@@ -356,7 +346,6 @@ auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Textu
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File> {
|
auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File> {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading text file lazily: %s", name.c_str());
|
|
||||||
auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT);
|
auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
for (const auto& file : text_file_list) {
|
for (const auto& file : text_file_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
@@ -367,8 +356,6 @@ auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
|
auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading text object lazily: %s", name.c_str());
|
|
||||||
|
|
||||||
// Mapeo de objetos de texto a sus recursos
|
// Mapeo de objetos de texto a sus recursos
|
||||||
struct TextMapping {
|
struct TextMapping {
|
||||||
std::string key;
|
std::string key;
|
||||||
@@ -406,7 +393,6 @@ auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Resource::loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer {
|
auto Resource::loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loading animation lazily: %s", name.c_str());
|
|
||||||
auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||||
for (const auto& file : animation_list) {
|
for (const auto& file : animation_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
@@ -428,31 +414,128 @@ void Resource::clear() {
|
|||||||
demos_.clear();
|
demos_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga todos los recursos del juego y muestra el progreso de carga
|
// Carga síncrona completa: usado por Resource::reload() (hot-reload en debug).
|
||||||
|
// En arranque normal la carga la bombea Director::iterate() vía loadStep().
|
||||||
void Resource::load() {
|
void Resource::load() {
|
||||||
// Prepara la gestión del progreso de carga
|
beginLoad();
|
||||||
|
while (!loadStep(INT_MAX)) {
|
||||||
|
// Presupuesto infinito: una sola pasada carga todo
|
||||||
|
}
|
||||||
|
Screen::get()->setVSync(saved_vsync_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepara el estado del cargador incremental. No carga nada todavía.
|
||||||
|
void Resource::beginLoad() {
|
||||||
calculateTotalResources();
|
calculateTotalResources();
|
||||||
initProgressBar();
|
initProgressBar();
|
||||||
|
|
||||||
// Muerstra la ventana y desactiva el sincronismo vertical
|
saved_vsync_ = Screen::getVSync();
|
||||||
auto* screen = Screen::get();
|
Screen::get()->setVSync(false); // Maximiza FPS durante el preload
|
||||||
auto vsync = Screen::getVSync();
|
|
||||||
screen->setVSync(false);
|
|
||||||
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES");
|
stage_ = LoadStage::SOUNDS;
|
||||||
loadSounds(); // Carga sonidos
|
stage_index_ = 0;
|
||||||
loadMusics(); // Carga músicas
|
}
|
||||||
loadTextures(); // Carga texturas
|
|
||||||
loadTextFiles(); // Carga ficheros de texto
|
|
||||||
loadAnimations(); // Carga animaciones
|
|
||||||
loadDemoData(); // Carga datos de demo
|
|
||||||
createText(); // Crea objetos de texto
|
|
||||||
createTextTextures(); // Crea texturas a partir de texto
|
|
||||||
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
|
||||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED");
|
|
||||||
|
|
||||||
// Restablece el sincronismo vertical a su valor original
|
auto Resource::isLoadDone() const -> bool {
|
||||||
screen->setVSync(vsync);
|
return stage_ == LoadStage::DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bombea la máquina de etapas hasta agotar el presupuesto de tiempo o completar la carga.
|
||||||
|
// Devuelve true cuando ya no queda nada por cargar.
|
||||||
|
auto Resource::loadStep(int budget_ms) -> bool {
|
||||||
|
if (stage_ == LoadStage::DONE) { return true; }
|
||||||
|
|
||||||
|
const Uint64 start_ns = SDL_GetTicksNS();
|
||||||
|
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
|
||||||
|
|
||||||
|
while (stage_ != LoadStage::DONE) {
|
||||||
|
switch (stage_) {
|
||||||
|
case LoadStage::SOUNDS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
|
if (stage_index_ == 0) { sounds_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::MUSICS;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneSound(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::MUSICS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
|
if (stage_index_ == 0) { musics_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::TEXTURES;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneMusic(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::TEXTURES: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||||
|
if (stage_index_ == 0) { textures_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::TEXT_FILES;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneTexture(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::TEXT_FILES: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
|
if (stage_index_ == 0) { text_files_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::ANIMATIONS;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneTextFile(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::ANIMATIONS: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||||
|
if (stage_index_ == 0) { animations_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::DEMO_DATA;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneAnimation(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::DEMO_DATA: {
|
||||||
|
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||||
|
if (stage_index_ == 0) { demos_.clear(); }
|
||||||
|
if (stage_index_ >= list.size()) {
|
||||||
|
stage_ = LoadStage::CREATE_TEXT;
|
||||||
|
stage_index_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loadOneDemoData(stage_index_++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LoadStage::CREATE_TEXT:
|
||||||
|
createText();
|
||||||
|
stage_ = LoadStage::CREATE_TEXT_TEXTURES;
|
||||||
|
break;
|
||||||
|
case LoadStage::CREATE_TEXT_TEXTURES:
|
||||||
|
createTextTextures();
|
||||||
|
stage_ = LoadStage::CREATE_PLAYER_TEXTURES;
|
||||||
|
break;
|
||||||
|
case LoadStage::CREATE_PLAYER_TEXTURES:
|
||||||
|
createPlayerTextures();
|
||||||
|
stage_ = LoadStage::DONE;
|
||||||
|
break;
|
||||||
|
case LoadStage::DONE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage_ == LoadStage::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recarga todos los recursos (limpia y vuelve a cargar)
|
// Recarga todos los recursos (limpia y vuelve a cargar)
|
||||||
@@ -465,112 +548,82 @@ void Resource::reload() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los sonidos del juego
|
// Carga un sonido concreto desde la lista de assets
|
||||||
void Resource::loadSounds() {
|
void Resource::loadOneSound(size_t idx) {
|
||||||
Logger::info("SOUND FILES");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
sounds_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
auto audio_data = loadAudioData(l);
|
auto audio_data = loadAudioData(path);
|
||||||
JA_Sound_t* sound = nullptr;
|
JA_Sound_t* sound = nullptr;
|
||||||
if (!audio_data.data.empty()) {
|
if (!audio_data.data.empty()) {
|
||||||
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
||||||
} else {
|
} else {
|
||||||
// Fallback a cargar desde disco si no está en pack
|
sound = JA_LoadSound(path.c_str());
|
||||||
sound = JA_LoadSound(l.c_str());
|
}
|
||||||
|
if (sound == nullptr) {
|
||||||
|
std::cout << "Sound load failed: " << name << '\n';
|
||||||
}
|
}
|
||||||
sounds_.emplace_back(name, sound);
|
sounds_.emplace_back(name, sound);
|
||||||
Logger::dots("Sound : ", name, "[ LOADED ]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las músicas del juego
|
// Carga una música concreta desde la lista de assets
|
||||||
void Resource::loadMusics() {
|
void Resource::loadOneMusic(size_t idx) {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("MUSIC FILES");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
musics_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
auto audio_data = loadAudioData(l);
|
auto audio_data = loadAudioData(path);
|
||||||
JA_Music_t* music = nullptr;
|
JA_Music_t* music = nullptr;
|
||||||
if (!audio_data.data.empty()) {
|
if (!audio_data.data.empty()) {
|
||||||
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
||||||
} else {
|
} else {
|
||||||
// Fallback a cargar desde disco si no está en pack
|
music = JA_LoadMusic(path.c_str());
|
||||||
music = JA_LoadMusic(l.c_str());
|
}
|
||||||
|
if (music == nullptr) {
|
||||||
|
std::cout << "Music load failed: " << name << '\n';
|
||||||
}
|
}
|
||||||
musics_.emplace_back(name, music);
|
musics_.emplace_back(name, music);
|
||||||
Logger::dots("Music : ", name, "[ LOADED ]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las texturas del juego
|
// Carga una textura concreta desde la lista de assets
|
||||||
void Resource::loadTextures() {
|
void Resource::loadOneTexture(size_t idx) {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("TEXTURES");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||||
textures_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l));
|
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los ficheros de texto del juego
|
// Carga un fichero de texto concreto desde la lista de assets
|
||||||
void Resource::loadTextFiles() {
|
void Resource::loadOneTextFile(size_t idx) {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("TEXT FILES");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||||
text_files_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
text_files_.emplace_back(name, Text::loadFile(l));
|
text_files_.emplace_back(name, Text::loadFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga las animaciones del juego
|
// Carga una animación concreta desde la lista de assets
|
||||||
void Resource::loadAnimations() {
|
void Resource::loadOneAnimation(size_t idx) {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("ANIMATIONS");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||||
animations_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
animations_.emplace_back(name, loadAnimationsFromFile(l));
|
animations_.emplace_back(name, loadAnimationsFromFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carga los datos para el modo demostración
|
// Carga un fichero de datos de demo concreto desde la lista de assets
|
||||||
void Resource::loadDemoData() {
|
void Resource::loadOneDemoData(size_t idx) {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("DEMO FILES");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||||
demos_.clear();
|
const auto& path = list[idx];
|
||||||
|
auto name = getFileName(path);
|
||||||
for (const auto& l : list) {
|
|
||||||
auto name = getFileName(l);
|
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
demos_.emplace_back(loadDemoDataFromFile(l));
|
demos_.emplace_back(loadDemoDataFromFile(path));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea las texturas de jugadores con todas sus variantes de paleta
|
// Crea las texturas de jugadores con todas sus variantes de paleta
|
||||||
void Resource::createPlayerTextures() {
|
void Resource::createPlayerTextures() {
|
||||||
Logger::cr();
|
|
||||||
Logger::info("CREATING PLAYER TEXTURES");
|
|
||||||
|
|
||||||
// Configuración de jugadores y sus paletas
|
// Configuración de jugadores y sus paletas
|
||||||
struct PlayerConfig {
|
struct PlayerConfig {
|
||||||
std::string base_texture;
|
std::string base_texture;
|
||||||
@@ -641,7 +694,6 @@ void Resource::createPlayerTextures() {
|
|||||||
// Guardar con nombre específico
|
// Guardar con nombre específico
|
||||||
std::string texture_name = player.name_prefix + "_pal" + std::to_string(palette_idx);
|
std::string texture_name = player.name_prefix + "_pal" + std::to_string(palette_idx);
|
||||||
textures_.emplace_back(texture_name, texture);
|
textures_.emplace_back(texture_name, texture);
|
||||||
Logger::dots("Player Texture : ", texture_name, "[ DONE ]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -657,9 +709,6 @@ void Resource::createTextTextures() {
|
|||||||
text(std::move(text_init)) {}
|
text(std::move(text_init)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Logger::cr();
|
|
||||||
Logger::info("CREATING TEXTURES");
|
|
||||||
|
|
||||||
// Texturas de tamaño normal con outline
|
// Texturas de tamaño normal con outline
|
||||||
std::vector<NameAndText> strings1 = {
|
std::vector<NameAndText> strings1 = {
|
||||||
{"game_text_1000_points", "1.000"},
|
{"game_text_1000_points", "1.000"},
|
||||||
@@ -673,7 +722,6 @@ void Resource::createTextTextures() {
|
|||||||
auto text1 = getText("04b_25_enhanced");
|
auto text1 = getText("04b_25_enhanced");
|
||||||
for (const auto& s : strings1) {
|
for (const auto& s : strings1) {
|
||||||
textures_.emplace_back(s.name, text1->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
textures_.emplace_back(s.name, text1->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
Logger::dots("Texture : ", s.name, "[ DONE ]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texturas de tamaño doble
|
// Texturas de tamaño doble
|
||||||
@@ -688,7 +736,6 @@ void Resource::createTextTextures() {
|
|||||||
auto text2 = getText("04b_25_2x_enhanced");
|
auto text2 = getText("04b_25_2x_enhanced");
|
||||||
for (const auto& s : strings2) {
|
for (const auto& s : strings2) {
|
||||||
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
Logger::dots("Texture : ", s.name, "[ DONE ]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,9 +754,6 @@ void Resource::createText() {
|
|||||||
white_texture_file(std::move(w_file)) {}
|
white_texture_file(std::move(w_file)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Logger::cr();
|
|
||||||
Logger::info("CREATING TEXT OBJECTS");
|
|
||||||
|
|
||||||
std::vector<ResourceInfo> resources = {
|
std::vector<ResourceInfo> resources = {
|
||||||
{"04b_25", "04b_25.png", "04b_25.txt"},
|
{"04b_25", "04b_25.png", "04b_25.txt"},
|
||||||
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
|
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
|
||||||
@@ -735,7 +779,6 @@ void Resource::createText() {
|
|||||||
// Crear texto normal
|
// Crear texto normal
|
||||||
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
|
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
|
||||||
}
|
}
|
||||||
Logger::dots("Text : ", resource.key, "[ DONE ]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,6 +836,16 @@ void Resource::renderProgress() {
|
|||||||
screen->start();
|
screen->start();
|
||||||
screen->clean();
|
screen->clean();
|
||||||
|
|
||||||
|
// Si la pantalla de carga está desactivada, dejamos todo en negro.
|
||||||
|
// wait_for_input solo tiene efecto cuando la pantalla está visible.
|
||||||
|
if (!Options::loading.show) {
|
||||||
|
screen->coreRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estamos en la fase de espera explícita al usuario tras terminar la carga
|
||||||
|
const bool WAITING_FOR_INPUT = isLoadDone() && Options::loading.wait_for_input;
|
||||||
|
|
||||||
auto text_color = param.resource.color;
|
auto text_color = param.resource.color;
|
||||||
auto bar_color = param.resource.color.DARKEN(100);
|
auto bar_color = param.resource.color.DARKEN(100);
|
||||||
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
|
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
|
||||||
@@ -805,14 +858,20 @@ void Resource::renderProgress() {
|
|||||||
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
||||||
SDL_RenderRect(renderer, &loading_wired_rect_);
|
SDL_RenderRect(renderer, &loading_wired_rect_);
|
||||||
|
|
||||||
// Escribe el texto de carga encima de la barra
|
// Texto centrado sobre la barra: mientras carga, el nombre del recurso;
|
||||||
/*
|
// al terminar en modo wait_for_input, el prompt traducido.
|
||||||
loading_text_->writeColored(
|
const std::string OVER_BAR_TEXT = WAITING_FOR_INPUT
|
||||||
loading_wired_rect_.x,
|
? Lang::getText("[RESOURCE] PRESS_TO_CONTINUE")
|
||||||
loading_wired_rect_.y - 9,
|
: loading_resource_name_;
|
||||||
Lang::getText("[RESOURCE] LOADING") + " : " + loading_resource_name_,
|
if ((Options::loading.show_resource_name || WAITING_FOR_INPUT) && !OVER_BAR_TEXT.empty()) {
|
||||||
|
loading_text_->writeDX(
|
||||||
|
Text::CENTER | Text::COLOR,
|
||||||
|
loading_wired_rect_.x + (loading_wired_rect_.w / 2),
|
||||||
|
loading_wired_rect_.y - TEXT_HEIGHT - 2,
|
||||||
|
OVER_BAR_TEXT,
|
||||||
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
*/
|
}
|
||||||
|
|
||||||
// Muestra nombre de la aplicación
|
// Muestra nombre de la aplicación
|
||||||
loading_text_->writeDX(
|
loading_text_->writeDX(
|
||||||
@@ -823,56 +882,21 @@ void Resource::renderProgress() {
|
|||||||
1,
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
|
|
||||||
// Muestra la versión
|
// Muestra la versión y el hash del commit
|
||||||
loading_text_->writeDX(
|
loading_text_->writeDX(
|
||||||
Text::CENTER | Text::COLOR,
|
Text::CENTER | Text::COLOR,
|
||||||
param.game.game_area.center_x,
|
param.game.game_area.center_x,
|
||||||
param.game.game_area.center_y + TEXT_HEIGHT,
|
param.game.game_area.center_y + TEXT_HEIGHT,
|
||||||
"(" + std::string(Version::GIT_HASH) + ")",
|
"ver. " + std::string(Texts::VERSION) + " (" + std::string(Version::GIT_HASH) + ")",
|
||||||
1,
|
1,
|
||||||
text_color);
|
text_color);
|
||||||
|
|
||||||
// Muestra información del monitor desplazada hacia abajo
|
|
||||||
/*loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 18,
|
|
||||||
screen->getDisplayMonitorName(),
|
|
||||||
text_color);
|
|
||||||
loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 27,
|
|
||||||
std::to_string(screen->getDisplayMonitorWidth()) + "x" + std::to_string(screen->getDisplayMonitorHeight()),
|
|
||||||
text_color);
|
|
||||||
loading_text_->writeColored(
|
|
||||||
X_PADDING,
|
|
||||||
Y_PADDING + 36,
|
|
||||||
std::to_string(screen->getDisplayMonitorRefreshRate()) + "Hz",
|
|
||||||
text_color);*/
|
|
||||||
|
|
||||||
// Renderiza el frame en pantalla
|
// Renderiza el frame en pantalla
|
||||||
screen->coreRender();
|
screen->coreRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comprueba los eventos durante la carga (permite salir con ESC o cerrar ventana)
|
|
||||||
void Resource::checkEvents() {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
switch (event.type) {
|
|
||||||
case SDL_EVENT_QUIT:
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
|
||||||
if (event.key.key == SDLK_ESCAPE) {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga los datos para el modo demostración (sin mostrar progreso)
|
// Carga los datos para el modo demostración (sin mostrar progreso)
|
||||||
void Resource::loadDemoDataQuiet() {
|
void Resource::loadDemoDataQuiet() {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES (quiet load)");
|
|
||||||
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||||
demos_.clear();
|
demos_.clear();
|
||||||
|
|
||||||
@@ -883,22 +907,22 @@ void Resource::loadDemoDataQuiet() {
|
|||||||
|
|
||||||
// Inicializa los rectangulos que definen la barra de progreso
|
// Inicializa los rectangulos que definen la barra de progreso
|
||||||
void Resource::initProgressBar() {
|
void Resource::initProgressBar() {
|
||||||
const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING;
|
const float WIRED_BAR_WIDTH = param.game.width * BAR_WIDTH_RATIO;
|
||||||
|
const float BAR_X_POSITION = (param.game.width - WIRED_BAR_WIDTH) / 2.0F;
|
||||||
|
const float BAR_Y_POSITION = (param.game.height * BAR_Y_RATIO) - (BAR_HEIGHT / 2.0F);
|
||||||
|
|
||||||
const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2);
|
loading_wired_rect_ = {.x = BAR_X_POSITION, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||||
loading_wired_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
|
|
||||||
|
|
||||||
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
|
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
|
||||||
loading_full_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
|
loading_full_rect_ = {.x = BAR_X_POSITION, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza el progreso de carga, muestra la barra y procesa eventos
|
// Actualiza el estado del progreso. No renderiza: el repintado lo hace
|
||||||
|
// Preload::iterate una vez por frame llamando a renderProgress().
|
||||||
void Resource::updateLoadingProgress(std::string name) {
|
void Resource::updateLoadingProgress(std::string name) {
|
||||||
loading_resource_name_ = std::move(name);
|
loading_resource_name_ = std::move(name);
|
||||||
loading_count_.increase();
|
loading_count_.increase();
|
||||||
updateProgressBar();
|
updateProgressBar();
|
||||||
renderProgress();
|
|
||||||
checkEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la barra de estado
|
// Actualiza la barra de estado
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "animated_sprite.hpp" // Para AnimationsFileBuffer
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimationsFileBuffer
|
||||||
#include "demo.hpp" // Para DemoData
|
#include "core/rendering/text.hpp" // Para Text, TextFile
|
||||||
#include "text.hpp" // Para Text, TextFile
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "texture.hpp" // Para Texture
|
#include "core/system/demo.hpp" // Para DemoData
|
||||||
|
|
||||||
struct JA_Music_t;
|
struct JA_Music_t;
|
||||||
struct JA_Sound_t;
|
struct JA_Sound_t;
|
||||||
@@ -42,6 +42,15 @@ class Resource {
|
|||||||
// --- Métodos de recarga de recursos ---
|
// --- Métodos de recarga de recursos ---
|
||||||
void reload(); // Recarga todos los recursos
|
void reload(); // Recarga todos los recursos
|
||||||
|
|
||||||
|
// --- Cargador incremental ---
|
||||||
|
// beginLoad prepara el estado; loadStep carga recursos hasta agotar el presupuesto;
|
||||||
|
// devuelve true cuando ya no queda nada. renderProgress se llama una vez por frame
|
||||||
|
// desde la escena Preload.
|
||||||
|
void beginLoad();
|
||||||
|
auto loadStep(int budget_ms) -> bool;
|
||||||
|
[[nodiscard]] auto isLoadDone() const -> bool;
|
||||||
|
void renderProgress();
|
||||||
|
|
||||||
// --- Método para obtener el modo de carga actual ---
|
// --- Método para obtener el modo de carga actual ---
|
||||||
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
|
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
|
||||||
|
|
||||||
@@ -117,12 +126,15 @@ class Resource {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Constantes para la pantalla de carga ---
|
// --- Constantes para la barra de progreso de la pantalla de carga ---
|
||||||
static constexpr float X_PADDING = 60.0F;
|
// BAR_WIDTH_RATIO: fracción del ancho de la pantalla ocupada por la barra (centrada)
|
||||||
static constexpr float Y_PADDING = 20.0F;
|
// 1.0 = ancho completo, 0.5 = la mitad del ancho
|
||||||
|
// BAR_Y_RATIO: posición vertical del CENTRO de la barra en fracción de la altura
|
||||||
|
// 0.0 = centro en el borde superior, 1.0 = centro en el borde inferior
|
||||||
|
// BAR_HEIGHT: grosor de la barra en píxeles
|
||||||
|
static constexpr float BAR_WIDTH_RATIO = 0.5F;
|
||||||
|
static constexpr float BAR_Y_RATIO = 0.85F;
|
||||||
static constexpr float BAR_HEIGHT = 5.0F;
|
static constexpr float BAR_HEIGHT = 5.0F;
|
||||||
static constexpr Color BAR_COLOR = Color(128, 128, 128);
|
|
||||||
static constexpr Color TEXT_COLOR = Color(255, 255, 255);
|
|
||||||
|
|
||||||
// --- Modo de carga ---
|
// --- Modo de carga ---
|
||||||
LoadingMode loading_mode_;
|
LoadingMode loading_mode_;
|
||||||
@@ -143,13 +155,24 @@ class Resource {
|
|||||||
SDL_FRect loading_wired_rect_;
|
SDL_FRect loading_wired_rect_;
|
||||||
SDL_FRect loading_full_rect_;
|
SDL_FRect loading_full_rect_;
|
||||||
|
|
||||||
|
// --- Estado del cargador incremental ---
|
||||||
|
enum class LoadStage {
|
||||||
|
SOUNDS,
|
||||||
|
MUSICS,
|
||||||
|
TEXTURES,
|
||||||
|
TEXT_FILES,
|
||||||
|
ANIMATIONS,
|
||||||
|
DEMO_DATA,
|
||||||
|
CREATE_TEXT,
|
||||||
|
CREATE_TEXT_TEXTURES,
|
||||||
|
CREATE_PLAYER_TEXTURES,
|
||||||
|
DONE
|
||||||
|
};
|
||||||
|
LoadStage stage_{LoadStage::DONE};
|
||||||
|
size_t stage_index_{0};
|
||||||
|
bool saved_vsync_{false}; // Vsync previo a beginLoad, restaurado por finishBoot/load
|
||||||
|
|
||||||
// --- Métodos internos de carga y gestión ---
|
// --- Métodos internos de carga y gestión ---
|
||||||
void loadSounds(); // Carga los sonidos
|
|
||||||
void loadMusics(); // Carga las músicas
|
|
||||||
void loadTextures(); // Carga las texturas
|
|
||||||
void loadTextFiles(); // Carga los ficheros de texto
|
|
||||||
void loadAnimations(); // Carga las animaciones
|
|
||||||
void loadDemoData(); // Carga los datos para el modo demostración
|
|
||||||
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
|
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
|
||||||
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
|
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
|
||||||
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
|
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
|
||||||
@@ -173,12 +196,18 @@ class Resource {
|
|||||||
|
|
||||||
// --- Métodos internos para gestionar el progreso ---
|
// --- Métodos internos para gestionar el progreso ---
|
||||||
void calculateTotalResources(); // Calcula el número de recursos para cargar
|
void calculateTotalResources(); // Calcula el número de recursos para cargar
|
||||||
void renderProgress(); // Muestra el progreso de carga
|
|
||||||
static void checkEvents(); // Comprueba los eventos durante la carga
|
|
||||||
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
|
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
|
||||||
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
|
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
|
||||||
void updateProgressBar(); // Actualiza la barra de estado
|
void updateProgressBar(); // Actualiza la barra de estado
|
||||||
|
|
||||||
|
// --- Helpers del cargador incremental (cargan un único recurso) ---
|
||||||
|
void loadOneSound(size_t idx);
|
||||||
|
void loadOneMusic(size_t idx);
|
||||||
|
void loadOneTexture(size_t idx);
|
||||||
|
void loadOneTextFile(size_t idx);
|
||||||
|
void loadOneAnimation(size_t idx);
|
||||||
|
void loadOneDemoData(size_t idx);
|
||||||
|
|
||||||
// --- Constructores y destructor privados (singleton) ---
|
// --- Constructores y destructor privados (singleton) ---
|
||||||
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
|
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
|
||||||
~Resource(); // Destructor privado
|
~Resource(); // Destructor privado
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "resource_helper.hpp"
|
#include "core/resources/resource_helper.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para replace
|
#include <algorithm> // Para replace
|
||||||
#include <cstddef> // Para size_t
|
#include <cstddef> // Para size_t
|
||||||
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ios, operator<<, ios, basic_istream, endl, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ios, operator<<, ios, basic_istream, endl, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
||||||
#include <iostream> // Para cout
|
#include <iostream> // Para cout
|
||||||
|
|
||||||
#include "resource_loader.hpp" // Para ResourceLoader
|
#include "core/resources/resource_loader.hpp" // Para ResourceLoader
|
||||||
|
|
||||||
namespace ResourceHelper {
|
namespace ResourceHelper {
|
||||||
static bool resource_system_initialized = false;
|
static bool resource_system_initialized = false;
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "resource_loader.hpp"
|
#include "core/resources/resource_loader.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para replace
|
#include <algorithm> // Para replace
|
||||||
#include <filesystem> // Para exists, path, recursive_directory_iterator, directory_entry, relative
|
#include <filesystem> // Para exists, path, recursive_directory_iterator, directory_entry, relative
|
||||||
#include <fstream> // Para basic_ostream, basic_ifstream, operator<<, basic_ios, endl, ios, basic_istream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
#include <fstream> // Para basic_ostream, basic_ifstream, operator<<, basic_ios, endl, ios, basic_istream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
||||||
#include <iostream> // Para cerr, cout
|
#include <iostream> // Para cerr, cout
|
||||||
|
|
||||||
#include "resource_pack.hpp" // Para ResourcePack
|
#include "core/resources/resource_pack.hpp" // Para ResourcePack
|
||||||
|
|
||||||
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||||
|
|
||||||
@@ -33,8 +33,6 @@ auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallba
|
|||||||
if (std::filesystem::exists(pack_file)) {
|
if (std::filesystem::exists(pack_file)) {
|
||||||
resource_pack_ = new ResourcePack();
|
resource_pack_ = new ResourcePack();
|
||||||
if (resource_pack_->loadPack(pack_file)) {
|
if (resource_pack_->loadPack(pack_file)) {
|
||||||
std::cout << "Resource pack loaded successfully: " << pack_file << '\n';
|
|
||||||
std::cout << "Resources available: " << resource_pack_->getResourceCount() << '\n';
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
delete resource_pack_;
|
delete resource_pack_;
|
||||||
@@ -43,7 +41,6 @@ auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fallback_to_files_) {
|
if (fallback_to_files_) {
|
||||||
std::cout << "Using fallback mode: loading resources from data/ directory" << '\n';
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "resource_pack.hpp"
|
#include "core/resources/resource_pack.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para replace
|
#include <algorithm> // Para replace
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "ui/notifier.hpp" // Para Notifier::Position
|
#include "game/ui/notifier.hpp" // Para Notifier::Position
|
||||||
|
|
||||||
namespace Defaults::Game {
|
namespace Defaults::Game {
|
||||||
constexpr float WIDTH = 320.0F;
|
constexpr float WIDTH = 320.0F;
|
||||||
@@ -213,3 +213,9 @@ namespace Defaults::Settings {
|
|||||||
constexpr bool SHUTDOWN_ENABLED = false;
|
constexpr bool SHUTDOWN_ENABLED = false;
|
||||||
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
||||||
} // namespace Defaults::Settings
|
} // namespace Defaults::Settings
|
||||||
|
|
||||||
|
namespace Defaults::Loading {
|
||||||
|
constexpr bool SHOW = false;
|
||||||
|
constexpr bool SHOW_RESOURCE_NAME = true;
|
||||||
|
constexpr bool WAIT_FOR_INPUT = false;
|
||||||
|
} // namespace Defaults::Loading
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "demo.hpp"
|
#include "core/system/demo.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_IOStream, SDL_IOFromConstMem, SDL_IOFromFile, SDL_ReadIO, SDL_WriteIO, SDL_CloseIO
|
#include <SDL3/SDL.h> // Para SDL_IOStream, SDL_IOFromConstMem, SDL_IOFromFile, SDL_ReadIO, SDL_WriteIO, SDL_CloseIO
|
||||||
|
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
|
|
||||||
#include "resource_helper.hpp" // Para ResourceHelper
|
#include "core/resources/resource_helper.hpp" // Para ResourceHelper
|
||||||
#include "ui/logger.hpp"
|
#include "utils/utils.hpp" // Para getFileName
|
||||||
#include "utils.hpp" // Para printWithDots, getFileName
|
|
||||||
|
|
||||||
// Carga el fichero de datos para la demo
|
// Carga el fichero de datos para la demo
|
||||||
auto loadDemoDataFromFile(const std::string& file_path) -> DemoData {
|
auto loadDemoDataFromFile(const std::string& file_path) -> DemoData {
|
||||||
@@ -24,10 +24,9 @@ auto loadDemoDataFromFile(const std::string& file_path) -> DemoData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::cout << "Error: Fichero no encontrado " << file_path << '\n';
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
}
|
}
|
||||||
Logger::dots("DemoData : ", getFileName(file_path), "[ LOADED ]");
|
|
||||||
|
|
||||||
// Lee todos los datos del fichero y los deja en el destino
|
// Lee todos los datos del fichero y los deja en el destino
|
||||||
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
for (int i = 0; i < TOTAL_DEMO_DATA; ++i) {
|
||||||
@@ -52,18 +51,15 @@ bool saveDemoFile(const std::string& file_path, const DemoData& dd) {
|
|||||||
// Guarda los datos
|
// Guarda los datos
|
||||||
for (const auto& data : dd) {
|
for (const auto& data : dd) {
|
||||||
if (SDL_WriteIO(file, &data, sizeof(DemoKeys)) != sizeof(DemoKeys)) {
|
if (SDL_WriteIO(file, &data, sizeof(DemoKeys)) != sizeof(DemoKeys)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al escribir el fichero %s", getFileName(file_path).c_str());
|
std::cout << "Error al escribir el fichero " << getFileName(file_path) << '\n';
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (success) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file %s", getFileName(file_path).c_str());
|
|
||||||
}
|
|
||||||
// Cierra el fichero
|
// Cierra el fichero
|
||||||
SDL_CloseIO(file);
|
SDL_CloseIO(file);
|
||||||
} else {
|
} else {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to save %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
|
std::cout << "Error: Unable to save " << getFileName(file_path) << " file! " << SDL_GetError() << '\n';
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
555
source/core/system/director.cpp
Normal file
555
source/core/system/director.cpp
Normal file
@@ -0,0 +1,555 @@
|
|||||||
|
// IWYU pragma: no_include <bits/chrono.h>
|
||||||
|
#include "core/system/director.hpp"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_SetLogPriority, SDL_LogCategory, SDL_LogPriority, SDL_Quit
|
||||||
|
|
||||||
|
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
|
||||||
|
#include <ctime> // Para time
|
||||||
|
#include <fstream> // Para ifstream, ofstream
|
||||||
|
#include <iostream> // Para basic_ostream, operator<<, cerr
|
||||||
|
#include <memory> // Para make_unique, unique_ptr
|
||||||
|
#include <stdexcept> // Para runtime_error
|
||||||
|
#include <string> // Para allocator, basic_string, char_traits, operator+, string, operator==
|
||||||
|
|
||||||
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
|
#include "core/input/input.hpp" // Para Input
|
||||||
|
#include "core/locale/lang.hpp" // Para setLanguage
|
||||||
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
|
#include "core/resources/asset.hpp" // Para Asset
|
||||||
|
#include "core/resources/resource.hpp" // Para Resource
|
||||||
|
#include "core/resources/resource_helper.hpp" // Para initializeResourceSystem
|
||||||
|
#include "core/system/global_events.hpp" // Para GlobalEvents::handle
|
||||||
|
#include "core/system/section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
||||||
|
#include "core/system/shutdown.hpp" // Para resultToString, shutdownSystem, ShutdownResult
|
||||||
|
#include "core/system/system_utils.hpp" // Para createApplicationFolder, resultToString, Result
|
||||||
|
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
||||||
|
#include "game/entities/player.hpp" // Para Player
|
||||||
|
#include "game/gameplay/manage_hiscore_table.hpp" // Para ManageHiScoreTable
|
||||||
|
#include "game/options.hpp" // Para Settings, loadFromFile, saveToFile, settings, setConfigFile, setControllersFile
|
||||||
|
#include "game/scenes/credits.hpp" // Para Credits
|
||||||
|
#include "game/scenes/game.hpp" // Para Game
|
||||||
|
#include "game/scenes/hiscore_table.hpp" // Para HiScoreTable
|
||||||
|
#include "game/scenes/instructions.hpp" // Para Instructions
|
||||||
|
#include "game/scenes/intro.hpp" // Para Intro
|
||||||
|
#include "game/scenes/logo.hpp" // Para Logo
|
||||||
|
#include "game/scenes/preload.hpp" // Para Preload
|
||||||
|
#include "game/scenes/title.hpp" // Para Title
|
||||||
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
|
#include "utils/param.hpp" // Para loadParamsFromFile
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
Director::Director() {
|
||||||
|
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
|
||||||
|
|
||||||
|
// Establece el nivel de prioridad de la categoría de registro
|
||||||
|
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||||
|
SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR);
|
||||||
|
|
||||||
|
// Inicia la semilla aleatoria usando el tiempo actual en segundos
|
||||||
|
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||||
|
|
||||||
|
std::cout << "Game start\n";
|
||||||
|
|
||||||
|
// Obtener la ruta del ejecutable desde SDL
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En Emscripten los assets viven en la raíz del MEMFS virtual (/data, /config),
|
||||||
|
// preloaded vía --preload-file en el linker. No hay ruta de ejecutable.
|
||||||
|
executable_path_ = "";
|
||||||
|
#else
|
||||||
|
const char* base_path = SDL_GetBasePath();
|
||||||
|
executable_path_ = (base_path != nullptr) ? base_path : "";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Crea la carpeta del sistema donde guardar los datos persistentes
|
||||||
|
createSystemFolder("jailgames");
|
||||||
|
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
|
||||||
|
|
||||||
|
// Establecer sección inicial según modo de compilación
|
||||||
|
#ifdef RECORDING
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
#elif _DEBUG
|
||||||
|
loadDebugConfig();
|
||||||
|
#else
|
||||||
|
Section::name = Section::Name::LOGO;
|
||||||
|
Section::options = Section::Options::NONE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Director::~Director() {
|
||||||
|
// Libera las secciones primero: sus destructores pueden tocar Audio/Resource/Screen,
|
||||||
|
// que close() destruye a continuación.
|
||||||
|
resetActiveSection();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializa todo
|
||||||
|
void Director::init() {
|
||||||
|
// Configuración inicial de parametros
|
||||||
|
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
||||||
|
|
||||||
|
// Determinar ruta del pack según la plataforma
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
std::string pack_path = executable_path_ + "../Resources/resources.pack";
|
||||||
|
#else
|
||||||
|
std::string pack_path = executable_path_ + "resources.pack";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Inicializar sistema de recursos con o sin fallback según el tipo de build
|
||||||
|
#if defined(RELEASE_BUILD) && !defined(__EMSCRIPTEN__)
|
||||||
|
// Release nativo: Sin fallback - Solo resources.pack (estricto)
|
||||||
|
ResourceHelper::initializeResourceSystem(pack_path, false);
|
||||||
|
#else
|
||||||
|
// Desarrollo o Emscripten: Con fallback - carga desde filesystem/MEMFS
|
||||||
|
ResourceHelper::initializeResourceSystem(pack_path, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
loadAssets(); // Crea el índice de archivos
|
||||||
|
|
||||||
|
Input::init(Asset::get()->getPath("gamecontrollerdb.txt"), Asset::get()->getPath("controllers.json")); // Carga configuración de controles
|
||||||
|
|
||||||
|
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
||||||
|
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
||||||
|
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
||||||
|
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
||||||
|
Options::loadFromFile(); // Carga el archivo de configuración
|
||||||
|
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
||||||
|
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// En la versión web el navegador gestiona la ventana: forzamos zoom=1,
|
||||||
|
// fullscreen para ocupar el canvas, e integer scale para píxeles nítidos.
|
||||||
|
Options::window.zoom = 1;
|
||||||
|
Options::video.fullscreen = true;
|
||||||
|
Options::video.integer_scale = true;
|
||||||
|
#endif
|
||||||
|
loadParams(); // Carga los parámetros del programa
|
||||||
|
loadScoreFile(); // Carga el archivo de puntuaciones
|
||||||
|
|
||||||
|
// Inicialización de subsistemas principales
|
||||||
|
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
||||||
|
|
||||||
|
Screen::init(); // Inicializa la pantalla y el sistema de renderizado
|
||||||
|
|
||||||
|
Audio::init(); // Activa el sistema de audio
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Resource::init(debug_config.resource_loading == "lazy" ? Resource::LoadingMode::LAZY_LOAD : Resource::LoadingMode::PRELOAD);
|
||||||
|
#else
|
||||||
|
Resource::init(Resource::LoadingMode::PRELOAD);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (Resource::get()->getLoadingMode() == Resource::LoadingMode::PRELOAD) {
|
||||||
|
// Guarda la sección destino (la que fijó loadDebugConfig o el default)
|
||||||
|
// y redirige el arranque a la escena PRELOAD hasta que loadStep termine.
|
||||||
|
Section::post_preload = Section::name;
|
||||||
|
Section::name = Section::Name::PRELOAD;
|
||||||
|
Resource::get()->beginLoad();
|
||||||
|
} else {
|
||||||
|
// LAZY_LOAD: el constructor de Resource ya cargó lo esencial síncronamente;
|
||||||
|
// no hay fase de preload, pasamos directamente a post-boot.
|
||||||
|
finishBoot();
|
||||||
|
boot_loading_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceMenu/Notifier/getSingletons se mueven a finishBoot() — dependen
|
||||||
|
// de Resource y se inicializan al terminar la carga incremental.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicializaciones que dependen del Resource cargado. Se llama desde iterate()
|
||||||
|
// cuando Resource::loadStep() devuelve true, con la ventana y el bucle vivos.
|
||||||
|
void Director::finishBoot() {
|
||||||
|
ServiceMenu::init(); // Inicializa el menú de servicio
|
||||||
|
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
||||||
|
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
|
||||||
|
|
||||||
|
// Restaura el vsync a la preferencia del usuario (beginLoad lo había puesto a false)
|
||||||
|
Screen::get()->setVSync(Options::video.vsync);
|
||||||
|
|
||||||
|
// Si NO estamos en modo "wait for input", transiciona ya al destino.
|
||||||
|
// Si wait_for_input está activo (y la pantalla es visible), nos quedamos
|
||||||
|
// en PRELOAD hasta que el usuario pulse tecla/botón.
|
||||||
|
if (!(Options::loading.show && Options::loading.wait_for_input)) {
|
||||||
|
Section::name = Section::post_preload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cierra todo y libera recursos del sistema y de los singletons
|
||||||
|
void Director::close() {
|
||||||
|
// Guarda las opciones actuales en el archivo de configuración
|
||||||
|
Options::saveToFile();
|
||||||
|
|
||||||
|
// Libera los singletons y recursos en orden inverso al de inicialización
|
||||||
|
Notifier::destroy(); // Libera el sistema de notificaciones
|
||||||
|
ServiceMenu::destroy(); // Libera el sistema de menú de servicio
|
||||||
|
Input::destroy(); // Libera el sistema de entrada
|
||||||
|
Resource::destroy(); // Libera el sistema de recursos gráficos y de texto
|
||||||
|
Audio::destroy(); // Libera el sistema de audio
|
||||||
|
Screen::destroy(); // Libera el sistema de pantalla y renderizado
|
||||||
|
Asset::destroy(); // Libera el gestor de archivos
|
||||||
|
|
||||||
|
std::cout << "\nBye!\n";
|
||||||
|
|
||||||
|
// Libera todos los recursos de SDL
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
// Apaga el sistema
|
||||||
|
shutdownSystem(Section::options == Section::Options::SHUTDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga los parametros
|
||||||
|
void Director::loadParams() {
|
||||||
|
// Carga los parametros para configurar el juego
|
||||||
|
#ifdef ANBERNIC
|
||||||
|
const std::string PARAM_FILE_PATH = Asset::get()->getPath("param_320x240.txt");
|
||||||
|
#else
|
||||||
|
const std::string PARAM_FILE_PATH = Asset::get()->getPath(Options::settings.params_file);
|
||||||
|
#endif
|
||||||
|
loadParamsFromFile(PARAM_FILE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga el fichero de puntuaciones
|
||||||
|
void Director::loadScoreFile() {
|
||||||
|
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
|
||||||
|
#ifdef _DEBUG
|
||||||
|
manager->clear();
|
||||||
|
#else
|
||||||
|
manager->loadFromFile(Asset::get()->getPath("score.bin"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga el indice de ficheros desde un fichero
|
||||||
|
void Director::loadAssets() {
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
const std::string PREFIX = "/../Resources";
|
||||||
|
#else
|
||||||
|
const std::string PREFIX;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
||||||
|
std::string config_path = executable_path_ + PREFIX + "/config/assets.txt";
|
||||||
|
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
||||||
|
|
||||||
|
// Si falta algun fichero, sale del programa
|
||||||
|
if (!Asset::get()->check()) {
|
||||||
|
throw std::runtime_error("Falta algun fichero");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carga debug.yaml desde la carpeta del sistema (solo en _DEBUG)
|
||||||
|
void Director::loadDebugConfig() {
|
||||||
|
const std::string DEBUG_FILE = system_folder_ + "/debug.yaml";
|
||||||
|
|
||||||
|
std::ifstream file(DEBUG_FILE);
|
||||||
|
if (!file.good()) {
|
||||||
|
// Crear fichero por defecto
|
||||||
|
std::ofstream out(DEBUG_FILE);
|
||||||
|
if (out.is_open()) {
|
||||||
|
out << "# Coffee Crisis Arcade Edition - Debug Configuration\n";
|
||||||
|
out << "# This file is only read in DEBUG builds.\n";
|
||||||
|
out << "#\n";
|
||||||
|
out << "# initial_section: logo, intro, title, game, credits, instructions, hiscore\n";
|
||||||
|
out << "# initial_options: none, 1p, 2p, both\n";
|
||||||
|
out << "# initial_stage: 0-based stage index (only when section is game)\n";
|
||||||
|
out << "# show_render_info: show FPS/driver/preset overlay\n";
|
||||||
|
out << "# resource_loading: preload, lazy\n";
|
||||||
|
out << "\n";
|
||||||
|
out << "initial_section: game\n";
|
||||||
|
out << "initial_options: 1p\n";
|
||||||
|
out << "initial_stage: 0\n";
|
||||||
|
out << "show_render_info: true\n";
|
||||||
|
out << "resource_loading: preload\n";
|
||||||
|
out << "autoplay: false\n";
|
||||||
|
out << "invincibility: false\n";
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
// Usar defaults de DebugConfig
|
||||||
|
} else {
|
||||||
|
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
try {
|
||||||
|
auto yaml = fkyaml::node::deserialize(content);
|
||||||
|
if (yaml.contains("initial_section")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_section = yaml["initial_section"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("initial_options")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("initial_stage")) {
|
||||||
|
try {
|
||||||
|
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("show_render_info")) {
|
||||||
|
try {
|
||||||
|
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("resource_loading")) {
|
||||||
|
try {
|
||||||
|
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("autoplay")) {
|
||||||
|
try {
|
||||||
|
debug_config.autoplay = yaml["autoplay"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
if (yaml.contains("invincibility")) {
|
||||||
|
try {
|
||||||
|
debug_config.invincibility = yaml["invincibility"].get_value<bool>();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "Error parsing debug.yaml, using defaults" << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapear strings a enums
|
||||||
|
const auto& sec = debug_config.initial_section;
|
||||||
|
if (sec == "logo") {
|
||||||
|
Section::name = Section::Name::LOGO;
|
||||||
|
} else if (sec == "intro") {
|
||||||
|
Section::name = Section::Name::INTRO;
|
||||||
|
} else if (sec == "title") {
|
||||||
|
Section::name = Section::Name::TITLE;
|
||||||
|
} else if (sec == "game") {
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
} else if (sec == "credits") {
|
||||||
|
Section::name = Section::Name::CREDITS;
|
||||||
|
} else if (sec == "instructions") {
|
||||||
|
Section::name = Section::Name::INSTRUCTIONS;
|
||||||
|
} else if (sec == "hiscore") {
|
||||||
|
Section::name = Section::Name::HI_SCORE_TABLE;
|
||||||
|
} else {
|
||||||
|
Section::name = Section::Name::GAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& opt = debug_config.initial_options;
|
||||||
|
if (opt == "none") {
|
||||||
|
Section::options = Section::Options::NONE;
|
||||||
|
} else if (opt == "1p") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
} else if (opt == "2p") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_2P;
|
||||||
|
} else if (opt == "both") {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_BOTH;
|
||||||
|
} else {
|
||||||
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea la carpeta del sistema donde guardar datos
|
||||||
|
void Director::createSystemFolder(const std::string& folder) {
|
||||||
|
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
|
||||||
|
|
||||||
|
if (result != SystemUtils::Result::SUCCESS) {
|
||||||
|
std::cerr << "Error creando carpeta del sistema: "
|
||||||
|
<< SystemUtils::resultToString(result) << '\n';
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Libera todos los unique_ptr de sección (solo uno tiene propiedad a la vez)
|
||||||
|
void Director::resetActiveSection() {
|
||||||
|
preload_.reset();
|
||||||
|
logo_.reset();
|
||||||
|
intro_.reset();
|
||||||
|
title_.reset();
|
||||||
|
game_.reset();
|
||||||
|
instructions_.reset();
|
||||||
|
hi_score_table_.reset();
|
||||||
|
credits_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destruye la sección anterior y construye la nueva cuando Section::name cambia
|
||||||
|
void Director::handleSectionTransition() {
|
||||||
|
// RESET: recarga recursos y vuelve a LOGO (el propio reset() cambia Section::name)
|
||||||
|
if (Section::name == Section::Name::RESET) {
|
||||||
|
resetActiveSection(); // libera recursos actuales antes del reload
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Section::name == last_built_section_name_) {
|
||||||
|
return; // ya tenemos la sección correcta viva
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destruye la sección anterior
|
||||||
|
resetActiveSection();
|
||||||
|
|
||||||
|
// Construye la nueva
|
||||||
|
switch (Section::name) {
|
||||||
|
case Section::Name::PRELOAD:
|
||||||
|
preload_ = std::make_unique<Preload>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::LOGO:
|
||||||
|
logo_ = std::make_unique<Logo>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::INTRO:
|
||||||
|
intro_ = std::make_unique<Intro>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::TITLE:
|
||||||
|
title_ = std::make_unique<Title>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::GAME: {
|
||||||
|
Player::Id player_id = Player::Id::PLAYER1;
|
||||||
|
switch (Section::options) {
|
||||||
|
case Section::Options::GAME_PLAY_1P:
|
||||||
|
player_id = Player::Id::PLAYER1;
|
||||||
|
break;
|
||||||
|
case Section::Options::GAME_PLAY_2P:
|
||||||
|
player_id = Player::Id::PLAYER2;
|
||||||
|
break;
|
||||||
|
case Section::Options::GAME_PLAY_BOTH:
|
||||||
|
player_id = Player::Id::BOTH_PLAYERS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
const int CURRENT_STAGE = debug_config.initial_stage;
|
||||||
|
#else
|
||||||
|
constexpr int CURRENT_STAGE = 0;
|
||||||
|
#endif
|
||||||
|
game_ = std::make_unique<Game>(player_id, CURRENT_STAGE, Game::DEMO_OFF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Section::Name::GAME_DEMO: {
|
||||||
|
const auto PLAYER_ID = static_cast<Player::Id>((rand() % 2) + 1);
|
||||||
|
constexpr auto CURRENT_STAGE = 0;
|
||||||
|
game_ = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, Game::DEMO_ON);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Section::Name::INSTRUCTIONS:
|
||||||
|
instructions_ = std::make_unique<Instructions>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::CREDITS:
|
||||||
|
credits_ = std::make_unique<Credits>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::HI_SCORE_TABLE:
|
||||||
|
hi_score_table_ = std::make_unique<HiScoreTable>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Section::Name::RESET:
|
||||||
|
case Section::Name::QUIT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_built_section_name_ = Section::name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinicia objetos y vuelve a la sección inicial
|
||||||
|
void Director::reset() {
|
||||||
|
Options::saveToFile();
|
||||||
|
Options::loadFromFile();
|
||||||
|
Lang::setLanguage(Options::settings.language);
|
||||||
|
Audio::get()->stopMusic();
|
||||||
|
Audio::get()->stopAllSounds();
|
||||||
|
Resource::get()->reload();
|
||||||
|
ServiceMenu::get()->reset();
|
||||||
|
Section::name = Section::Name::LOGO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avanza un frame de la sección activa (llamado desde SDL_AppIterate)
|
||||||
|
auto Director::iterate() -> SDL_AppResult {
|
||||||
|
if (Section::name == Section::Name::QUIT) {
|
||||||
|
return SDL_APP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fase de boot: carga incremental frame a frame con presupuesto de 50ms.
|
||||||
|
// Durante esta fase la escena activa es Preload (una barra de progreso).
|
||||||
|
if (boot_loading_) {
|
||||||
|
try {
|
||||||
|
if (Resource::get()->loadStep(50 /*ms*/)) {
|
||||||
|
finishBoot();
|
||||||
|
boot_loading_ = false;
|
||||||
|
// Los SDL_EVENT_GAMEPAD_ADDED iniciales ya los ha drenado la rama
|
||||||
|
// durante la carga: marcamos startup completo ahora para que los
|
||||||
|
// ADDED/REMOVED posteriores sí generen notificación.
|
||||||
|
GlobalEvents::markStartupComplete();
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Fatal error during resource load: " << e.what() << '\n';
|
||||||
|
Section::name = Section::Name::QUIT;
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestiona las transiciones entre secciones (destruye la anterior y construye la nueva)
|
||||||
|
handleSectionTransition();
|
||||||
|
|
||||||
|
// Ejecuta un frame de la sección activa
|
||||||
|
if (preload_) {
|
||||||
|
preload_->iterate();
|
||||||
|
} else if (logo_) {
|
||||||
|
logo_->iterate();
|
||||||
|
} else if (intro_) {
|
||||||
|
intro_->iterate();
|
||||||
|
} else if (title_) {
|
||||||
|
title_->iterate();
|
||||||
|
} else if (game_) {
|
||||||
|
game_->iterate();
|
||||||
|
} else if (instructions_) {
|
||||||
|
instructions_->iterate();
|
||||||
|
} else if (hi_score_table_) {
|
||||||
|
hi_score_table_->iterate();
|
||||||
|
} else if (credits_) {
|
||||||
|
credits_->iterate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Section::name == Section::Name::QUIT) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Procesa un evento SDL (llamado desde SDL_AppEvent)
|
||||||
|
auto Director::handleEvent(SDL_Event& event) -> SDL_AppResult {
|
||||||
|
// Eventos globales (SDL_EVENT_QUIT, resize, render target reset, hotplug, service menu, ratón)
|
||||||
|
GlobalEvents::handle(event);
|
||||||
|
|
||||||
|
// Reenvía a la sección activa
|
||||||
|
if (preload_) {
|
||||||
|
preload_->handleEvent(event);
|
||||||
|
} else if (logo_) {
|
||||||
|
logo_->handleEvent(event);
|
||||||
|
} else if (intro_) {
|
||||||
|
intro_->handleEvent(event);
|
||||||
|
} else if (title_) {
|
||||||
|
title_->handleEvent(event);
|
||||||
|
} else if (game_) {
|
||||||
|
game_->handleEvent(event);
|
||||||
|
} else if (instructions_) {
|
||||||
|
instructions_->handleEvent(event);
|
||||||
|
} else if (hi_score_table_) {
|
||||||
|
hi_score_table_->handleEvent(event);
|
||||||
|
} else if (credits_) {
|
||||||
|
credits_->handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Section::name == Section::Name::QUIT) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apaga el sistema de forma segura
|
||||||
|
void Director::shutdownSystem(bool should_shutdown) {
|
||||||
|
if (should_shutdown) {
|
||||||
|
auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps
|
||||||
|
|
||||||
|
if (result != SystemShutdown::ShutdownResult::SUCCESS) {
|
||||||
|
std::cerr << SystemShutdown::resultToString(result) << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
source/core/system/director.hpp
Normal file
95
source/core/system/director.hpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_AppResult, SDL_Event
|
||||||
|
|
||||||
|
#include <memory> // Para unique_ptr
|
||||||
|
#include <string> // Para string
|
||||||
|
|
||||||
|
#include "core/system/section.hpp" // Para Section::Name
|
||||||
|
|
||||||
|
namespace Lang {
|
||||||
|
enum class Code : int;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaraciones adelantadas de las secciones
|
||||||
|
class Preload;
|
||||||
|
class Logo;
|
||||||
|
class Intro;
|
||||||
|
class Title;
|
||||||
|
class Game;
|
||||||
|
class Instructions;
|
||||||
|
class HiScoreTable;
|
||||||
|
class Credits;
|
||||||
|
|
||||||
|
// --- Clase Director: gestor principal de la aplicación ---
|
||||||
|
class Director {
|
||||||
|
public:
|
||||||
|
// --- Constructor y destructor ---
|
||||||
|
Director();
|
||||||
|
~Director();
|
||||||
|
|
||||||
|
// --- Callbacks para SDL_MAIN_USE_CALLBACKS ---
|
||||||
|
auto iterate() -> SDL_AppResult; // Avanza un frame de la sección activa
|
||||||
|
auto handleEvent(SDL_Event& event) -> SDL_AppResult; // Procesa un evento SDL
|
||||||
|
|
||||||
|
// --- Debug config (accesible desde otras clases) ---
|
||||||
|
struct DebugConfig {
|
||||||
|
std::string initial_section;
|
||||||
|
std::string initial_options;
|
||||||
|
int initial_stage = 0;
|
||||||
|
bool show_render_info = true;
|
||||||
|
std::string resource_loading;
|
||||||
|
bool autoplay = false;
|
||||||
|
bool invincibility = false;
|
||||||
|
|
||||||
|
DebugConfig()
|
||||||
|
: initial_section("game"),
|
||||||
|
initial_options("1p"),
|
||||||
|
resource_loading("preload") {}
|
||||||
|
};
|
||||||
|
static inline DebugConfig debug_config;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// --- Variables internas ---
|
||||||
|
std::string executable_path_; // Ruta del ejecutable
|
||||||
|
std::string system_folder_; // Carpeta del sistema para almacenar datos
|
||||||
|
|
||||||
|
// --- Sección activa (una y sólo una viva en cada momento) ---
|
||||||
|
std::unique_ptr<Preload> preload_;
|
||||||
|
std::unique_ptr<Logo> logo_;
|
||||||
|
std::unique_ptr<Intro> intro_;
|
||||||
|
std::unique_ptr<Title> title_;
|
||||||
|
std::unique_ptr<Game> game_;
|
||||||
|
std::unique_ptr<Instructions> instructions_;
|
||||||
|
std::unique_ptr<HiScoreTable> hi_score_table_;
|
||||||
|
std::unique_ptr<Credits> credits_;
|
||||||
|
Section::Name last_built_section_name_ = Section::Name::RESET;
|
||||||
|
|
||||||
|
// --- Fase de arranque no bloqueante ---
|
||||||
|
bool boot_loading_ = true; // True mientras Resource::loadStep está cargando incremental
|
||||||
|
|
||||||
|
// --- Inicialización y cierre del sistema ---
|
||||||
|
void init(); // Inicializa la aplicación (pre-boot)
|
||||||
|
void finishBoot(); // Post-boot: inicializa lo que depende de recursos cargados
|
||||||
|
static void close(); // Cierra y libera recursos
|
||||||
|
|
||||||
|
// --- Configuración inicial ---
|
||||||
|
static void loadParams(); // Carga los parámetros del programa
|
||||||
|
static void loadScoreFile(); // Carga el fichero de puntuaciones
|
||||||
|
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
|
||||||
|
void loadDebugConfig(); // Carga debug.yaml (solo en _DEBUG)
|
||||||
|
|
||||||
|
// --- Gestión de entrada y archivos ---
|
||||||
|
void loadAssets(); // Crea el índice de archivos disponibles
|
||||||
|
|
||||||
|
// --- Gestión de secciones ---
|
||||||
|
void handleSectionTransition(); // Destruye la sección anterior y construye la nueva si Section::name ha cambiado
|
||||||
|
void resetActiveSection(); // Libera todos los unique_ptr de sección
|
||||||
|
static void reset(); // Reinicia objetos y vuelve a la sección inicial
|
||||||
|
|
||||||
|
// --- Gestión de archivos de idioma ---
|
||||||
|
auto getLangFile(Lang::Code code) -> std::string; // Obtiene un fichero de idioma según el código
|
||||||
|
|
||||||
|
// --- Apagado del sistema ---
|
||||||
|
static void shutdownSystem(bool should_shutdown); // Apaga el sistema
|
||||||
|
};
|
||||||
97
source/core/system/global_events.cpp
Normal file
97
source/core/system/global_events.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include "core/system/global_events.hpp"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_EventType, SDL_Event, SDL_LogInfo, SDL_LogCategory
|
||||||
|
|
||||||
|
#include <cstddef> // Para size_t
|
||||||
|
#include <iostream> // Para std::cout
|
||||||
|
#include <string> // Para allocator, operator+, string
|
||||||
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "core/input/input.hpp" // Para Input
|
||||||
|
#include "core/input/mouse.hpp" // Para handleEvent
|
||||||
|
#include "core/locale/lang.hpp" // Para getText
|
||||||
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
|
#include "core/system/section.hpp" // Para Name, Options, name, options
|
||||||
|
#include "game/options.hpp" // Para GamepadManager, gamepad_manager
|
||||||
|
#include "game/ui/notifier.hpp" // Para Notifier
|
||||||
|
#include "game/ui/service_menu.hpp" // Para ServiceMenu
|
||||||
|
|
||||||
|
namespace GlobalEvents {
|
||||||
|
namespace {
|
||||||
|
// Durante el arranque se drenan los SDL_EVENT_GAMEPAD_ADDED de los mandos
|
||||||
|
// ya conectados. Esos eventos sí deben reasignar mandos a jugadores, pero
|
||||||
|
// no deben mostrar notificación: no son hotplug, son detección inicial.
|
||||||
|
bool startup_in_progress = true;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Comprueba los eventos de Input y muestra notificaciones
|
||||||
|
void handleInputEvents(const SDL_Event& event) {
|
||||||
|
if (event.type != SDL_EVENT_GAMEPAD_ADDED && event.type != SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto* input_ = Input::get();
|
||||||
|
auto message = input_->handleEvent(event);
|
||||||
|
|
||||||
|
// Reasignar siempre: tanto en arranque como en hotplug en caliente.
|
||||||
|
Options::gamepad_manager.assignAndLinkGamepads();
|
||||||
|
Options::gamepad_manager.resyncGamepadsWithPlayers();
|
||||||
|
|
||||||
|
// Durante el preload ServiceMenu aún no existe: solo refresca si está vivo.
|
||||||
|
if (ServiceMenu::get() != nullptr) {
|
||||||
|
ServiceMenu::get()->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startup_in_progress || message.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reemplazo de palabras clave por texto localizado
|
||||||
|
size_t pos;
|
||||||
|
while ((pos = message.find(" CONNECTED")) != std::string::npos) {
|
||||||
|
message.replace(pos, std::string(" CONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] CONNECTED"));
|
||||||
|
}
|
||||||
|
while ((pos = message.find(" DISCONNECTED")) != std::string::npos) {
|
||||||
|
message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notifier también puede no existir todavía si la notificación se
|
||||||
|
// disparase antes de finishBoot(). Protegido por si acaso.
|
||||||
|
if (Notifier::get() != nullptr) {
|
||||||
|
Notifier::get()->show({message});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markStartupComplete() {
|
||||||
|
startup_in_progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||||
|
void handle(const SDL_Event& event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case SDL_EVENT_QUIT: // Evento de salida de la aplicación
|
||||||
|
Section::name = Section::Name::QUIT;
|
||||||
|
Section::options = Section::Options::NONE;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SDL_EVENT_RENDER_DEVICE_RESET:
|
||||||
|
case SDL_EVENT_RENDER_TARGETS_RESET:
|
||||||
|
std::cout << "SDL_RENDER_TARGETS_RESET" << '\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_EVENT_WINDOW_RESIZED:
|
||||||
|
Screen::initShaders();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Durante el preload ServiceMenu aún no existe
|
||||||
|
if (ServiceMenu::get() != nullptr) {
|
||||||
|
ServiceMenu::get()->handleEvent(event);
|
||||||
|
}
|
||||||
|
Mouse::handleEvent(event);
|
||||||
|
handleInputEvents(event);
|
||||||
|
}
|
||||||
|
} // namespace GlobalEvents
|
||||||
15
source/core/system/global_events.hpp
Normal file
15
source/core/system/global_events.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
// --- Namespace GlobalEvents: maneja eventos globales del juego ---
|
||||||
|
namespace GlobalEvents {
|
||||||
|
// --- Funciones ---
|
||||||
|
void handle(const SDL_Event& event); // Comprueba los eventos que se pueden producir en cualquier sección del juego
|
||||||
|
|
||||||
|
// Marca el fin del arranque: a partir de aquí, los eventos de mando
|
||||||
|
// generan notificaciones en pantalla. Se debe llamar desde Director::iterate
|
||||||
|
// en el primer fotograma, después de que SDL haya drenado los
|
||||||
|
// SDL_EVENT_GAMEPAD_ADDED de los mandos ya conectados al iniciar.
|
||||||
|
void markStartupComplete();
|
||||||
|
} // namespace GlobalEvents
|
||||||
@@ -10,6 +10,7 @@ namespace Section {
|
|||||||
// --- Enumeraciones de secciones del programa ---
|
// --- Enumeraciones de secciones del programa ---
|
||||||
enum class Name {
|
enum class Name {
|
||||||
RESET, // Inicialización
|
RESET, // Inicialización
|
||||||
|
PRELOAD, // Carga incremental de recursos
|
||||||
LOGO, // Pantalla de logo
|
LOGO, // Pantalla de logo
|
||||||
INTRO, // Introducción
|
INTRO, // Introducción
|
||||||
TITLE, // Pantalla de título/menú principal
|
TITLE, // Pantalla de título/menú principal
|
||||||
@@ -43,6 +44,7 @@ namespace Section {
|
|||||||
|
|
||||||
// --- Variables globales de estado ---
|
// --- Variables globales de estado ---
|
||||||
inline Name name = Name::RESET;
|
inline Name name = Name::RESET;
|
||||||
|
inline Name post_preload = Name::LOGO; // Sección a la que transiciona PRELOAD al terminar
|
||||||
inline Options options = Options::NONE;
|
inline Options options = Options::NONE;
|
||||||
inline AttractMode attract_mode = AttractMode::TITLE_TO_DEMO;
|
inline AttractMode attract_mode = AttractMode::TITLE_TO_DEMO;
|
||||||
} // namespace Section
|
} // namespace Section
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "shutdown.hpp"
|
#include "core/system/shutdown.hpp"
|
||||||
|
|
||||||
#include <sys/types.h> // Para pid_t
|
#include <sys/types.h> // Para pid_t
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "system_utils.hpp"
|
#include "core/system/system_utils.hpp"
|
||||||
|
|
||||||
#include <sys/stat.h> // Para stat, mkdir, S_ISDIR
|
#include <sys/stat.h> // Para stat, mkdir, S_ISDIR
|
||||||
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
// IWYU pragma: no_include <bits/chrono.h>
|
|
||||||
#include "director.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_SetLogPriority, SDL_LogCategory, SDL_LogPriority, SDL_Quit
|
|
||||||
|
|
||||||
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
|
|
||||||
#include <ctime> // Para time
|
|
||||||
#include <filesystem> // Para path, absolute
|
|
||||||
#include <fstream> // Para ifstream, ofstream
|
|
||||||
#include <iostream> // Para basic_ostream, operator<<, cerr
|
|
||||||
#include <memory> // Para make_unique, unique_ptr
|
|
||||||
#include <span> // Para span
|
|
||||||
#include <stdexcept> // Para runtime_error
|
|
||||||
#include <string> // Para allocator, basic_string, char_traits, operator+, string, operator==
|
|
||||||
|
|
||||||
#include "asset.hpp" // Para Asset
|
|
||||||
#include "audio.hpp" // Para Audio
|
|
||||||
#include "external/fkyaml_node.hpp" // Para fkyaml::node
|
|
||||||
#include "input.hpp" // Para Input
|
|
||||||
#include "lang.hpp" // Para setLanguage
|
|
||||||
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable
|
|
||||||
#include "options.hpp" // Para Settings, loadFromFile, saveToFile, settings, setConfigFile, setControllersFile
|
|
||||||
#include "param.hpp" // Para loadParamsFromFile
|
|
||||||
#include "player.hpp" // Para Player
|
|
||||||
#include "resource.hpp" // Para Resource
|
|
||||||
#include "resource_helper.hpp" // Para initializeResourceSystem
|
|
||||||
#include "screen.hpp" // Para Screen
|
|
||||||
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
|
||||||
#include "sections/credits.hpp" // Para Credits
|
|
||||||
#include "sections/game.hpp" // Para Game
|
|
||||||
#include "sections/hiscore_table.hpp" // Para HiScoreTable
|
|
||||||
#include "sections/instructions.hpp" // Para Instructions
|
|
||||||
#include "sections/intro.hpp" // Para Intro
|
|
||||||
#include "sections/logo.hpp" // Para Logo
|
|
||||||
#include "sections/title.hpp" // Para Title
|
|
||||||
#include "shutdown.hpp" // Para resultToString, shutdownSystem, ShutdownResult
|
|
||||||
#include "system_utils.hpp" // Para createApplicationFolder, resultToString, Result
|
|
||||||
#include "ui/logger.hpp" // Para section, put
|
|
||||||
#include "ui/notifier.hpp" // Para Notifier
|
|
||||||
#include "ui/service_menu.hpp" // Para ServiceMenu
|
|
||||||
#include "utils.hpp" // Para Overrides, overrides
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
Director::Director(int argc, std::span<char*> argv) {
|
|
||||||
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
|
|
||||||
|
|
||||||
// Establece el nivel de prioridad de la categoría de registro
|
|
||||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
|
||||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR);
|
|
||||||
|
|
||||||
Logger::put("Game start\n");
|
|
||||||
|
|
||||||
// Inicia la semilla aleatoria usando el tiempo actual en segundos
|
|
||||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
|
||||||
checkProgramArguments(argc, argv);
|
|
||||||
|
|
||||||
// Crea la carpeta del sistema donde guardar los datos persistentes
|
|
||||||
createSystemFolder("jailgames");
|
|
||||||
createSystemFolder("jailgames/coffee_crisis_arcade_edition");
|
|
||||||
|
|
||||||
// Establecer sección inicial según modo de compilación
|
|
||||||
#ifdef RECORDING
|
|
||||||
Section::name = Section::Name::GAME;
|
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
|
||||||
#elif _DEBUG
|
|
||||||
loadDebugConfig();
|
|
||||||
#else
|
|
||||||
Section::name = Section::Name::LOGO;
|
|
||||||
Section::options = Section::Options::NONE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Director::~Director() {
|
|
||||||
close();
|
|
||||||
Logger::put("\nBye!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inicializa todo
|
|
||||||
void Director::init() {
|
|
||||||
// Configuración inicial de parametros
|
|
||||||
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
|
||||||
|
|
||||||
// Determinar ruta del pack según la plataforma
|
|
||||||
#ifdef MACOS_BUNDLE
|
|
||||||
std::string pack_path = executable_path_ + "../Resources/resources.pack";
|
|
||||||
#else
|
|
||||||
std::string pack_path = executable_path_ + "resources.pack";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Inicializar sistema de recursos con o sin fallback según el tipo de build
|
|
||||||
#ifdef RELEASE_BUILD
|
|
||||||
// Release: Sin fallback - Solo resources.pack (estricto)
|
|
||||||
ResourceHelper::initializeResourceSystem(pack_path, false);
|
|
||||||
#else
|
|
||||||
// Desarrollo: Con fallback - Puede usar data/ si falta el pack (flexible)
|
|
||||||
ResourceHelper::initializeResourceSystem(pack_path, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
loadAssets(); // Crea el índice de archivos
|
|
||||||
|
|
||||||
Logger::section("INIT INPUT");
|
|
||||||
Input::init(Asset::get()->getPath("gamecontrollerdb.txt"), Asset::get()->getPath("controllers.json")); // Carga configuración de controles
|
|
||||||
|
|
||||||
Logger::section("INIT CONFIG");
|
|
||||||
Options::setConfigFile(Asset::get()->getPath("config.yaml")); // Establece el fichero de configuración
|
|
||||||
Options::setControllersFile(Asset::get()->getPath("controllers.json")); // Establece el fichero de configuración de mandos
|
|
||||||
Options::setPostFXFile(Asset::get()->getPath("postfx.yaml")); // Establece el fichero de presets PostFX
|
|
||||||
Options::setCrtPiFile(Asset::get()->getPath("crtpi.yaml")); // Establece el fichero de presets CrtPi
|
|
||||||
Options::loadFromFile(); // Carga el archivo de configuración
|
|
||||||
Options::loadPostFXFromFile(); // Carga los presets PostFX
|
|
||||||
Options::loadCrtPiFromFile(); // Carga los presets CrtPi
|
|
||||||
loadParams(); // Carga los parámetros del programa
|
|
||||||
loadScoreFile(); // Carga el archivo de puntuaciones
|
|
||||||
|
|
||||||
// Inicialización de subsistemas principales
|
|
||||||
Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma
|
|
||||||
|
|
||||||
Logger::section("INIT VIDEO");
|
|
||||||
Screen::init(); // Inicializa la pantalla y el sistema de renderizado
|
|
||||||
|
|
||||||
Logger::section("INIT AUDIO");
|
|
||||||
Audio::init(); // Activa el sistema de audio
|
|
||||||
|
|
||||||
Logger::section("INIT RESOURCES");
|
|
||||||
#ifdef _DEBUG
|
|
||||||
Resource::init(debug_config.resource_loading == "lazy" ? Resource::LoadingMode::LAZY_LOAD : Resource::LoadingMode::PRELOAD);
|
|
||||||
#else
|
|
||||||
Resource::init(Resource::LoadingMode::PRELOAD);
|
|
||||||
#endif
|
|
||||||
ServiceMenu::init(); // Inicializa el menú de servicio
|
|
||||||
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
|
|
||||||
Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones
|
|
||||||
|
|
||||||
Logger::section("GAME LOG");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cierra todo y libera recursos del sistema y de los singletons
|
|
||||||
void Director::close() {
|
|
||||||
// Guarda las opciones actuales en el archivo de configuración
|
|
||||||
Options::saveToFile();
|
|
||||||
|
|
||||||
// Libera los singletons y recursos en orden inverso al de inicialización
|
|
||||||
Notifier::destroy(); // Libera el sistema de notificaciones
|
|
||||||
ServiceMenu::destroy(); // Libera el sistema de menú de servicio
|
|
||||||
Input::destroy(); // Libera el sistema de entrada
|
|
||||||
Resource::destroy(); // Libera el sistema de recursos gráficos y de texto
|
|
||||||
Audio::destroy(); // Libera el sistema de audio
|
|
||||||
Screen::destroy(); // Libera el sistema de pantalla y renderizado
|
|
||||||
Asset::destroy(); // Libera el gestor de archivos
|
|
||||||
|
|
||||||
// Libera todos los recursos de SDL
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
// Apaga el sistema
|
|
||||||
shutdownSystem(Section::options == Section::Options::SHUTDOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga los parametros
|
|
||||||
void Director::loadParams() {
|
|
||||||
// Carga los parametros para configurar el juego
|
|
||||||
#ifdef ANBERNIC
|
|
||||||
const std::string PARAM_FILE_PATH = Asset::get()->getPath("param_320x240.txt");
|
|
||||||
#else
|
|
||||||
const std::string PARAM_FILE_PATH = Asset::get()->getPath(Options::settings.params_file);
|
|
||||||
#endif
|
|
||||||
loadParamsFromFile(PARAM_FILE_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga el fichero de puntuaciones
|
|
||||||
void Director::loadScoreFile() {
|
|
||||||
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
manager->clear();
|
|
||||||
#else
|
|
||||||
if (overrides.clear_hi_score_table) {
|
|
||||||
manager->clear();
|
|
||||||
} else {
|
|
||||||
manager->loadFromFile(Asset::get()->getPath("score.bin"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga el indice de ficheros desde un fichero
|
|
||||||
void Director::loadAssets() {
|
|
||||||
#ifdef MACOS_BUNDLE
|
|
||||||
const std::string PREFIX = "/../Resources";
|
|
||||||
#else
|
|
||||||
const std::string PREFIX;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
|
||||||
std::string config_path = executable_path_ + PREFIX + "/config/assets.txt";
|
|
||||||
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
|
||||||
|
|
||||||
Logger::put("Assets configuration loaded successfully");
|
|
||||||
|
|
||||||
// Si falta algun fichero, sale del programa
|
|
||||||
if (!Asset::get()->check()) {
|
|
||||||
throw std::runtime_error("Falta algun fichero");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba los parametros del programa
|
|
||||||
void Director::checkProgramArguments(int argc, std::span<char*> argv) {
|
|
||||||
// Obtener la ruta absoluta del ejecutable
|
|
||||||
std::filesystem::path exe_path = std::filesystem::absolute(argv[0]);
|
|
||||||
executable_path_ = exe_path.parent_path().string();
|
|
||||||
|
|
||||||
// Asegurar que termine con separador de directorio
|
|
||||||
if (!executable_path_.empty() && executable_path_.back() != '/' && executable_path_.back() != '\\') {
|
|
||||||
executable_path_ += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comprueba el resto de parámetros
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
std::string arg = argv[i];
|
|
||||||
|
|
||||||
if (arg == "--320x240") {
|
|
||||||
overrides.param_file = arg;
|
|
||||||
} else if (arg == "--clear_score") {
|
|
||||||
overrides.clear_hi_score_table = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carga debug.yaml desde la carpeta del sistema (solo en _DEBUG)
|
|
||||||
void Director::loadDebugConfig() {
|
|
||||||
const std::string DEBUG_FILE = system_folder_ + "/debug.yaml";
|
|
||||||
|
|
||||||
std::ifstream file(DEBUG_FILE);
|
|
||||||
if (!file.good()) {
|
|
||||||
// Crear fichero por defecto
|
|
||||||
std::ofstream out(DEBUG_FILE);
|
|
||||||
if (out.is_open()) {
|
|
||||||
out << "# Coffee Crisis Arcade Edition - Debug Configuration\n";
|
|
||||||
out << "# This file is only read in DEBUG builds.\n";
|
|
||||||
out << "#\n";
|
|
||||||
out << "# initial_section: logo, intro, title, game, credits, instructions, hiscore\n";
|
|
||||||
out << "# initial_options: none, 1p, 2p, both\n";
|
|
||||||
out << "# initial_stage: 0-based stage index (only when section is game)\n";
|
|
||||||
out << "# show_render_info: show FPS/driver/preset overlay\n";
|
|
||||||
out << "# resource_loading: preload, lazy\n";
|
|
||||||
out << "\n";
|
|
||||||
out << "initial_section: game\n";
|
|
||||||
out << "initial_options: 1p\n";
|
|
||||||
out << "initial_stage: 0\n";
|
|
||||||
out << "show_render_info: true\n";
|
|
||||||
out << "resource_loading: preload\n";
|
|
||||||
out.close();
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config created: %s", DEBUG_FILE.c_str());
|
|
||||||
}
|
|
||||||
// Usar defaults de DebugConfig
|
|
||||||
} else {
|
|
||||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
||||||
file.close();
|
|
||||||
try {
|
|
||||||
auto yaml = fkyaml::node::deserialize(content);
|
|
||||||
if (yaml.contains("initial_section")) {
|
|
||||||
try {
|
|
||||||
debug_config.initial_section = yaml["initial_section"].get_value<std::string>();
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
if (yaml.contains("initial_options")) {
|
|
||||||
try {
|
|
||||||
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
if (yaml.contains("initial_stage")) {
|
|
||||||
try {
|
|
||||||
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
if (yaml.contains("show_render_info")) {
|
|
||||||
try {
|
|
||||||
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
if (yaml.contains("resource_loading")) {
|
|
||||||
try {
|
|
||||||
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Debug config loaded: section=%s options=%s stage=%d", debug_config.initial_section.c_str(), debug_config.initial_options.c_str(), debug_config.initial_stage);
|
|
||||||
} catch (...) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Error parsing debug.yaml, using defaults");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapear strings a enums
|
|
||||||
const auto& sec = debug_config.initial_section;
|
|
||||||
if (sec == "logo") {
|
|
||||||
Section::name = Section::Name::LOGO;
|
|
||||||
} else if (sec == "intro") {
|
|
||||||
Section::name = Section::Name::INTRO;
|
|
||||||
} else if (sec == "title") {
|
|
||||||
Section::name = Section::Name::TITLE;
|
|
||||||
} else if (sec == "game") {
|
|
||||||
Section::name = Section::Name::GAME;
|
|
||||||
} else if (sec == "credits") {
|
|
||||||
Section::name = Section::Name::CREDITS;
|
|
||||||
} else if (sec == "instructions") {
|
|
||||||
Section::name = Section::Name::INSTRUCTIONS;
|
|
||||||
} else if (sec == "hiscore") {
|
|
||||||
Section::name = Section::Name::HI_SCORE_TABLE;
|
|
||||||
} else {
|
|
||||||
Section::name = Section::Name::GAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& opt = debug_config.initial_options;
|
|
||||||
if (opt == "none") {
|
|
||||||
Section::options = Section::Options::NONE;
|
|
||||||
} else if (opt == "1p") {
|
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
|
||||||
} else if (opt == "2p") {
|
|
||||||
Section::options = Section::Options::GAME_PLAY_2P;
|
|
||||||
} else if (opt == "both") {
|
|
||||||
Section::options = Section::Options::GAME_PLAY_BOTH;
|
|
||||||
} else {
|
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crea la carpeta del sistema donde guardar datos
|
|
||||||
void Director::createSystemFolder(const std::string& folder) {
|
|
||||||
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
|
|
||||||
|
|
||||||
if (result != SystemUtils::Result::SUCCESS) {
|
|
||||||
std::cerr << "Error creando carpeta del sistema: "
|
|
||||||
<< SystemUtils::resultToString(result) << '\n';
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección con el logo
|
|
||||||
void Director::runLogo() {
|
|
||||||
auto logo = std::make_unique<Logo>();
|
|
||||||
logo->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección con la secuencia de introducción
|
|
||||||
void Director::runIntro() {
|
|
||||||
auto intro = std::make_unique<Intro>();
|
|
||||||
intro->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección con el título del juego
|
|
||||||
void Director::runTitle() {
|
|
||||||
auto title = std::make_unique<Title>();
|
|
||||||
title->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección donde se juega al juego
|
|
||||||
void Director::runGame() {
|
|
||||||
Player::Id player_id = Player::Id::PLAYER1;
|
|
||||||
|
|
||||||
switch (Section::options) {
|
|
||||||
case Section::Options::GAME_PLAY_1P:
|
|
||||||
player_id = Player::Id::PLAYER1;
|
|
||||||
break;
|
|
||||||
case Section::Options::GAME_PLAY_2P:
|
|
||||||
player_id = Player::Id::PLAYER2;
|
|
||||||
break;
|
|
||||||
case Section::Options::GAME_PLAY_BOTH:
|
|
||||||
player_id = Player::Id::BOTH_PLAYERS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
const int CURRENT_STAGE = debug_config.initial_stage;
|
|
||||||
#else
|
|
||||||
constexpr int CURRENT_STAGE = 0;
|
|
||||||
#endif
|
|
||||||
auto game = std::make_unique<Game>(player_id, CURRENT_STAGE, Game::DEMO_OFF);
|
|
||||||
game->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección donde se muestran las instrucciones
|
|
||||||
void Director::runInstructions() {
|
|
||||||
auto instructions = std::make_unique<Instructions>();
|
|
||||||
instructions->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección donde se muestran los creditos del programa
|
|
||||||
void Director::runCredits() {
|
|
||||||
auto credits = std::make_unique<Credits>();
|
|
||||||
credits->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta la sección donde se muestra la tabla de puntuaciones
|
|
||||||
void Director::runHiScoreTable() {
|
|
||||||
auto hi_score_table = std::make_unique<HiScoreTable>();
|
|
||||||
hi_score_table->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecuta el juego en modo demo
|
|
||||||
void Director::runDemoGame() {
|
|
||||||
const auto PLAYER_ID = static_cast<Player::Id>((rand() % 2) + 1);
|
|
||||||
constexpr auto CURRENT_STAGE = 0;
|
|
||||||
auto game = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, Game::DEMO_ON);
|
|
||||||
game->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reinicia objetos y vuelve a la sección inicial
|
|
||||||
void Director::reset() {
|
|
||||||
Options::saveToFile();
|
|
||||||
Options::loadFromFile();
|
|
||||||
Lang::setLanguage(Options::settings.language);
|
|
||||||
Audio::get()->stopMusic();
|
|
||||||
Audio::get()->stopAllSounds();
|
|
||||||
Resource::get()->reload();
|
|
||||||
ServiceMenu::get()->reset();
|
|
||||||
Section::name = Section::Name::LOGO;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Director::run() -> int {
|
|
||||||
// Bucle principal
|
|
||||||
while (Section::name != Section::Name::QUIT) {
|
|
||||||
switch (Section::name) {
|
|
||||||
case Section::Name::RESET:
|
|
||||||
reset();
|
|
||||||
break;
|
|
||||||
case Section::Name::LOGO:
|
|
||||||
runLogo();
|
|
||||||
break;
|
|
||||||
case Section::Name::INTRO:
|
|
||||||
runIntro();
|
|
||||||
break;
|
|
||||||
case Section::Name::TITLE:
|
|
||||||
runTitle();
|
|
||||||
break;
|
|
||||||
case Section::Name::GAME:
|
|
||||||
runGame();
|
|
||||||
break;
|
|
||||||
case Section::Name::HI_SCORE_TABLE:
|
|
||||||
runHiScoreTable();
|
|
||||||
break;
|
|
||||||
case Section::Name::GAME_DEMO:
|
|
||||||
runDemoGame();
|
|
||||||
break;
|
|
||||||
case Section::Name::INSTRUCTIONS:
|
|
||||||
runInstructions();
|
|
||||||
break;
|
|
||||||
case Section::Name::CREDITS:
|
|
||||||
runCredits();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apaga el sistema de forma segura
|
|
||||||
void Director::shutdownSystem(bool should_shutdown) {
|
|
||||||
if (should_shutdown) {
|
|
||||||
auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps
|
|
||||||
|
|
||||||
if (result != SystemShutdown::ShutdownResult::SUCCESS) {
|
|
||||||
std::cerr << SystemShutdown::resultToString(result) << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <span> // Para Span
|
|
||||||
#include <string> // Para string
|
|
||||||
|
|
||||||
namespace Lang {
|
|
||||||
enum class Code : int;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Clase Director: gestor principal de la aplicación ---
|
|
||||||
class Director {
|
|
||||||
public:
|
|
||||||
// --- Constructor y destructor ---
|
|
||||||
Director(int argc, std::span<char*> argv);
|
|
||||||
~Director();
|
|
||||||
|
|
||||||
// --- Bucle principal ---
|
|
||||||
static auto run() -> int;
|
|
||||||
|
|
||||||
// --- Debug config (accesible desde otras clases) ---
|
|
||||||
struct DebugConfig {
|
|
||||||
std::string initial_section;
|
|
||||||
std::string initial_options;
|
|
||||||
int initial_stage = 0;
|
|
||||||
bool show_render_info = true;
|
|
||||||
std::string resource_loading;
|
|
||||||
|
|
||||||
DebugConfig()
|
|
||||||
: initial_section("game"),
|
|
||||||
initial_options("1p"),
|
|
||||||
resource_loading("preload") {}
|
|
||||||
};
|
|
||||||
static inline DebugConfig debug_config;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// --- Variables internas ---
|
|
||||||
std::string executable_path_; // Ruta del ejecutable
|
|
||||||
std::string system_folder_; // Carpeta del sistema para almacenar datos
|
|
||||||
|
|
||||||
// --- Inicialización y cierre del sistema ---
|
|
||||||
void init(); // Inicializa la aplicación
|
|
||||||
static void close(); // Cierra y libera recursos
|
|
||||||
|
|
||||||
// --- Configuración inicial ---
|
|
||||||
static void loadParams(); // Carga los parámetros del programa
|
|
||||||
static void loadScoreFile(); // Carga el fichero de puntuaciones
|
|
||||||
void createSystemFolder(const std::string& folder); // Crea la carpeta del sistema
|
|
||||||
void loadDebugConfig(); // Carga debug.yaml (solo en _DEBUG)
|
|
||||||
|
|
||||||
// --- Gestión de entrada y archivos ---
|
|
||||||
void loadAssets(); // Crea el índice de archivos disponibles
|
|
||||||
void checkProgramArguments(int argc, std::span<char*> argv); // Verifica los parámetros del programa // NOLINT(modernize-avoid-c-arrays)
|
|
||||||
|
|
||||||
// --- Secciones del programa ---
|
|
||||||
static void runLogo(); // Ejecuta la pantalla con el logo
|
|
||||||
static void runIntro(); // Ejecuta la introducción del juego
|
|
||||||
static void runTitle(); // Ejecuta la pantalla de título
|
|
||||||
static void runGame(); // Inicia el juego
|
|
||||||
static void runInstructions(); // Muestra las instrucciones
|
|
||||||
static void runCredits(); // Muestra los créditos del juego
|
|
||||||
static void runHiScoreTable(); // Muestra la tabla de puntuaciones
|
|
||||||
static void runDemoGame(); // Ejecuta el modo demo
|
|
||||||
static void reset(); // Reinicia objetos y vuelve a la sección inicial
|
|
||||||
|
|
||||||
// --- Gestión de archivos de idioma ---
|
|
||||||
auto getLangFile(Lang::Code code) -> std::string; // Obtiene un fichero de idioma según el código
|
|
||||||
|
|
||||||
// --- Apagado del sistema ---
|
|
||||||
static void shutdownSystem(bool should_shutdown); // Apaga el sistema
|
|
||||||
};
|
|
||||||
256
source/external/gif.cpp
vendored
256
source/external/gif.cpp
vendored
@@ -1,256 +0,0 @@
|
|||||||
#include "gif.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo
|
|
||||||
#include <cstring> // Para memcpy, size_t
|
|
||||||
#include <stdexcept> // Para runtime_error
|
|
||||||
#include <string> // Para char_traits, operator==, basic_string, string
|
|
||||||
|
|
||||||
namespace GIF {
|
|
||||||
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) {
|
|
||||||
std::memcpy(dst, buffer, size);
|
|
||||||
buffer += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) {
|
|
||||||
if (code_length < 2 || code_length > 12) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code length: %d", code_length);
|
|
||||||
throw std::runtime_error("Invalid LZW code length");
|
|
||||||
}
|
|
||||||
|
|
||||||
int i, bit;
|
|
||||||
int prev = -1;
|
|
||||||
std::vector<DictionaryEntry> dictionary;
|
|
||||||
int dictionary_ind;
|
|
||||||
unsigned int mask = 0x01;
|
|
||||||
int reset_code_length = code_length;
|
|
||||||
int clear_code = 1 << code_length;
|
|
||||||
int stop_code = clear_code + 1;
|
|
||||||
int match_len = 0;
|
|
||||||
|
|
||||||
dictionary.resize(1 << (code_length + 1));
|
|
||||||
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
|
|
||||||
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
|
||||||
dictionary[dictionary_ind].prev = -1;
|
|
||||||
dictionary[dictionary_ind].len = 1;
|
|
||||||
}
|
|
||||||
dictionary_ind += 2;
|
|
||||||
|
|
||||||
while (input_length > 0) {
|
|
||||||
int code = 0;
|
|
||||||
for (i = 0; i < (code_length + 1); i++) {
|
|
||||||
if (input_length <= 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unexpected end of input in decompress");
|
|
||||||
throw std::runtime_error("Unexpected end of input in decompress");
|
|
||||||
}
|
|
||||||
bit = ((*input & mask) != 0) ? 1 : 0;
|
|
||||||
mask <<= 1;
|
|
||||||
if (mask == 0x100) {
|
|
||||||
mask = 0x01;
|
|
||||||
input++;
|
|
||||||
input_length--;
|
|
||||||
}
|
|
||||||
code |= (bit << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code == clear_code) {
|
|
||||||
code_length = reset_code_length;
|
|
||||||
dictionary.resize(1 << (code_length + 1));
|
|
||||||
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
|
|
||||||
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
|
||||||
dictionary[dictionary_ind].prev = -1;
|
|
||||||
dictionary[dictionary_ind].len = 1;
|
|
||||||
}
|
|
||||||
dictionary_ind += 2;
|
|
||||||
prev = -1;
|
|
||||||
continue;
|
|
||||||
} else if (code == stop_code) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev > -1 && code_length < 12) {
|
|
||||||
if (code > dictionary_ind) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LZW error: code (%d) exceeds dictionary_ind (%d)", code, dictionary_ind);
|
|
||||||
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int ptr;
|
|
||||||
if (code == dictionary_ind) {
|
|
||||||
ptr = prev;
|
|
||||||
while (dictionary[ptr].prev != -1)
|
|
||||||
ptr = dictionary[ptr].prev;
|
|
||||||
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
|
||||||
} else {
|
|
||||||
ptr = code;
|
|
||||||
while (dictionary[ptr].prev != -1)
|
|
||||||
ptr = dictionary[ptr].prev;
|
|
||||||
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
|
||||||
}
|
|
||||||
dictionary[dictionary_ind].prev = prev;
|
|
||||||
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
|
|
||||||
dictionary_ind++;
|
|
||||||
|
|
||||||
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
|
|
||||||
code_length++;
|
|
||||||
dictionary.resize(1 << (code_length + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = code;
|
|
||||||
|
|
||||||
if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code %d, dictionary size %lu", code, static_cast<unsigned long>(dictionary.size()));
|
|
||||||
throw std::runtime_error("LZW error: invalid code encountered");
|
|
||||||
}
|
|
||||||
|
|
||||||
int curCode = code;
|
|
||||||
match_len = dictionary[curCode].len;
|
|
||||||
while (curCode != -1) {
|
|
||||||
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
|
|
||||||
if (dictionary[curCode].prev == curCode) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Internal error; self-reference detected.");
|
|
||||||
throw std::runtime_error("Internal error in decompress: self-reference");
|
|
||||||
}
|
|
||||||
curCode = dictionary[curCode].prev;
|
|
||||||
}
|
|
||||||
out += match_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer) {
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
uint8_t block_size = *buffer;
|
|
||||||
buffer++;
|
|
||||||
while (block_size != 0) {
|
|
||||||
data.insert(data.end(), buffer, buffer + block_size);
|
|
||||||
buffer += block_size;
|
|
||||||
block_size = *buffer;
|
|
||||||
buffer++;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) {
|
|
||||||
ImageDescriptor image_descriptor;
|
|
||||||
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
|
|
||||||
|
|
||||||
uint8_t lzw_code_size;
|
|
||||||
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
|
|
||||||
|
|
||||||
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
|
|
||||||
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
|
|
||||||
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
|
|
||||||
|
|
||||||
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
|
|
||||||
return uncompressed_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) {
|
|
||||||
uint8_t header[6];
|
|
||||||
std::memcpy(header, buffer, 6);
|
|
||||||
buffer += 6;
|
|
||||||
|
|
||||||
ScreenDescriptor screen_descriptor;
|
|
||||||
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
|
|
||||||
buffer += sizeof(ScreenDescriptor);
|
|
||||||
|
|
||||||
std::vector<uint32_t> global_color_table;
|
|
||||||
if (screen_descriptor.fields & 0x80) {
|
|
||||||
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
|
||||||
global_color_table.resize(global_color_table_size);
|
|
||||||
for (int i = 0; i < global_color_table_size; ++i) {
|
|
||||||
uint8_t r = buffer[0];
|
|
||||||
uint8_t g = buffer[1];
|
|
||||||
uint8_t b = buffer[2];
|
|
||||||
global_color_table[i] = (r << 16) | (g << 8) | b;
|
|
||||||
buffer += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return global_color_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
|
|
||||||
uint8_t header[6];
|
|
||||||
std::memcpy(header, buffer, 6);
|
|
||||||
buffer += 6;
|
|
||||||
|
|
||||||
std::string headerStr(reinterpret_cast<char *>(header), 6);
|
|
||||||
if (headerStr != "GIF87a" && headerStr != "GIF89a") {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Formato de archivo GIF inválido: %s", headerStr.c_str());
|
|
||||||
throw std::runtime_error("Formato de archivo GIF inválido.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Procesando GIF con cabecera: %s", headerStr.c_str());
|
|
||||||
|
|
||||||
ScreenDescriptor screen_descriptor;
|
|
||||||
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
|
|
||||||
|
|
||||||
w = screen_descriptor.width;
|
|
||||||
h = screen_descriptor.height;
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Resolución del GIF: %dx%d", w, h);
|
|
||||||
|
|
||||||
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
|
|
||||||
std::vector<RGB> global_color_table;
|
|
||||||
if (screen_descriptor.fields & 0x80) {
|
|
||||||
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
|
||||||
global_color_table.resize(global_color_table_size);
|
|
||||||
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
|
|
||||||
buffer += 3 * global_color_table_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t block_type = *buffer++;
|
|
||||||
while (block_type != TRAILER) {
|
|
||||||
if (block_type == EXTENSION_INTRODUCER) {
|
|
||||||
uint8_t extension_label = *buffer++;
|
|
||||||
switch (extension_label) {
|
|
||||||
case GRAPHIC_CONTROL: {
|
|
||||||
uint8_t blockSize = *buffer++;
|
|
||||||
buffer += blockSize;
|
|
||||||
uint8_t subBlockSize = *buffer++;
|
|
||||||
while (subBlockSize != 0) {
|
|
||||||
buffer += subBlockSize;
|
|
||||||
subBlockSize = *buffer++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case APPLICATION_EXTENSION:
|
|
||||||
case COMMENT_EXTENSION:
|
|
||||||
case PLAINTEXT_EXTENSION: {
|
|
||||||
uint8_t blockSize = *buffer++;
|
|
||||||
buffer += blockSize;
|
|
||||||
uint8_t subBlockSize = *buffer++;
|
|
||||||
while (subBlockSize != 0) {
|
|
||||||
buffer += subBlockSize;
|
|
||||||
subBlockSize = *buffer++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
uint8_t blockSize = *buffer++;
|
|
||||||
buffer += blockSize;
|
|
||||||
uint8_t subBlockSize = *buffer++;
|
|
||||||
while (subBlockSize != 0) {
|
|
||||||
buffer += subBlockSize;
|
|
||||||
subBlockSize = *buffer++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (block_type == IMAGE_DESCRIPTOR) {
|
|
||||||
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
|
|
||||||
} else {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized block type: 0x%X", block_type);
|
|
||||||
return std::vector<uint8_t>{};
|
|
||||||
}
|
|
||||||
block_type = *buffer++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF procesado correctamente.");
|
|
||||||
return std::vector<uint8_t>{};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
|
|
||||||
return processGifStream(buffer, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace GIF
|
|
||||||
92
source/external/gif.hpp
vendored
92
source/external/gif.hpp
vendored
@@ -1,92 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
|
|
||||||
#include <vector> // Para vector
|
|
||||||
|
|
||||||
namespace GIF {
|
|
||||||
|
|
||||||
// Constantes definidas con constexpr, en lugar de macros
|
|
||||||
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
|
|
||||||
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
|
|
||||||
constexpr uint8_t TRAILER = 0x3B;
|
|
||||||
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
|
|
||||||
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
|
|
||||||
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
|
|
||||||
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct ScreenDescriptor {
|
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
uint8_t fields;
|
|
||||||
uint8_t background_color_index;
|
|
||||||
uint8_t pixel_aspect_ratio;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RGB {
|
|
||||||
uint8_t r, g, b;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ImageDescriptor {
|
|
||||||
uint16_t image_left_position;
|
|
||||||
uint16_t image_top_position;
|
|
||||||
uint16_t image_width;
|
|
||||||
uint16_t image_height;
|
|
||||||
uint8_t fields;
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct DictionaryEntry {
|
|
||||||
uint8_t byte;
|
|
||||||
int prev;
|
|
||||||
int len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Extension {
|
|
||||||
uint8_t extension_code;
|
|
||||||
uint8_t block_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GraphicControlExtension {
|
|
||||||
uint8_t fields;
|
|
||||||
uint16_t delay_time;
|
|
||||||
uint8_t transparent_color_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ApplicationExtension {
|
|
||||||
uint8_t application_id[8];
|
|
||||||
uint8_t version[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlaintextExtension {
|
|
||||||
uint16_t left, top, width, height;
|
|
||||||
uint8_t cell_width, cell_height;
|
|
||||||
uint8_t foreground_color, background_color;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Gif {
|
|
||||||
public:
|
|
||||||
// Descompone (uncompress) el bloque comprimido usando LZW.
|
|
||||||
// Este método puede lanzar std::runtime_error en caso de error.
|
|
||||||
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
|
|
||||||
|
|
||||||
// Carga la paleta (global color table) a partir de un buffer,
|
|
||||||
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
|
|
||||||
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
|
|
||||||
|
|
||||||
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
|
|
||||||
// asigna el ancho y alto mediante referencias.
|
|
||||||
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
|
|
||||||
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
|
|
||||||
|
|
||||||
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
|
|
||||||
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
|
|
||||||
|
|
||||||
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
|
|
||||||
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace GIF
|
|
||||||
477
source/external/jail_audio.cpp
vendored
477
source/external/jail_audio.cpp
vendored
@@ -1,477 +0,0 @@
|
|||||||
#ifndef JA_USESDLMIXER
|
|
||||||
#include "jail_audio.h"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_SetAudioStreamGain, SDL_PutAudioStreamData, SDL_DestroyAudioStream, SDL_GetAudioStreamAvailable, Uint8, SDL_CreateAudioStream, SDL_UnbindAudioStream, Uint32, SDL_CloseAudioDevice, SDL_GetTicks, SDL_Log, SDL_free, SDL_AudioSpec, SDL_AudioStream, SDL_IOFromMem, SDL_LoadWAV, SDL_LoadWAV_IO, SDL_OpenAudioDevice, SDL_clamp, SDL_malloc, SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, SDL_AudioDeviceID, SDL_memcpy
|
|
||||||
#include <stdint.h> // Para uint32_t, uint8_t
|
|
||||||
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
|
|
||||||
#include <stdlib.h> // Para free, malloc
|
|
||||||
#include <string.h> // Para strcpy, strlen
|
|
||||||
|
|
||||||
#include "stb_vorbis.h" // Para stb_vorbis_decode_memory
|
|
||||||
|
|
||||||
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
|
|
||||||
#define JA_MAX_GROUPS 2
|
|
||||||
|
|
||||||
struct JA_Sound_t
|
|
||||||
{
|
|
||||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
|
||||||
Uint32 length { 0 };
|
|
||||||
Uint8 *buffer { NULL };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct JA_Channel_t
|
|
||||||
{
|
|
||||||
JA_Sound_t *sound { nullptr };
|
|
||||||
int pos { 0 };
|
|
||||||
int times { 0 };
|
|
||||||
int group { 0 };
|
|
||||||
SDL_AudioStream *stream { nullptr };
|
|
||||||
JA_Channel_state state { JA_CHANNEL_FREE };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct JA_Music_t
|
|
||||||
{
|
|
||||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
|
||||||
Uint32 length { 0 };
|
|
||||||
Uint8 *buffer { nullptr };
|
|
||||||
char *filename { nullptr };
|
|
||||||
|
|
||||||
int pos { 0 };
|
|
||||||
int times { 0 };
|
|
||||||
SDL_AudioStream *stream { nullptr };
|
|
||||||
JA_Music_state state { JA_MUSIC_INVALID };
|
|
||||||
};
|
|
||||||
|
|
||||||
JA_Music_t *current_music { nullptr };
|
|
||||||
JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
|
|
||||||
|
|
||||||
SDL_AudioSpec JA_audioSpec { SDL_AUDIO_S16, 2, 48000 };
|
|
||||||
float JA_musicVolume { 1.0f };
|
|
||||||
float JA_soundVolume[JA_MAX_GROUPS];
|
|
||||||
bool JA_musicEnabled { true };
|
|
||||||
bool JA_soundEnabled { true };
|
|
||||||
SDL_AudioDeviceID sdlAudioDevice { 0 };
|
|
||||||
//SDL_TimerID JA_timerID { 0 };
|
|
||||||
|
|
||||||
bool fading = false;
|
|
||||||
int fade_start_time;
|
|
||||||
int fade_duration;
|
|
||||||
int fade_initial_volume;
|
|
||||||
|
|
||||||
|
|
||||||
void JA_Update()
|
|
||||||
{
|
|
||||||
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING)
|
|
||||||
{
|
|
||||||
if (fading) {
|
|
||||||
int time = SDL_GetTicks();
|
|
||||||
if (time > (fade_start_time+fade_duration)) {
|
|
||||||
fading = false;
|
|
||||||
JA_StopMusic();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
const int time_passed = time - fade_start_time;
|
|
||||||
const float percent = (float)time_passed / (float)fade_duration;
|
|
||||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume*(1.0 - percent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current_music->times != 0)
|
|
||||||
{
|
|
||||||
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length/2)) {
|
|
||||||
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
|
|
||||||
}
|
|
||||||
if (current_music->times>0) current_music->times--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) JA_StopMusic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JA_soundEnabled)
|
|
||||||
{
|
|
||||||
for (int i=0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
|
|
||||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
|
||||||
{
|
|
||||||
if (channels[i].times != 0)
|
|
||||||
{
|
|
||||||
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length/2)) {
|
|
||||||
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
|
|
||||||
if (channels[i].times>0) channels[i].times--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JA_audioSpec = {format, num_channels, freq };
|
|
||||||
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
|
|
||||||
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
|
|
||||||
if (sdlAudioDevice==0) SDL_Log("Failed to initialize SDL audio!");
|
|
||||||
for (int i=0; i<JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
|
|
||||||
for (int i=0; i<JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_Quit()
|
|
||||||
{
|
|
||||||
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
|
|
||||||
sdlAudioDevice = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length)
|
|
||||||
{
|
|
||||||
JA_Music_t *music = new JA_Music_t();
|
|
||||||
|
|
||||||
int chan, samplerate;
|
|
||||||
short *output;
|
|
||||||
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
|
|
||||||
|
|
||||||
music->spec.channels = chan;
|
|
||||||
music->spec.freq = samplerate;
|
|
||||||
music->spec.format = SDL_AUDIO_S16;
|
|
||||||
music->buffer = (Uint8*)SDL_malloc(music->length);
|
|
||||||
SDL_memcpy(music->buffer, output, music->length);
|
|
||||||
free(output);
|
|
||||||
music->pos = 0;
|
|
||||||
music->state = JA_MUSIC_STOPPED;
|
|
||||||
|
|
||||||
return music;
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Music_t *JA_LoadMusic(const char* filename)
|
|
||||||
{
|
|
||||||
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
|
|
||||||
FILE *f = fopen(filename, "rb");
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
long fsize = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
Uint8 *buffer = (Uint8*)malloc(fsize + 1);
|
|
||||||
if (fread(buffer, fsize, 1, f)!=1) return NULL;
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
JA_Music_t *music = JA_LoadMusic(buffer, fsize);
|
|
||||||
music->filename = (char*)malloc(strlen(filename)+1);
|
|
||||||
strcpy(music->filename, filename);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
return music;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_PlayMusic(JA_Music_t *music, const int loop)
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return;
|
|
||||||
|
|
||||||
JA_StopMusic();
|
|
||||||
|
|
||||||
current_music = music;
|
|
||||||
current_music->pos = 0;
|
|
||||||
current_music->state = JA_MUSIC_PLAYING;
|
|
||||||
current_music->times = loop;
|
|
||||||
|
|
||||||
current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec);
|
|
||||||
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) printf("[ERROR] SDL_PutAudioStreamData failed!\n");
|
|
||||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
|
||||||
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n");
|
|
||||||
//SDL_ResumeAudioStreamDevice(current_music->stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *JA_GetMusicFilename(JA_Music_t *music)
|
|
||||||
{
|
|
||||||
if (!music) music = current_music;
|
|
||||||
return music->filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_PauseMusic()
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return;
|
|
||||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
|
||||||
|
|
||||||
current_music->state = JA_MUSIC_PAUSED;
|
|
||||||
//SDL_PauseAudioStreamDevice(current_music->stream);
|
|
||||||
SDL_UnbindAudioStream(current_music->stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_ResumeMusic()
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return;
|
|
||||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
|
||||||
|
|
||||||
current_music->state = JA_MUSIC_PLAYING;
|
|
||||||
//SDL_ResumeAudioStreamDevice(current_music->stream);
|
|
||||||
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_StopMusic()
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return;
|
|
||||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
|
||||||
|
|
||||||
current_music->pos = 0;
|
|
||||||
current_music->state = JA_MUSIC_STOPPED;
|
|
||||||
//SDL_PauseAudioStreamDevice(current_music->stream);
|
|
||||||
SDL_DestroyAudioStream(current_music->stream);
|
|
||||||
current_music->stream = nullptr;
|
|
||||||
free(current_music->filename);
|
|
||||||
current_music->filename = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_FadeOutMusic(const int milliseconds)
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return;
|
|
||||||
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return;
|
|
||||||
|
|
||||||
fading = true;
|
|
||||||
fade_start_time = SDL_GetTicks();
|
|
||||||
fade_duration = milliseconds;
|
|
||||||
fade_initial_volume = JA_musicVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Music_state JA_GetMusicState()
|
|
||||||
{
|
|
||||||
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
|
|
||||||
if (!current_music) return JA_MUSIC_INVALID;
|
|
||||||
|
|
||||||
return current_music->state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_DeleteMusic(JA_Music_t *music)
|
|
||||||
{
|
|
||||||
if (current_music == music) current_music = nullptr;
|
|
||||||
SDL_free(music->buffer);
|
|
||||||
if (music->stream) SDL_DestroyAudioStream(music->stream);
|
|
||||||
delete music;
|
|
||||||
}
|
|
||||||
|
|
||||||
float JA_SetMusicVolume(float volume)
|
|
||||||
{
|
|
||||||
JA_musicVolume = SDL_clamp( volume, 0.0f, 1.0f );
|
|
||||||
if (current_music) SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
|
||||||
return JA_musicVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_SetMusicPosition(float value)
|
|
||||||
{
|
|
||||||
if (!current_music) return;
|
|
||||||
current_music->pos = value * current_music->spec.freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
float JA_GetMusicPosition()
|
|
||||||
{
|
|
||||||
if (!current_music) return 0;
|
|
||||||
return float(current_music->pos)/float(current_music->spec.freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_EnableMusic(const bool value)
|
|
||||||
{
|
|
||||||
if ( !value && current_music && (current_music->state==JA_MUSIC_PLAYING) ) JA_StopMusic();
|
|
||||||
|
|
||||||
JA_musicEnabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length)
|
|
||||||
{
|
|
||||||
JA_Sound_t *sound = new JA_Sound_t();
|
|
||||||
sound->buffer = buffer;
|
|
||||||
sound->length = length;
|
|
||||||
return sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size)
|
|
||||||
{
|
|
||||||
JA_Sound_t *sound = new JA_Sound_t();
|
|
||||||
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
|
|
||||||
|
|
||||||
return sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Sound_t *JA_LoadSound(const char* filename)
|
|
||||||
{
|
|
||||||
JA_Sound_t *sound = new JA_Sound_t();
|
|
||||||
SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length);
|
|
||||||
|
|
||||||
return sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return -1;
|
|
||||||
|
|
||||||
int channel = 0;
|
|
||||||
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
|
|
||||||
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) channel = 0;
|
|
||||||
JA_StopChannel(channel);
|
|
||||||
|
|
||||||
channels[channel].sound = sound;
|
|
||||||
channels[channel].times = loop;
|
|
||||||
channels[channel].pos = 0;
|
|
||||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
|
||||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
|
||||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
|
||||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
|
|
||||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
|
||||||
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return -1;
|
|
||||||
|
|
||||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
|
|
||||||
JA_StopChannel(channel);
|
|
||||||
|
|
||||||
channels[channel].sound = sound;
|
|
||||||
channels[channel].times = loop;
|
|
||||||
channels[channel].pos = 0;
|
|
||||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
|
||||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
|
||||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
|
||||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
|
|
||||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
|
||||||
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_DeleteSound(JA_Sound_t *sound)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
|
||||||
if (channels[i].sound == sound) JA_StopChannel(i);
|
|
||||||
}
|
|
||||||
SDL_free(sound->buffer);
|
|
||||||
delete sound;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_PauseChannel(const int channel)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return;
|
|
||||||
|
|
||||||
if (channel == -1)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
|
||||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
|
||||||
{
|
|
||||||
channels[i].state = JA_CHANNEL_PAUSED;
|
|
||||||
//SDL_PauseAudioStreamDevice(channels[i].stream);
|
|
||||||
SDL_UnbindAudioStream(channels[i].stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
|
||||||
{
|
|
||||||
if (channels[channel].state == JA_CHANNEL_PLAYING)
|
|
||||||
{
|
|
||||||
channels[channel].state = JA_CHANNEL_PAUSED;
|
|
||||||
//SDL_PauseAudioStreamDevice(channels[channel].stream);
|
|
||||||
SDL_UnbindAudioStream(channels[channel].stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_ResumeChannel(const int channel)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return;
|
|
||||||
|
|
||||||
if (channel == -1)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
|
||||||
if (channels[i].state == JA_CHANNEL_PAUSED)
|
|
||||||
{
|
|
||||||
channels[i].state = JA_CHANNEL_PLAYING;
|
|
||||||
//SDL_ResumeAudioStreamDevice(channels[i].stream);
|
|
||||||
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
|
||||||
{
|
|
||||||
if (channels[channel].state == JA_CHANNEL_PAUSED)
|
|
||||||
{
|
|
||||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
|
||||||
//SDL_ResumeAudioStreamDevice(channels[channel].stream);
|
|
||||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_StopChannel(const int channel)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return;
|
|
||||||
|
|
||||||
if (channel == -1)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
|
||||||
if (channels[i].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[i].stream);
|
|
||||||
channels[i].stream = nullptr;
|
|
||||||
channels[i].state = JA_CHANNEL_FREE;
|
|
||||||
channels[i].pos = 0;
|
|
||||||
channels[i].sound = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
|
||||||
{
|
|
||||||
if (channels[channel].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[channel].stream);
|
|
||||||
channels[channel].stream = nullptr;
|
|
||||||
channels[channel].state = JA_CHANNEL_FREE;
|
|
||||||
channels[channel].pos = 0;
|
|
||||||
channels[channel].sound = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JA_Channel_state JA_GetChannelState(const int channel)
|
|
||||||
{
|
|
||||||
if (!JA_soundEnabled) return JA_SOUND_DISABLED;
|
|
||||||
|
|
||||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID;
|
|
||||||
|
|
||||||
return channels[channel].state;
|
|
||||||
}
|
|
||||||
|
|
||||||
float JA_SetSoundVolume(float volume, const int group)
|
|
||||||
{
|
|
||||||
const float v = SDL_clamp( volume, 0.0f, 1.0f );
|
|
||||||
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
|
|
||||||
if (group==-1 || group==i) JA_soundVolume[i]=v;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
|
||||||
if ( ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) &&
|
|
||||||
((group==-1) || (channels[i].group==group)) )
|
|
||||||
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[i]);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JA_EnableSound(const bool value)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
|
||||||
{
|
|
||||||
if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i);
|
|
||||||
}
|
|
||||||
JA_soundEnabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
float JA_SetVolume(float volume)
|
|
||||||
{
|
|
||||||
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
|
|
||||||
|
|
||||||
return JA_musicVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
43
source/external/jail_audio.h
vendored
43
source/external/jail_audio.h
vendored
@@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED };
|
|
||||||
enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED, JA_MUSIC_DISABLED };
|
|
||||||
|
|
||||||
struct JA_Sound_t;
|
|
||||||
struct JA_Music_t;
|
|
||||||
|
|
||||||
void JA_Update();
|
|
||||||
|
|
||||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels);
|
|
||||||
void JA_Quit();
|
|
||||||
|
|
||||||
JA_Music_t *JA_LoadMusic(const char* filename);
|
|
||||||
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length);
|
|
||||||
void JA_PlayMusic(JA_Music_t *music, const int loop = -1);
|
|
||||||
char *JA_GetMusicFilename(JA_Music_t *music = nullptr);
|
|
||||||
void JA_PauseMusic();
|
|
||||||
void JA_ResumeMusic();
|
|
||||||
void JA_StopMusic();
|
|
||||||
void JA_FadeOutMusic(const int milliseconds);
|
|
||||||
JA_Music_state JA_GetMusicState();
|
|
||||||
void JA_DeleteMusic(JA_Music_t *music);
|
|
||||||
float JA_SetMusicVolume(float volume);
|
|
||||||
void JA_SetMusicPosition(float value);
|
|
||||||
float JA_GetMusicPosition();
|
|
||||||
void JA_EnableMusic(const bool value);
|
|
||||||
|
|
||||||
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length);
|
|
||||||
JA_Sound_t *JA_LoadSound(Uint8* buffer, Uint32 length);
|
|
||||||
JA_Sound_t *JA_LoadSound(const char* filename);
|
|
||||||
int JA_PlaySound(JA_Sound_t *sound, const int loop = 0, const int group=0);
|
|
||||||
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0, const int group=0);
|
|
||||||
void JA_PauseChannel(const int channel);
|
|
||||||
void JA_ResumeChannel(const int channel);
|
|
||||||
void JA_StopChannel(const int channel);
|
|
||||||
JA_Channel_state JA_GetChannelState(const int channel);
|
|
||||||
void JA_DeleteSound(JA_Sound_t *sound);
|
|
||||||
float JA_SetSoundVolume(float volume, const int group=0);
|
|
||||||
void JA_EnableSound(const bool value);
|
|
||||||
|
|
||||||
float JA_SetVolume(float volume);
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
#include "balloon.hpp"
|
#include "game/entities/balloon.hpp"
|
||||||
|
|
||||||
#include <algorithm> // Para clamp
|
#include <algorithm> // Para clamp
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <cmath> // Para fabs
|
#include <cmath> // Para fabs
|
||||||
|
|
||||||
#include "animated_sprite.hpp" // Para AnimatedSprite
|
#include "core/audio/audio.hpp" // Para Audio
|
||||||
#include "audio.hpp" // Para Audio
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||||
#include "param.hpp" // Para Param, ParamBalloon, param
|
#include "core/rendering/sprite/sprite.hpp" // Para Sprite
|
||||||
#include "sprite.hpp" // Para Sprite
|
#include "core/rendering/texture.hpp" // Para Texture
|
||||||
#include "texture.hpp" // Para Texture
|
#include "utils/param.hpp" // Para Param, ParamBalloon, param
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Balloon::Balloon(const Config& config)
|
Balloon::Balloon(const Config& config)
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "animated_sprite.hpp" // Para AnimatedSprite
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
||||||
#include "utils.hpp" // Para Circle
|
#include "utils/utils.hpp" // Para Circle
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user