Compare commits
48 Commits
cb0c3266d5
...
metal-inte
| Author | SHA1 | Date | |
|---|---|---|---|
| b3f6e2fcf0 | |||
| 7f00942517 | |||
| fb9c78eb49 | |||
| 62f65cbd5a | |||
| 057d3dcfee | |||
| c85336a4d0 | |||
| e4702e4e24 | |||
| 928335576c | |||
| fe950e6f17 | |||
| 6e81b6e60c | |||
| 74f6fe3501 | |||
| dfdb679054 | |||
| 26ed479306 | |||
| 32e9da55ef | |||
| 610083578e | |||
| 7250073d60 | |||
| dfa06870e4 | |||
| 81f3a25143 | |||
| 5aca95f3d2 | |||
| 7b193605e6 | |||
| 089a5b15d7 | |||
| e6a14ca57d | |||
| 467d609c28 | |||
| e03c798000 | |||
| 52d76b7338 | |||
| 83ee9c2649 | |||
| 43788bb01a | |||
| 58cf78e1e3 | |||
| 6bf8490776 | |||
| 8cfe28922c | |||
| 63990c75c2 | |||
| 94dca528ab | |||
| 4b6b89ceb2 | |||
| ed077c1da5 | |||
| 2819b3628e | |||
| 1e9e664012 | |||
| 0c8b39cee7 | |||
| d7b3af5ab8 | |||
| 5e5227305f | |||
| 3fc15a9512 | |||
| e774e0e8ad | |||
| a95776e6c7 | |||
| 0428ff26d5 | |||
| fc3e2deb1f | |||
| 65ca17f938 | |||
| ff2a51a507 | |||
| fe0083abd4 | |||
| 1c058694fd |
@@ -6,7 +6,16 @@ BreakBeforeBraces: Attach # Llaves en la misma línea
|
|||||||
AllowShortIfStatementsOnASingleLine: true
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
AllowShortBlocksOnASingleLine: true
|
AllowShortBlocksOnASingleLine: true
|
||||||
AllowShortFunctionsOnASingleLine: All
|
AllowShortFunctionsOnASingleLine: All
|
||||||
AlignOperands: false
|
AlignOperands: DontAlign
|
||||||
AlignAfterOpenBracket: DontAlign
|
AlignAfterOpenBracket: DontAlign
|
||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
PackConstructorInitializers: Never
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ set(APP_SOURCES
|
|||||||
source/main.cpp
|
source/main.cpp
|
||||||
source/param.cpp
|
source/param.cpp
|
||||||
source/resource.cpp
|
source/resource.cpp
|
||||||
|
source/resource_helper.cpp
|
||||||
|
source/resource_loader.cpp
|
||||||
|
source/resource_pack.cpp
|
||||||
source/screen.cpp
|
source/screen.cpp
|
||||||
source/text.cpp
|
source/text.cpp
|
||||||
source/writer.cpp
|
source/writer.cpp
|
||||||
|
|||||||
22
LICENSE
@@ -1 +1,21 @@
|
|||||||
GNU General Public License v3.0 only
|
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Coffee Crisis Arcade Edition
|
||||||
|
|
||||||
|
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
|
|
||||||
|
You are free to:
|
||||||
|
- Share — copy and redistribute the material in any medium or format
|
||||||
|
- Adapt — remix, transform, and build upon the material
|
||||||
|
|
||||||
|
Under the following terms:
|
||||||
|
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||||
|
- NonCommercial — You may not use the material for commercial purposes.
|
||||||
|
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||||
|
|
||||||
|
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||||
|
|
||||||
|
To view a copy of this license, visit:
|
||||||
|
https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
25
Makefile
@@ -71,6 +71,9 @@ APP_SOURCES := \
|
|||||||
source/path_sprite.cpp \
|
source/path_sprite.cpp \
|
||||||
source/player.cpp \
|
source/player.cpp \
|
||||||
source/resource.cpp \
|
source/resource.cpp \
|
||||||
|
source/resource_helper.cpp \
|
||||||
|
source/resource_loader.cpp \
|
||||||
|
source/resource_pack.cpp \
|
||||||
source/scoreboard.cpp \
|
source/scoreboard.cpp \
|
||||||
source/screen.cpp \
|
source/screen.cpp \
|
||||||
source/sections/credits.cpp \
|
source/sections/credits.cpp \
|
||||||
@@ -104,7 +107,7 @@ INCLUDES := -Isource -Isource/external
|
|||||||
# Variables según el sistema operativo
|
# Variables según el sistema operativo
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
FixPath = $(subst /,\\,$1)
|
FixPath = $(subst /,\\,$1)
|
||||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -DWINDOWS_BUILD
|
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows -DWINDOWS_BUILD
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
||||||
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
||||||
RM := del /Q
|
RM := del /Q
|
||||||
@@ -158,8 +161,9 @@ windows_release:
|
|||||||
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
|
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
|
||||||
powershell if (-not (Test-Path "$(RELEASE_FOLDER)")) {New-Item "$(RELEASE_FOLDER)" -ItemType Directory}
|
powershell if (-not (Test-Path "$(RELEASE_FOLDER)")) {New-Item "$(RELEASE_FOLDER)" -ItemType Directory}
|
||||||
|
|
||||||
# Copia la carpeta 'data'
|
# Copia la carpeta 'config' y el archivo 'resources.pack'
|
||||||
powershell Copy-Item -Path "data" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
powershell Copy-Item -Path "config" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
||||||
|
powershell Copy-Item -Path "resources.pack" -Destination "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia los ficheros que estan en la raíz del proyecto
|
# Copia los ficheros que estan en la raíz del proyecto
|
||||||
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
||||||
@@ -203,7 +207,8 @@ macos_release:
|
|||||||
$(MKDIR) Frameworks
|
$(MKDIR) Frameworks
|
||||||
|
|
||||||
# Copia carpetas y ficheros
|
# Copia carpetas y ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
cp -R config "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||||
cp -R release/frameworks/SDL3.xcframework Frameworks
|
cp -R release/frameworks/SDL3.xcframework Frameworks
|
||||||
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
@@ -262,7 +267,8 @@ linux_release:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"
|
cp -R config "$(RELEASE_FOLDER)"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -291,7 +297,8 @@ linux_release_desktop:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)"
|
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)"
|
||||||
|
|
||||||
# Copia ficheros del juego
|
# Copia ficheros del juego
|
||||||
cp -R data "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
cp -R config "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
|
|
||||||
@@ -391,7 +398,8 @@ raspi_release:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"
|
cp -R config "$(RELEASE_FOLDER)"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -416,7 +424,8 @@ anbernic:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"_anbernic
|
$(MKDIR) "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"_anbernic
|
cp -R config "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
|
||||||
# Compila
|
# Compila
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
|
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
|||||||
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
||||||
|
|
||||||
# Archivos de configuración del juego
|
# Archivos de configuración del juego
|
||||||
DATA|${PREFIX}/data/config/formations.txt
|
DATA|${PREFIX}/config/formations.txt
|
||||||
DATA|${PREFIX}/data/config/gamecontrollerdb.txt
|
DATA|${PREFIX}/config/gamecontrollerdb.txt
|
||||||
DATA|${PREFIX}/data/config/param_320x240.txt
|
DATA|${PREFIX}/config/param_320x240.txt
|
||||||
DATA|${PREFIX}/data/config/param_320x256.txt
|
DATA|${PREFIX}/config/param_320x256.txt
|
||||||
DATA|${PREFIX}/data/config/pools.txt
|
DATA|${PREFIX}/config/param_red.txt
|
||||||
DATA|${PREFIX}/data/config/stages.txt
|
DATA|${PREFIX}/config/pools.txt
|
||||||
DEMODATA|${PREFIX}/data/config/demo1.bin
|
DATA|${PREFIX}/config/stages.txt
|
||||||
DEMODATA|${PREFIX}/data/config/demo2.bin
|
|
||||||
|
# Archivos con los datos de la demo
|
||||||
|
DEMODATA|${PREFIX}/data/demo/demo1.bin
|
||||||
|
DEMODATA|${PREFIX}/data/demo/demo2.bin
|
||||||
|
|
||||||
# Música
|
# Música
|
||||||
MUSIC|${PREFIX}/data/music/credits.ogg
|
MUSIC|${PREFIX}/data/music/credits.ogg
|
||||||
@@ -49,6 +52,7 @@ SOUND|${PREFIX}/data/sound/notify.wav
|
|||||||
SOUND|${PREFIX}/data/sound/player_collision.wav
|
SOUND|${PREFIX}/data/sound/player_collision.wav
|
||||||
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav
|
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav
|
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav
|
||||||
|
SOUND|${PREFIX}/data/sound/service_menu_back.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_move.wav
|
SOUND|${PREFIX}/data/sound/service_menu_move.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_select.wav
|
SOUND|${PREFIX}/data/sound/service_menu_select.wav
|
||||||
SOUND|${PREFIX}/data/sound/stage_change.wav
|
SOUND|${PREFIX}/data/sound/stage_change.wav
|
||||||
@@ -56,12 +60,12 @@ SOUND|${PREFIX}/data/sound/tabe_hit.wav
|
|||||||
SOUND|${PREFIX}/data/sound/tabe.wav
|
SOUND|${PREFIX}/data/sound/tabe.wav
|
||||||
SOUND|${PREFIX}/data/sound/title.wav
|
SOUND|${PREFIX}/data/sound/title.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
|
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_brbrbr.wav
|
|
||||||
SOUND|${PREFIX}/data/sound/voice_coffee.wav
|
SOUND|${PREFIX}/data/sound/voice_coffee.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
|
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_get_ready.wav
|
SOUND|${PREFIX}/data/sound/voice_get_ready.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_no.wav
|
SOUND|${PREFIX}/data/sound/voice_no.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_power_up.wav
|
SOUND|${PREFIX}/data/sound/voice_power_up.wav
|
||||||
|
SOUND|${PREFIX}/data/sound/voice_recover.wav
|
||||||
SOUND|${PREFIX}/data/sound/voice_thankyou.wav
|
SOUND|${PREFIX}/data/sound/voice_thankyou.wav
|
||||||
SOUND|${PREFIX}/data/sound/walk.wav
|
SOUND|${PREFIX}/data/sound/walk.wav
|
||||||
|
|
||||||
@@ -171,12 +175,14 @@ BITMAP|${PREFIX}/data/gfx/player/hit.png
|
|||||||
|
|
||||||
# Fuentes de texto
|
# Fuentes de texto
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_2x.png
|
BITMAP|${PREFIX}/data/font/04b_25_2x.png
|
||||||
|
BITMAP|${PREFIX}/data/font/04b_25_2x_white.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
|
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_flat.png
|
BITMAP|${PREFIX}/data/font/04b_25_flat.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_grey.png
|
BITMAP|${PREFIX}/data/font/04b_25_grey.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_metal.png
|
BITMAP|${PREFIX}/data/font/04b_25_metal.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png
|
BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_reversed.png
|
BITMAP|${PREFIX}/data/font/04b_25_reversed.png
|
||||||
|
BITMAP|${PREFIX}/data/font/04b_25_white.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25.png
|
BITMAP|${PREFIX}/data/font/04b_25.png
|
||||||
BITMAP|${PREFIX}/data/font/8bithud.png
|
BITMAP|${PREFIX}/data/font/8bithud.png
|
||||||
BITMAP|${PREFIX}/data/font/aseprite.png
|
BITMAP|${PREFIX}/data/font/aseprite.png
|
||||||
@@ -2,26 +2,26 @@
|
|||||||
# Formato: PARAMETRO VALOR
|
# Formato: PARAMETRO VALOR
|
||||||
|
|
||||||
# --- GAME ---
|
# --- GAME ---
|
||||||
game.item_size 20 # Tamaño de los items del juego (en píxeles)
|
game.item_size 20 # Tamaño de los items del juego (en píxeles)
|
||||||
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
|
game.item_text_outline_color E0E0E0F0 # Color del outline del texto de los items (RGBA hex)
|
||||||
game.height 240 # Alto de la resolución nativa del juego (en píxeles)
|
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
|
||||||
game.play_area.rect.x 0 # Posición X de la zona jugable
|
game.height 240 # Alto de la resolución nativa del juego (en píxeles)
|
||||||
game.play_area.rect.y 0 # Posición Y de la zona jugable
|
game.play_area.rect.x 0 # Posición X de la zona jugable
|
||||||
game.play_area.rect.w 320 # Ancho de la zona jugable
|
game.play_area.rect.y 0 # Posición Y de la zona jugable
|
||||||
game.play_area.rect.h 200 # Alto de la zona jugable
|
game.play_area.rect.w 320 # Ancho de la zona jugable
|
||||||
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
|
game.play_area.rect.h 200 # Alto de la zona jugable
|
||||||
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
|
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
|
||||||
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
|
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
|
||||||
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
|
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
|
||||||
|
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
|
||||||
|
|
||||||
# --- FADE ---
|
# --- FADE ---
|
||||||
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
|
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
|
||||||
fade.num_squares_width 160 # Número de cuadrados en el eje X para el fundido
|
fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
|
||||||
fade.num_squares_height 120 # Número de cuadrados en el eje Y para el fundido
|
fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
|
||||||
fade.random_squares_delay 1 # Delay entre aparición de cuadrados aleatorios (frames)
|
fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
|
||||||
fade.random_squares_mult 500 # Multiplicador para la velocidad de aparición aleatoria
|
fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
|
||||||
fade.post_duration 80 # Duración tras el fundido (frames)
|
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
|
||||||
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
|
|
||||||
|
|
||||||
# --- SCOREBOARD ---
|
# --- SCOREBOARD ---
|
||||||
scoreboard.rect.x 0 # Posición X del marcador
|
scoreboard.rect.x 0 # Posición X del marcador
|
||||||
@@ -84,8 +84,8 @@ service_menu.window_message.title_color 6496C8FF # Color del tít
|
|||||||
service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal)
|
service_menu.window_message.text_color DCDCDCFF # Color del texto en ventanas de mensaje (RGBA hexadecimal)
|
||||||
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
|
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
|
||||||
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
|
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
|
||||||
service_menu.window_message.title_separator_spacing 10.0f # Espaciado entre título y contenido (píxeles)
|
service_menu.window_message.title_separator_spacing 20.0f # Espaciado entre título y contenido (píxeles)
|
||||||
service_menu.window_message.min_width 250.0f # Ancho mínimo de ventanas de mensaje (píxeles)
|
service_menu.window_message.min_width 200.0f # Ancho mínimo de ventanas de mensaje (píxeles)
|
||||||
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
|
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
|
||||||
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
|
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
|
||||||
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
|
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
|
||||||
@@ -110,6 +110,18 @@ tabe.min_spawn_time 2.0f # Tiempo mínimo en minutos para que aparezca el Tabe
|
|||||||
tabe.max_spawn_time 3.0f # Tiempo máximo en minutos para que aparezca el Tabe
|
tabe.max_spawn_time 3.0f # Tiempo máximo en minutos para que aparezca el Tabe
|
||||||
|
|
||||||
# --- PLAYER ---
|
# --- PLAYER ---
|
||||||
|
# Jugador 1 - Camiseta por defecto
|
||||||
|
player.default_shirt[0].darkest 028ECFFF # Tono más oscuro - bordes y contornos (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].dark 0297DBFF # Tono oscuro - sombras (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].base 029FE8FF # Tono principal - color base (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].light 03A9F4FF # Tono claro - zonas iluminadas (Jugador 1, por defecto)
|
||||||
|
|
||||||
|
# Jugador 2 - Camiseta por defecto
|
||||||
|
player.default_shirt[1].darkest 8E8E8EFF # Tono más oscuro - bordes y contornos (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].dark AEADADFF # Tono oscuro - sombras (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].base E4E4E4FF # Tono principal - color base (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].light F7F1F1FF # Tono claro - zonas iluminadas (Jugador 2, por defecto)
|
||||||
|
|
||||||
# Jugador 1 - Camiseta con 1 café
|
# Jugador 1 - Camiseta con 1 café
|
||||||
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
|
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
|
||||||
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
|
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
|
||||||
@@ -132,4 +144,8 @@ player.one_coffee_shirt[1].light 55EF8DFF # Tono claro - zonas iluminadas
|
|||||||
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
|
||||||
|
|
||||||
|
# Colores de contorno de los jugadores
|
||||||
|
player.outline_color[0] 66323FFF # Color del contorno del sprite del Jugador 1
|
||||||
|
player.outline_color[1] 422028FF # Color del contorno del sprite del Jugador 2
|
||||||
@@ -2,26 +2,26 @@
|
|||||||
# Formato: PARAMETRO VALOR
|
# Formato: PARAMETRO VALOR
|
||||||
|
|
||||||
# --- GAME ---
|
# --- GAME ---
|
||||||
game.item_size 20 # Tamaño de los items del juego (en píxeles)
|
game.item_size 20 # Tamaño de los items del juego (en píxeles)
|
||||||
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
|
game.item_text_outline_color E0E0E0F0 # Color del outline del texto de los items (RGBA hex)
|
||||||
game.height 256 # Alto de la resolución nativa del juego (en píxeles)
|
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
|
||||||
game.play_area.rect.x 0 # Posición X de la zona jugable
|
game.height 256 # Alto de la resolución nativa del juego (en píxeles)
|
||||||
game.play_area.rect.y 0 # Posición Y de la zona jugable
|
game.play_area.rect.x 0 # Posición X de la zona jugable
|
||||||
game.play_area.rect.w 320 # Ancho de la zona jugable
|
game.play_area.rect.y 0 # Posición Y de la zona jugable
|
||||||
game.play_area.rect.h 216 # Alto de la zona jugable
|
game.play_area.rect.w 320 # Ancho de la zona jugable
|
||||||
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
|
game.play_area.rect.h 216 # Alto de la zona jugable
|
||||||
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
|
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
|
||||||
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
|
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
|
||||||
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
|
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
|
||||||
|
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
|
||||||
|
|
||||||
# --- FADE ---
|
# --- FADE ---
|
||||||
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
|
fade.color 1F2B30 # Color hexadecimal para el efecto de fundido
|
||||||
fade.num_squares_width 160 # Número de cuadrados en el eje X para el fundido
|
fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
|
||||||
fade.num_squares_height 128 # Número de cuadrados en el eje Y para el fundido
|
fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
|
||||||
fade.random_squares_delay 1 # Delay entre aparición de cuadrados aleatorios (frames)
|
fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
|
||||||
fade.random_squares_mult 500 # Multiplicador para la velocidad de aparición aleatoria
|
fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
|
||||||
fade.post_duration 80 # Duración tras el fundido (frames)
|
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
|
||||||
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
|
|
||||||
|
|
||||||
# --- SCOREBOARD ---
|
# --- SCOREBOARD ---
|
||||||
scoreboard.rect.x 0 # Posición X del marcador
|
scoreboard.rect.x 0 # Posición X del marcador
|
||||||
@@ -109,6 +109,18 @@ tabe.min_spawn_time 2.0f # Tiempo mínimo en segundos para que aparezca el Ta
|
|||||||
tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe
|
tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe
|
||||||
|
|
||||||
# --- PLAYER ---
|
# --- PLAYER ---
|
||||||
|
# Jugador 1 - Camiseta por defecto
|
||||||
|
player.default_shirt[0].darkest 028ECFFF # Tono más oscuro - bordes y contornos (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].dark 0297DBFF # Tono oscuro - sombras (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].base 029FE8FF # Tono principal - color base (Jugador 1, por defecto)
|
||||||
|
player.default_shirt[0].light 03A9F4FF # Tono claro - zonas iluminadas (Jugador 1, por defecto)
|
||||||
|
|
||||||
|
# Jugador 2 - Camiseta por defecto
|
||||||
|
player.default_shirt[1].darkest 8E8E8EFF # Tono más oscuro - bordes y contornos (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].dark AEADADFF # Tono oscuro - sombras (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].base E4E4E4FF # Tono principal - color base (Jugador 2, por defecto)
|
||||||
|
player.default_shirt[1].light F7F1F1FF # Tono claro - zonas iluminadas (Jugador 2, por defecto)
|
||||||
|
|
||||||
# Jugador 1 - Camiseta con 1 café
|
# Jugador 1 - Camiseta con 1 café
|
||||||
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
|
player.one_coffee_shirt[0].darkest 3D9C70FF # Tono más oscuro - bordes y contornos (Jugador 1, 1 café)
|
||||||
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
|
player.one_coffee_shirt[0].dark 4FA370FF # Tono oscuro - sombras (Jugador 1, 1 café)
|
||||||
@@ -131,4 +143,8 @@ player.one_coffee_shirt[1].light 55EF8DFF # Tono claro - zonas iluminadas
|
|||||||
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].darkest E08500FF # Tono más oscuro - bordes y contornos (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].dark FA7D00FF # Tono oscuro - sombras (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].base FAA200FF # Tono principal - color base (Jugador 2, 2 cafés)
|
||||||
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
|
player.two_coffee_shirt[1].light FA8500FF # Tono claro - zonas iluminadas (Jugador 2, 2 cafés)
|
||||||
|
|
||||||
|
# Colores de contorno de los jugadores
|
||||||
|
player.outline_color[0] 66323FFF # Color del contorno del sprite del Jugador 1
|
||||||
|
player.outline_color[1] 422028FF # Color del contorno del sprite del Jugador 2
|
||||||
150
config/param_red.txt
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# Coffee Crisis Arcade Edition - Fichero de parametros - RED THEME
|
||||||
|
# Formato: PARAMETRO VALOR
|
||||||
|
|
||||||
|
# --- GAME ---
|
||||||
|
game.item_size 20 # Tamaño de los items del juego (en píxeles)
|
||||||
|
game.item_text_outline_color FFB8B8F0 # Color del outline del texto de los items (RGBA hex) - Rojo claro
|
||||||
|
game.width 320 # Ancho de la resolución nativa del juego (en píxeles)
|
||||||
|
game.height 256 # Alto de la resolución nativa del juego (en píxeles)
|
||||||
|
game.play_area.rect.x 0 # Posición X de la zona jugable
|
||||||
|
game.play_area.rect.y 0 # Posición Y de la zona jugable
|
||||||
|
game.play_area.rect.w 320 # Ancho de la zona jugable
|
||||||
|
game.play_area.rect.h 216 # Alto de la zona jugable
|
||||||
|
game.name_entry_idle_time 10 # Segundos para introducir el nombre al finalizar la partida si no se pulsa nada
|
||||||
|
game.name_entry_total_time 60 # Segundos totales para introducir el nombre al finalizar la partida
|
||||||
|
game.hit_stop false # Indica si debe haber un paro cuando el jugador es golpeado por un globo
|
||||||
|
game.hit_stop_ms 500 # Cantidad de milisegundos que dura el hit_stop
|
||||||
|
|
||||||
|
# --- FADE ---
|
||||||
|
fade.color 5C1F1F # Color hexadecimal para el efecto de fundido - Rojo oscuro
|
||||||
|
fade.num_squares_width 64 # Número de cuadrados en el eje X para el fundido
|
||||||
|
fade.num_squares_height 48 # Número de cuadrados en el eje Y para el fundido
|
||||||
|
fade.random_squares_duration_ms 1200 # Duración del fade en milisegundos
|
||||||
|
fade.post_duration_ms 500 # Duración tras el fundido en milisegundos
|
||||||
|
fade.venetian_size 12 # Tamaño de las bandas para el efecto veneciano (en píxeles)
|
||||||
|
|
||||||
|
# --- SCOREBOARD ---
|
||||||
|
scoreboard.rect.x 0 # Posición X del marcador
|
||||||
|
scoreboard.rect.y 216 # Posición Y del marcador
|
||||||
|
scoreboard.rect.w 320 # Ancho del marcador
|
||||||
|
scoreboard.rect.h 40 # Alto del marcador
|
||||||
|
scoreboard.separator_autocolor true # ¿El separador usa color automático?
|
||||||
|
scoreboard.separator_color 2B0D0D # Color del separador - Rojo muy oscuro
|
||||||
|
scoreboard.easy_color 8B4A2F # Color para la dificultad fácil - Marrón rojizo
|
||||||
|
scoreboard.normal_color 6B2F2F # Color para la dificultad normal - Rojo medio
|
||||||
|
scoreboard.hard_color A73030 # Color para la dificultad difícil - Rojo fuerte
|
||||||
|
scoreboard.text_autocolor true # ¿El texto usa color automático?
|
||||||
|
scoreboard.text_color1 FFE6E6 # Color principal del texto del marcador - Blanco rosado
|
||||||
|
scoreboard.text_color2 FFE6E6 # Color secundario del texto del marcador - Blanco rosado
|
||||||
|
scoreboard.skip_countdown_value 8 # Valor para saltar la cuenta atrás (segundos)
|
||||||
|
|
||||||
|
# --- TITLE ---
|
||||||
|
title.press_start_position 180 # Posición Y del texto "Press Start"
|
||||||
|
title.title_duration 800 # Duración de la pantalla de título (frames)
|
||||||
|
title.arcade_edition_position 123 # Posición Y del subtítulo "Arcade Edition"
|
||||||
|
title.title_c_c_position 80 # Posición Y del título principal
|
||||||
|
title.bg_color 8B4A3A # Color de fondo en la sección titulo - Marrón rojizo
|
||||||
|
|
||||||
|
# --- BACKGROUND ---
|
||||||
|
background.attenuate_color FF4A3A40 # Color de atenuación del fondo (RGBA hexadecimal) - Blanco rosado
|
||||||
|
|
||||||
|
# --- BALLOONS ---
|
||||||
|
balloon.settings[0].vel 2.75f # Velocidad inicial del globo 1
|
||||||
|
balloon.settings[0].grav 0.09f # Gravedad aplicada al globo 1
|
||||||
|
balloon.settings[1].vel 3.70f # Velocidad inicial del globo 2
|
||||||
|
balloon.settings[1].grav 0.10f # Gravedad aplicada al globo 2
|
||||||
|
balloon.settings[2].vel 4.70f # Velocidad inicial del globo 3
|
||||||
|
balloon.settings[2].grav 0.10f # Gravedad aplicada al globo 3
|
||||||
|
balloon.settings[3].vel 5.45f # Velocidad inicial del globo 4
|
||||||
|
balloon.settings[3].grav 0.10f # Gravedad aplicada al globo 4
|
||||||
|
|
||||||
|
balloon.color[0] orange # Color de creación del globo normal
|
||||||
|
balloon.color[1] red # Color del globo normal
|
||||||
|
balloon.color[2] blue # Color de creación del globo que rebota
|
||||||
|
balloon.color[3] green # Color del globo que rebota
|
||||||
|
|
||||||
|
balloon.bouncing_sound false # Indica si los globos hacer sonido al rebotar
|
||||||
|
|
||||||
|
# --- NOTIFICATION ---
|
||||||
|
notification.pos_v TOP # Posición vertical de la notificación (TOP/BOTTOM)
|
||||||
|
notification.pos_h LEFT # Posición horizontal de la notificación (LEFT/RIGHT)
|
||||||
|
notification.sound false # ¿La notificación reproduce sonido?
|
||||||
|
notification.color 4D1A1A # Color de fondo de la notificación - Rojo oscuro
|
||||||
|
|
||||||
|
# --- SERVICE MENU ---
|
||||||
|
service_menu.title_color FF9966 # Color del título del menú de servicio - Naranja claro
|
||||||
|
service_menu.text_color FFE6E6 # Color del texto del menú de servicio - Blanco rosado
|
||||||
|
service_menu.selected_color FFB366 # Color de la opción seleccionada - Naranja dorado
|
||||||
|
service_menu.bg_color 331A0AF5 # Color de fondo del menú de servicio - Marrón rojizo oscuro con alpha
|
||||||
|
service_menu.drop_shadow false # ¿El menú de servicio tiene sombra?
|
||||||
|
|
||||||
|
service_menu.window_message.bg_color 4D1A1AF0 # Color de fondo de ventanas - Rojo oscuro con alpha
|
||||||
|
service_menu.window_message.border_color CC6633FF # Color del borde de ventanas - Naranja rojizo
|
||||||
|
service_menu.window_message.title_color CC6633FF # Color del título en ventanas - Naranja rojizo
|
||||||
|
service_menu.window_message.text_color FFE6E6FF # Color del texto en ventanas - Blanco rosado
|
||||||
|
service_menu.window_message.padding 15.0f # Espaciado interno de ventanas de mensaje (píxeles)
|
||||||
|
service_menu.window_message.line_spacing 5.0f # Espaciado entre líneas de texto (píxeles)
|
||||||
|
service_menu.window_message.title_separator_spacing 20.0f # Espaciado entre título y contenido (píxeles)
|
||||||
|
service_menu.window_message.min_width 200.0f # Ancho mínimo de ventanas de mensaje (píxeles)
|
||||||
|
service_menu.window_message.min_height 32.0f # Alto mínimo de ventanas de mensaje (píxeles)
|
||||||
|
service_menu.window_message.max_width_ratio 0.8f # Ratio máximo de ancho respecto a pantalla (0.0-1.0)
|
||||||
|
service_menu.window_message.max_height_ratio 0.8f # Ratio máximo de alto respecto a pantalla (0.0-1.0)
|
||||||
|
service_menu.window_message.text_safety_margin 15.0f # Margen de seguridad para el texto (píxeles)
|
||||||
|
service_menu.window_message.animation_duration 0.3f # Duración de animaciones de ventanas (segundos)
|
||||||
|
|
||||||
|
# --- INTRO ---
|
||||||
|
intro.bg_color B8664D # Color de fondo de la intro - Naranja tierra
|
||||||
|
intro.card_color FFE6CC # Color de las tarjetas en la intro - Crema rojizo
|
||||||
|
intro.shadow_color 66000080 # Color de la sombra de las tarjetas - Rojo muy oscuro con alpha
|
||||||
|
intro.text_distance_from_bottom 48 # Posición del texto desde la parte inferior
|
||||||
|
|
||||||
|
# --- DEBUG ---
|
||||||
|
debug.color FF6666 # Color para elementos de depuración - Rojo claro
|
||||||
|
|
||||||
|
# --- RESOURCE ---
|
||||||
|
resource.color FFE6E6 # Color de recursos - Blanco rosado
|
||||||
|
|
||||||
|
# --- TABE ---
|
||||||
|
tabe.min_spawn_time 2.0f # Tiempo mínimo en segundos para que aparezca el Tabe
|
||||||
|
tabe.max_spawn_time 3.0f # Tiempo máximo en segundos para que aparezca el Tabe
|
||||||
|
|
||||||
|
# --- PLAYER ---
|
||||||
|
# Jugador 1 - Camiseta por defecto (tonos rojos)
|
||||||
|
player.default_shirt[0].darkest B33030FF # Tono más oscuro - Rojo fuerte
|
||||||
|
player.default_shirt[0].dark CC4040FF # Tono oscuro - Rojo medio
|
||||||
|
player.default_shirt[0].base E65050FF # Tono principal - Rojo claro
|
||||||
|
player.default_shirt[0].light FF6666FF # Tono claro - Rojo muy claro
|
||||||
|
|
||||||
|
# Jugador 2 - Camiseta por defecto (tonos naranjas)
|
||||||
|
player.default_shirt[1].darkest B36030FF # Tono más oscuro - Naranja oscuro
|
||||||
|
player.default_shirt[1].dark CC7540FF # Tono oscuro - Naranja medio
|
||||||
|
player.default_shirt[1].base E68A50FF # Tono principal - Naranja claro
|
||||||
|
player.default_shirt[1].light FF9966FF # Tono claro - Naranja muy claro
|
||||||
|
|
||||||
|
# Jugador 1 - Camiseta con 1 café (tonos rojizos más intensos)
|
||||||
|
player.one_coffee_shirt[0].darkest 8B2635FF # Tono más oscuro - Rojo granate
|
||||||
|
player.one_coffee_shirt[0].dark A53040FF # Tono oscuro - Rojo granate claro
|
||||||
|
player.one_coffee_shirt[0].base BF3A50FF # Tono principal - Rojo vibrante
|
||||||
|
player.one_coffee_shirt[0].light D94460FF # Tono claro - Rojo vibrante claro
|
||||||
|
|
||||||
|
# Jugador 1 - Camiseta con 2 cafés (tonos naranja dorado)
|
||||||
|
player.two_coffee_shirt[0].darkest CC6600FF # Tono más oscuro - Naranja dorado oscuro
|
||||||
|
player.two_coffee_shirt[0].dark E6750AFF # Tono oscuro - Naranja dorado
|
||||||
|
player.two_coffee_shirt[0].base FF8514FF # Tono principal - Naranja dorado claro
|
||||||
|
player.two_coffee_shirt[0].light FF991EFF # Tono claro - Naranja dorado muy claro
|
||||||
|
|
||||||
|
# Jugador 2 - Camiseta con 1 café (tonos terracotas)
|
||||||
|
player.one_coffee_shirt[1].darkest A0472DFF # Tono más oscuro - Terracota oscuro
|
||||||
|
player.one_coffee_shirt[1].dark B8553AFF # Tono oscuro - Terracota medio
|
||||||
|
player.one_coffee_shirt[1].base D06347FF # Tono principal - Terracota claro
|
||||||
|
player.one_coffee_shirt[1].light E87154FF # Tono claro - Terracota muy claro
|
||||||
|
|
||||||
|
# Jugador 2 - Camiseta con 2 cafés (tonos naranjas cálidos)
|
||||||
|
player.two_coffee_shirt[1].darkest CC5500FF # Tono más oscuro - Naranja intenso
|
||||||
|
player.two_coffee_shirt[1].dark E66600FF # Tono oscuro - Naranja fuerte
|
||||||
|
player.two_coffee_shirt[1].base FF7700FF # Tono principal - Naranja brillante
|
||||||
|
player.two_coffee_shirt[1].light FF8800FF # Tono claro - Naranja muy brillante
|
||||||
|
|
||||||
|
# Colores de contorno de los jugadores
|
||||||
|
player.outline_color[0] 994D33FF # Color del contorno del Jugador 1 - Marrón rojizo
|
||||||
|
player.outline_color[1] 664433FF # Color del contorno del Jugador 2 - Marrón tierra
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
data/font/04b_25_2x_white.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
data/font/04b_25_white.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 882 B |
BIN
data/sound/click-2.wav
Normal file
BIN
data/sound/click-3.wav
Normal file
BIN
data/sound/service_menu_back.wav
Normal file
BIN
data/sound/voice_recover.wav
Normal file
BIN
define_buttons.o
@@ -9,22 +9,40 @@
|
|||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
|
|
||||||
#include "texture.h" // Para Texture
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "utils.h" // Para printWithDots
|
#include "texture.h" // Para Texture
|
||||||
|
#include "utils.h" // Para printWithDots
|
||||||
|
|
||||||
// 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 {
|
||||||
std::ifstream file(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!file) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::istringstream stream;
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
bool using_resource_data = false;
|
||||||
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file);
|
||||||
|
|
||||||
printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
printWithDots("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(file, line)) {
|
while (std::getline(input_stream, line)) {
|
||||||
if (!line.empty()) {
|
if (!line.empty()) {
|
||||||
buffer.push_back(line);
|
buffer.push_back(line);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ class AnimatedSprite : public MovingSprite {
|
|||||||
// --- Constructores y destructor ---
|
// --- Constructores y destructor ---
|
||||||
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
|
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
|
||||||
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
|
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
|
||||||
explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(std::move(texture)) {}
|
explicit AnimatedSprite(std::shared_ptr<Texture> texture)
|
||||||
|
: MovingSprite(std::move(texture)) {}
|
||||||
~AnimatedSprite() override = default;
|
~AnimatedSprite() override = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
|
|
||||||
#include "utils.h" // Para getFileName
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
#include "utils.h" // Para getFileName
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Asset *Asset::instance = nullptr;
|
Asset *Asset::instance = nullptr;
|
||||||
@@ -139,6 +140,17 @@ auto Asset::get(const std::string &filename) const -> std::string {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Carga datos del archivo usando ResourceHelper
|
||||||
|
auto Asset::loadData(const std::string &filename) const -> std::vector<uint8_t> {
|
||||||
|
auto it = file_list_.find(filename);
|
||||||
|
if (it != file_list_.end()) {
|
||||||
|
return ResourceHelper::loadFile(it->second.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found for data loading", filename.c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Verifica si un recurso existe
|
// Verifica si un recurso existe
|
||||||
auto Asset::exists(const std::string &filename) const -> bool {
|
auto Asset::exists(const std::string &filename) const -> bool {
|
||||||
return file_list_.find(filename) != file_list_.end();
|
return file_list_.find(filename) != file_list_.end();
|
||||||
@@ -194,9 +206,16 @@ auto Asset::check() const -> bool {
|
|||||||
|
|
||||||
// Comprueba que existe un fichero
|
// Comprueba que existe un fichero
|
||||||
auto Asset::checkFile(const std::string &path) -> bool {
|
auto Asset::checkFile(const std::string &path) -> bool {
|
||||||
std::ifstream file(path);
|
// Intentar primero con ResourceHelper
|
||||||
bool success = file.good();
|
auto data = ResourceHelper::loadFile(path);
|
||||||
file.close();
|
bool success = !data.empty();
|
||||||
|
|
||||||
|
// Si no se encuentra en el pack, intentar con filesystem directo
|
||||||
|
if (!success) {
|
||||||
|
std::ifstream file(path);
|
||||||
|
success = file.good();
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // Para uint8_t
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <unordered_map> // Para unordered_map
|
#include <unordered_map> // Para unordered_map
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
@@ -33,6 +34,7 @@ class Asset {
|
|||||||
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
|
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
|
||||||
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
|
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
|
||||||
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
|
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
|
||||||
|
[[nodiscard]] auto loadData(const std::string &filename) const -> std::vector<uint8_t>; // Carga datos del archivo
|
||||||
[[nodiscard]] auto check() const -> bool;
|
[[nodiscard]] auto check() const -> bool;
|
||||||
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
||||||
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
|
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
|
||||||
@@ -45,7 +47,9 @@ class Asset {
|
|||||||
bool required; // Indica si el archivo es obligatorio
|
bool required; // Indica si el archivo es obligatorio
|
||||||
|
|
||||||
Item(std::string path, Type asset_type, bool is_required)
|
Item(std::string path, Type asset_type, bool is_required)
|
||||||
: file(std::move(path)), type(asset_type), required(is_required) {}
|
: file(std::move(path)),
|
||||||
|
type(asset_type),
|
||||||
|
required(is_required) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables internas ---
|
// --- Variables internas ---
|
||||||
|
|||||||
106
source/asset_integrated.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include "asset_integrated.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
bool AssetIntegrated::resource_pack_enabled_ = false;
|
||||||
|
|
||||||
|
void AssetIntegrated::initWithResourcePack(const std::string &executable_path,
|
||||||
|
const std::string &resource_pack_path) {
|
||||||
|
// Inicializar Asset normal
|
||||||
|
Asset::init(executable_path);
|
||||||
|
|
||||||
|
// Inicializar ResourceLoader
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
if (loader.initialize(resource_pack_path, true)) {
|
||||||
|
resource_pack_enabled_ = true;
|
||||||
|
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << std::endl;
|
||||||
|
} else {
|
||||||
|
resource_pack_enabled_ = false;
|
||||||
|
std::cout << "Asset system initialized in fallback mode (filesystem)" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
|
||||||
|
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
|
||||||
|
// Intentar cargar del pack de recursos
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
|
||||||
|
// Convertir ruta completa a ruta relativa para el pack
|
||||||
|
std::string relativePath = filename;
|
||||||
|
|
||||||
|
// Si la ruta contiene "data/", extraer la parte relativa
|
||||||
|
size_t dataPos = filename.find("data/");
|
||||||
|
if (dataPos != std::string::npos) {
|
||||||
|
relativePath = filename.substr(dataPos + 5); // +5 para saltar "data/"
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = loader.loadResource(relativePath);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: cargar del filesystem
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char *>(data.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetIntegrated::fileExists(const std::string &filename) const {
|
||||||
|
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
|
||||||
|
// Convertir ruta completa a ruta relativa para el pack
|
||||||
|
std::string relativePath = filename;
|
||||||
|
size_t dataPos = filename.find("data/");
|
||||||
|
if (dataPos != std::string::npos) {
|
||||||
|
relativePath = filename.substr(dataPos + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader.resourceExists(relativePath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar en filesystem
|
||||||
|
return std::filesystem::exists(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AssetIntegrated::getSystemPath(const std::string &filename) const {
|
||||||
|
// Los archivos de sistema/config siempre van al filesystem
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetIntegrated::shouldUseResourcePack(const std::string &filepath) const {
|
||||||
|
// Los archivos que NO van al pack:
|
||||||
|
// - Archivos de config/ (ahora están fuera de data/)
|
||||||
|
// - Archivos con absolute=true en assets.txt
|
||||||
|
// - Archivos de sistema (${SYSTEM_FOLDER})
|
||||||
|
|
||||||
|
if (filepath.find("/config/") != std::string::npos ||
|
||||||
|
filepath.find("config/") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filepath.find("/data/") != std::string::npos ||
|
||||||
|
filepath.find("data/") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
29
source/asset_integrated.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "asset.h"
|
||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
// Extensión de Asset que integra ResourceLoader
|
||||||
|
class AssetIntegrated : public Asset {
|
||||||
|
public:
|
||||||
|
// Inicializa Asset con ResourceLoader
|
||||||
|
static void initWithResourcePack(const std::string &executable_path,
|
||||||
|
const std::string &resource_pack_path = "resources.pack");
|
||||||
|
|
||||||
|
// Carga un archivo usando ResourceLoader como primera opción
|
||||||
|
std::vector<uint8_t> loadFile(const std::string &filename);
|
||||||
|
|
||||||
|
// Verifica si un archivo existe (pack o filesystem)
|
||||||
|
bool fileExists(const std::string &filename) const;
|
||||||
|
|
||||||
|
// Obtiene la ruta completa para archivos del sistema/config
|
||||||
|
std::string getSystemPath(const std::string &filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool resource_pack_enabled_;
|
||||||
|
|
||||||
|
// Determina si un archivo debe cargarse del pack o del filesystem
|
||||||
|
bool shouldUseResourcePack(const std::string &filepath) const;
|
||||||
|
};
|
||||||
@@ -100,7 +100,7 @@ void Audio::stopAllSounds() const {
|
|||||||
|
|
||||||
// Realiza un fundido de salida de la música
|
// Realiza un fundido de salida de la música
|
||||||
void Audio::fadeOutMusic(int milliseconds) const {
|
void Audio::fadeOutMusic(int milliseconds) const {
|
||||||
if (music_enabled_) {
|
if (music_enabled_ && music_.state == MusicState::PLAYING) {
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
JA_FadeOutMusic(milliseconds);
|
JA_FadeOutMusic(milliseconds);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -75,11 +75,15 @@ class Audio {
|
|||||||
bool loop; // Indica si la última pista de música se debe reproducir en bucle
|
bool loop; // Indica si la última pista de música se debe reproducir en bucle
|
||||||
|
|
||||||
// Constructor para inicializar la música con valores predeterminados
|
// Constructor para inicializar la música con valores predeterminados
|
||||||
Music() : state(MusicState::STOPPED), loop(false) {}
|
Music()
|
||||||
|
: state(MusicState::STOPPED),
|
||||||
|
loop(false) {}
|
||||||
|
|
||||||
// Constructor para inicializar con valores específicos
|
// Constructor para inicializar con valores específicos
|
||||||
Music(MusicState init_state, std::string init_name, bool init_loop)
|
Music(MusicState init_state, std::string init_name, bool init_loop)
|
||||||
: state(init_state), name(std::move(init_name)), loop(init_loop) {}
|
: state(init_state),
|
||||||
|
name(std::move(init_name)),
|
||||||
|
loop(init_loop) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
|
|||||||
@@ -11,20 +11,21 @@
|
|||||||
#include "texture.h" // Para Texture
|
#include "texture.h" // Para Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
|
Balloon::Balloon(const Config& config)
|
||||||
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
|
: sprite_(std::make_unique<AnimatedSprite>(config.texture, config.animation)),
|
||||||
x_(x),
|
x_(config.x),
|
||||||
y_(y),
|
y_(config.y),
|
||||||
vx_(vel_x),
|
vx_(config.vel_x),
|
||||||
being_created_(creation_timer > 0),
|
being_created_(config.creation_counter > 0),
|
||||||
invulnerable_(creation_timer > 0),
|
invulnerable_(config.creation_counter > 0),
|
||||||
stopped_(creation_timer > 0),
|
stopped_(config.creation_counter > 0),
|
||||||
creation_counter_(creation_timer),
|
creation_counter_(config.creation_counter),
|
||||||
creation_counter_ini_(creation_timer),
|
creation_counter_ini_(config.creation_counter),
|
||||||
type_(type),
|
type_(config.type),
|
||||||
size_(size),
|
size_(config.size),
|
||||||
speed_(speed),
|
speed_(config.speed),
|
||||||
play_area_(play_area) {
|
play_area_(config.play_area),
|
||||||
|
sound_(config.sound) {
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case Type::BALLOON: {
|
case Type::BALLOON: {
|
||||||
vy_ = 0;
|
vy_ = 0;
|
||||||
@@ -37,9 +38,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
power_ = POWER.at(INDEX);
|
power_ = POWER.at(INDEX);
|
||||||
menace_ = MENACE.at(INDEX);
|
menace_ = MENACE.at(INDEX);
|
||||||
score_ = SCORE.at(INDEX);
|
score_ = SCORE.at(INDEX);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(INDEX);
|
sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
|
||||||
popping_sound_ = POPPING_SOUND.at(INDEX);
|
sound_.popping_file = POPPING_SOUND.at(INDEX);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,17 +52,16 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
power_ = POWER.at(INDEX);
|
power_ = POWER.at(INDEX);
|
||||||
menace_ = MENACE.at(INDEX);
|
menace_ = MENACE.at(INDEX);
|
||||||
score_ = SCORE.at(INDEX);
|
score_ = SCORE.at(INDEX);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(INDEX);
|
sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
|
||||||
popping_sound_ = POPPING_SOUND.at(INDEX);
|
sound_.popping_file = POPPING_SOUND.at(INDEX);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type::POWERBALL: {
|
case Type::POWERBALL: {
|
||||||
constexpr int INDEX = 3;
|
constexpr int INDEX = 3;
|
||||||
h_ = w_ = WIDTH.at(4);
|
h_ = w_ = WIDTH.at(4);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(3);
|
sound_.bouncing_file = BOUNCING_SOUND.at(3);
|
||||||
popping_sound_ = "power_ball_explosion.wav";
|
sound_.popping_file = "power_ball_explosion.wav";
|
||||||
power_ = score_ = menace_ = 0;
|
power_ = score_ = menace_ = 0;
|
||||||
|
|
||||||
vy_ = 0;
|
vy_ = 0;
|
||||||
@@ -70,9 +69,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
gravity_ = param.balloon.settings.at(INDEX).grav;
|
gravity_ = param.balloon.settings.at(INDEX).grav;
|
||||||
default_vy_ = param.balloon.settings.at(INDEX).vel;
|
default_vy_ = param.balloon.settings.at(INDEX).vel;
|
||||||
|
|
||||||
sprite_->setRotate(creation_timer <= 0);
|
sprite_->setRotate(config.creation_counter <= 0);
|
||||||
sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0);
|
sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,8 +231,14 @@ void Balloon::applyGravity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Balloon::playBouncingSound() {
|
void Balloon::playBouncingSound() {
|
||||||
if (bouncing_sound_enabled_) {
|
if (sound_.enabled && sound_.bouncing_enabled) {
|
||||||
playSound(bouncing_sound_);
|
Audio::get()->playSound(sound_.bouncing_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Balloon::playPoppingSound() {
|
||||||
|
if (sound_.enabled && sound_.poping_enabled) {
|
||||||
|
Audio::get()->playSound(sound_.popping_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,23 +372,8 @@ void Balloon::useNormalColor() {
|
|||||||
setAnimation();
|
setAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reproduce sonido
|
|
||||||
void Balloon::playSound(const std::string &name) const {
|
|
||||||
if (!sound_enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto *audio_ = Audio::get();
|
|
||||||
audio_->playSound(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explota el globo
|
// Explota el globo
|
||||||
void Balloon::pop(bool should_sound) {
|
void Balloon::pop(bool should_sound) {
|
||||||
if (should_sound) {
|
if (should_sound) { playPoppingSound(); }
|
||||||
if (poping_sound_enabled_) {
|
|
||||||
playSound(popping_sound_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled_ = false;
|
enabled_ = false;
|
||||||
}
|
}
|
||||||
140
source/balloon.h
@@ -25,10 +25,16 @@ class Balloon {
|
|||||||
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
|
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
|
||||||
|
|
||||||
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
|
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
|
||||||
"balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"};
|
"balloon_bounce0.wav",
|
||||||
|
"balloon_bounce1.wav",
|
||||||
|
"balloon_bounce2.wav",
|
||||||
|
"balloon_bounce3.wav"};
|
||||||
|
|
||||||
static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
|
static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
|
||||||
"balloon_pop0.wav", "balloon_pop1.wav", "balloon_pop2.wav", "balloon_pop3.wav"};
|
"balloon_pop0.wav",
|
||||||
|
"balloon_pop1.wav",
|
||||||
|
"balloon_pop2.wav",
|
||||||
|
"balloon_pop3.wav"};
|
||||||
|
|
||||||
static constexpr float VELX_POSITIVE = 0.7F;
|
static constexpr float VELX_POSITIVE = 0.7F;
|
||||||
static constexpr float VELX_NEGATIVE = -0.7F;
|
static constexpr float VELX_NEGATIVE = -0.7F;
|
||||||
@@ -52,18 +58,32 @@ class Balloon {
|
|||||||
POWERBALL = 2, // Globo de poder
|
POWERBALL = 2, // Globo de poder
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Estructura para manejo de sonido ---
|
||||||
|
struct Sound {
|
||||||
|
std::string bouncing_file; // Archivo de sonido al rebotar
|
||||||
|
std::string popping_file; // Archivo de sonido al explotar
|
||||||
|
bool bouncing_enabled = false; // Si debe sonar el globo al rebotar
|
||||||
|
bool poping_enabled = true; // Si debe sonar el globo al explotar
|
||||||
|
bool enabled = true; // Indica si los globos deben hacer algun sonido
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Estructura de configuración para inicialización ---
|
||||||
|
struct Config {
|
||||||
|
float x = 0.0F;
|
||||||
|
float y = 0.0F;
|
||||||
|
Type type = Type::BALLOON;
|
||||||
|
Size size = Size::EXTRALARGE;
|
||||||
|
float vel_x = VELX_POSITIVE;
|
||||||
|
float speed = SPEED.at(0);
|
||||||
|
Uint16 creation_counter = 0;
|
||||||
|
SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||||
|
std::shared_ptr<Texture> texture = nullptr;
|
||||||
|
std::vector<std::string> animation;
|
||||||
|
Sound sound;
|
||||||
|
};
|
||||||
|
|
||||||
// --- Constructores y destructor ---
|
// --- Constructores y destructor ---
|
||||||
Balloon(
|
Balloon(const Config& config);
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
Type type,
|
|
||||||
Size size,
|
|
||||||
float vel_x,
|
|
||||||
float speed,
|
|
||||||
Uint16 creation_timer,
|
|
||||||
SDL_FRect play_area,
|
|
||||||
const std::shared_ptr<Texture>& texture,
|
|
||||||
const std::vector<std::string>& animation);
|
|
||||||
~Balloon() = default;
|
~Balloon() = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
@@ -102,9 +122,9 @@ class Balloon {
|
|||||||
void setVelY(float vel_y) { vy_ = vel_y; }
|
void setVelY(float vel_y) { vy_ = vel_y; }
|
||||||
void setSpeed(float speed) { speed_ = speed; }
|
void setSpeed(float speed) { speed_ = speed; }
|
||||||
void setInvulnerable(bool value) { invulnerable_ = value; }
|
void setInvulnerable(bool value) { invulnerable_ = value; }
|
||||||
void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; }
|
void setBouncingSound(bool value) { sound_.bouncing_enabled = value; }
|
||||||
void setPoppingSound(bool value) { poping_sound_enabled_ = value; }
|
void setPoppingSound(bool value) { sound_.poping_enabled = value; }
|
||||||
void setSound(bool value) { sound_enabled_ = value; }
|
void setSound(bool value) { sound_.enabled = value; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Estructura para el efecto de rebote ---
|
// --- Estructura para el efecto de rebote ---
|
||||||
@@ -114,10 +134,28 @@ class Balloon {
|
|||||||
|
|
||||||
// Tablas de valores predefinidos para el efecto de rebote
|
// Tablas de valores predefinidos para el efecto de rebote
|
||||||
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
|
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
|
||||||
1.10F, 1.05F, 1.00F, 0.95F, 0.90F, 0.95F, 1.00F, 1.02F, 1.05F, 1.02F};
|
1.10F,
|
||||||
|
1.05F,
|
||||||
|
1.00F,
|
||||||
|
0.95F,
|
||||||
|
0.90F,
|
||||||
|
0.95F,
|
||||||
|
1.00F,
|
||||||
|
1.02F,
|
||||||
|
1.05F,
|
||||||
|
1.02F};
|
||||||
|
|
||||||
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
|
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
|
||||||
0.90F, 0.95F, 1.00F, 1.05F, 1.10F, 1.05F, 1.00F, 0.98F, 0.95F, 0.98F};
|
0.90F,
|
||||||
|
0.95F,
|
||||||
|
1.00F,
|
||||||
|
1.05F,
|
||||||
|
1.10F,
|
||||||
|
1.05F,
|
||||||
|
1.00F,
|
||||||
|
0.98F,
|
||||||
|
0.95F,
|
||||||
|
0.98F};
|
||||||
|
|
||||||
// Estado del efecto
|
// Estado del efecto
|
||||||
bool enabled_ = false; // Si el efecto está activo
|
bool enabled_ = false; // Si el efecto está activo
|
||||||
@@ -203,47 +241,43 @@ class Balloon {
|
|||||||
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
|
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
|
||||||
|
|
||||||
// --- Variables de estado y físicas ---
|
// --- Variables de estado y físicas ---
|
||||||
float x_; // Posición X
|
float x_; // Posición X
|
||||||
float y_; // Posición Y
|
float y_; // Posición Y
|
||||||
float w_; // Ancho
|
float w_; // Ancho
|
||||||
float h_; // Alto
|
float h_; // Alto
|
||||||
float vx_; // Velocidad X
|
float vx_; // Velocidad X
|
||||||
float vy_; // Velocidad Y
|
float vy_; // Velocidad Y
|
||||||
float gravity_; // Aceleración en Y
|
float gravity_; // Aceleración en Y
|
||||||
float default_vy_; // Velocidad inicial al rebotar
|
float default_vy_; // Velocidad inicial al rebotar
|
||||||
float max_vy_; // Máxima velocidad en Y
|
float max_vy_; // Máxima velocidad en Y
|
||||||
bool being_created_; // Si el globo se está creando
|
bool being_created_; // Si el globo se está creando
|
||||||
bool enabled_ = true; // Si el globo está activo
|
bool enabled_ = true; // Si el globo está activo
|
||||||
bool invulnerable_; // Si el globo es invulnerable
|
bool invulnerable_; // Si el globo es invulnerable
|
||||||
bool stopped_; // Si el globo está parado
|
bool stopped_; // Si el globo está parado
|
||||||
bool use_reversed_colors_ = false; // Si se usa el color alternativo
|
bool use_reversed_colors_ = false; // Si se usa el color alternativo
|
||||||
Circle collider_; // Círculo de colisión
|
Circle collider_; // Círculo de colisión
|
||||||
Uint16 creation_counter_; // Temporizador de creación
|
Uint16 creation_counter_; // Temporizador de creación
|
||||||
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
|
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
|
||||||
Uint16 score_; // Puntos al destruir el globo
|
Uint16 score_; // Puntos al destruir el globo
|
||||||
Type type_; // Tipo de globo
|
Type type_; // Tipo de globo
|
||||||
Size size_; // Tamaño de globo
|
Size size_; // Tamaño de globo
|
||||||
Uint8 menace_; // Amenaza que genera el globo
|
Uint8 menace_; // Amenaza que genera el globo
|
||||||
Uint32 counter_ = 0; // Contador interno
|
Uint32 counter_ = 0; // Contador interno
|
||||||
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad
|
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad
|
||||||
float speed_; // Velocidad del globo
|
float speed_; // Velocidad del globo
|
||||||
Uint8 power_; // Poder que alberga el globo
|
Uint8 power_; // Poder que alberga el globo
|
||||||
SDL_FRect play_area_; // Zona de movimiento del globo
|
SDL_FRect play_area_; // Zona de movimiento del globo
|
||||||
std::string bouncing_sound_; // Archivo de sonido al rebotar
|
Sound sound_; // Configuración de sonido del globo
|
||||||
std::string popping_sound_; // Archivo de sonido al explotar
|
BounceEffect bounce_effect_; // Efecto de rebote
|
||||||
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
|
|
||||||
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
|
|
||||||
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
|
|
||||||
BounceEffect bounce_effect_; // Efecto de rebote
|
|
||||||
|
|
||||||
// --- Posicionamiento y transformación ---
|
// --- Posicionamiento y transformación ---
|
||||||
void shiftColliders(); // Alinea el círculo de colisión con el sprite
|
void shiftColliders(); // Alinea el círculo de colisión con el sprite
|
||||||
void shiftSprite(); // Alinea el sprite en pantalla
|
void shiftSprite(); // Alinea el sprite en pantalla
|
||||||
|
|
||||||
// --- Animación y sonido ---
|
// --- Animación y sonido ---
|
||||||
void setAnimation(); // Establece la animación correspondiente
|
void setAnimation(); // Establece la animación correspondiente
|
||||||
void playSound(const std::string& name) const; // Reproduce un sonido por nombre
|
void playBouncingSound(); // Reproduce el sonido de rebote
|
||||||
void playBouncingSound(); // Reproduce el sonido de rebote
|
void playPoppingSound(); // Reproduce el sonido de reventar
|
||||||
|
|
||||||
// --- Movimiento y física ---
|
// --- Movimiento y física ---
|
||||||
void handleHorizontalMovement(); // Maneja el movimiento horizontal
|
void handleHorizontalMovement(); // Maneja el movimiento horizontal
|
||||||
|
|||||||
@@ -15,19 +15,24 @@ class BalloonFormations {
|
|||||||
public:
|
public:
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct SpawnParams {
|
struct SpawnParams {
|
||||||
int x = 0; // Posición en el eje X donde crear el globo
|
float x = 0; // Posición en el eje X donde crear el globo
|
||||||
int y = 0; // Posición en el eje Y donde crear el globo
|
float y = 0; // Posición en el eje Y donde crear el globo
|
||||||
float vel_x = 0.0F; // Velocidad inicial en el eje X
|
float vel_x = 0.0F; // Velocidad inicial en el eje X
|
||||||
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
|
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
|
||||||
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
|
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
|
||||||
int creation_counter = 0; // Temporizador para la creación del globo
|
Uint16 creation_counter = 0; // Temporizador para la creación del globo
|
||||||
|
|
||||||
// Constructor por defecto
|
// Constructor por defecto
|
||||||
SpawnParams() = default;
|
SpawnParams() = default;
|
||||||
|
|
||||||
// Constructor con parámetros
|
// Constructor con parámetros
|
||||||
SpawnParams(int x, int y, float vel_x, Balloon::Type type, Balloon::Size size, int creation_counter)
|
SpawnParams(float x, float y, float vel_x, Balloon::Type type, Balloon::Size size, Uint16 creation_counter)
|
||||||
: x(x), y(y), vel_x(vel_x), type(type), size(size), creation_counter(creation_counter) {}
|
: x(x),
|
||||||
|
y(y),
|
||||||
|
vel_x(vel_x),
|
||||||
|
type(type),
|
||||||
|
size(size),
|
||||||
|
creation_counter(creation_counter) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Formation {
|
struct Formation {
|
||||||
|
|||||||
@@ -107,7 +107,15 @@ void BalloonManager::deployRandomFormation(int stage) {
|
|||||||
// Crea los globos de la formación
|
// Crea los globos de la formación
|
||||||
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, (creation_time_enabled_) ? balloon.creation_counter : 0);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = balloon.y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = static_cast<Uint16>(creation_time_enabled_ ? balloon.creation_counter : 0)};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reinicia el contador para el próximo despliegue
|
// Reinicia el contador para el próximo despliegue
|
||||||
@@ -120,15 +128,31 @@ void BalloonManager::deployRandomFormation(int stage) {
|
|||||||
void BalloonManager::deployFormation(int formation_id) {
|
void BalloonManager::deployFormation(int formation_id) {
|
||||||
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = balloon.y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = balloon.creation_counter};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea una formación de globos específica a una altura determinada
|
// Crea una formación de globos específica a una altura determinada
|
||||||
void BalloonManager::deployFormation(int formation_id, int y) {
|
void BalloonManager::deployFormation(int formation_id, float y) {
|
||||||
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = balloon.creation_counter};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,13 +178,16 @@ auto BalloonManager::calculateScreenPower() -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crea un globo nuevo en el vector de globos
|
// Crea un globo nuevo en el vector de globos
|
||||||
auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon> {
|
auto BalloonManager::createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon> {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
const int INDEX = static_cast<int>(size);
|
const int INDEX = static_cast<int>(config.size);
|
||||||
balloons_.emplace_back(std::make_shared<Balloon>(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(INDEX), balloon_animations_.at(INDEX)));
|
config.play_area = play_area_;
|
||||||
balloons_.back()->setSound(sound_enabled_);
|
config.texture = balloon_textures_.at(INDEX);
|
||||||
balloons_.back()->setBouncingSound(bouncing_sound_enabled_);
|
config.animation = balloon_animations_.at(INDEX);
|
||||||
balloons_.back()->setPoppingSound(poping_sound_enabled_);
|
config.sound.enabled = sound_enabled_;
|
||||||
|
config.sound.bouncing_enabled = bouncing_sound_enabled_;
|
||||||
|
config.sound.poping_enabled = poping_sound_enabled_;
|
||||||
|
balloons_.emplace_back(std::make_shared<Balloon>(config));
|
||||||
return balloons_.back();
|
return balloons_.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,19 +198,24 @@ auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::
|
|||||||
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
|
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
// Calcula parametros
|
// Calcula parametros
|
||||||
const float VX = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE;
|
|
||||||
const auto SIZE = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
|
|
||||||
const int PARENT_HEIGHT = balloon->getHeight();
|
const int PARENT_HEIGHT = balloon->getHeight();
|
||||||
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
|
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
|
||||||
const int CHILD_WIDTH = CHILD_HEIGHT;
|
const int CHILD_WIDTH = CHILD_HEIGHT;
|
||||||
const float Y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2);
|
|
||||||
float x = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
|
const float X = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
|
||||||
const float MIN_X = play_area_.x;
|
const float MIN_X = play_area_.x;
|
||||||
const float MAX_X = play_area_.w - CHILD_WIDTH;
|
const float MAX_X = play_area_.w - CHILD_WIDTH;
|
||||||
x = std::clamp(x - (CHILD_WIDTH / 2), MIN_X, MAX_X);
|
|
||||||
|
Balloon::Config config = {
|
||||||
|
.x = std::clamp(X - (CHILD_WIDTH / 2), MIN_X, MAX_X),
|
||||||
|
.y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2),
|
||||||
|
.size = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1),
|
||||||
|
.vel_x = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = 0};
|
||||||
|
|
||||||
// Crea el globo
|
// Crea el globo
|
||||||
auto b = createBalloon(x, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0);
|
auto b = createBalloon(config);
|
||||||
|
|
||||||
// Establece parametros
|
// Establece parametros
|
||||||
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
|
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
|
||||||
@@ -198,18 +230,32 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
|
|||||||
void BalloonManager::createPowerBall() {
|
void BalloonManager::createPowerBall() {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
constexpr int VALUES = 6;
|
constexpr int VALUES = 6;
|
||||||
constexpr float POS_Y = -Balloon::WIDTH.at(4);
|
const int LUCK = rand() % VALUES;
|
||||||
constexpr int CREATION_TIME = 0;
|
|
||||||
|
|
||||||
const float LEFT = param.game.play_area.rect.x;
|
const float LEFT = param.game.play_area.rect.x;
|
||||||
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
|
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
|
||||||
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
|
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
|
||||||
|
|
||||||
const int LUCK = rand() % VALUES;
|
|
||||||
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
|
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
|
||||||
const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE};
|
const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE};
|
||||||
|
|
||||||
balloons_.emplace_back(std::make_unique<Balloon>(POS_X[LUCK], POS_Y, Balloon::Type::POWERBALL, Balloon::Size::EXTRALARGE, VEL_X[LUCK], balloon_speed_, CREATION_TIME, play_area_, balloon_textures_[4], balloon_animations_[4]));
|
Balloon::Config config = {
|
||||||
|
.x = POS_X.at(LUCK),
|
||||||
|
.y = -Balloon::WIDTH.at(4),
|
||||||
|
.type = Balloon::Type::POWERBALL,
|
||||||
|
.size = Balloon::Size::EXTRALARGE,
|
||||||
|
.vel_x = VEL_X.at(LUCK),
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = 0,
|
||||||
|
.play_area = play_area_,
|
||||||
|
.texture = balloon_textures_.at(4),
|
||||||
|
.animation = balloon_animations_.at(4),
|
||||||
|
.sound = {
|
||||||
|
.bouncing_enabled = bouncing_sound_enabled_,
|
||||||
|
.poping_enabled = poping_sound_enabled_,
|
||||||
|
.enabled = sound_enabled_}};
|
||||||
|
|
||||||
|
balloons_.emplace_back(std::make_unique<Balloon>(config));
|
||||||
balloons_.back()->setInvulnerable(true);
|
balloons_.back()->setInvulnerable(true);
|
||||||
|
|
||||||
power_ball_enabled_ = true;
|
power_ball_enabled_ = true;
|
||||||
@@ -291,7 +337,7 @@ auto BalloonManager::destroyAllBalloons() -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
balloon_deploy_counter_ = 300;
|
balloon_deploy_counter_ = 300;
|
||||||
Screen::get()->flash(FLASH_COLOR, 3);
|
Screen::get()->flash(Colors::FLASH, 3);
|
||||||
Screen::get()->shake();
|
Screen::get()->shake();
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
@@ -334,19 +380,6 @@ void BalloonManager::createTwoBigBalloons() {
|
|||||||
deployFormation(1);
|
deployFormation(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea una disposición de globos aleatoria
|
|
||||||
void BalloonManager::createRandomBalloons() {
|
|
||||||
const int NUM_BALLOONS = 2 + (rand() % 4);
|
|
||||||
for (int i = 0; i < NUM_BALLOONS; ++i) {
|
|
||||||
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
|
|
||||||
const int Y = param.game.game_area.rect.y + (rand() % 50);
|
|
||||||
const auto SIZE = static_cast<Balloon::Size>(rand() % 4);
|
|
||||||
const float VEL_X = (rand() % 2 == 0) ? Balloon::VELX_POSITIVE : Balloon::VELX_NEGATIVE;
|
|
||||||
const int CREATION_COUNTER = 0;
|
|
||||||
createBalloon(X, Y, Balloon::Type::BALLOON, SIZE, VEL_X, balloon_speed_, CREATION_COUNTER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtiene el nivel de ameza actual generado por los globos
|
// Obtiene el nivel de ameza actual generado por los globos
|
||||||
auto BalloonManager::getMenace() -> int {
|
auto BalloonManager::getMenace() -> int {
|
||||||
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
|
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
|
||||||
|
|||||||
@@ -37,14 +37,13 @@ class BalloonManager {
|
|||||||
// --- Creación de formaciones enemigas ---
|
// --- Creación de formaciones enemigas ---
|
||||||
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
|
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
|
||||||
void deployFormation(int formation_id); // Crea una formación específica
|
void deployFormation(int formation_id); // Crea una formación específica
|
||||||
void deployFormation(int formation_id, int y); // Crea una formación específica con coordenadas
|
void deployFormation(int formation_id, float y); // Crea una formación específica con coordenadas
|
||||||
|
|
||||||
// --- Creación de globos ---
|
// --- Creación de globos ---
|
||||||
auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
|
auto createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
|
||||||
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
|
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
|
||||||
void createPowerBall(); // Crea una PowerBall
|
void createPowerBall(); // Crea una PowerBall
|
||||||
void createTwoBigBalloons(); // Crea dos globos grandes
|
void createTwoBigBalloons(); // Crea dos globos grandes
|
||||||
void createRandomBalloons(); // Crea una disposición aleatoria de globos
|
|
||||||
|
|
||||||
// --- Control de velocidad y despliegue ---
|
// --- Control de velocidad y despliegue ---
|
||||||
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
|
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
|
||||||
|
|||||||
@@ -42,22 +42,8 @@ auto Color::fromHex(const std::string &hex_str) -> Color {
|
|||||||
return Color(r, g, b, a);
|
return Color(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtiene un color del vector de colores imitando al Coche Fantástico
|
// Implementaciones de métodos estáticos de Color
|
||||||
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
|
constexpr auto Color::rgbToHsv(Color color) -> HSV {
|
||||||
int cycle_length = (colors.size() * 2) - 2;
|
|
||||||
size_t n = counter % cycle_length;
|
|
||||||
|
|
||||||
size_t index;
|
|
||||||
if (n < colors.size()) {
|
|
||||||
index = n; // Avanza: 0,1,2,3
|
|
||||||
} else {
|
|
||||||
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
|
|
||||||
}
|
|
||||||
|
|
||||||
return colors[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto rgbToHsv(Color color) -> HSV {
|
|
||||||
float r = color.r / 255.0F;
|
float r = color.r / 255.0F;
|
||||||
float g = color.g / 255.0F;
|
float g = color.g / 255.0F;
|
||||||
float b = color.b / 255.0F;
|
float b = color.b / 255.0F;
|
||||||
@@ -87,7 +73,7 @@ constexpr auto rgbToHsv(Color color) -> HSV {
|
|||||||
return {.h = h, .s = s, .v = v};
|
return {.h = h, .s = s, .v = v};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto hsvToRgb(HSV hsv) -> Color {
|
constexpr auto Color::hsvToRgb(HSV hsv) -> Color {
|
||||||
float c = hsv.v * hsv.s;
|
float c = hsv.v * hsv.s;
|
||||||
float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1));
|
float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0F, 2) - 1));
|
||||||
float m = hsv.v - c;
|
float m = hsv.v - c;
|
||||||
@@ -128,12 +114,29 @@ constexpr auto hsvToRgb(HSV hsv) -> Color {
|
|||||||
static_cast<uint8_t>(roundf((b + m) * 255)));
|
static_cast<uint8_t>(roundf((b + m) * 255)));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> ColorCycle {
|
// Implementaciones del namespace Colors
|
||||||
ColorCycle result{};
|
namespace Colors {
|
||||||
HSV base_hsv = rgbToHsv(base);
|
// Obtiene un color del vector de colores imitando al Coche Fantástico
|
||||||
|
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
|
||||||
|
int cycle_length = (colors.size() * 2) - 2;
|
||||||
|
size_t n = counter % cycle_length;
|
||||||
|
|
||||||
for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) {
|
size_t index;
|
||||||
float t = static_cast<float>(i) / (COLOR_CYCLE_SIZE - 1); // 0 → 1
|
if (n < colors.size()) {
|
||||||
|
index = n; // Avanza: 0,1,2,3
|
||||||
|
} else {
|
||||||
|
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
|
||||||
|
Cycle result{};
|
||||||
|
HSV base_hsv = Color::rgbToHsv(base);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
|
||||||
|
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
|
||||||
float hue_shift = 0.0F;
|
float hue_shift = 0.0F;
|
||||||
float sat_shift = 0.0F;
|
float sat_shift = 0.0F;
|
||||||
float val_shift = 0.0F;
|
float val_shift = 0.0F;
|
||||||
@@ -173,10 +176,11 @@ auto generateMirroredCycle(Color base, ColorCycleStyle style) -> ColorCycle {
|
|||||||
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
|
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
|
||||||
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
|
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
|
||||||
|
|
||||||
Color c = hsvToRgb(adjusted);
|
Color c = Color::hsvToRgb(adjusted);
|
||||||
result[i] = c;
|
result[i] = c;
|
||||||
result[(2 * COLOR_CYCLE_SIZE) - 1 - i] = c; // espejo
|
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
} // namespace Colors
|
||||||
@@ -9,8 +9,12 @@
|
|||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
// --- Constantes ---
|
// --- Estructura HSV: define un color en formato HSV ---
|
||||||
constexpr size_t COLOR_CYCLE_SIZE = 6; // Mitad del ciclo espejado
|
struct HSV {
|
||||||
|
float h; // Matiz (Hue)
|
||||||
|
float s; // Saturación (Saturation)
|
||||||
|
float v; // Valor (Value)
|
||||||
|
};
|
||||||
|
|
||||||
// --- Estructura Color: define un color RGBA ---
|
// --- Estructura Color: define un color RGBA ---
|
||||||
struct Color {
|
struct Color {
|
||||||
@@ -32,10 +36,17 @@ struct Color {
|
|||||||
|
|
||||||
Uint8 r, g, b, a;
|
Uint8 r, g, b, a;
|
||||||
|
|
||||||
constexpr Color() : r(MIN_COLOR_VALUE), g(MIN_COLOR_VALUE), b(MIN_COLOR_VALUE), a(DEFAULT_ALPHA) {}
|
constexpr Color()
|
||||||
|
: r(MIN_COLOR_VALUE),
|
||||||
|
g(MIN_COLOR_VALUE),
|
||||||
|
b(MIN_COLOR_VALUE),
|
||||||
|
a(DEFAULT_ALPHA) {}
|
||||||
|
|
||||||
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
|
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
|
||||||
: r(red), g(green), b(blue), a(alpha) {}
|
: r(red),
|
||||||
|
g(green),
|
||||||
|
b(blue),
|
||||||
|
a(alpha) {}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto INVERSE() const -> Color {
|
[[nodiscard]] constexpr auto INVERSE() const -> Color {
|
||||||
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
|
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
|
||||||
@@ -60,6 +71,10 @@ struct Color {
|
|||||||
// Método estático para crear Color desde string hexadecimal
|
// Método estático para crear Color desde string hexadecimal
|
||||||
static auto fromHex(const std::string &hex_str) -> Color;
|
static auto fromHex(const std::string &hex_str) -> Color;
|
||||||
|
|
||||||
|
// Conversiones de formato de color
|
||||||
|
[[nodiscard]] constexpr static auto rgbToHsv(Color color) -> HSV;
|
||||||
|
[[nodiscard]] constexpr static auto hsvToRgb(HSV hsv) -> Color;
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto IS_EQUAL_TO(const Color &other) const -> bool {
|
[[nodiscard]] constexpr auto IS_EQUAL_TO(const Color &other) const -> bool {
|
||||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||||
}
|
}
|
||||||
@@ -89,13 +104,6 @@ struct Color {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Estructura HSV: define un color en formato HSV ---
|
|
||||||
struct HSV {
|
|
||||||
float h; // Matiz (Hue)
|
|
||||||
float s; // Saturación (Saturation)
|
|
||||||
float v; // Valor (Value)
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Enum ColorCycleStyle: define estilos de ciclo de color ---
|
// --- Enum ColorCycleStyle: define estilos de ciclo de color ---
|
||||||
enum class ColorCycleStyle {
|
enum class ColorCycleStyle {
|
||||||
SUBTLE_PULSE, // Variación leve en brillo (por defecto)
|
SUBTLE_PULSE, // Variación leve en brillo (por defecto)
|
||||||
@@ -105,23 +113,27 @@ enum class ColorCycleStyle {
|
|||||||
LIGHT_FLASH // Ilumina hacia el centro y regresa
|
LIGHT_FLASH // Ilumina hacia el centro y regresa
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Namespace Colors: constantes y utilidades de color ---
|
||||||
|
namespace Colors {
|
||||||
|
// --- Constantes ---
|
||||||
|
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
|
||||||
|
|
||||||
// --- Alias ---
|
// --- Alias ---
|
||||||
using ColorCycle = std::array<Color, 2 * COLOR_CYCLE_SIZE>;
|
using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
|
||||||
|
|
||||||
// --- Colores predefinidos ---
|
// --- Colores predefinidos ---
|
||||||
constexpr Color NO_TEXT_COLOR = Color(0XFF, 0XFF, 0XFF);
|
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
|
||||||
constexpr Color SHADOW_TEXT_COLOR = Color(0X43, 0X43, 0X4F);
|
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
|
||||||
constexpr Color TITLE_SHADOW_TEXT_COLOR = Color(0x14, 0x87, 0xc4);
|
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
|
||||||
constexpr Color ORANGE_TEXT_COLOR = Color(0XFF, 0X7A, 0X00);
|
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
|
||||||
|
|
||||||
constexpr Color FLASH_COLOR = Color(0XFF, 0XFF, 0XFF);
|
constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
|
||||||
|
|
||||||
constexpr Color BLUE_SKY_COLOR = Color(0X02, 0X88, 0XD1);
|
constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
|
||||||
constexpr Color PINK_SKY_COLOR = Color(0XFF, 0X6B, 0X97);
|
constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
|
||||||
constexpr Color GREEN_SKY_COLOR = Color(0X00, 0X79, 0X6B);
|
constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
|
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
|
||||||
constexpr auto rgbToHsv(Color color) -> HSV;
|
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
|
||||||
constexpr auto hsvToRgb(HSV hsv) -> Color;
|
} // namespace Colors
|
||||||
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> ColorCycle;
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h> // Para SDL_ScaleMode
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
@@ -17,6 +19,7 @@ constexpr int NAME_ENTRY_IDLE_TIME = 10;
|
|||||||
constexpr int NAME_ENTRY_TOTAL_TIME = 60;
|
constexpr int NAME_ENTRY_TOTAL_TIME = 60;
|
||||||
constexpr bool HIT_STOP = false;
|
constexpr bool HIT_STOP = false;
|
||||||
constexpr int HIT_STOP_MS = 500;
|
constexpr int HIT_STOP_MS = 500;
|
||||||
|
constexpr const char* ITEM_TEXT_OUTLINE_COLOR = "FFFFFF00"; // 255, 255, 255, 0
|
||||||
|
|
||||||
// Play area por defecto
|
// Play area por defecto
|
||||||
constexpr float PLAY_AREA_X = 0.0F;
|
constexpr float PLAY_AREA_X = 0.0F;
|
||||||
@@ -30,9 +33,8 @@ namespace Fade {
|
|||||||
constexpr const char* COLOR = "1F2B30";
|
constexpr const char* COLOR = "1F2B30";
|
||||||
constexpr float NUM_SQUARES_WIDTH = 160.0F;
|
constexpr float NUM_SQUARES_WIDTH = 160.0F;
|
||||||
constexpr float NUM_SQUARES_HEIGHT = 128.0F;
|
constexpr float NUM_SQUARES_HEIGHT = 128.0F;
|
||||||
constexpr int RANDOM_SQUARES_DELAY = 1;
|
constexpr int RANDOM_SQUARES_DURATION_MS = 1;
|
||||||
constexpr int RANDOM_SQUARES_MULT = 500;
|
constexpr int POST_DURATION_MS = 80;
|
||||||
constexpr int POST_DURATION = 80;
|
|
||||||
constexpr float VENETIAN_SIZE = 12.0F;
|
constexpr float VENETIAN_SIZE = 12.0F;
|
||||||
} // namespace Fade
|
} // namespace Fade
|
||||||
|
|
||||||
@@ -73,7 +75,9 @@ namespace Balloon {
|
|||||||
struct BalloonSettings {
|
struct BalloonSettings {
|
||||||
float vel;
|
float vel;
|
||||||
float grav;
|
float grav;
|
||||||
constexpr BalloonSettings(float v, float g) : vel(v), grav(g) {}
|
constexpr BalloonSettings(float v, float g)
|
||||||
|
: vel(v),
|
||||||
|
grav(g) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
||||||
@@ -84,7 +88,10 @@ constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
constexpr std::array<const char*, 4> COLORS = {
|
constexpr std::array<const char*, 4> COLORS = {
|
||||||
"blue", "orange", "red", "green"};
|
"blue",
|
||||||
|
"orange",
|
||||||
|
"red",
|
||||||
|
"green"};
|
||||||
|
|
||||||
constexpr bool BOUNCING_SOUND = false;
|
constexpr bool BOUNCING_SOUND = false;
|
||||||
} // namespace Balloon
|
} // namespace Balloon
|
||||||
@@ -149,6 +156,20 @@ constexpr float MAX_SPAWN_TIME = 3.0F;
|
|||||||
|
|
||||||
// --- PLAYER ---
|
// --- PLAYER ---
|
||||||
namespace Player {
|
namespace Player {
|
||||||
|
namespace DefaultShirt {
|
||||||
|
// Player 0 (Jugador 1)
|
||||||
|
constexpr const char* PLAYER0_DARKEST = "028ECFFF"; // 2, 142, 207, 255
|
||||||
|
constexpr const char* PLAYER0_DARK = "0297DBFF"; // 2, 151, 219, 255
|
||||||
|
constexpr const char* PLAYER0_BASE = "029FE8FF"; // 2, 159, 232, 255
|
||||||
|
constexpr const char* PLAYER0_LIGHT = "03A9F4FF"; // 3, 169, 244, 255
|
||||||
|
|
||||||
|
// Player 1 (Jugador 2)
|
||||||
|
constexpr const char* PLAYER1_DARKEST = "8E8E8EFF"; // 142, 142, 142, 255
|
||||||
|
constexpr const char* PLAYER1_DARK = "AEADADFF"; // 174, 173, 173, 255
|
||||||
|
constexpr const char* PLAYER1_BASE = "E4E4E4FF"; // 228, 228, 228, 255
|
||||||
|
constexpr const char* PLAYER1_LIGHT = "F7F1F1FF"; // 247, 241, 241, 255
|
||||||
|
} // namespace DefaultShirt
|
||||||
|
|
||||||
namespace OneCoffeeShirt {
|
namespace OneCoffeeShirt {
|
||||||
// Player 0 (Jugador 1)
|
// Player 0 (Jugador 1)
|
||||||
constexpr const char* PLAYER0_DARKEST = "3D9C70FF"; // 61, 156, 112, 255
|
constexpr const char* PLAYER0_DARKEST = "3D9C70FF"; // 61, 156, 112, 255
|
||||||
@@ -176,5 +197,45 @@ constexpr const char* PLAYER1_DARK = "FA7D00FF"; // 250, 125, 0, 255
|
|||||||
constexpr const char* PLAYER1_BASE = "FAA200FF"; // 250, 162, 0, 255
|
constexpr const char* PLAYER1_BASE = "FAA200FF"; // 250, 162, 0, 255
|
||||||
constexpr const char* PLAYER1_LIGHT = "FA8500FF"; // 250, 133, 0, 255
|
constexpr const char* PLAYER1_LIGHT = "FA8500FF"; // 250, 133, 0, 255
|
||||||
} // namespace TwoCoffeeShirt
|
} // namespace TwoCoffeeShirt
|
||||||
|
|
||||||
|
namespace OutlineColor {
|
||||||
|
// Player 0 (Jugador 1)
|
||||||
|
constexpr const char* PLAYER0 = "66323FFF";
|
||||||
|
|
||||||
|
// Player 1 (Jugador 2)
|
||||||
|
constexpr const char* PLAYER1 = "422028FF";
|
||||||
|
} // namespace OutlineColor
|
||||||
} // namespace Player
|
} // namespace Player
|
||||||
|
|
||||||
|
// --- OPTIONS ---
|
||||||
|
namespace Options {
|
||||||
|
// Window
|
||||||
|
constexpr const char* WINDOW_CAPTION = "Coffee Crisis Arcade Edition";
|
||||||
|
constexpr int WINDOW_ZOOM = 2;
|
||||||
|
constexpr int WINDOW_MAX_ZOOM = 2;
|
||||||
|
|
||||||
|
// Video
|
||||||
|
constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
|
||||||
|
constexpr bool VIDEO_FULLSCREEN = false;
|
||||||
|
constexpr bool VIDEO_VSYNC = true;
|
||||||
|
constexpr bool VIDEO_INTEGER_SCALE = true;
|
||||||
|
constexpr bool VIDEO_SHADERS = true;
|
||||||
|
|
||||||
|
// Music
|
||||||
|
constexpr bool MUSIC_ENABLED = true;
|
||||||
|
constexpr int MUSIC_VOLUME = 100;
|
||||||
|
|
||||||
|
// Sound
|
||||||
|
constexpr bool SOUND_ENABLED = true;
|
||||||
|
constexpr int SOUND_VOLUME = 100;
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
constexpr bool AUDIO_ENABLED = true;
|
||||||
|
constexpr int AUDIO_VOLUME = 100;
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
constexpr bool SETTINGS_AUTOFIRE = true;
|
||||||
|
constexpr bool SETTINGS_SHUTDOWN_ENABLED = false;
|
||||||
|
constexpr const char* PARAMS_FILE = "param_320x256.txt";
|
||||||
|
} // namespace Options
|
||||||
} // namespace GameDefaults
|
} // namespace GameDefaults
|
||||||
@@ -25,7 +25,9 @@ class DefineButtons {
|
|||||||
int button;
|
int button;
|
||||||
|
|
||||||
Button(std::string label, Input::Action action, int button)
|
Button(std::string label, Input::Action action, int button)
|
||||||
: label(std::move(label)), action(action), button(button) {}
|
: label(std::move(label)),
|
||||||
|
action(action),
|
||||||
|
button(button) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Constructor y destructor ---
|
// --- Constructor y destructor ---
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "param.h" // Para loadParamsFromFile
|
#include "param.h" // Para loadParamsFromFile
|
||||||
#include "player.h" // Para Player
|
#include "player.h" // Para Player
|
||||||
#include "resource.h" // Para Resource
|
#include "resource.h" // Para Resource
|
||||||
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "screen.h" // Para Screen
|
#include "screen.h" // Para Screen
|
||||||
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
||||||
#include "sections/credits.h" // Para Credits
|
#include "sections/credits.h" // Para Credits
|
||||||
@@ -41,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
|
|||||||
Section::name = Section::Name::GAME;
|
Section::name = Section::Name::GAME;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#elif _DEBUG
|
#elif _DEBUG
|
||||||
Section::name = Section::Name::GAME;
|
Section::name = Section::Name::HI_SCORE_TABLE;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#else // NORMAL GAME
|
#else // NORMAL GAME
|
||||||
Section::name = Section::Name::LOGO;
|
Section::name = Section::Name::LOGO;
|
||||||
@@ -76,7 +77,13 @@ Director::~Director() {
|
|||||||
// Inicializa todo
|
// Inicializa todo
|
||||||
void Director::init() {
|
void Director::init() {
|
||||||
// Configuración inicial de parametros
|
// Configuración inicial de parametros
|
||||||
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
||||||
|
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
ResourceHelper::initializeResourceSystem(executable_path_ + "/../Resources/resources.pack");
|
||||||
|
#else
|
||||||
|
ResourceHelper::initializeResourceSystem("resources.pack");
|
||||||
|
#endif
|
||||||
loadAssets(); // Crea el índice de archivos
|
loadAssets(); // Crea el índice de archivos
|
||||||
Input::init(Asset::get()->get("gamecontrollerdb.txt"), Asset::get()->get("controllers.json")); // Carga configuración de controles
|
Input::init(Asset::get()->get("gamecontrollerdb.txt"), Asset::get()->get("controllers.json")); // Carga configuración de controles
|
||||||
Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
|
Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
|
||||||
@@ -124,9 +131,9 @@ void Director::close() {
|
|||||||
void Director::loadParams() {
|
void Director::loadParams() {
|
||||||
// Carga los parametros para configurar el juego
|
// Carga los parametros para configurar el juego
|
||||||
#ifdef ANBERNIC
|
#ifdef ANBERNIC
|
||||||
const std::string paramFilePath = asset->get("param_320x240.txt");
|
const std::string PARAM_FILE_PATH = Asset::get()->get("param_320x240.txt");
|
||||||
#else
|
#else
|
||||||
const std::string PARAM_FILE_PATH = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt");
|
const std::string PARAM_FILE_PATH = Asset::get()->get(Options::settings.params_file);
|
||||||
#endif
|
#endif
|
||||||
loadParamsFromFile(PARAM_FILE_PATH);
|
loadParamsFromFile(PARAM_FILE_PATH);
|
||||||
}
|
}
|
||||||
@@ -154,7 +161,7 @@ void Director::loadAssets() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
||||||
std::string config_path = executable_path_ + PREFIX + "/data/config/assets.txt";
|
std::string config_path = executable_path_ + PREFIX + "/config/assets.txt";
|
||||||
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Assets configuration loaded successfully");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Assets configuration loaded successfully");
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ void EnterName::incPosition() {
|
|||||||
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
|
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
|
||||||
{
|
{
|
||||||
// Copiamos el índice del carácter anterior si es posible.
|
// Copiamos el índice del carácter anterior si es posible.
|
||||||
character_index_[position_] = character_index_[position_ - 1];
|
// character_index_[position_] = character_index_[position_ - 1];
|
||||||
|
|
||||||
|
// Ponemos el caracter "espacio"
|
||||||
|
character_index_[position_] = 0;
|
||||||
} else {
|
} else {
|
||||||
// Si position_ es 0, inicializamos el carácter actual.
|
// Si position_ es 0, inicializamos el carácter actual.
|
||||||
character_index_[position_] = 0;
|
character_index_[position_] = 0;
|
||||||
@@ -144,12 +147,19 @@ auto EnterName::findIndex(char character) const -> int {
|
|||||||
// Devuelve un nombre al azar
|
// Devuelve un nombre al azar
|
||||||
auto EnterName::getRandomName() -> std::string {
|
auto EnterName::getRandomName() -> std::string {
|
||||||
static constexpr std::array<std::string_view, 8> NAMES = {
|
static constexpr std::array<std::string_view, 8> NAMES = {
|
||||||
"BAL1", "TABE", "DOC", "MON", "SAM1", "JORDI", "JDES", "PEPE"};
|
"BAL1",
|
||||||
|
"TABE",
|
||||||
|
"DOC",
|
||||||
|
"MON",
|
||||||
|
"SAM1",
|
||||||
|
"JORDI",
|
||||||
|
"JDES",
|
||||||
|
"PEPE"};
|
||||||
return std::string(NAMES[rand() % NAMES.size()]);
|
return std::string(NAMES[rand() % NAMES.size()]);
|
||||||
}
|
}
|
||||||
// Obtiene el nombre final introducido
|
// Obtiene el nombre final introducido
|
||||||
auto EnterName::getFinalName() -> std::string {
|
auto EnterName::getFinalName() -> std::string {
|
||||||
auto name = trim(name_.substr(0, position_));
|
auto name = trim(name_.substr(0, position_ + 1)); // Devuelve el texto intruducido incluyendo el del selector
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
name = getRandomName();
|
name = getRandomName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ struct ExplosionTexture {
|
|||||||
std::vector<std::string> animation; // Animación para la textura
|
std::vector<std::string> animation; // Animación para la textura
|
||||||
|
|
||||||
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
|
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
|
||||||
: size(sz), texture(std::move(tex)), animation(anim) {}
|
: size(sz),
|
||||||
|
texture(std::move(tex)),
|
||||||
|
animation(anim) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Clase Explosions: gestor de explosiones ---
|
// --- Clase Explosions: gestor de explosiones ---
|
||||||
|
|||||||
16
source/external/jail_shader.h
vendored
@@ -4,6 +4,20 @@
|
|||||||
#include <string> // Para basic_string, string
|
#include <string> // Para basic_string, string
|
||||||
|
|
||||||
namespace shader {
|
namespace shader {
|
||||||
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = "");
|
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &shaderSource, const std::string &fragmentShader = "");
|
||||||
void render();
|
void render();
|
||||||
|
void cleanup();
|
||||||
|
bool isUsingOpenGL();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
namespace metal {
|
||||||
|
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
|
||||||
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height);
|
||||||
|
void updateMetalTexture(SDL_Texture* backBuffer);
|
||||||
|
void renderMetal();
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture);
|
||||||
|
void cleanupMetal();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace shader
|
} // namespace shader
|
||||||
387
source/external/jail_shader_metal.mm
vendored
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
#include "jail_shader.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_metal.h>
|
||||||
|
#include <Metal/Metal.h>
|
||||||
|
#include <QuartzCore/CAMetalLayer.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include "../asset.h"
|
||||||
|
|
||||||
|
namespace shader {
|
||||||
|
namespace metal {
|
||||||
|
|
||||||
|
// Metal objects
|
||||||
|
id<MTLDevice> device = nullptr;
|
||||||
|
id<MTLRenderPipelineState> pipelineState = nullptr;
|
||||||
|
id<MTLBuffer> vertexBuffer = nullptr;
|
||||||
|
id<MTLTexture> backBufferTexture = nullptr;
|
||||||
|
id<MTLTexture> gameCanvasTexture = nullptr; // Our custom render target texture
|
||||||
|
id<MTLSamplerState> sampler = nullptr;
|
||||||
|
|
||||||
|
// SDL objects (references from main shader module)
|
||||||
|
SDL_Window* win = nullptr;
|
||||||
|
SDL_Renderer* renderer = nullptr;
|
||||||
|
SDL_Texture* backBuffer = nullptr;
|
||||||
|
|
||||||
|
// Vertex data for fullscreen quad
|
||||||
|
struct Vertex {
|
||||||
|
float position[4]; // x, y, z, w
|
||||||
|
float texcoord[2]; // u, v
|
||||||
|
};
|
||||||
|
|
||||||
|
const Vertex quadVertices[] = {
|
||||||
|
// Position (x, y, z, w) // TexCoord (u, v) - Standard OpenGL-style coordinates
|
||||||
|
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
|
||||||
|
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
|
||||||
|
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
|
||||||
|
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, // Top-right
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string loadMetalShader(const std::string& filename) {
|
||||||
|
// Try to load the .metal file from the same location as GLSL files
|
||||||
|
auto data = Asset::get()->loadData(filename);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return std::string(data.begin(), data.end());
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height) {
|
||||||
|
if (!renderer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "createMetalRenderTarget: No renderer provided");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear textura Metal como render target
|
||||||
|
SDL_Texture* metalTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
|
||||||
|
if (!metalTexture) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render target texture: %s", SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar filtrado nearest neighbor para look pixelado
|
||||||
|
SDL_SetTextureScaleMode(metalTexture, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
|
// Try to extract and store the Metal texture directly
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(metalTexture);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
gameCanvasTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (gameCanvasTexture) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Successfully extracted Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [gameCanvasTexture width], [gameCanvasTexture height]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameCanvasTexture) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not extract Metal texture from SDL texture - shaders may not work");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Created Metal render target texture (%dx%d)", width, height);
|
||||||
|
return metalTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initMetal(SDL_Window* window, SDL_Texture* backBufferTexture, const std::string& shaderFilename) {
|
||||||
|
// Store references
|
||||||
|
win = window;
|
||||||
|
backBuffer = backBufferTexture;
|
||||||
|
renderer = SDL_GetRenderer(window);
|
||||||
|
|
||||||
|
if (!renderer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL renderer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Metal layer from SDL renderer and extract device from it
|
||||||
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)SDL_GetRenderMetalLayer(renderer);
|
||||||
|
if (!metalLayer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer from SDL renderer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = metalLayer.device;
|
||||||
|
if (!device) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal device from layer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal device from SDL layer: %s", [[device name] UTF8String]);
|
||||||
|
|
||||||
|
// Note: We no longer need our own texture - we'll use the backBuffer directly
|
||||||
|
|
||||||
|
// Load and compile shaders
|
||||||
|
std::string metalShaderSource = loadMetalShader(shaderFilename);
|
||||||
|
if (metalShaderSource.empty()) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader: %s", shaderFilename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded Metal shader %s (length: %zu)",
|
||||||
|
shaderFilename.c_str(), metalShaderSource.length());
|
||||||
|
|
||||||
|
NSString* shaderNSString = [NSString stringWithUTF8String:metalShaderSource.c_str()];
|
||||||
|
NSError* error = nil;
|
||||||
|
id<MTLLibrary> library = [device newLibraryWithSource:shaderNSString options:nil error:&error];
|
||||||
|
if (!library || error) {
|
||||||
|
if (error) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile Metal shader: %s",
|
||||||
|
[[error localizedDescription] UTF8String]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
|
||||||
|
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
|
||||||
|
|
||||||
|
if (!vertexFunction || !fragmentFunction) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader functions");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create render pipeline
|
||||||
|
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||||
|
pipelineDescriptor.vertexFunction = vertexFunction;
|
||||||
|
pipelineDescriptor.fragmentFunction = fragmentFunction;
|
||||||
|
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
|
||||||
|
// Set up vertex descriptor
|
||||||
|
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
|
||||||
|
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat4;
|
||||||
|
vertexDescriptor.attributes[0].offset = 0;
|
||||||
|
vertexDescriptor.attributes[0].bufferIndex = 0;
|
||||||
|
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2;
|
||||||
|
vertexDescriptor.attributes[1].offset = 4 * sizeof(float);
|
||||||
|
vertexDescriptor.attributes[1].bufferIndex = 0;
|
||||||
|
vertexDescriptor.layouts[0].stride = sizeof(Vertex);
|
||||||
|
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||||
|
|
||||||
|
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
|
||||||
|
|
||||||
|
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
|
||||||
|
if (!pipelineState || error) {
|
||||||
|
if (error) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render pipeline: %s",
|
||||||
|
[[error localizedDescription] UTF8String]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create vertex buffer
|
||||||
|
vertexBuffer = [device newBufferWithBytes:quadVertices
|
||||||
|
length:sizeof(quadVertices)
|
||||||
|
options:MTLResourceOptionCPUCacheModeDefault];
|
||||||
|
|
||||||
|
// Create sampler state for nearest neighbor filtering (pixelated look)
|
||||||
|
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
||||||
|
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
|
||||||
|
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
|
||||||
|
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
|
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
|
|
||||||
|
sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMetalTexture(SDL_Texture* backBuffer) {
|
||||||
|
if (!device || !backBuffer) return;
|
||||||
|
|
||||||
|
// Only log this occasionally to avoid spam
|
||||||
|
static int attemptCount = 0;
|
||||||
|
static bool hasLogged = false;
|
||||||
|
|
||||||
|
// Try multiple property names that SDL3 might use
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (sdlMetalTexture) {
|
||||||
|
backBufferTexture = sdlMetalTexture;
|
||||||
|
if (!hasLogged) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't get the texture after several attempts, log once and continue
|
||||||
|
if (!hasLogged && attemptCount++ > 10) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not access SDL Metal texture after %d attempts - shader will be skipped", attemptCount);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderMetal() {
|
||||||
|
if (!renderer || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct pipeline: backBuffer → Metal shaders → screen (no double rendering)
|
||||||
|
|
||||||
|
// Try to get the Metal texture directly from the SDL backBuffer texture
|
||||||
|
updateMetalTexture(backBuffer);
|
||||||
|
|
||||||
|
if (!backBufferTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Metal texture not available - falling back to normal SDL rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render without shaders using normal SDL path
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader directly: backBuffer texture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal render attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, backBufferTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect directly to backBuffer texture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:backBufferTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
static int successCount = 0;
|
||||||
|
if (successCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT shader to backBuffer (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
|
||||||
|
static int fallbackCount = 0;
|
||||||
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture) {
|
||||||
|
if (!renderer || !sourceTexture || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing failed: missing components");
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use our stored Metal texture if available
|
||||||
|
id<MTLTexture> metalTexture = gameCanvasTexture;
|
||||||
|
|
||||||
|
if (!metalTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "gameCanvasTexture not available - falling back to normal rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader post-processing: sourceTexture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, metalTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect to sourceTexture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:metalTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
static int successCount = 0;
|
||||||
|
if (successCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT post-processing (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [metalTexture width], [metalTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
|
||||||
|
static int fallbackCount = 0;
|
||||||
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder for post-processing - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupMetal() {
|
||||||
|
// Release Metal objects (ARC handles most of this automatically)
|
||||||
|
pipelineState = nullptr;
|
||||||
|
backBufferTexture = nullptr;
|
||||||
|
gameCanvasTexture = nullptr;
|
||||||
|
vertexBuffer = nullptr;
|
||||||
|
sampler = nullptr;
|
||||||
|
device = nullptr;
|
||||||
|
renderer = nullptr;
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace metal
|
||||||
|
} // namespace shader
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
||||||
427
source/fade.cpp
@@ -35,24 +35,32 @@ void Fade::init() {
|
|||||||
b_ = 0;
|
b_ = 0;
|
||||||
a_ = 0;
|
a_ = 0;
|
||||||
post_duration_ = 0;
|
post_duration_ = 0;
|
||||||
post_counter_ = 0;
|
post_start_time_ = 0;
|
||||||
pre_duration_ = 0;
|
pre_duration_ = 0;
|
||||||
pre_counter_ = 0;
|
pre_start_time_ = 0;
|
||||||
num_squares_width_ = param.fade.num_squares_width;
|
num_squares_width_ = param.fade.num_squares_width;
|
||||||
num_squares_height_ = param.fade.num_squares_height;
|
num_squares_height_ = param.fade.num_squares_height;
|
||||||
fade_random_squares_delay_ = param.fade.random_squares_delay;
|
random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms
|
||||||
fade_random_squares_mult_ = param.fade.random_squares_mult;
|
square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual
|
||||||
|
random_squares_start_time_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros
|
// Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros
|
||||||
void Fade::reset() {
|
void Fade::reset() {
|
||||||
state_ = State::NOT_ENABLED;
|
state_ = State::NOT_ENABLED;
|
||||||
counter_ = 0;
|
counter_ = 0;
|
||||||
|
post_start_time_ = 0;
|
||||||
|
pre_start_time_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinta una transición en pantalla
|
// Pinta una transición en pantalla
|
||||||
void Fade::render() {
|
void Fade::render() {
|
||||||
if (state_ != State::NOT_ENABLED) {
|
if (state_ != State::NOT_ENABLED) {
|
||||||
|
// Para fade IN terminado, no renderizar (auto-desactivación visual)
|
||||||
|
if (state_ == State::FINISHED && mode_ == Mode::IN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +83,15 @@ void Fade::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Fade::updatePreState() {
|
void Fade::updatePreState() {
|
||||||
if (pre_counter_ == pre_duration_) {
|
// Sistema basado en tiempo únicamente
|
||||||
|
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;
|
||||||
|
|
||||||
|
if (elapsed_time >= static_cast<Uint32>(pre_duration_)) {
|
||||||
state_ = State::FADING;
|
state_ = State::FADING;
|
||||||
} else {
|
// CRÍTICO: Reinicializar tiempo de inicio para tipos que usan random_squares_start_time_
|
||||||
pre_counter_++;
|
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
|
||||||
|
random_squares_start_time_ = SDL_GetTicks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +106,12 @@ void Fade::updateFadingState() {
|
|||||||
case Type::RANDOM_SQUARE:
|
case Type::RANDOM_SQUARE:
|
||||||
updateRandomSquareFade();
|
updateRandomSquareFade();
|
||||||
break;
|
break;
|
||||||
|
case Type::RANDOM_SQUARE2:
|
||||||
|
updateRandomSquare2Fade();
|
||||||
|
break;
|
||||||
|
case Type::DIAGONAL:
|
||||||
|
updateDiagonalFade();
|
||||||
|
break;
|
||||||
case Type::VENETIAN:
|
case Type::VENETIAN:
|
||||||
updateVenetianFade();
|
updateVenetianFade();
|
||||||
break;
|
break;
|
||||||
@@ -102,13 +121,26 @@ void Fade::updateFadingState() {
|
|||||||
counter_++;
|
counter_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Fade::changeToPostState() {
|
||||||
|
state_ = State::POST;
|
||||||
|
post_start_time_ = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
|
||||||
void Fade::updatePostState() {
|
void Fade::updatePostState() {
|
||||||
if (post_counter_ == post_duration_) {
|
// Sistema basado en tiempo únicamente
|
||||||
|
Uint32 elapsed_time = SDL_GetTicks() - post_start_time_;
|
||||||
|
|
||||||
|
if (elapsed_time >= static_cast<Uint32>(post_duration_)) {
|
||||||
state_ = State::FINISHED;
|
state_ = State::FINISHED;
|
||||||
} else {
|
|
||||||
post_counter_++;
|
|
||||||
}
|
}
|
||||||
cleanBackbuffer(r_, g_, b_, a_);
|
|
||||||
|
// Mantener el alpha final correcto para cada tipo de fade
|
||||||
|
Uint8 post_alpha = a_;
|
||||||
|
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
|
||||||
|
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanBackbuffer(r_, g_, b_, post_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fade::updateFullscreenFade() {
|
void Fade::updateFullscreenFade() {
|
||||||
@@ -118,7 +150,7 @@ void Fade::updateFullscreenFade() {
|
|||||||
|
|
||||||
// Comprueba si ha terminado
|
// Comprueba si ha terminado
|
||||||
if (counter_ >= 255 / 4) {
|
if (counter_ >= 255 / 4) {
|
||||||
state_ = State::POST;
|
changeToPostState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +159,8 @@ void Fade::updateCenterFade() {
|
|||||||
|
|
||||||
// Comprueba si ha terminado
|
// Comprueba si ha terminado
|
||||||
if ((counter_ * 4) > param.game.height) {
|
if ((counter_ * 4) > param.game.height) {
|
||||||
state_ = State::POST;
|
|
||||||
a_ = 255;
|
a_ = 255;
|
||||||
|
changeToPostState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,20 +183,243 @@ void Fade::drawCenterFadeRectangles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Fade::updateRandomSquareFade() {
|
void Fade::updateRandomSquareFade() {
|
||||||
if (counter_ % fade_random_squares_delay_ == 0) {
|
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
drawRandomSquares();
|
float progress = static_cast<float>(elapsed_time) / random_squares_duration_;
|
||||||
}
|
|
||||||
|
|
||||||
value_ = calculateValue(0, (num_squares_width_ * num_squares_height_), (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_));
|
// Calcula cuántos cuadrados deberían estar activos
|
||||||
|
int total_squares = num_squares_width_ * num_squares_height_;
|
||||||
|
int active_squares = static_cast<int>(progress * total_squares);
|
||||||
|
active_squares = std::min(active_squares, total_squares);
|
||||||
|
|
||||||
|
// Dibuja los cuadrados activos
|
||||||
|
drawRandomSquares(active_squares);
|
||||||
|
|
||||||
|
value_ = calculateValue(0, total_squares, active_squares);
|
||||||
|
|
||||||
// Comprueba si ha terminado
|
// Comprueba si ha terminado
|
||||||
if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >=
|
if (elapsed_time >= static_cast<Uint32>(random_squares_duration_)) {
|
||||||
num_squares_width_ * num_squares_height_) {
|
changeToPostState();
|
||||||
state_ = State::POST;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fade::drawRandomSquares() {
|
void Fade::updateRandomSquare2Fade() {
|
||||||
|
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
|
int total_squares = num_squares_width_ * num_squares_height_;
|
||||||
|
|
||||||
|
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
||||||
|
int activation_time = random_squares_duration_ - square_transition_duration_;
|
||||||
|
activation_time = std::max(activation_time, square_transition_duration_); // Mínimo igual a la duración de transición
|
||||||
|
|
||||||
|
// Lógica diferente según el modo
|
||||||
|
int squares_to_activate = 0;
|
||||||
|
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
// OUT: Activa cuadrados gradualmente
|
||||||
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
|
squares_to_activate = static_cast<int>(activation_progress * total_squares);
|
||||||
|
} else {
|
||||||
|
squares_to_activate = total_squares; // Activar todos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activa nuevos cuadrados y guarda su tiempo de activación
|
||||||
|
for (int i = 0; i < squares_to_activate && i < total_squares; ++i) {
|
||||||
|
if (square_age_[i] == -1) {
|
||||||
|
square_age_[i] = elapsed_time; // Guarda el tiempo de activación
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// IN: Todos los cuadrados empiezan activos desde el inicio
|
||||||
|
squares_to_activate = total_squares;
|
||||||
|
|
||||||
|
// Activa cuadrados gradualmente con tiempo de inicio escalonado
|
||||||
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
|
int squares_starting_transition = static_cast<int>(activation_progress * total_squares);
|
||||||
|
|
||||||
|
// Asegurar que al menos 1 cuadrado se active desde el primer frame
|
||||||
|
squares_starting_transition = std::max(squares_starting_transition, 1);
|
||||||
|
squares_starting_transition = std::min(squares_starting_transition, total_squares);
|
||||||
|
|
||||||
|
for (int i = 0; i < squares_starting_transition; ++i) {
|
||||||
|
if (square_age_[i] == -1) {
|
||||||
|
square_age_[i] = elapsed_time; // Empieza la transición a transparente
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawRandomSquares2();
|
||||||
|
|
||||||
|
value_ = calculateValue(0, total_squares, squares_to_activate);
|
||||||
|
|
||||||
|
// Comprueba si ha terminado - todos los cuadrados han completado su transición
|
||||||
|
bool all_completed = (squares_to_activate >= total_squares);
|
||||||
|
if (all_completed) {
|
||||||
|
// Verificar que todos han completado su transición individual
|
||||||
|
for (int i = 0; i < total_squares; ++i) {
|
||||||
|
if (square_age_[i] >= 0) { // Cuadrado activado
|
||||||
|
Uint32 square_elapsed = elapsed_time - square_age_[i];
|
||||||
|
if (square_elapsed < static_cast<Uint32>(square_transition_duration_)) {
|
||||||
|
all_completed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_completed) {
|
||||||
|
// Pintar textura final: OUT opaca, IN transparente
|
||||||
|
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
|
cleanBackbuffer(r_, g_, b_, final_alpha);
|
||||||
|
changeToPostState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fade::updateDiagonalFade() {
|
||||||
|
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
|
int total_squares = num_squares_width_ * num_squares_height_;
|
||||||
|
|
||||||
|
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
||||||
|
int activation_time = random_squares_duration_ - square_transition_duration_;
|
||||||
|
activation_time = std::max(activation_time, square_transition_duration_);
|
||||||
|
|
||||||
|
// Calcula cuántas diagonales deberían estar activas
|
||||||
|
int max_diagonal = num_squares_width_ + num_squares_height_ - 1; // Número total de diagonales
|
||||||
|
int active_diagonals = 0;
|
||||||
|
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
// OUT: Activa diagonales gradualmente desde esquina superior izquierda
|
||||||
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
|
active_diagonals = static_cast<int>(activation_progress * max_diagonal);
|
||||||
|
} else {
|
||||||
|
active_diagonals = max_diagonal; // Activar todas
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activa cuadrados por diagonales
|
||||||
|
for (int diagonal = 0; diagonal < active_diagonals; ++diagonal) {
|
||||||
|
activateDiagonal(diagonal, elapsed_time);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// IN: Todas las diagonales empiezan activas, van desapareciendo
|
||||||
|
active_diagonals = max_diagonal;
|
||||||
|
|
||||||
|
// Activa diagonales gradualmente para transición
|
||||||
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
|
int diagonals_starting_transition = static_cast<int>(activation_progress * max_diagonal);
|
||||||
|
|
||||||
|
for (int diagonal = 0; diagonal < diagonals_starting_transition; ++diagonal) {
|
||||||
|
activateDiagonal(diagonal, elapsed_time);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Activar transición en todas las diagonales restantes
|
||||||
|
for (int diagonal = 0; diagonal < max_diagonal; ++diagonal) {
|
||||||
|
activateDiagonal(diagonal, elapsed_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawDiagonal();
|
||||||
|
|
||||||
|
value_ = calculateValue(0, total_squares, active_diagonals * (total_squares / max_diagonal));
|
||||||
|
|
||||||
|
// Comprueba si ha terminado - todas las diagonales activadas y último cuadrado completó transición
|
||||||
|
bool all_completed = (active_diagonals >= max_diagonal);
|
||||||
|
if (all_completed) {
|
||||||
|
// Verificar que todos han completado su transición individual
|
||||||
|
for (int i = 0; i < total_squares; ++i) {
|
||||||
|
if (square_age_[i] >= 0) { // Cuadrado activado
|
||||||
|
Uint32 square_elapsed = elapsed_time - square_age_[i];
|
||||||
|
if (square_elapsed < static_cast<Uint32>(square_transition_duration_)) {
|
||||||
|
all_completed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_completed) {
|
||||||
|
// Pintar textura final: OUT opaca, IN transparente
|
||||||
|
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
|
cleanBackbuffer(r_, g_, b_, final_alpha);
|
||||||
|
changeToPostState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fade::activateDiagonal(int diagonal_index, Uint32 current_time) {
|
||||||
|
// Para cada diagonal, activamos los cuadrados que pertenecen a esa diagonal
|
||||||
|
// Diagonal 0: (0,0)
|
||||||
|
// Diagonal 1: (1,0), (0,1)
|
||||||
|
// Diagonal 2: (2,0), (1,1), (0,2)
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
for (int x = 0; x < num_squares_width_; ++x) {
|
||||||
|
int y = diagonal_index - x;
|
||||||
|
|
||||||
|
// Verificar que y está dentro de los límites
|
||||||
|
if (y >= 0 && y < num_squares_height_) {
|
||||||
|
// Convertir coordenadas (x,y) a índice en el vector
|
||||||
|
int index = y * num_squares_width_ + x;
|
||||||
|
|
||||||
|
if (index >= 0 && index < static_cast<int>(square_age_.size())) {
|
||||||
|
if (square_age_[index] == -1) {
|
||||||
|
square_age_[index] = current_time; // Guarda el tiempo de activación
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fade::drawDiagonal() {
|
||||||
|
auto *temp = SDL_GetRenderTarget(renderer_);
|
||||||
|
SDL_SetRenderTarget(renderer_, backbuffer_);
|
||||||
|
|
||||||
|
// CRÍTICO: Limpiar la textura antes de dibujar
|
||||||
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
|
||||||
|
SDL_RenderClear(renderer_);
|
||||||
|
|
||||||
|
SDL_BlendMode blend_mode;
|
||||||
|
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
||||||
|
|
||||||
|
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
|
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
||||||
|
for (size_t i = 0; i < square_.size(); ++i) {
|
||||||
|
Uint8 current_alpha = 0;
|
||||||
|
|
||||||
|
if (square_age_[i] == -1) {
|
||||||
|
// Cuadrado no activado
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
current_alpha = 0; // OUT: transparente si no activado
|
||||||
|
} else {
|
||||||
|
current_alpha = a_; // IN: opaco si no activado
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Cuadrado activado - calculamos progreso
|
||||||
|
Uint32 square_elapsed = current_time - square_age_[i];
|
||||||
|
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
||||||
|
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
||||||
|
} else {
|
||||||
|
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_alpha > 0) {
|
||||||
|
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
||||||
|
SDL_RenderFillRect(renderer_, &square_[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
|
||||||
|
SDL_SetRenderTarget(renderer_, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fade::drawRandomSquares(int active_count) {
|
||||||
auto *temp = SDL_GetRenderTarget(renderer_);
|
auto *temp = SDL_GetRenderTarget(renderer_);
|
||||||
SDL_SetRenderTarget(renderer_, backbuffer_);
|
SDL_SetRenderTarget(renderer_, backbuffer_);
|
||||||
|
|
||||||
@@ -173,13 +428,56 @@ void Fade::drawRandomSquares() {
|
|||||||
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
|
||||||
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
|
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_);
|
||||||
|
|
||||||
const int INDEX = std::min(counter_ / fade_random_squares_delay_,
|
// Dibuja solo los cuadrados activos
|
||||||
(num_squares_width_ * num_squares_height_) - 1);
|
for (int i = 0; i < active_count && i < static_cast<int>(square_.size()); ++i) {
|
||||||
|
SDL_RenderFillRect(renderer_, &square_[i]);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fade_random_squares_mult_; ++i) {
|
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
|
||||||
const int INDEX2 = std::min((INDEX * fade_random_squares_mult_) + i,
|
SDL_SetRenderTarget(renderer_, temp);
|
||||||
static_cast<int>(square_.size()) - 1);
|
}
|
||||||
SDL_RenderFillRect(renderer_, &square_[INDEX2]);
|
|
||||||
|
void Fade::drawRandomSquares2() {
|
||||||
|
auto *temp = SDL_GetRenderTarget(renderer_);
|
||||||
|
SDL_SetRenderTarget(renderer_, backbuffer_);
|
||||||
|
|
||||||
|
// CRÍTICO: Limpiar la textura antes de dibujar
|
||||||
|
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
|
||||||
|
SDL_RenderClear(renderer_);
|
||||||
|
|
||||||
|
SDL_BlendMode blend_mode;
|
||||||
|
SDL_GetRenderDrawBlendMode(renderer_, &blend_mode);
|
||||||
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
||||||
|
|
||||||
|
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
|
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
||||||
|
for (size_t i = 0; i < square_.size(); ++i) {
|
||||||
|
Uint8 current_alpha = 0;
|
||||||
|
|
||||||
|
if (square_age_[i] == -1) {
|
||||||
|
// Cuadrado no activado
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
current_alpha = 0; // OUT: transparente si no activado
|
||||||
|
} else {
|
||||||
|
current_alpha = a_; // IN: opaco si no activado
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Cuadrado activado - calculamos progreso
|
||||||
|
Uint32 square_elapsed = current_time - square_age_[i];
|
||||||
|
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
||||||
|
|
||||||
|
if (mode_ == Mode::OUT) {
|
||||||
|
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
||||||
|
} else {
|
||||||
|
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_alpha > 0) {
|
||||||
|
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
||||||
|
SDL_RenderFillRect(renderer_, &square_[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
|
SDL_SetRenderDrawBlendMode(renderer_, blend_mode);
|
||||||
@@ -192,7 +490,7 @@ void Fade::updateVenetianFade() {
|
|||||||
updateVenetianRectangles();
|
updateVenetianRectangles();
|
||||||
calculateVenetianProgress();
|
calculateVenetianProgress();
|
||||||
} else {
|
} else {
|
||||||
state_ = State::POST;
|
changeToPostState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,8 +537,7 @@ void Fade::activate() {
|
|||||||
|
|
||||||
state_ = State::PRE;
|
state_ = State::PRE;
|
||||||
counter_ = 0;
|
counter_ = 0;
|
||||||
post_counter_ = 0;
|
pre_start_time_ = SDL_GetTicks();
|
||||||
pre_counter_ = 0;
|
|
||||||
|
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case Type::FULLSCREEN: {
|
case Type::FULLSCREEN: {
|
||||||
@@ -284,6 +581,76 @@ void Fade::activate() {
|
|||||||
// Deja el color listo para usar
|
// Deja el color listo para usar
|
||||||
a_ = mode_ == Mode::OUT ? 255 : 0;
|
a_ = mode_ == Mode::OUT ? 255 : 0;
|
||||||
|
|
||||||
|
// Inicializa el tiempo de inicio
|
||||||
|
random_squares_start_time_ = SDL_GetTicks();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::RANDOM_SQUARE2: {
|
||||||
|
rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
|
||||||
|
square_.clear();
|
||||||
|
square_age_.clear();
|
||||||
|
|
||||||
|
// Añade los cuadrados al vector
|
||||||
|
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
|
||||||
|
rect1_.x = (i % num_squares_width_) * rect1_.w;
|
||||||
|
rect1_.y = (i / num_squares_width_) * rect1_.h;
|
||||||
|
square_.push_back(rect1_);
|
||||||
|
square_age_.push_back(-1); // -1 indica cuadrado no activado aún
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desordena el vector de cuadrados y edades
|
||||||
|
auto num = num_squares_width_ * num_squares_height_;
|
||||||
|
while (num > 1) {
|
||||||
|
auto num_arreu = rand() % num;
|
||||||
|
SDL_FRect temp_rect = square_[num_arreu];
|
||||||
|
int temp_age = square_age_[num_arreu];
|
||||||
|
square_[num_arreu] = square_[num - 1];
|
||||||
|
square_age_[num_arreu] = square_age_[num - 1];
|
||||||
|
square_[num - 1] = temp_rect;
|
||||||
|
square_age_[num - 1] = temp_age;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Textura inicial: OUT transparente, IN opaca
|
||||||
|
Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255;
|
||||||
|
cleanBackbuffer(r_, g_, b_, initial_alpha);
|
||||||
|
|
||||||
|
// Deja el color listo para usar (alpha target para los cuadrados)
|
||||||
|
a_ = 255; // Siempre usar 255 como alpha target
|
||||||
|
|
||||||
|
// Inicializa el tiempo de inicio y recalcula la duración de transición
|
||||||
|
random_squares_start_time_ = SDL_GetTicks();
|
||||||
|
square_transition_duration_ = std::max(random_squares_duration_ / 4, 100); // Mínimo 100ms
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::DIAGONAL: {
|
||||||
|
rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
|
||||||
|
square_.clear();
|
||||||
|
square_age_.clear();
|
||||||
|
|
||||||
|
// Añade los cuadrados al vector en orden (sin desordenar)
|
||||||
|
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
|
||||||
|
rect1_.x = (i % num_squares_width_) * rect1_.w;
|
||||||
|
rect1_.y = (i / num_squares_width_) * rect1_.h;
|
||||||
|
square_.push_back(rect1_);
|
||||||
|
square_age_.push_back(-1); // -1 indica cuadrado no activado aún
|
||||||
|
}
|
||||||
|
|
||||||
|
// Textura inicial: OUT transparente, IN opaca
|
||||||
|
Uint8 initial_alpha = (mode_ == Mode::OUT) ? 0 : 255;
|
||||||
|
cleanBackbuffer(r_, g_, b_, initial_alpha);
|
||||||
|
|
||||||
|
// Deja el color listo para usar (alpha target para los cuadrados)
|
||||||
|
a_ = 255; // Siempre usar 255 como alpha target
|
||||||
|
|
||||||
|
// Inicializa el tiempo de inicio y recalcula la duración de transición
|
||||||
|
random_squares_start_time_ = SDL_GetTicks();
|
||||||
|
square_transition_duration_ = std::max(random_squares_duration_ / 4, 100); // Mínimo 100ms
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ class Fade {
|
|||||||
public:
|
public:
|
||||||
// --- Enums ---
|
// --- Enums ---
|
||||||
enum class Type : Uint8 {
|
enum class Type : Uint8 {
|
||||||
FULLSCREEN = 0, // Fundido de pantalla completa
|
FULLSCREEN = 0, // Fundido de pantalla completa
|
||||||
CENTER = 1, // Fundido desde el centro
|
CENTER = 1, // Fundido desde el centro
|
||||||
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
|
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
|
||||||
VENETIAN = 3, // Fundido tipo persiana veneciana
|
RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2)
|
||||||
|
DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda
|
||||||
|
VENETIAN = 5, // Fundido tipo persiana veneciana
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Mode : Uint8 {
|
enum class Mode : Uint8 {
|
||||||
@@ -45,8 +47,8 @@ class Fade {
|
|||||||
void setColor(Color color); // Establece el color del fade
|
void setColor(Color color); // Establece el color del fade
|
||||||
void setType(Type type) { type_ = type; } // Establece el tipo de fade
|
void setType(Type type) { type_ = type; } // Establece el tipo de fade
|
||||||
void setMode(Mode mode) { mode_ = mode; } // Establece el modo de fade
|
void setMode(Mode mode) { mode_ = mode; } // Establece el modo de fade
|
||||||
void setPostDuration(int value) { post_duration_ = value; } // Duración posterior al fade
|
void setPostDuration(int value) { post_duration_ = value; } // Duración posterior al fade en milisegundos
|
||||||
void setPreDuration(int value) { pre_duration_ = value; } // Duración previa al fade
|
void setPreDuration(int value) { pre_duration_ = value; } // Duración previa al fade en milisegundos
|
||||||
|
|
||||||
// --- Getters ---
|
// --- Getters ---
|
||||||
[[nodiscard]] auto getValue() const -> int { return value_; }
|
[[nodiscard]] auto getValue() const -> int { return value_; }
|
||||||
@@ -60,6 +62,7 @@ class Fade {
|
|||||||
|
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
std::vector<SDL_FRect> square_; // Vector de cuadrados
|
std::vector<SDL_FRect> square_; // Vector de cuadrados
|
||||||
|
std::vector<int> square_age_; // Edad de cada cuadrado (para RANDOM_SQUARE2)
|
||||||
SDL_FRect rect1_, rect2_; // Rectángulos para efectos
|
SDL_FRect rect1_, rect2_; // Rectángulos para efectos
|
||||||
Type type_; // Tipo de fade
|
Type type_; // Tipo de fade
|
||||||
Mode mode_; // Modo de fade
|
Mode mode_; // Modo de fade
|
||||||
@@ -68,12 +71,13 @@ class Fade {
|
|||||||
Uint8 r_, g_, b_, a_; // Color del fade (RGBA)
|
Uint8 r_, g_, b_, a_; // Color del fade (RGBA)
|
||||||
int num_squares_width_; // Cuadrados en horizontal
|
int num_squares_width_; // Cuadrados en horizontal
|
||||||
int num_squares_height_; // Cuadrados en vertical
|
int num_squares_height_; // Cuadrados en vertical
|
||||||
int fade_random_squares_delay_; // Delay entre cuadrados
|
Uint32 random_squares_start_time_; // Tiempo de inicio del fade de cuadrados
|
||||||
int fade_random_squares_mult_; // Cuadrados por paso
|
int random_squares_duration_; // Duración total en milisegundos
|
||||||
int post_duration_ = 0; // Duración posterior
|
int square_transition_duration_; // Duración de transición de cada cuadrado en ms
|
||||||
int post_counter_ = 0; // Contador posterior
|
int post_duration_ = 0; // Duración posterior en milisegundos
|
||||||
int pre_duration_ = 0; // Duración previa
|
Uint32 post_start_time_ = 0; // Tiempo de inicio del estado POST
|
||||||
int pre_counter_ = 0; // Contador previo
|
int pre_duration_ = 0; // Duración previa en milisegundos
|
||||||
|
Uint32 pre_start_time_ = 0; // Tiempo de inicio del estado PRE
|
||||||
int value_ = 0; // Estado del fade (0-100)
|
int value_ = 0; // Estado del fade (0-100)
|
||||||
|
|
||||||
// --- Inicialización y limpieza ---
|
// --- Inicialización y limpieza ---
|
||||||
@@ -87,17 +91,23 @@ class Fade {
|
|||||||
void updatePreState(); // Actualiza el estado previo al fade
|
void updatePreState(); // Actualiza el estado previo al fade
|
||||||
void updateFadingState(); // Actualiza el estado durante el fade
|
void updateFadingState(); // Actualiza el estado durante el fade
|
||||||
void updatePostState(); // Actualiza el estado posterior al fade
|
void updatePostState(); // Actualiza el estado posterior al fade
|
||||||
|
void changeToPostState(); // Cambia al estado POST e inicializa el tiempo
|
||||||
|
|
||||||
// --- Efectos de fundido (fade) ---
|
// --- Efectos de fundido (fade) ---
|
||||||
void updateFullscreenFade(); // Actualiza el fundido de pantalla completa
|
void updateFullscreenFade(); // Actualiza el fundido de pantalla completa
|
||||||
void updateCenterFade(); // Actualiza el fundido desde el centro
|
void updateCenterFade(); // Actualiza el fundido desde el centro
|
||||||
void updateRandomSquareFade(); // Actualiza el fundido con cuadrados aleatorios
|
void updateRandomSquareFade(); // Actualiza el fundido con cuadrados aleatorios
|
||||||
|
void updateRandomSquare2Fade(); // Actualiza el fundido con cuadrados aleatorios (variante 2)
|
||||||
|
void updateDiagonalFade(); // Actualiza el fundido diagonal
|
||||||
void updateVenetianFade(); // Actualiza el fundido tipo persiana veneciana
|
void updateVenetianFade(); // Actualiza el fundido tipo persiana veneciana
|
||||||
void updateVenetianRectangles(); // Actualiza los rectángulos del efecto veneciano
|
void updateVenetianRectangles(); // Actualiza los rectángulos del efecto veneciano
|
||||||
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
|
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
|
||||||
|
|
||||||
// --- Dibujo de efectos visuales ---
|
// --- Dibujo de efectos visuales ---
|
||||||
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
|
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
|
||||||
void drawRandomSquares(); // Dibuja los cuadrados aleatorios del fundido
|
void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido
|
||||||
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
|
void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2)
|
||||||
|
void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal
|
||||||
|
void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica
|
||||||
|
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,12 @@ class GameLogo {
|
|||||||
|
|
||||||
Shake() = default;
|
Shake() = default;
|
||||||
Shake(int d, int de, int l, int o)
|
Shake(int d, int de, int l, int o)
|
||||||
: desp(d), delay(de), length(l), remaining(l), counter(de), origin(o) {}
|
: desp(d),
|
||||||
|
delay(de),
|
||||||
|
length(l),
|
||||||
|
remaining(l),
|
||||||
|
counter(de),
|
||||||
|
origin(o) {}
|
||||||
|
|
||||||
void init(int d, int de, int l, int o) {
|
void init(int d, int de, int l, int o) {
|
||||||
desp = d;
|
desp = d;
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class Input {
|
|||||||
bool just_pressed; // Se acaba de pulsar en este fotograma
|
bool just_pressed; // Se acaba de pulsar en este fotograma
|
||||||
|
|
||||||
KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
|
KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
|
||||||
: scancode(scancode), is_held(is_held), just_pressed(just_pressed) {}
|
: scancode(scancode),
|
||||||
|
is_held(is_held),
|
||||||
|
just_pressed(just_pressed) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ButtonState {
|
struct ButtonState {
|
||||||
@@ -43,7 +45,10 @@ class Input {
|
|||||||
bool trigger_active{false}; // Estado del trigger como botón digital
|
bool trigger_active{false}; // Estado del trigger como botón digital
|
||||||
|
|
||||||
ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
|
ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
|
||||||
: button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {}
|
: button(btn),
|
||||||
|
is_held(is_held),
|
||||||
|
just_pressed(just_pressed),
|
||||||
|
axis_active(axis_act) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Keyboard {
|
struct Keyboard {
|
||||||
|
|||||||
@@ -29,7 +29,15 @@ Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::sha
|
|||||||
height_ = param.game.item_size;
|
height_ = param.game.item_size;
|
||||||
pos_x_ = x;
|
pos_x_ = x;
|
||||||
pos_y_ = y;
|
pos_y_ = y;
|
||||||
vel_x_ = -1.0F + ((rand() % 5) * 0.5F);
|
// 6 velocidades: 3 negativas (-1.0, -0.66, -0.33) y 3 positivas (0.33, 0.66, 1.0)
|
||||||
|
const int direction = rand() % 6;
|
||||||
|
if (direction < 3) {
|
||||||
|
// Velocidades negativas: -1.0, -0.66, -0.33
|
||||||
|
vel_x_ = -1.0F + (direction * 0.33F);
|
||||||
|
} else {
|
||||||
|
// Velocidades positivas: 0.33, 0.66, 1.0
|
||||||
|
vel_x_ = 0.33F + ((direction - 3) * 0.33F);
|
||||||
|
}
|
||||||
vel_y_ = -4.0F;
|
vel_y_ = -4.0F;
|
||||||
accel_y_ = 0.2F;
|
accel_y_ = 0.2F;
|
||||||
collider_.r = width_ / 2;
|
collider_.r = width_ / 2;
|
||||||
@@ -82,9 +90,9 @@ void Item::move() {
|
|||||||
const float MAX_X = play_area_.w - width_;
|
const float MAX_X = play_area_.w - width_;
|
||||||
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
|
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
|
||||||
|
|
||||||
// Si toca el borde lateral, invierte la velocidad horizontal
|
// Si toca el borde lateral
|
||||||
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
|
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
|
||||||
vel_x_ = -vel_x_;
|
vel_x_ = -vel_x_; // Invierte la velocidad horizontal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si colisiona por arriba, rebota (excepto la máquina de café)
|
// Si colisiona por arriba, rebota (excepto la máquina de café)
|
||||||
@@ -92,8 +100,8 @@ void Item::move() {
|
|||||||
// Corrige
|
// Corrige
|
||||||
pos_y_ = param.game.play_area.rect.y;
|
pos_y_ = param.game.play_area.rect.y;
|
||||||
|
|
||||||
// Invierte la velocidad
|
// Fuerza la velocidad hacia abajo para evitar oscilaciones
|
||||||
vel_y_ = -vel_y_;
|
vel_y_ = std::abs(vel_y_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si colisiona con la parte inferior
|
// Si colisiona con la parte inferior
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "difficulty.h" // Para Difficulty
|
#include "difficulty.h" // Para Difficulty
|
||||||
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
||||||
#include "options.h" // Para SettingsOpt...
|
#include "options.h" // Para SettingsOpt...
|
||||||
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -27,14 +28,24 @@ std::vector<Language> languages = {
|
|||||||
auto loadFromFile(const std::string &file_path) -> bool {
|
auto loadFromFile(const std::string &file_path) -> bool {
|
||||||
texts.clear();
|
texts.clear();
|
||||||
|
|
||||||
std::ifstream rfile(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!rfile.is_open()) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json j;
|
json j;
|
||||||
rfile >> j;
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
// Cargar desde datos del pack
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
j = json::parse(content);
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream rfile(file_path);
|
||||||
|
if (!rfile.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rfile >> j;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &el : j.items()) {
|
for (const auto &el : j.items()) {
|
||||||
texts[el.key()] = el.value();
|
texts[el.key()] = el.value();
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ struct Language {
|
|||||||
std::string file_name; // Nombre del fichero con los textos
|
std::string file_name; // Nombre del fichero con los textos
|
||||||
|
|
||||||
Language(Code c, std::string n, std::string fn)
|
Language(Code c, std::string n, std::string fn)
|
||||||
: code(c), name(std::move(n)), file_name(std::move(fn)) {}
|
: code(c),
|
||||||
|
name(std::move(n)),
|
||||||
|
file_name(std::move(fn)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ struct HiScoreEntry {
|
|||||||
bool one_credit_complete; // Indica si se ha conseguido 1CC
|
bool one_credit_complete; // Indica si se ha conseguido 1CC
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
explicit HiScoreEntry(const std::string &n = "", int s = 0, bool occ = false)
|
explicit HiScoreEntry(const std::string &name = "", int score = 0, bool one_credit_complete = false)
|
||||||
: name(n.substr(0, 6)), score(s), one_credit_complete(occ) {}
|
: name(name.substr(0, 6)),
|
||||||
|
score(score),
|
||||||
|
one_credit_complete(one_credit_complete) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Tipos ---
|
// --- Tipos ---
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ auto saveToFile() -> bool {
|
|||||||
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
|
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
|
||||||
file << "game.autofire=" << boolToString(settings.autofire) << "\n";
|
file << "game.autofire=" << boolToString(settings.autofire) << "\n";
|
||||||
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
|
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
|
||||||
|
file << "game.params_file=" << settings.params_file << "\n";
|
||||||
|
|
||||||
// Opciones de mandos
|
// Opciones de mandos
|
||||||
file << "\n## CONTROLLERS\n";
|
file << "\n## CONTROLLERS\n";
|
||||||
@@ -208,6 +209,7 @@ auto set(const std::string& var, const std::string& value) -> bool {
|
|||||||
}},
|
}},
|
||||||
{"game.autofire", [](const auto& val) { settings.autofire = stringToBool(val); }},
|
{"game.autofire", [](const auto& val) { settings.autofire = stringToBool(val); }},
|
||||||
{"game.shutdown_enabled", [](const auto& val) { settings.shutdown_enabled = stringToBool(val); }},
|
{"game.shutdown_enabled", [](const auto& val) { settings.shutdown_enabled = stringToBool(val); }},
|
||||||
|
{"game.params_file", [](const auto& val) { settings.params_file = val; }},
|
||||||
// Teclado
|
// Teclado
|
||||||
{"keyboard.player", [](const auto& val) { keyboard.player_id = static_cast<Player::Id>(stoi(val)); }}};
|
{"keyboard.player", [](const auto& val) { keyboard.player_id = static_cast<Player::Id>(stoi(val)); }}};
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,10 @@
|
|||||||
#include <stdexcept> // Para out_of_range, invalid_argument
|
#include <stdexcept> // Para out_of_range, invalid_argument
|
||||||
#include <string> // Para char_traits, string, allocator, operator==, swap, operator<<, basic_string, stoi
|
#include <string> // Para char_traits, string, allocator, operator==, swap, operator<<, basic_string, stoi
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
#include <utility>
|
#include <utility> // Para swap
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
|
#include "defaults.h" // Para GameDefaults
|
||||||
#include "difficulty.h" // Para Code
|
#include "difficulty.h" // Para Code
|
||||||
#include "input.h" // Para Input
|
#include "input.h" // Para Input
|
||||||
#include "lang.h" // Para Code
|
#include "lang.h" // Para Code
|
||||||
@@ -25,66 +26,47 @@ namespace Options {
|
|||||||
|
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct Window {
|
struct Window {
|
||||||
std::string caption; // Texto que aparece en la barra de título de la ventana
|
std::string caption = GameDefaults::Options::WINDOW_CAPTION; // Texto que aparece en la barra de título de la ventana
|
||||||
int zoom{2}; // Valor por el que se multiplica el tamaño de la ventana
|
int zoom = GameDefaults::Options::WINDOW_ZOOM; // Valor por el que se multiplica el tamaño de la ventana
|
||||||
int max_zoom{2}; // Tamaño máximo para que la ventana no sea mayor que la pantalla
|
int max_zoom = GameDefaults::Options::WINDOW_MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla
|
||||||
|
|
||||||
// Constructor por defecto con valores iniciales
|
|
||||||
Window()
|
|
||||||
: caption("Coffee Crisis Arcade Edition") {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Video {
|
struct Video {
|
||||||
SDL_ScaleMode scale_mode{SDL_ScaleMode::SDL_SCALEMODE_NEAREST}; // Filtro usado para el escalado de la imagen
|
SDL_ScaleMode scale_mode = GameDefaults::Options::VIDEO_SCALE_MODE; // Filtro usado para el escalado de la imagen
|
||||||
bool fullscreen{false}; // Indica si se usa pantalla completa
|
bool fullscreen = GameDefaults::Options::VIDEO_FULLSCREEN; // Indica si se usa pantalla completa
|
||||||
bool vsync{true}; // Indica si se usa vsync
|
bool vsync = GameDefaults::Options::VIDEO_VSYNC; // Indica si se usa vsync
|
||||||
bool integer_scale{true}; // Indica si se usa escalado entero
|
bool integer_scale = GameDefaults::Options::VIDEO_INTEGER_SCALE; // Indica si se usa escalado entero
|
||||||
bool shaders{false}; // Indica si se usan shaders para los filtros de vídeo
|
bool shaders = GameDefaults::Options::VIDEO_SHADERS; // Indica si se usan shaders para los filtros de vídeo
|
||||||
std::string info; // Información sobre el modo de vídeo
|
std::string info; // Información sobre el modo de vídeo
|
||||||
|
|
||||||
// Constructor por defecto con valores iniciales
|
|
||||||
Video() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Music {
|
struct Music {
|
||||||
bool enabled{true}; // Indica si la música suena o no
|
bool enabled = GameDefaults::Options::MUSIC_ENABLED; // Indica si la música suena o no
|
||||||
int volume{100}; // Volumen de la música
|
int volume = GameDefaults::Options::MUSIC_VOLUME; // Volumen de la música
|
||||||
|
|
||||||
// Constructor por defecto
|
|
||||||
Music() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Sound {
|
struct Sound {
|
||||||
bool enabled{true}; // Indica si los sonidos suenan o no
|
bool enabled = GameDefaults::Options::SOUND_ENABLED; // Indica si los sonidos suenan o no
|
||||||
int volume{100}; // Volumen de los sonidos
|
int volume = GameDefaults::Options::SOUND_VOLUME; // Volumen de los sonidos
|
||||||
|
|
||||||
// Constructor por defecto
|
|
||||||
Sound() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Audio {
|
struct Audio {
|
||||||
Music music; // Opciones para la música
|
Music music; // Opciones para la música
|
||||||
Sound sound; // Opciones para los efectos de sonido
|
Sound sound; // Opciones para los efectos de sonido
|
||||||
bool enabled{true}; // Indica si el audio está activo o no
|
bool enabled = GameDefaults::Options::AUDIO_ENABLED; // Indica si el audio está activo o no
|
||||||
int volume{100}; // Volumen general del audio
|
int volume = GameDefaults::Options::AUDIO_VOLUME; // Volumen general del audio
|
||||||
|
|
||||||
// Constructor por defecto
|
|
||||||
Audio() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
Difficulty::Code difficulty{Difficulty::Code::NORMAL}; // Dificultad del juego
|
Difficulty::Code difficulty = Difficulty::Code::NORMAL; // Dificultad del juego
|
||||||
Lang::Code language{Lang::Code::VALENCIAN}; // Idioma usado en el juego
|
Lang::Code language = Lang::Code::VALENCIAN; // Idioma usado en el juego
|
||||||
bool autofire{true}; // Indicador de autofire
|
bool autofire = GameDefaults::Options::SETTINGS_AUTOFIRE; // Indicador de autofire
|
||||||
bool shutdown_enabled{false}; // Especifica si se puede apagar el sistema
|
bool shutdown_enabled = GameDefaults::Options::SETTINGS_SHUTDOWN_ENABLED; // Especifica si se puede apagar el sistema
|
||||||
Table hi_score_table; // Tabla de mejores puntuaciones
|
Table hi_score_table; // Tabla de mejores puntuaciones
|
||||||
std::vector<int> glowing_entries; // Últimas posiciones de entrada en la tabla
|
std::vector<int> glowing_entries = {ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}; // Últimas posiciones de entrada en la tabla
|
||||||
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
|
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
|
||||||
std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos
|
std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos
|
||||||
|
std::string params_file = GameDefaults::Options::PARAMS_FILE; // Ruta al fichero de parámetros del juego
|
||||||
// Constructor por defecto con valores iniciales
|
|
||||||
Settings()
|
|
||||||
: glowing_entries({ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}) {}
|
|
||||||
|
|
||||||
// Reinicia las últimas entradas de puntuación
|
// Reinicia las últimas entradas de puntuación
|
||||||
void clearLastHiScoreEntries() {
|
void clearLastHiScoreEntries() {
|
||||||
@@ -288,12 +270,9 @@ struct Keyboard {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct PendingChanges {
|
struct PendingChanges {
|
||||||
Lang::Code new_language{Lang::Code::VALENCIAN}; // Idioma en espera de aplicar
|
Lang::Code new_language = Lang::Code::VALENCIAN; // Idioma en espera de aplicar
|
||||||
Difficulty::Code new_difficulty{Difficulty::Code::NORMAL}; // Dificultad en espera de aplicar
|
Difficulty::Code new_difficulty = Difficulty::Code::NORMAL; // Dificultad en espera de aplicar
|
||||||
bool has_pending_changes{false}; // Indica si hay cambios pendientes
|
bool has_pending_changes = false; // Indica si hay cambios pendientes
|
||||||
|
|
||||||
// Constructor por defecto con valores iniciales
|
|
||||||
PendingChanges() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables ---
|
// --- Variables ---
|
||||||
|
|||||||
@@ -97,9 +97,8 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
|
|||||||
{"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }},
|
{"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }},
|
||||||
{"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }},
|
{"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }},
|
||||||
{"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }},
|
{"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }},
|
||||||
{"fade.random_squares_delay", [](const std::string& v) { param.fade.random_squares_delay = std::stoi(v); }},
|
{"fade.random_squares_duration_ms", [](const std::string& v) { param.fade.random_squares_duration_ms = std::stoi(v); }},
|
||||||
{"fade.random_squares_mult", [](const std::string& v) { param.fade.random_squares_mult = std::stoi(v); }},
|
{"fade.post_duration_ms", [](const std::string& v) { param.fade.post_duration_ms = std::stoi(v); }},
|
||||||
{"fade.post_duration", [](const std::string& v) { param.fade.post_duration = std::stoi(v); }},
|
|
||||||
{"fade.venetian_size", [](const std::string& v) { param.fade.venetian_size = std::stoi(v); }},
|
{"fade.venetian_size", [](const std::string& v) { param.fade.venetian_size = std::stoi(v); }},
|
||||||
{"scoreboard.rect.x", [](const std::string& v) { param.scoreboard.rect.x = std::stoi(v); }},
|
{"scoreboard.rect.x", [](const std::string& v) { param.scoreboard.rect.x = std::stoi(v); }},
|
||||||
{"scoreboard.rect.y", [](const std::string& v) { param.scoreboard.rect.y = std::stoi(v); }},
|
{"scoreboard.rect.y", [](const std::string& v) { param.scoreboard.rect.y = std::stoi(v); }},
|
||||||
@@ -136,6 +135,15 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
|
|||||||
{"intro.shadow_color", [](const std::string& v) { param.intro.shadow_color = Color::fromHex(v); }},
|
{"intro.shadow_color", [](const std::string& v) { param.intro.shadow_color = Color::fromHex(v); }},
|
||||||
{"debug.color", [](const std::string& v) { param.debug.color = Color::fromHex(v); }},
|
{"debug.color", [](const std::string& v) { param.debug.color = Color::fromHex(v); }},
|
||||||
{"resource.color", [](const std::string& v) { param.resource.color = Color::fromHex(v); }},
|
{"resource.color", [](const std::string& v) { param.resource.color = Color::fromHex(v); }},
|
||||||
|
{"game.item_text_outline_color", [](const std::string& v) { param.game.item_text_outline_color = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[0].darkest", [](const std::string& v) { param.player.default_shirt[0].darkest = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[0].dark", [](const std::string& v) { param.player.default_shirt[0].dark = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[0].base", [](const std::string& v) { param.player.default_shirt[0].base = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[0].light", [](const std::string& v) { param.player.default_shirt[0].light = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[1].darkest", [](const std::string& v) { param.player.default_shirt[1].darkest = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[1].dark", [](const std::string& v) { param.player.default_shirt[1].dark = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[1].base", [](const std::string& v) { param.player.default_shirt[1].base = Color::fromHex(v); }},
|
||||||
|
{"player.default_shirt[1].light", [](const std::string& v) { param.player.default_shirt[1].light = Color::fromHex(v); }},
|
||||||
{"player.one_coffee_shirt[0].darkest", [](const std::string& v) { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }},
|
{"player.one_coffee_shirt[0].darkest", [](const std::string& v) { param.player.one_coffee_shirt[0].darkest = Color::fromHex(v); }},
|
||||||
{"player.one_coffee_shirt[0].dark", [](const std::string& v) { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }},
|
{"player.one_coffee_shirt[0].dark", [](const std::string& v) { param.player.one_coffee_shirt[0].dark = Color::fromHex(v); }},
|
||||||
{"player.one_coffee_shirt[0].base", [](const std::string& v) { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }},
|
{"player.one_coffee_shirt[0].base", [](const std::string& v) { param.player.one_coffee_shirt[0].base = Color::fromHex(v); }},
|
||||||
@@ -151,7 +159,9 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
|
|||||||
{"player.two_coffee_shirt[1].darkest", [](const std::string& v) { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }},
|
{"player.two_coffee_shirt[1].darkest", [](const std::string& v) { param.player.two_coffee_shirt[1].darkest = Color::fromHex(v); }},
|
||||||
{"player.two_coffee_shirt[1].dark", [](const std::string& v) { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }},
|
{"player.two_coffee_shirt[1].dark", [](const std::string& v) { param.player.two_coffee_shirt[1].dark = Color::fromHex(v); }},
|
||||||
{"player.two_coffee_shirt[1].base", [](const std::string& v) { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }},
|
{"player.two_coffee_shirt[1].base", [](const std::string& v) { param.player.two_coffee_shirt[1].base = Color::fromHex(v); }},
|
||||||
{"player.two_coffee_shirt[1].light", [](const std::string& v) { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }}};
|
{"player.two_coffee_shirt[1].light", [](const std::string& v) { param.player.two_coffee_shirt[1].light = Color::fromHex(v); }},
|
||||||
|
{"player.outline_color[0]", [](const std::string& v) { param.player.outline_color[0] = Color::fromHex(v); }},
|
||||||
|
{"player.outline_color[1]", [](const std::string& v) { param.player.outline_color[1] = Color::fromHex(v); }}};
|
||||||
|
|
||||||
static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
|
static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
|
||||||
{"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }},
|
{"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }},
|
||||||
@@ -184,11 +194,50 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
|
|||||||
|
|
||||||
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = {};
|
static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS_EXTRA = {};
|
||||||
|
|
||||||
|
// Colores válidos para globos
|
||||||
|
static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
|
||||||
|
{"blue", true},
|
||||||
|
{"orange", true},
|
||||||
|
{"red", true},
|
||||||
|
{"green", true}};
|
||||||
|
|
||||||
|
auto validateBalloonColor = [](const std::string& color) -> bool {
|
||||||
|
return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end();
|
||||||
|
};
|
||||||
|
|
||||||
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
|
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
|
||||||
{"balloon.color[0]", [](const std::string& v) { param.balloon.color.at(0) = v; }},
|
{"balloon.color[0]", [validateBalloonColor](const std::string& v) {
|
||||||
{"balloon.color[1]", [](const std::string& v) { param.balloon.color.at(1) = v; }},
|
if (!validateBalloonColor(v)) {
|
||||||
{"balloon.color[2]", [](const std::string& v) { param.balloon.color.at(2) = v; }},
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str());
|
||||||
{"balloon.color[3]", [](const std::string& v) { param.balloon.color.at(3) = v; }}};
|
param.balloon.color.at(0) = "blue";
|
||||||
|
} else {
|
||||||
|
param.balloon.color.at(0) = v;
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"balloon.color[1]", [validateBalloonColor](const std::string& v) {
|
||||||
|
if (!validateBalloonColor(v)) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str());
|
||||||
|
param.balloon.color.at(1) = "orange";
|
||||||
|
} else {
|
||||||
|
param.balloon.color.at(1) = v;
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"balloon.color[2]", [validateBalloonColor](const std::string& v) {
|
||||||
|
if (!validateBalloonColor(v)) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str());
|
||||||
|
param.balloon.color.at(2) = "red";
|
||||||
|
} else {
|
||||||
|
param.balloon.color.at(2) = v;
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"balloon.color[3]", [validateBalloonColor](const std::string& v) {
|
||||||
|
if (!validateBalloonColor(v)) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str());
|
||||||
|
param.balloon.color.at(3) = "green";
|
||||||
|
} else {
|
||||||
|
param.balloon.color.at(3) = v;
|
||||||
|
}
|
||||||
|
}}};
|
||||||
|
|
||||||
// Lambda para intentar cada mapa de parámetros
|
// Lambda para intentar cada mapa de parámetros
|
||||||
auto try_map = [&](const auto& param_map) -> bool {
|
auto try_map = [&](const auto& param_map) -> bool {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ struct ParamGame {
|
|||||||
Uint32 speed = 15; // Este valor no estaba en el archivo de configuración
|
Uint32 speed = 15; // Este valor no estaba en el archivo de configuración
|
||||||
bool hit_stop = GameDefaults::Game::HIT_STOP;
|
bool hit_stop = GameDefaults::Game::HIT_STOP;
|
||||||
Uint32 hit_stop_ms = GameDefaults::Game::HIT_STOP_MS;
|
Uint32 hit_stop_ms = GameDefaults::Game::HIT_STOP_MS;
|
||||||
|
Color item_text_outline_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Parámetros del fade ---
|
// --- Parámetros del fade ---
|
||||||
@@ -29,9 +30,8 @@ struct ParamFade {
|
|||||||
Color color = Color::fromHex(GameDefaults::Fade::COLOR);
|
Color color = Color::fromHex(GameDefaults::Fade::COLOR);
|
||||||
float num_squares_width = GameDefaults::Fade::NUM_SQUARES_WIDTH;
|
float num_squares_width = GameDefaults::Fade::NUM_SQUARES_WIDTH;
|
||||||
float num_squares_height = GameDefaults::Fade::NUM_SQUARES_HEIGHT;
|
float num_squares_height = GameDefaults::Fade::NUM_SQUARES_HEIGHT;
|
||||||
int random_squares_delay = GameDefaults::Fade::RANDOM_SQUARES_DELAY;
|
int random_squares_duration_ms = GameDefaults::Fade::RANDOM_SQUARES_DURATION_MS;
|
||||||
int random_squares_mult = GameDefaults::Fade::RANDOM_SQUARES_MULT;
|
int post_duration_ms = GameDefaults::Fade::POST_DURATION_MS;
|
||||||
int post_duration = GameDefaults::Fade::POST_DURATION;
|
|
||||||
float venetian_size = GameDefaults::Fade::VENETIAN_SIZE;
|
float venetian_size = GameDefaults::Fade::VENETIAN_SIZE;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,7 +57,8 @@ struct ParamBalloon {
|
|||||||
|
|
||||||
// Constructor por defecto
|
// Constructor por defecto
|
||||||
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
|
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
|
||||||
: grav(grav_val), vel(vel_val) {}
|
: grav(grav_val),
|
||||||
|
vel(vel_val) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inicialización con los valores por defecto desde GameDefaults
|
// Inicialización con los valores por defecto desde GameDefaults
|
||||||
@@ -164,27 +165,58 @@ struct ParamPlayer {
|
|||||||
|
|
||||||
// Constructor con tonalidades específicas
|
// Constructor con tonalidades específicas
|
||||||
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
|
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
|
||||||
: darkest(darkest_tone), dark(dark_tone), base(base_tone), light(light_tone) {}
|
: darkest(darkest_tone),
|
||||||
|
dark(dark_tone),
|
||||||
|
base(base_tone),
|
||||||
|
light(light_tone) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inicialización con valores por defecto
|
// Inicialización con valores por defecto
|
||||||
std::array<Shirt, 2> one_coffee_shirt = {{Shirt(Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARKEST),
|
const Shirt default_player0_shirt = Shirt(
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARK),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_DARKEST),
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_BASE),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_DARK),
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_LIGHT)),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_BASE),
|
||||||
Shirt(Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARKEST),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER0_LIGHT));
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARK),
|
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_BASE),
|
|
||||||
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_LIGHT))}};
|
|
||||||
|
|
||||||
std::array<Shirt, 2> two_coffee_shirt = {{Shirt(Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARKEST),
|
const Shirt default_player1_shirt = Shirt(
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARK),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_DARKEST),
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_BASE),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_DARK),
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_LIGHT)),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_BASE),
|
||||||
Shirt(Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARKEST),
|
Color::fromHex(GameDefaults::Player::DefaultShirt::PLAYER1_LIGHT));
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARK),
|
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_BASE),
|
std::array<Shirt, 2> default_shirt = {default_player0_shirt, default_player1_shirt};
|
||||||
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_LIGHT))}};
|
|
||||||
|
const Shirt one_coffee_player0_shirt = Shirt(
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARKEST),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_DARK),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_BASE),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER0_LIGHT));
|
||||||
|
|
||||||
|
const Shirt one_coffee_player1_shirt = Shirt(
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARKEST),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_DARK),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_BASE),
|
||||||
|
Color::fromHex(GameDefaults::Player::OneCoffeeShirt::PLAYER1_LIGHT));
|
||||||
|
|
||||||
|
std::array<Shirt, 2> one_coffee_shirt = {one_coffee_player0_shirt, one_coffee_player1_shirt};
|
||||||
|
|
||||||
|
const Shirt two_coffee_player0_shirt = Shirt(
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARKEST),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_DARK),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_BASE),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER0_LIGHT));
|
||||||
|
|
||||||
|
const Shirt two_coffee_player1_shirt = Shirt(
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARKEST),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_DARK),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_BASE),
|
||||||
|
Color::fromHex(GameDefaults::Player::TwoCoffeeShirt::PLAYER1_LIGHT));
|
||||||
|
|
||||||
|
std::array<Shirt, 2> two_coffee_shirt = {two_coffee_player0_shirt, two_coffee_player1_shirt};
|
||||||
|
|
||||||
|
const Color outline_player0_color = Color::fromHex(GameDefaults::Player::OutlineColor::PLAYER0);
|
||||||
|
const Color outline_player1_color = Color::fromHex(GameDefaults::Player::OutlineColor::PLAYER1);
|
||||||
|
std::array<Color, 2> outline_color = {outline_player0_color, outline_player1_color};
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Estructura Param: almacena todos los parámetros del juego ---
|
// --- Estructura Param: almacena todos los parámetros del juego ---
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ struct Path { // Define un recorrido para el sprite
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
|
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
|
||||||
: spots(spots_init), waiting_counter(waiting_counter_init) {}
|
: spots(spots_init),
|
||||||
|
waiting_counter(waiting_counter_init) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ void Player::handlePlayingMovement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Player::handleRecoverMovement() {
|
void Player::handleRecoverMovement() {
|
||||||
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_brbrbr.wav"); }
|
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_recover.wav"); }
|
||||||
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
|
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,6 +595,14 @@ void Player::update() {
|
|||||||
updateShowingName();
|
updateShowingName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::passShowingName() {
|
||||||
|
if (game_completed_) {
|
||||||
|
setPlayingState(State::LEAVING_SCREEN);
|
||||||
|
} else {
|
||||||
|
setPlayingState(State::CONTINUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Incrementa la puntuación del jugador
|
// Incrementa la puntuación del jugador
|
||||||
void Player::addScore(int score, int lowest_hi_score_entry) {
|
void Player::addScore(int score, int lowest_hi_score_entry) {
|
||||||
if (isPlaying()) {
|
if (isPlaying()) {
|
||||||
@@ -634,6 +642,9 @@ void Player::setPlayingState(State state) {
|
|||||||
|
|
||||||
switch (playing_state_) {
|
switch (playing_state_) {
|
||||||
case State::RECOVER: {
|
case State::RECOVER: {
|
||||||
|
score_ = 0; // Pon los puntos a cero para que no se vea en el marcador
|
||||||
|
score_multiplier_ = 1.0F;
|
||||||
|
setScoreboardMode(Scoreboard::Mode::SCORE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::RESPAWNING: {
|
case State::RESPAWNING: {
|
||||||
@@ -788,15 +799,30 @@ void Player::setInvulnerable(bool value) {
|
|||||||
|
|
||||||
// Monitoriza el estado
|
// Monitoriza el estado
|
||||||
void Player::updateInvulnerable() {
|
void Player::updateInvulnerable() {
|
||||||
if (playing_state_ == State::PLAYING) {
|
if (playing_state_ == State::PLAYING && invulnerable_) {
|
||||||
if (invulnerable_) {
|
if (invulnerable_counter_ > 0) {
|
||||||
if (invulnerable_counter_ > 0) {
|
--invulnerable_counter_;
|
||||||
--invulnerable_counter_;
|
|
||||||
invulnerable_counter_ % 8 > 3 ? player_sprite_->setActiveTexture(coffees_) : player_sprite_->setActiveTexture(3);
|
// Frecuencia fija de parpadeo (como el original)
|
||||||
} else {
|
constexpr int blink_speed = 8;
|
||||||
setInvulnerable(false);
|
|
||||||
player_sprite_->setActiveTexture(coffees_);
|
// Calcula proporción decreciente: menos textura blanca hacia el final
|
||||||
|
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco)
|
||||||
|
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER);
|
||||||
|
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed);
|
||||||
|
|
||||||
|
// Alterna entre texturas con proporción variable
|
||||||
|
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames;
|
||||||
|
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
|
||||||
|
|
||||||
|
// Solo cambia textura si es diferente (optimización)
|
||||||
|
if (player_sprite_->getActiveTexture() != target_texture) {
|
||||||
|
player_sprite_->setActiveTexture(target_texture);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Fin de invulnerabilidad
|
||||||
|
setInvulnerable(false);
|
||||||
|
player_sprite_->setActiveTexture(coffees_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class Player {
|
|||||||
[[nodiscard]] static auto getWidth() -> int { return WIDTH; }
|
[[nodiscard]] static auto getWidth() -> int { return WIDTH; }
|
||||||
[[nodiscard]] auto getPlayingState() const -> State { return playing_state_; }
|
[[nodiscard]] auto getPlayingState() const -> State { return playing_state_; }
|
||||||
[[nodiscard]] auto getName() const -> const std::string & { return name_; }
|
[[nodiscard]] auto getName() const -> const std::string & { return name_; }
|
||||||
[[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ == 1; }
|
[[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ <= 1; }
|
||||||
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
|
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
|
||||||
|
|
||||||
// Setters inline
|
// Setters inline
|
||||||
@@ -186,6 +186,7 @@ class Player {
|
|||||||
void setWalkingState(State state) { walking_state_ = state; }
|
void setWalkingState(State state) { walking_state_ = state; }
|
||||||
|
|
||||||
void addCredit();
|
void addCredit();
|
||||||
|
void passShowingName();
|
||||||
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
|
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
|
||||||
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
|
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
|
||||||
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
|
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
|
||||||
@@ -193,12 +194,13 @@ class Player {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Constantes ---
|
// --- Constantes ---
|
||||||
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp
|
static constexpr int POWERUP_COUNTER = 1500; // Duración del estado PowerUp
|
||||||
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable
|
static constexpr int INVULNERABLE_COUNTER = 200; // Duración del estado invulnerable
|
||||||
static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador
|
static constexpr size_t INVULNERABLE_TEXTURE = 3; // Textura usada durante invulnerabilidad
|
||||||
static constexpr int COOLING_DURATION = 50;
|
static constexpr float BASE_SPEED = 1.5F; // Velocidad base del jugador
|
||||||
static constexpr int COOLING_COMPLETE = 0;
|
static constexpr int COOLING_DURATION = 50; // Duración del enfriamiento tras disparar
|
||||||
static constexpr int WAITING_COUNTER = 1000;
|
static constexpr int COOLING_COMPLETE = 0; // Valor que indica enfriamiento completado
|
||||||
|
static constexpr int WAITING_COUNTER = 1000; // Tiempo de espera en estado de espera
|
||||||
|
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
|
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
|
||||||
|
|||||||
@@ -2,25 +2,58 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
|
||||||
#include <algorithm> // Para find_if, max, find
|
#include <algorithm> // Para find_if, max, find
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <cstdlib> // Para exit
|
#include <cstdlib> // Para exit, getenv
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <filesystem> // Para filesystem::remove, filesystem::exists
|
||||||
#include <utility> // Para move
|
#include <fstream> // Para ofstream
|
||||||
|
#include <stdexcept> // Para runtime_error
|
||||||
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "asset.h" // Para Asset
|
#include "asset.h" // Para Asset
|
||||||
#include "color.h" // Para Color
|
#include "color.h" // Para Color
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
||||||
#endif
|
#endif
|
||||||
#include "lang.h" // Para getText
|
#include "lang.h" // Para getText
|
||||||
#include "param.h" // Para Param, param, ParamResource, ParamGame
|
#include "param.h" // Para Param, param, ParamResource, ParamGame
|
||||||
#include "screen.h" // Para Screen
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "text.h" // Para Text
|
#include "screen.h" // Para Screen
|
||||||
|
#include "text.h" // Para Text
|
||||||
|
|
||||||
struct JA_Music_t; // lines 11-11
|
struct JA_Music_t; // lines 11-11
|
||||||
struct JA_Sound_t; // lines 12-12
|
struct JA_Sound_t; // lines 12-12
|
||||||
|
|
||||||
|
// Helper para cargar archivos de audio desde pack o filesystem
|
||||||
|
namespace {
|
||||||
|
std::string createTempAudioFile(const std::string &file_path, std::vector<std::string> &temp_files_tracker) {
|
||||||
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
// Crear archivo temporal
|
||||||
|
std::string temp_dir;
|
||||||
|
#ifdef _WIN32
|
||||||
|
temp_dir = std::getenv("TEMP") ? std::getenv("TEMP") : "C:\\temp";
|
||||||
|
#else
|
||||||
|
temp_dir = "/tmp";
|
||||||
|
#endif
|
||||||
|
std::string temp_path = temp_dir + "/ccae_audio_" + std::to_string(std::hash<std::string>{}(file_path));
|
||||||
|
std::ofstream temp_file(temp_path, std::ios::binary);
|
||||||
|
if (!temp_file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot create temp file %s", temp_path.c_str());
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
temp_file.write(reinterpret_cast<const char *>(resource_data.data()), resource_data.size());
|
||||||
|
temp_file.close();
|
||||||
|
|
||||||
|
// Agregar a la lista de archivos temporales para limpieza posterior
|
||||||
|
temp_files_tracker.push_back(temp_path);
|
||||||
|
|
||||||
|
return temp_path;
|
||||||
|
}
|
||||||
|
return file_path; // Usar ruta original si no está en pack
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// Declaraciones de funciones que necesitas implementar en otros archivos
|
// Declaraciones de funciones que necesitas implementar en otros archivos
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
@@ -42,7 +75,8 @@ auto Resource::get() -> Resource * { return Resource::instance; }
|
|||||||
|
|
||||||
// Constructor con modo de carga
|
// Constructor con modo de carga
|
||||||
Resource::Resource(LoadingMode mode)
|
Resource::Resource(LoadingMode mode)
|
||||||
: loading_mode_(mode), loading_text_(nullptr) {
|
: loading_mode_(mode),
|
||||||
|
loading_text_(nullptr) {
|
||||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||||
loading_text_ = Screen::get()->getText();
|
loading_text_ = Screen::get()->getText();
|
||||||
load();
|
load();
|
||||||
@@ -55,6 +89,7 @@ Resource::Resource(LoadingMode mode)
|
|||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
Resource::~Resource() {
|
Resource::~Resource() {
|
||||||
|
cleanupTempAudioFiles();
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +328,8 @@ auto Resource::loadSoundLazy(const std::string &name) -> JA_Sound_t * {
|
|||||||
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) {
|
||||||
return JA_LoadSound(file.c_str());
|
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
|
||||||
|
return JA_LoadSound(audio_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -306,7 +342,8 @@ auto Resource::loadMusicLazy(const std::string &name) -> JA_Music_t * {
|
|||||||
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) {
|
||||||
return JA_LoadMusic(file.c_str());
|
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
|
||||||
|
return JA_LoadMusic(audio_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -448,7 +485,8 @@ void Resource::loadSounds() {
|
|||||||
auto name = getFileName(l);
|
auto name = getFileName(l);
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
sounds_.emplace_back(name, JA_LoadSound(l.c_str()));
|
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
|
||||||
|
sounds_.emplace_back(name, JA_LoadSound(audio_path.c_str()));
|
||||||
#else
|
#else
|
||||||
sounds_.emplace_back(name, nullptr);
|
sounds_.emplace_back(name, nullptr);
|
||||||
#endif
|
#endif
|
||||||
@@ -466,7 +504,8 @@ void Resource::loadMusics() {
|
|||||||
auto name = getFileName(l);
|
auto name = getFileName(l);
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
musics_.emplace_back(name, JA_LoadMusic(l.c_str()));
|
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
|
||||||
|
musics_.emplace_back(name, JA_LoadMusic(audio_path.c_str()));
|
||||||
#else
|
#else
|
||||||
musics_.emplace_back(name, nullptr);
|
musics_.emplace_back(name, nullptr);
|
||||||
#endif
|
#endif
|
||||||
@@ -540,7 +579,7 @@ void Resource::createPlayerTextures() {
|
|||||||
{.base_texture = "player1.gif", .palette_files = {"player1_coffee1.pal", "player1_coffee2.pal", "player1_invencible.pal"}, .name_prefix = "player1"},
|
{.base_texture = "player1.gif", .palette_files = {"player1_coffee1.pal", "player1_coffee2.pal", "player1_invencible.pal"}, .name_prefix = "player1"},
|
||||||
{.base_texture = "player2.gif", .palette_files = {"player2_coffee1.pal", "player2_coffee2.pal", "player2_invencible.pal"}, .name_prefix = "player2"}};
|
{.base_texture = "player2.gif", .palette_files = {"player2_coffee1.pal", "player2_coffee2.pal", "player2_invencible.pal"}, .name_prefix = "player2"}};
|
||||||
|
|
||||||
// Bucle principal modificado para usar un índice (player_idx)
|
// Bucle principal
|
||||||
for (size_t player_idx = 0; player_idx < players.size(); ++player_idx) {
|
for (size_t player_idx = 0; player_idx < players.size(); ++player_idx) {
|
||||||
const auto &player = players[player_idx]; // Obtenemos el jugador actual
|
const auto &player = players[player_idx]; // Obtenemos el jugador actual
|
||||||
|
|
||||||
@@ -554,40 +593,52 @@ void Resource::createPlayerTextures() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear variante con paleta original (pal0) - usar la textura ya cargada
|
// Crear las 4 texturas con sus respectivas paletas
|
||||||
auto base_texture = getTexture(player.base_texture);
|
for (int palette_idx = 0; palette_idx < 4; ++palette_idx) {
|
||||||
std::string pal0_name = player.name_prefix + "_pal0";
|
std::shared_ptr<Texture> texture;
|
||||||
textures_.emplace_back(pal0_name, base_texture);
|
|
||||||
printWithDots("Player Texture : ", pal0_name, "[ DONE ]");
|
|
||||||
|
|
||||||
// Crear variantes con paletas adicionales - CADA UNA DESDE EL ARCHIVO
|
if (palette_idx == 0) {
|
||||||
for (size_t i = 0; i < player.palette_files.size(); ++i) {
|
// Textura 0 - usar la ya cargada y modificar solo paleta 0 (default_shirt)
|
||||||
// Crear textura completamente nueva desde el archivo
|
texture = getTexture(player.base_texture);
|
||||||
auto texture_copy = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
|
texture->setPaletteColor(0, 16, param.player.default_shirt[player_idx].darkest.TO_UINT32());
|
||||||
|
texture->setPaletteColor(0, 17, param.player.default_shirt[player_idx].dark.TO_UINT32());
|
||||||
|
texture->setPaletteColor(0, 18, param.player.default_shirt[player_idx].base.TO_UINT32());
|
||||||
|
texture->setPaletteColor(0, 19, param.player.default_shirt[player_idx].light.TO_UINT32());
|
||||||
|
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||||
|
} else {
|
||||||
|
// Crear textura nueva desde archivo usando ResourceHelper
|
||||||
|
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
|
||||||
|
|
||||||
// Añadir todas las paletas
|
// Añadir todas las paletas
|
||||||
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[0]));
|
texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[0]));
|
||||||
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[1]));
|
texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[1]));
|
||||||
texture_copy->addPaletteFromPalFile(Asset::get()->get(player.palette_files[2]));
|
texture->addPaletteFromPalFile(Asset::get()->get(player.palette_files[2]));
|
||||||
|
|
||||||
// Añade los colores establecidos en param.player usando el índice del jugador (player_idx)
|
if (palette_idx == 1) {
|
||||||
texture_copy->setPaletteColor(1, 16, param.player.one_coffee_shirt[player_idx].darkest.TO_UINT32());
|
// Textura 1 - modificar solo paleta 1 (one_coffee_shirt)
|
||||||
texture_copy->setPaletteColor(1, 17, param.player.one_coffee_shirt[player_idx].dark.TO_UINT32());
|
texture->setPaletteColor(1, 16, param.player.one_coffee_shirt[player_idx].darkest.TO_UINT32());
|
||||||
texture_copy->setPaletteColor(1, 18, param.player.one_coffee_shirt[player_idx].base.TO_UINT32());
|
texture->setPaletteColor(1, 17, param.player.one_coffee_shirt[player_idx].dark.TO_UINT32());
|
||||||
texture_copy->setPaletteColor(1, 19, param.player.one_coffee_shirt[player_idx].light.TO_UINT32());
|
texture->setPaletteColor(1, 18, param.player.one_coffee_shirt[player_idx].base.TO_UINT32());
|
||||||
|
texture->setPaletteColor(1, 19, param.player.one_coffee_shirt[player_idx].light.TO_UINT32());
|
||||||
|
texture->setPaletteColor(1, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||||
|
} else if (palette_idx == 2) {
|
||||||
|
// Textura 2 - modificar solo paleta 2 (two_coffee_shirt)
|
||||||
|
texture->setPaletteColor(2, 16, param.player.two_coffee_shirt[player_idx].darkest.TO_UINT32());
|
||||||
|
texture->setPaletteColor(2, 17, param.player.two_coffee_shirt[player_idx].dark.TO_UINT32());
|
||||||
|
texture->setPaletteColor(2, 18, param.player.two_coffee_shirt[player_idx].base.TO_UINT32());
|
||||||
|
texture->setPaletteColor(2, 19, param.player.two_coffee_shirt[player_idx].light.TO_UINT32());
|
||||||
|
texture->setPaletteColor(2, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||||
|
}
|
||||||
|
// Textura 3 (palette_idx == 3) - no modificar nada, usar colores originales
|
||||||
|
}
|
||||||
|
|
||||||
texture_copy->setPaletteColor(2, 16, param.player.two_coffee_shirt[player_idx].darkest.TO_UINT32());
|
// Asignar la paleta correspondiente
|
||||||
texture_copy->setPaletteColor(2, 17, param.player.two_coffee_shirt[player_idx].dark.TO_UINT32());
|
texture->setPalette(palette_idx);
|
||||||
texture_copy->setPaletteColor(2, 18, param.player.two_coffee_shirt[player_idx].base.TO_UINT32());
|
|
||||||
texture_copy->setPaletteColor(2, 19, param.player.two_coffee_shirt[player_idx].light.TO_UINT32());
|
|
||||||
|
|
||||||
// Cambiar a la paleta específica (índice i+1 porque 0 es la original)
|
|
||||||
texture_copy->setPalette(i + 1);
|
|
||||||
|
|
||||||
// Guardar con nombre específico
|
// Guardar con nombre específico
|
||||||
std::string variant_name = player.name_prefix + "_pal" + std::to_string(i + 1);
|
std::string texture_name = player.name_prefix + "_pal" + std::to_string(palette_idx);
|
||||||
textures_.emplace_back(variant_name, texture_copy);
|
textures_.emplace_back(texture_name, texture);
|
||||||
printWithDots("Player Texture : ", variant_name, "[ DONE ]");
|
printWithDots("Player Texture : ", texture_name, "[ DONE ]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -599,13 +650,14 @@ void Resource::createTextTextures() {
|
|||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
NameAndText(std::string name_init, std::string text_init)
|
NameAndText(std::string name_init, std::string text_init)
|
||||||
: name(std::move(name_init)), text(std::move(text_init)) {}
|
: name(std::move(name_init)),
|
||||||
|
text(std::move(text_init)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
|
||||||
|
|
||||||
// Texturas de tamaño normal
|
// Texturas de tamaño normal con outline
|
||||||
std::vector<NameAndText> strings = {
|
std::vector<NameAndText> strings1 = {
|
||||||
{"game_text_1000_points", "1.000"},
|
{"game_text_1000_points", "1.000"},
|
||||||
{"game_text_2500_points", "2.500"},
|
{"game_text_2500_points", "2.500"},
|
||||||
{"game_text_5000_points", "5.000"},
|
{"game_text_5000_points", "5.000"},
|
||||||
@@ -614,23 +666,23 @@ void Resource::createTextTextures() {
|
|||||||
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")},
|
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")},
|
||||||
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
|
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
|
||||||
|
|
||||||
auto text = getText("04b_25");
|
auto text1 = getText("04b_25_enhanced");
|
||||||
for (const auto &s : strings) {
|
for (const auto &s : strings1) {
|
||||||
textures_.emplace_back(s.name, text->writeToTexture(s.text, 1, -2));
|
textures_.emplace_back(s.name, text1->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
printWithDots("Texture : ", s.name, "[ DONE ]");
|
printWithDots("Texture : ", s.name, "[ DONE ]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texturas de tamaño doble
|
// Texturas de tamaño doble
|
||||||
std::vector<NameAndText> strings2_x = {
|
std::vector<NameAndText> strings2 = {
|
||||||
{"game_text_100000_points", "100.000"},
|
{"game_text_100000_points", "100.000"},
|
||||||
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
|
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
|
||||||
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
|
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
|
||||||
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
|
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
|
||||||
{"game_text_game_over", "Game Over"}};
|
{"game_text_game_over", "Game Over"}};
|
||||||
|
|
||||||
auto text2 = getText("04b_25_2x");
|
auto text2 = getText("04b_25_2x_enhanced");
|
||||||
for (const auto &s : strings2_x) {
|
for (const auto &s : strings2) {
|
||||||
textures_.emplace_back(s.name, text2->writeToTexture(s.text, 1, -4));
|
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
printWithDots("Texture : ", s.name, "[ DONE ]");
|
printWithDots("Texture : ", s.name, "[ DONE ]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,16 +693,23 @@ void Resource::createText() {
|
|||||||
std::string key;
|
std::string key;
|
||||||
std::string texture_file;
|
std::string texture_file;
|
||||||
std::string text_file;
|
std::string text_file;
|
||||||
|
std::string white_texture_file; // Textura blanca opcional
|
||||||
|
|
||||||
ResourceInfo(std::string k, std::string t_file, std::string txt_file)
|
ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
|
||||||
: key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)) {}
|
: key(std::move(k)),
|
||||||
|
texture_file(std::move(t_file)),
|
||||||
|
text_file(std::move(txt_file)),
|
||||||
|
white_texture_file(std::move(w_file)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> 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_white", "04b_25_white.png", "04b_25.txt"},
|
||||||
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
|
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
|
||||||
|
{"04b_25_2x_enhanced", "04b_25_2x.png", "04b_25_2x.txt", "04b_25_2x_white.png"}, // Nueva fuente con textura blanca
|
||||||
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
|
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
|
||||||
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
|
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
|
||||||
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
|
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
|
||||||
@@ -663,7 +722,13 @@ void Resource::createText() {
|
|||||||
{"smb2_grad", "smb2_grad.png", "smb2.txt"}};
|
{"smb2_grad", "smb2_grad.png", "smb2.txt"}};
|
||||||
|
|
||||||
for (const auto &resource : resources) {
|
for (const auto &resource : resources) {
|
||||||
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
|
if (!resource.white_texture_file.empty()) {
|
||||||
|
// Crear texto con textura blanca
|
||||||
|
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTexture(resource.white_texture_file), getTextFile(resource.text_file)));
|
||||||
|
} else {
|
||||||
|
// Crear texto normal
|
||||||
|
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
|
||||||
|
}
|
||||||
printWithDots("Text : ", resource.key, "[ DONE ]");
|
printWithDots("Text : ", resource.key, "[ DONE ]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -803,3 +868,18 @@ void Resource::updateLoadingProgress(std::string name) {
|
|||||||
void Resource::updateProgressBar() {
|
void Resource::updateProgressBar() {
|
||||||
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
|
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Limpia archivos temporales de audio
|
||||||
|
void Resource::cleanupTempAudioFiles() {
|
||||||
|
for (const auto &temp_path : temp_audio_files_) {
|
||||||
|
try {
|
||||||
|
if (std::filesystem::exists(temp_path)) {
|
||||||
|
std::filesystem::remove(temp_path);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Removed temp audio file: %s", temp_path.c_str());
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to remove temp audio file %s: %s", temp_path.c_str(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp_audio_files_.clear();
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class Resource {
|
|||||||
JA_Sound_t *sound; // Objeto con el sonido
|
JA_Sound_t *sound; // Objeto con el sonido
|
||||||
|
|
||||||
ResourceSound(std::string name, JA_Sound_t *sound = nullptr)
|
ResourceSound(std::string name, JA_Sound_t *sound = nullptr)
|
||||||
: name(std::move(name)), sound(sound) {}
|
: name(std::move(name)),
|
||||||
|
sound(sound) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceMusic {
|
struct ResourceMusic {
|
||||||
@@ -60,7 +61,8 @@ class Resource {
|
|||||||
JA_Music_t *music; // Objeto con la música
|
JA_Music_t *music; // Objeto con la música
|
||||||
|
|
||||||
ResourceMusic(std::string name, JA_Music_t *music = nullptr)
|
ResourceMusic(std::string name, JA_Music_t *music = nullptr)
|
||||||
: name(std::move(name)), music(music) {}
|
: name(std::move(name)),
|
||||||
|
music(music) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceTexture {
|
struct ResourceTexture {
|
||||||
@@ -68,7 +70,8 @@ class Resource {
|
|||||||
std::shared_ptr<Texture> texture; // Objeto con la textura
|
std::shared_ptr<Texture> texture; // Objeto con la textura
|
||||||
|
|
||||||
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
|
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
|
||||||
: name(std::move(name)), texture(std::move(texture)) {}
|
: name(std::move(name)),
|
||||||
|
texture(std::move(texture)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceTextFile {
|
struct ResourceTextFile {
|
||||||
@@ -76,7 +79,8 @@ class Resource {
|
|||||||
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
|
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
|
||||||
|
|
||||||
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
|
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
|
||||||
: name(std::move(name)), text_file(std::move(text_file)) {}
|
: name(std::move(name)),
|
||||||
|
text_file(std::move(text_file)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceText {
|
struct ResourceText {
|
||||||
@@ -84,7 +88,8 @@ class Resource {
|
|||||||
std::shared_ptr<Text> text; // Objeto de texto
|
std::shared_ptr<Text> text; // Objeto de texto
|
||||||
|
|
||||||
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
|
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
|
||||||
: name(std::move(name)), text(std::move(text)) {}
|
: name(std::move(name)),
|
||||||
|
text(std::move(text)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceAnimation {
|
struct ResourceAnimation {
|
||||||
@@ -92,7 +97,8 @@ class Resource {
|
|||||||
AnimationsFileBuffer animation; // Objeto con las animaciones
|
AnimationsFileBuffer animation; // Objeto con las animaciones
|
||||||
|
|
||||||
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
|
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
|
||||||
: name(std::move(name)), animation(std::move(animation)) {}
|
: name(std::move(name)),
|
||||||
|
animation(std::move(animation)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Estructura para el progreso de carga ---
|
// --- Estructura para el progreso de carga ---
|
||||||
@@ -100,8 +106,12 @@ class Resource {
|
|||||||
size_t total; // Número total de recursos
|
size_t total; // Número total de recursos
|
||||||
size_t loaded; // Número de recursos cargados
|
size_t loaded; // Número de recursos cargados
|
||||||
|
|
||||||
ResourceCount() : total(0), loaded(0) {}
|
ResourceCount()
|
||||||
ResourceCount(size_t total) : total(total), loaded(0) {}
|
: total(0),
|
||||||
|
loaded(0) {}
|
||||||
|
ResourceCount(size_t total)
|
||||||
|
: total(total),
|
||||||
|
loaded(0) {}
|
||||||
|
|
||||||
void add(size_t amount) { loaded += amount; }
|
void add(size_t amount) { loaded += amount; }
|
||||||
void increase() { loaded++; }
|
void increase() { loaded++; }
|
||||||
@@ -129,6 +139,9 @@ class Resource {
|
|||||||
SDL_FRect loading_wired_rect_;
|
SDL_FRect loading_wired_rect_;
|
||||||
SDL_FRect loading_full_rect_;
|
SDL_FRect loading_full_rect_;
|
||||||
|
|
||||||
|
// --- Archivos temporales ---
|
||||||
|
std::vector<std::string> temp_audio_files_; // Rutas de archivos temporales de audio para limpieza
|
||||||
|
|
||||||
// --- Métodos internos de carga y gestión ---
|
// --- Métodos internos de carga y gestión ---
|
||||||
void loadSounds(); // Carga los sonidos
|
void loadSounds(); // Carga los sonidos
|
||||||
void loadMusics(); // Carga las músicas
|
void loadMusics(); // Carga las músicas
|
||||||
@@ -147,6 +160,7 @@ class Resource {
|
|||||||
void load(); // Carga todos los recursos
|
void load(); // Carga todos los recursos
|
||||||
void clearSounds(); // Vacía el vector de sonidos
|
void clearSounds(); // Vacía el vector de sonidos
|
||||||
void clearMusics(); // Vacía el vector de músicas
|
void clearMusics(); // Vacía el vector de músicas
|
||||||
|
void cleanupTempAudioFiles(); // Limpia archivos temporales de audio
|
||||||
|
|
||||||
// --- Métodos para carga perezosa ---
|
// --- Métodos para carga perezosa ---
|
||||||
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido
|
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido
|
||||||
|
|||||||
96
source/resource_helper.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "resource_helper.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ResourceHelper {
|
||||||
|
static bool resource_system_initialized = false;
|
||||||
|
|
||||||
|
bool initializeResourceSystem(const std::string& pack_file) {
|
||||||
|
auto& loader = ResourceLoader::getInstance();
|
||||||
|
resource_system_initialized = loader.initialize(pack_file, true);
|
||||||
|
|
||||||
|
if (resource_system_initialized) {
|
||||||
|
std::cout << "Resource system initialized with pack: " << pack_file << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Resource system using fallback mode (filesystem only)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Always return true as fallback is acceptable
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownResourceSystem() {
|
||||||
|
if (resource_system_initialized) {
|
||||||
|
ResourceLoader::getInstance().shutdown();
|
||||||
|
resource_system_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> loadFile(const std::string& filepath) {
|
||||||
|
if (resource_system_initialized && shouldUseResourcePack(filepath)) {
|
||||||
|
auto& loader = ResourceLoader::getInstance();
|
||||||
|
std::string pack_path = getPackPath(filepath);
|
||||||
|
|
||||||
|
auto data = loader.loadResource(pack_path);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a filesystem
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldUseResourcePack(const std::string& filepath) {
|
||||||
|
// Archivos que NO van al pack:
|
||||||
|
// - config/ (ahora está fuera de data/)
|
||||||
|
// - archivos absolutos del sistema
|
||||||
|
|
||||||
|
if (filepath.find("config/") != std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si contiene "data/" es candidato para el pack
|
||||||
|
if (filepath.find("data/") != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getPackPath(const std::string& asset_path) {
|
||||||
|
std::string pack_path = asset_path;
|
||||||
|
|
||||||
|
// Normalizar separadores de path a '/'
|
||||||
|
std::replace(pack_path.begin(), pack_path.end(), '\\', '/');
|
||||||
|
|
||||||
|
// Remover prefijo "data/" si existe
|
||||||
|
size_t data_pos = pack_path.find("data/");
|
||||||
|
if (data_pos != std::string::npos) {
|
||||||
|
pack_path = pack_path.substr(data_pos + 5); // +5 para saltar "data/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remover cualquier prefijo de path absoluto
|
||||||
|
size_t last_data = pack_path.rfind("data/");
|
||||||
|
if (last_data != std::string::npos) {
|
||||||
|
pack_path = pack_path.substr(last_data + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pack_path;
|
||||||
|
}
|
||||||
|
} // namespace ResourceHelper
|
||||||
47
source/resource_helper.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
// Helper functions para integrar ResourceLoader con el sistema existente
|
||||||
|
namespace ResourceHelper {
|
||||||
|
// Inicializa ResourceLoader (llamar al inicio del programa)
|
||||||
|
bool initializeResourceSystem(const std::string& pack_file = "resources.pack");
|
||||||
|
|
||||||
|
// Cierra ResourceLoader
|
||||||
|
void shutdownResourceSystem();
|
||||||
|
|
||||||
|
// Carga un archivo usando ResourceLoader o fallback a filesystem
|
||||||
|
std::vector<uint8_t> loadFile(const std::string& filepath);
|
||||||
|
|
||||||
|
// Verifica si un archivo debería cargarse del pack vs filesystem
|
||||||
|
bool shouldUseResourcePack(const std::string& filepath);
|
||||||
|
|
||||||
|
// Convierte ruta Asset a ruta relativa para ResourceLoader
|
||||||
|
std::string getPackPath(const std::string& asset_path);
|
||||||
|
|
||||||
|
// Wrappea la carga de archivos para mantener compatibilidad
|
||||||
|
template <typename T>
|
||||||
|
T* loadResourceFile(const std::string& asset_path, T* (*loader_func)(const char*)) {
|
||||||
|
auto data = loadFile(asset_path);
|
||||||
|
if (data.empty()) {
|
||||||
|
return loader_func(asset_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear archivo temporal para funciones que esperan path
|
||||||
|
std::string temp_path = "/tmp/ccae_" + std::to_string(std::hash<std::string>{}(asset_path));
|
||||||
|
std::ofstream temp_file(temp_path, std::ios::binary);
|
||||||
|
temp_file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
|
temp_file.close();
|
||||||
|
|
||||||
|
T* result = loader_func(temp_path.c_str());
|
||||||
|
std::filesystem::remove(temp_path);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace ResourceHelper
|
||||||
135
source/resource_loader.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||||
|
|
||||||
|
ResourceLoader::ResourceLoader()
|
||||||
|
: resourcePack(nullptr),
|
||||||
|
fallbackToFiles(true) {}
|
||||||
|
|
||||||
|
ResourceLoader& ResourceLoader::getInstance() {
|
||||||
|
if (!instance) {
|
||||||
|
instance = std::unique_ptr<ResourceLoader>(new ResourceLoader());
|
||||||
|
}
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLoader::~ResourceLoader() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceLoader::initialize(const std::string& packFile, bool enableFallback) {
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
fallbackToFiles = enableFallback;
|
||||||
|
packPath = packFile;
|
||||||
|
|
||||||
|
if (std::filesystem::exists(packFile)) {
|
||||||
|
resourcePack = new ResourcePack();
|
||||||
|
if (resourcePack->loadPack(packFile)) {
|
||||||
|
std::cout << "Resource pack loaded successfully: " << packFile << std::endl;
|
||||||
|
std::cout << "Resources available: " << resourcePack->getResourceCount() << std::endl;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
delete resourcePack;
|
||||||
|
resourcePack = nullptr;
|
||||||
|
std::cerr << "Failed to load resource pack: " << packFile << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
std::cout << "Using fallback mode: loading resources from data/ directory" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Resource pack not found and fallback disabled: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceLoader::shutdown() {
|
||||||
|
if (resourcePack) {
|
||||||
|
delete resourcePack;
|
||||||
|
resourcePack = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourceLoader::loadResource(const std::string& filename) {
|
||||||
|
if (resourcePack && resourcePack->hasResource(filename)) {
|
||||||
|
return resourcePack->getResource(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
return loadFromFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Resource not found: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceLoader::resourceExists(const std::string& filename) {
|
||||||
|
if (resourcePack && resourcePack->hasResource(filename)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
std::string fullPath = getDataPath(filename);
|
||||||
|
return std::filesystem::exists(fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourceLoader::loadFromFile(const std::string& filename) {
|
||||||
|
std::string fullPath = getDataPath(filename);
|
||||||
|
|
||||||
|
std::ifstream file(fullPath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << fullPath << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << fullPath << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceLoader::getDataPath(const std::string& filename) {
|
||||||
|
return "data/" + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ResourceLoader::getLoadedResourceCount() const {
|
||||||
|
if (resourcePack) {
|
||||||
|
return resourcePack->getResourceCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ResourceLoader::getAvailableResources() const {
|
||||||
|
if (resourcePack) {
|
||||||
|
return resourcePack->getResourceList();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (fallbackToFiles && std::filesystem::exists("data")) {
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
std::string filename = std::filesystem::relative(entry.path(), "data").string();
|
||||||
|
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||||
|
result.push_back(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
38
source/resource_loader.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef RESOURCE_LOADER_H
|
||||||
|
#define RESOURCE_LOADER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "resource_pack.h"
|
||||||
|
|
||||||
|
class ResourceLoader {
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<ResourceLoader> instance;
|
||||||
|
ResourcePack* resourcePack;
|
||||||
|
std::string packPath;
|
||||||
|
bool fallbackToFiles;
|
||||||
|
|
||||||
|
ResourceLoader();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ResourceLoader& getInstance();
|
||||||
|
~ResourceLoader();
|
||||||
|
|
||||||
|
bool initialize(const std::string& packFile, bool enableFallback = true);
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
std::vector<uint8_t> loadResource(const std::string& filename);
|
||||||
|
bool resourceExists(const std::string& filename);
|
||||||
|
|
||||||
|
void setFallbackToFiles(bool enable) { fallbackToFiles = enable; }
|
||||||
|
bool getFallbackToFiles() const { return fallbackToFiles; }
|
||||||
|
|
||||||
|
size_t getLoadedResourceCount() const;
|
||||||
|
std::vector<std::string> getAvailableResources() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> loadFromFile(const std::string& filename);
|
||||||
|
std::string getDataPath(const std::string& filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
224
source/resource_pack.cpp
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
#include "resource_pack.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCAE_RESOURCES_2024";
|
||||||
|
|
||||||
|
ResourcePack::ResourcePack()
|
||||||
|
: loaded(false) {}
|
||||||
|
|
||||||
|
ResourcePack::~ResourcePack() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) {
|
||||||
|
uint32_t checksum = 0x12345678;
|
||||||
|
for (size_t i = 0; i < data.size(); ++i) {
|
||||||
|
checksum = ((checksum << 5) + checksum) + data[i];
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||||
|
if (key.empty()) return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data.size(); ++i) {
|
||||||
|
data[i] ^= key[i % key.length()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::decryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||||
|
encryptData(data, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::loadPack(const std::string& packFile) {
|
||||||
|
std::ifstream file(packFile, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open pack file: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char header[4];
|
||||||
|
file.read(header, 4);
|
||||||
|
if (std::string(header, 4) != "CCAE") {
|
||||||
|
std::cerr << "Error: Invalid pack file format" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||||
|
if (version != 1) {
|
||||||
|
std::cerr << "Error: Unsupported pack version: " << version << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t resourceCount;
|
||||||
|
file.read(reinterpret_cast<char*>(&resourceCount), sizeof(resourceCount));
|
||||||
|
|
||||||
|
resources.clear();
|
||||||
|
resources.reserve(resourceCount);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < resourceCount; ++i) {
|
||||||
|
uint32_t filenameLength;
|
||||||
|
file.read(reinterpret_cast<char*>(&filenameLength), sizeof(filenameLength));
|
||||||
|
|
||||||
|
std::string filename(filenameLength, '\0');
|
||||||
|
file.read(&filename[0], filenameLength);
|
||||||
|
|
||||||
|
ResourceEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
|
||||||
|
resources[filename] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t dataSize;
|
||||||
|
file.read(reinterpret_cast<char*>(&dataSize), sizeof(dataSize));
|
||||||
|
|
||||||
|
data.resize(dataSize);
|
||||||
|
file.read(reinterpret_cast<char*>(data.data()), dataSize);
|
||||||
|
|
||||||
|
decryptData(data, DEFAULT_ENCRYPT_KEY);
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::savePack(const std::string& packFile) {
|
||||||
|
std::ofstream file(packFile, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not create pack file: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write("CCAE", 4);
|
||||||
|
|
||||||
|
uint32_t version = 1;
|
||||||
|
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||||
|
|
||||||
|
uint32_t resourceCount = static_cast<uint32_t>(resources.size());
|
||||||
|
file.write(reinterpret_cast<const char*>(&resourceCount), sizeof(resourceCount));
|
||||||
|
|
||||||
|
for (const auto& [filename, entry] : resources) {
|
||||||
|
uint32_t filenameLength = static_cast<uint32_t>(filename.length());
|
||||||
|
file.write(reinterpret_cast<const char*>(&filenameLength), sizeof(filenameLength));
|
||||||
|
file.write(filename.c_str(), filenameLength);
|
||||||
|
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> encryptedData = data;
|
||||||
|
encryptData(encryptedData, DEFAULT_ENCRYPT_KEY);
|
||||||
|
|
||||||
|
uint64_t dataSize = encryptedData.size();
|
||||||
|
file.write(reinterpret_cast<const char*>(&dataSize), sizeof(dataSize));
|
||||||
|
file.write(reinterpret_cast<const char*>(encryptedData.data()), dataSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::addFile(const std::string& filename, const std::string& filepath) {
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> fileData(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(fileData.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
entry.offset = data.size();
|
||||||
|
entry.size = fileData.size();
|
||||||
|
entry.checksum = calculateChecksum(fileData);
|
||||||
|
|
||||||
|
data.insert(data.end(), fileData.begin(), fileData.end());
|
||||||
|
resources[filename] = entry;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::addDirectory(const std::string& directory) {
|
||||||
|
if (!std::filesystem::exists(directory)) {
|
||||||
|
std::cerr << "Error: Directory does not exist: " << directory << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
std::string filepath = entry.path().string();
|
||||||
|
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||||
|
|
||||||
|
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||||
|
|
||||||
|
if (!addFile(filename, filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourcePack::getResource(const std::string& filename) {
|
||||||
|
auto it = resources.find(filename);
|
||||||
|
if (it == resources.end()) {
|
||||||
|
std::cerr << "Error: Resource not found: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResourceEntry& entry = it->second;
|
||||||
|
if (entry.offset + entry.size > data.size()) {
|
||||||
|
std::cerr << "Error: Invalid resource data: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> result(data.begin() + entry.offset,
|
||||||
|
data.begin() + entry.offset + entry.size);
|
||||||
|
|
||||||
|
uint32_t checksum = calculateChecksum(result);
|
||||||
|
if (checksum != entry.checksum) {
|
||||||
|
std::cerr << "Warning: Checksum mismatch for resource: " << filename << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::hasResource(const std::string& filename) const {
|
||||||
|
return resources.find(filename) != resources.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::clear() {
|
||||||
|
resources.clear();
|
||||||
|
data.clear();
|
||||||
|
loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ResourcePack::getResourceCount() const {
|
||||||
|
return resources.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ResourcePack::getResourceList() const {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(resources.size());
|
||||||
|
|
||||||
|
for (const auto& [filename, entry] : resources) {
|
||||||
|
result.push_back(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
46
source/resource_pack.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef RESOURCE_PACK_H
|
||||||
|
#define RESOURCE_PACK_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct ResourceEntry {
|
||||||
|
std::string filename;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourcePack {
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, ResourceEntry> resources;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
bool loaded;
|
||||||
|
|
||||||
|
uint32_t calculateChecksum(const std::vector<uint8_t>& data);
|
||||||
|
void encryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||||
|
void decryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ResourcePack();
|
||||||
|
~ResourcePack();
|
||||||
|
|
||||||
|
bool loadPack(const std::string& packFile);
|
||||||
|
bool savePack(const std::string& packFile);
|
||||||
|
|
||||||
|
bool addFile(const std::string& filename, const std::string& filepath);
|
||||||
|
bool addDirectory(const std::string& directory);
|
||||||
|
|
||||||
|
std::vector<uint8_t> getResource(const std::string& filename);
|
||||||
|
bool hasResource(const std::string& filename) const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
size_t getResourceCount() const;
|
||||||
|
std::vector<std::string> getResourceList() const;
|
||||||
|
|
||||||
|
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -40,7 +40,8 @@ Scoreboard::Scoreboard()
|
|||||||
: renderer_(Screen::get()->getRenderer()),
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
||||||
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
||||||
text_scoreboard_(Resource::get()->getText("8bithud")) {
|
text_(Resource::get()->getText("8bithud")),
|
||||||
|
enter_name_text_(Resource::get()->getText("smb2")) {
|
||||||
// Inicializa variables
|
// Inicializa variables
|
||||||
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
||||||
name_.at(i).clear();
|
name_.at(i).clear();
|
||||||
@@ -198,50 +199,50 @@ void Scoreboard::renderPanelContent(size_t panel_index) {
|
|||||||
|
|
||||||
void Scoreboard::renderScoreMode(size_t panel_index) {
|
void Scoreboard::renderScoreMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// MULT
|
// MULT
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderDemoMode() {
|
void Scoreboard::renderDemoMode() {
|
||||||
// DEMO MODE
|
// DEMO MODE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
|
||||||
|
|
||||||
// PRESS START TO PLAY
|
// PRESS START TO PLAY
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderWaitingMode() {
|
void Scoreboard::renderWaitingMode() {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// PRESS START TO PLAY
|
// PRESS START TO PLAY
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderGameOverMode() {
|
void Scoreboard::renderGameOverMode() {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// PLEASE WAIT
|
// PLEASE WAIT
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderStageInfoMode() {
|
void Scoreboard::renderStageInfoMode() {
|
||||||
// STAGE
|
// STAGE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
|
||||||
|
|
||||||
// POWERMETER
|
// POWERMETER
|
||||||
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
|
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
|
||||||
@@ -250,76 +251,75 @@ void Scoreboard::renderStageInfoMode() {
|
|||||||
power_meter_sprite_->render();
|
power_meter_sprite_->render();
|
||||||
|
|
||||||
// HI-SCORE
|
// HI-SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
|
||||||
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
|
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderContinueMode(size_t panel_index) {
|
void Scoreboard::renderContinueMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// CONTINUE
|
// CONTINUE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// ENTER NAME
|
// ENTER NAME
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
renderNameInputField(panel_index);
|
renderNameInputField(panel_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderNameInputField(size_t panel_index) {
|
void Scoreboard::renderNameInputField(size_t panel_index) {
|
||||||
SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0F, 7.0F};
|
SDL_FRect rect = {
|
||||||
|
.x = enter_name_pos_.x,
|
||||||
|
.y = enter_name_pos_.y,
|
||||||
|
.w = static_cast<float>(enter_name_text_->getCharacterSize() - 2),
|
||||||
|
.h = static_cast<float>(enter_name_text_->getCharacterSize())};
|
||||||
|
|
||||||
// Recorre todos los slots de letras del nombre
|
// Recorre todos los slots de letras del nombre
|
||||||
for (size_t j = 0; j < NAME_SIZE; ++j) {
|
for (size_t j = 0; j < NAME_SIZE; ++j) {
|
||||||
// Selecciona el color
|
// Dibuja la linea. Si coincide con el selector solo se dibuja 2 de cada 4 veces
|
||||||
const Color COLOR = j < selector_pos_.at(panel_index) ? text_color2_ : text_color1_;
|
if (j != selector_pos_.at(panel_index) || time_counter_ % 4 >= 2) {
|
||||||
|
SDL_SetRenderDrawColor(renderer_, text_color1_.r, text_color1_.g, text_color1_.b, 255);
|
||||||
if (j != selector_pos_.at(panel_index) || time_counter_ % 3 == 0) {
|
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
|
||||||
// Dibuja la linea
|
|
||||||
if (j >= selector_pos_.at(panel_index)) {
|
|
||||||
SDL_SetRenderDrawColor(renderer_, COLOR.r, COLOR.g, COLOR.b, 255);
|
|
||||||
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dibuja la letra
|
|
||||||
if (j < record_name_.at(panel_index).size()) {
|
|
||||||
text_scoreboard_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), COLOR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rect.x += 7;
|
|
||||||
|
// Dibuja la letra
|
||||||
|
if (j < record_name_.at(panel_index).size()) {
|
||||||
|
enter_name_text_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), text_color2_);
|
||||||
|
}
|
||||||
|
rect.x += enter_name_text_->getCharacterSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// NAME
|
// NAME
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
/* TEXTO CENTRADO */
|
// NOMBRE INTRODUCIDO
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
|
enter_name_text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// SCORE
|
// SCORE
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ void Scoreboard::recalculateAnchors() {
|
|||||||
slot4_4_ = {.x = COL, .y = ROW4};
|
slot4_4_ = {.x = COL, .y = ROW4};
|
||||||
|
|
||||||
// Primer cuadrado para poner el nombre de record
|
// Primer cuadrado para poner el nombre de record
|
||||||
const int ENTER_NAME_LENGTH = text_scoreboard_->length(std::string(NAME_SIZE, 'A'));
|
const int ENTER_NAME_LENGTH = enter_name_text_->length(std::string(NAME_SIZE, 'A'));
|
||||||
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
||||||
enter_name_pos_.y = ROW4;
|
enter_name_pos_.y = ROW4;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ class Scoreboard {
|
|||||||
SDL_Renderer *renderer_; // El renderizador de la ventana
|
SDL_Renderer *renderer_; // El renderizador de la ventana
|
||||||
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
||||||
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
||||||
std::shared_ptr<Text> text_scoreboard_; // Fuente para el marcador del juego
|
std::shared_ptr<Text> text_; // Fuente para el marcador del juego
|
||||||
|
std::shared_ptr<Text> enter_name_text_; // Fuente para la introducción de nombre del jugador
|
||||||
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
|
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
|
||||||
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
|
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,21 @@ Screen::Screen()
|
|||||||
initSDLVideo();
|
initSDLVideo();
|
||||||
|
|
||||||
// Crea la textura de destino
|
// Crea la textura de destino
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Usar nuestra propia Metal texture como render target
|
||||||
|
game_canvas_ = shader::metal::createMetalRenderTarget(renderer_, param.game.width, param.game.height);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using custom Metal render target for game_canvas_");
|
||||||
|
} else {
|
||||||
|
// Fallback para otros renderers
|
||||||
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
}
|
||||||
|
#else
|
||||||
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Crea el objeto de texto
|
// Crea el objeto de texto
|
||||||
createText();
|
createText();
|
||||||
@@ -99,7 +112,18 @@ void Screen::renderPresent() {
|
|||||||
clean();
|
clean();
|
||||||
|
|
||||||
if (Options::video.shaders) {
|
if (Options::video.shaders) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Use Metal post-processing with our custom render target
|
||||||
|
shader::metal::renderWithPostProcessing(renderer_, game_canvas_);
|
||||||
|
} else {
|
||||||
|
// Fallback to standard shader system for non-Metal renderers
|
||||||
|
shader::render();
|
||||||
|
}
|
||||||
|
#else
|
||||||
shader::render();
|
shader::render();
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
@@ -224,8 +248,10 @@ void Screen::renderInfo() {
|
|||||||
void Screen::loadShaders() {
|
void Screen::loadShaders() {
|
||||||
if (shader_source_.empty()) {
|
if (shader_source_.empty()) {
|
||||||
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
|
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
|
||||||
std::ifstream f(Asset::get()->get(GLSL_FILE).c_str());
|
auto data = Asset::get()->loadData(GLSL_FILE);
|
||||||
shader_source_ = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
if (!data.empty()) {
|
||||||
|
shader_source_ = std::string(data.begin(), data.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,17 +320,29 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
// Obtener información de la pantalla
|
// Obtener información de la pantalla
|
||||||
getDisplayInfo();
|
getDisplayInfo();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Configurar hint para Metal
|
||||||
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Warning: Failed to set Metal hint!");
|
||||||
|
}
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
|
SDL_WindowFlags window_flags = SDL_WINDOW_METAL;
|
||||||
|
#else // NOT APPLE
|
||||||
// Configurar hint para OpenGL
|
// Configurar hint para OpenGL
|
||||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Warning: Failed to set OpenGL hint!");
|
"Warning: Failed to set OpenGL hint!");
|
||||||
}
|
}
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
// Crear ventana
|
|
||||||
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
|
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
|
||||||
|
#endif
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
if (Options::video.fullscreen) {
|
if (Options::video.fullscreen) {
|
||||||
window_flags |= SDL_WINDOW_FULLSCREEN;
|
window_flags |= SDL_WINDOW_FULLSCREEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crear ventana
|
||||||
window_ = SDL_CreateWindow(
|
window_ = SDL_CreateWindow(
|
||||||
Options::window.caption.c_str(),
|
Options::window.caption.c_str(),
|
||||||
param.game.width * Options::window.zoom,
|
param.game.width * Options::window.zoom,
|
||||||
|
|||||||
@@ -91,7 +91,11 @@ class Screen {
|
|||||||
Color color; // Color del flash
|
Color color; // Color del flash
|
||||||
|
|
||||||
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
|
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
|
||||||
: enabled(enabled), length(length), delay(delay), counter(length), color(color) {}
|
: enabled(enabled),
|
||||||
|
length(length),
|
||||||
|
delay(delay),
|
||||||
|
counter(length),
|
||||||
|
color(color) {}
|
||||||
|
|
||||||
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
|
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
|
||||||
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
|
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
|
||||||
@@ -109,7 +113,14 @@ class Screen {
|
|||||||
bool enabled; // Indica si el efecto está activo
|
bool enabled; // Indica si el efecto está activo
|
||||||
|
|
||||||
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
|
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
|
||||||
: desp(dp), delay(dl), counter(cnt), length(len), remaining(rem), original_pos(orig_pos), original_width(orig_width), enabled(en) {}
|
: desp(dp),
|
||||||
|
delay(dl),
|
||||||
|
counter(cnt),
|
||||||
|
length(len),
|
||||||
|
remaining(rem),
|
||||||
|
original_pos(orig_pos),
|
||||||
|
original_width(orig_width),
|
||||||
|
enabled(en) {}
|
||||||
|
|
||||||
// Activa el efecto de sacudida y guarda la posición y tamaño originales
|
// Activa el efecto de sacudida y guarda la posición y tamaño originales
|
||||||
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {
|
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include "audio.h" // Para Audio
|
#include "audio.h" // Para Audio
|
||||||
#include "balloon_manager.h" // Para BalloonManager
|
#include "balloon_manager.h" // Para BalloonManager
|
||||||
#include "color.h" // Para Zone, SHADOW_TEXT_COLOR, NO_TEXT_COLOR, Color
|
#include "color.h" // Para Zone, Colors::SHADOW_TEXT, Colors::NO_COLOR_MOD, Color
|
||||||
#include "fade.h" // Para Fade, FadeType, FadeMode
|
#include "fade.h" // Para Fade, FadeType, FadeMode
|
||||||
#include "global_events.h" // Para check
|
#include "global_events.h" // Para check
|
||||||
#include "global_inputs.h" // Para check
|
#include "global_inputs.h" // Para check
|
||||||
@@ -173,7 +173,7 @@ void Credits::fillTextTexture() {
|
|||||||
const int TEXTS_HEIGHT = (1 * text->getCharacterSize()) + (8 * SPACE_POST_TITLE) + (3 * SPACE_PRE_TITLE);
|
const int TEXTS_HEIGHT = (1 * text->getCharacterSize()) + (8 * SPACE_POST_TITLE) + (3 * SPACE_PRE_TITLE);
|
||||||
const int POS_X = static_cast<int>(param.game.game_area.center_x);
|
const int POS_X = static_cast<int>(param.game.game_area.center_x);
|
||||||
credits_rect_dst_.h = credits_rect_src_.h = static_cast<float>(TEXTS_HEIGHT);
|
credits_rect_dst_.h = credits_rect_src_.h = static_cast<float>(TEXTS_HEIGHT);
|
||||||
auto text_style = Text::Style(Text::CENTER | Text::SHADOW, NO_TEXT_COLOR, SHADOW_TEXT_COLOR);
|
auto text_style = Text::Style(Text::CENTER | Text::SHADOW, Colors::NO_COLOR_MOD, Colors::SHADOW_TEXT);
|
||||||
|
|
||||||
// PROGRAMMED_AND_DESIGNED_BY
|
// PROGRAMMED_AND_DESIGNED_BY
|
||||||
int y = 0;
|
int y = 0;
|
||||||
@@ -214,7 +214,7 @@ void Credits::fillTextTexture() {
|
|||||||
mini_logo_rect_src_.y = static_cast<float>(y);
|
mini_logo_rect_src_.y = static_cast<float>(y);
|
||||||
auto mini_logo_sprite = std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"));
|
auto mini_logo_sprite = std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"));
|
||||||
mini_logo_sprite->setPosition(1 + POS_X - (mini_logo_sprite->getWidth() / 2), 1 + y);
|
mini_logo_sprite->setPosition(1 + POS_X - (mini_logo_sprite->getWidth() / 2), 1 + y);
|
||||||
Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(SHADOW_TEXT_COLOR.r, SHADOW_TEXT_COLOR.g, SHADOW_TEXT_COLOR.b);
|
Resource::get()->getTexture("logo_jailgames_mini.png")->setColor(Colors::SHADOW_TEXT.r, Colors::SHADOW_TEXT.g, Colors::SHADOW_TEXT.b);
|
||||||
mini_logo_sprite->render();
|
mini_logo_sprite->render();
|
||||||
|
|
||||||
mini_logo_sprite->setPosition(POS_X - (mini_logo_sprite->getWidth() / 2), y);
|
mini_logo_sprite->setPosition(POS_X - (mini_logo_sprite->getWidth() / 2), y);
|
||||||
@@ -223,7 +223,7 @@ void Credits::fillTextTexture() {
|
|||||||
|
|
||||||
// Texto con el copyright
|
// Texto con el copyright
|
||||||
y += mini_logo_sprite->getHeight() + 3;
|
y += mini_logo_sprite->getHeight() + 3;
|
||||||
text->writeDX(Text::CENTER | Text::SHADOW, POS_X, y, std::string(TEXT_COPYRIGHT), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR);
|
text->writeDX(Text::CENTER | Text::SHADOW, POS_X, y, std::string(TEXT_COPYRIGHT), 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT);
|
||||||
|
|
||||||
// Resetea el renderizador
|
// Resetea el renderizador
|
||||||
SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr);
|
SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "balloon.h" // Para Balloon
|
#include "balloon.h" // Para Balloon
|
||||||
#include "balloon_manager.h" // Para BalloonManager
|
#include "balloon_manager.h" // Para BalloonManager
|
||||||
#include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus
|
#include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus
|
||||||
#include "color.h" // Para Color, FLASH_COLOR
|
#include "color.h" // Para Color, Colors::FLASH
|
||||||
#include "difficulty.h" // Para Code
|
#include "difficulty.h" // Para Code
|
||||||
#include "fade.h" // Para Fade, FadeType, FadeMode
|
#include "fade.h" // Para Fade, FadeType, FadeMode
|
||||||
#include "global_events.h" // Para check
|
#include "global_events.h" // Para check
|
||||||
@@ -79,14 +79,14 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
|
|||||||
scoreboard_ = Scoreboard::get();
|
scoreboard_ = Scoreboard::get();
|
||||||
|
|
||||||
fade_in_->setColor(param.fade.color);
|
fade_in_->setColor(param.fade.color);
|
||||||
fade_in_->setPreDuration(demo_.enabled ? 80 : 0);
|
fade_in_->setPreDuration(demo_.enabled ? 500 : 0);
|
||||||
fade_in_->setPostDuration(0);
|
fade_in_->setPostDuration(0);
|
||||||
fade_in_->setType(Fade::Type::RANDOM_SQUARE);
|
fade_in_->setType(Fade::Type::RANDOM_SQUARE2);
|
||||||
fade_in_->setMode(Fade::Mode::IN);
|
fade_in_->setMode(Fade::Mode::IN);
|
||||||
fade_in_->activate();
|
fade_in_->activate();
|
||||||
|
|
||||||
fade_out_->setColor(param.fade.color);
|
fade_out_->setColor(param.fade.color);
|
||||||
fade_out_->setPostDuration(param.fade.post_duration);
|
fade_out_->setPostDuration(param.fade.post_duration_ms);
|
||||||
fade_out_->setType(Fade::Type::VENETIAN);
|
fade_out_->setType(Fade::Type::VENETIAN);
|
||||||
|
|
||||||
background_->setPos(param.game.play_area.rect);
|
background_->setPos(param.game.play_area.rect);
|
||||||
@@ -230,7 +230,8 @@ void Game::updatePlayers() {
|
|||||||
handlePlayerCollision(player, balloon);
|
handlePlayerCollision(player, balloon);
|
||||||
|
|
||||||
if (demo_.enabled && allPlayersAreNotPlaying()) {
|
if (demo_.enabled && allPlayersAreNotPlaying()) {
|
||||||
fade_out_->setType(Fade::Type::RANDOM_SQUARE);
|
fade_out_->setType(Fade::Type::RANDOM_SQUARE2);
|
||||||
|
fade_out_->setPostDuration(500);
|
||||||
fade_out_->activate();
|
fade_out_->activate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,7 +282,7 @@ void Game::updateStage() {
|
|||||||
// Efectos de cambio de fase
|
// Efectos de cambio de fase
|
||||||
playSound("stage_change.wav");
|
playSound("stage_change.wav");
|
||||||
balloon_manager_->resetBalloonSpeed();
|
balloon_manager_->resetBalloonSpeed();
|
||||||
screen_->flash(FLASH_COLOR, 3);
|
screen_->flash(Colors::FLASH, 3);
|
||||||
screen_->shake();
|
screen_->shake();
|
||||||
|
|
||||||
// Obtener datos de la nueva fase
|
// Obtener datos de la nueva fase
|
||||||
@@ -295,11 +296,11 @@ void Game::updateStage() {
|
|||||||
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
|
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
|
||||||
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
|
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
|
||||||
} else {
|
} else {
|
||||||
auto text = Resource::get()->getText("04b_25_2x");
|
auto text = Resource::get()->getText("04b_25_2x_enhanced");
|
||||||
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
|
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
|
||||||
std::to_string(total_stages - current_stage_index) +
|
std::to_string(total_stages - current_stage_index) +
|
||||||
Lang::getText("[GAME_TEXT] 2A");
|
Lang::getText("[GAME_TEXT] 2A");
|
||||||
createMessage(paths, text->writeToTexture(CAPTION, 1, -4));
|
createMessage(paths, text->writeDXToTexture(Text::STROKE, CAPTION, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,15 +378,6 @@ void Game::updateGameStateCompleted() {
|
|||||||
updatePathSprites();
|
updatePathSprites();
|
||||||
cleanVectors();
|
cleanVectors();
|
||||||
|
|
||||||
// Para la música y elimina todos los globos e items
|
|
||||||
if (game_completed_counter_ == 0) {
|
|
||||||
stopMusic(); // Detiene la música
|
|
||||||
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
|
|
||||||
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
|
|
||||||
destroyAllItems(); // Destruye todos los items
|
|
||||||
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comienza las celebraciones
|
// Comienza las celebraciones
|
||||||
// Muestra el mensaje de felicitación y da los puntos a los jugadores
|
// Muestra el mensaje de felicitación y da los puntos a los jugadores
|
||||||
if (game_completed_counter_ == START_CELEBRATIONS) {
|
if (game_completed_counter_ == START_CELEBRATIONS) {
|
||||||
@@ -616,6 +608,7 @@ void Game::handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::sh
|
|||||||
|
|
||||||
if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) {
|
if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) {
|
||||||
createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
|
createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
|
||||||
|
playSound("item_drop.wav");
|
||||||
} else {
|
} else {
|
||||||
createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT);
|
createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT);
|
||||||
coffee_machine_enabled_ = true;
|
coffee_machine_enabled_ = true;
|
||||||
@@ -743,7 +736,6 @@ auto Game::dropItem() -> ItemType {
|
|||||||
// Crea un objeto item
|
// Crea un objeto item
|
||||||
void Game::createItem(ItemType type, float x, float y) {
|
void Game::createItem(ItemType type, float x, float y) {
|
||||||
items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1]));
|
items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1]));
|
||||||
playSound("item_drop.wav");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vacia el vector de items
|
// Vacia el vector de items
|
||||||
@@ -1381,17 +1373,27 @@ void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) {
|
|||||||
|
|
||||||
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
||||||
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
|
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
|
||||||
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::UP);
|
handleFireInput(player, BulletType::UP);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire = 1;
|
demo_.keys.fire = 1;
|
||||||
#endif
|
#endif
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::LEFT);
|
handleFireInput(player, BulletType::LEFT);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire_left = 1;
|
demo_.keys.fire_left = 1;
|
||||||
#endif
|
#endif
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::RIGHT);
|
handleFireInput(player, BulletType::RIGHT);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire_right = 1;
|
demo_.keys.fire_right = 1;
|
||||||
@@ -1405,6 +1407,7 @@ void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
|
|||||||
player->setPlayingState(Player::State::RECOVER);
|
player->setPlayingState(Player::State::RECOVER);
|
||||||
player->addCredit();
|
player->addCredit();
|
||||||
sendPlayerToTheFront(player);
|
sendPlayerToTheFront(player);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
||||||
@@ -1430,33 +1433,53 @@ void Game::handlePlayerWaitingInput(const std::shared_ptr<Player> &player) {
|
|||||||
void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
||||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else if (player->getEnterNamePositionOverflow()) {
|
return;
|
||||||
|
}
|
||||||
|
if (player->getEnterNamePositionOverflow()) {
|
||||||
player->setInput(Input::Action::START);
|
player->setInput(Input::Action::START);
|
||||||
player->setPlayingState(Player::State::SHOWING_NAME);
|
player->setPlayingState(Player::State::SHOWING_NAME);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
updateHiScoreName();
|
updateHiScoreName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::RIGHT);
|
|
||||||
}
|
}
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) ||
|
player->setInput(Input::Action::RIGHT);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) ||
|
||||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::LEFT);
|
|
||||||
}
|
}
|
||||||
} else if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
player->setInput(Input::Action::LEFT);
|
||||||
|
playSound("service_menu_back.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::UP);
|
player->setInput(Input::Action::UP);
|
||||||
} else if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
playSound("service_menu_move.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::DOWN);
|
player->setInput(Input::Action::DOWN);
|
||||||
} else if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
playSound("service_menu_move.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::START);
|
|
||||||
player->setPlayingState(Player::State::SHOWING_NAME);
|
|
||||||
updateHiScoreName();
|
|
||||||
}
|
}
|
||||||
|
player->setInput(Input::Action::START);
|
||||||
|
player->setPlayingState(Player::State::SHOWING_NAME);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
|
updateHiScoreName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1467,20 +1490,16 @@ void Game::initDemo(Player::Id player_id) {
|
|||||||
setState(State::PLAYING);
|
setState(State::PLAYING);
|
||||||
|
|
||||||
// Aleatoriza la asignación del fichero con los datos del modo demostracion
|
// Aleatoriza la asignación del fichero con los datos del modo demostracion
|
||||||
{
|
const auto DEMO1 = rand() % 2;
|
||||||
const auto DEMO1 = rand() % 2;
|
const auto DEMO2 = (DEMO1 == 0) ? 1 : 0;
|
||||||
const auto DEMO2 = (DEMO1 == 0) ? 1 : 0;
|
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1));
|
||||||
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1));
|
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2));
|
||||||
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selecciona una pantalla al azar
|
// Selecciona una pantalla al azar
|
||||||
{
|
constexpr auto NUM_DEMOS = 3;
|
||||||
constexpr auto NUM_DEMOS = 3;
|
const auto DEMO = rand() % NUM_DEMOS;
|
||||||
const auto DEMO = rand() % NUM_DEMOS;
|
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
|
||||||
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
|
stage_manager_->jumpToStage(STAGES.at(DEMO));
|
||||||
stage_manager_->jumpToStage(STAGES.at(DEMO));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activa o no al otro jugador
|
// Activa o no al otro jugador
|
||||||
if (rand() % 3 != 0) {
|
if (rand() % 3 != 0) {
|
||||||
@@ -1491,8 +1510,10 @@ void Game::initDemo(Player::Id player_id) {
|
|||||||
|
|
||||||
// Asigna cafes a los jugadores
|
// Asigna cafes a los jugadores
|
||||||
for (auto &player : players_) {
|
for (auto &player : players_) {
|
||||||
for (int i = 0; i < rand() % 3; ++i) {
|
if (player->isPlaying()) {
|
||||||
player->giveExtraHit();
|
for (int i = 0; i < rand() % 3; ++i) {
|
||||||
|
player->giveExtraHit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player->setInvulnerable(true);
|
player->setInvulnerable(true);
|
||||||
@@ -1562,60 +1583,51 @@ void Game::initDifficultyVars() {
|
|||||||
// Inicializa los jugadores
|
// Inicializa los jugadores
|
||||||
void Game::initPlayers(Player::Id player_id) {
|
void Game::initPlayers(Player::Id player_id) {
|
||||||
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
|
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
|
||||||
|
const Player::State STATE = demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN;
|
||||||
|
|
||||||
// Crea al jugador uno y lo pone en modo espera
|
// Crea al jugador uno y lo pone en modo espera
|
||||||
Player::Config config_player1;
|
Player::Config config_player1{
|
||||||
config_player1.id = Player::Id::PLAYER1;
|
.id = Player::Id::PLAYER1,
|
||||||
config_player1.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2);
|
.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2),
|
||||||
config_player1.y = Y;
|
.y = Y,
|
||||||
config_player1.demo = demo_.enabled;
|
.demo = demo_.enabled,
|
||||||
config_player1.play_area = ¶m.game.play_area.rect;
|
.play_area = ¶m.game.play_area.rect,
|
||||||
config_player1.texture = player_textures_.at(0);
|
.texture = player_textures_.at(0),
|
||||||
config_player1.animations = player_animations_;
|
.animations = player_animations_,
|
||||||
config_player1.hi_score_table = &Options::settings.hi_score_table;
|
.hi_score_table = &Options::settings.hi_score_table,
|
||||||
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
|
.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1),
|
||||||
config_player1.stage_info = stage_manager_.get();
|
.stage_info = stage_manager_.get()};
|
||||||
|
|
||||||
auto player1 = std::make_unique<Player>(config_player1);
|
auto player1 = std::make_unique<Player>(config_player1);
|
||||||
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
|
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
|
||||||
player1->setName(Lang::getText("[SCOREBOARD] 1"));
|
player1->setName(Lang::getText("[SCOREBOARD] 1"));
|
||||||
player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance);
|
player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance);
|
||||||
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
|
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
|
||||||
player1->setPlayingState(Player::State::WAITING);
|
player1->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER1) ? STATE : Player::State::WAITING);
|
||||||
players_.push_back(std::move(player1));
|
|
||||||
|
|
||||||
// Crea al jugador dos y lo pone en modo espera
|
// Crea al jugador dos y lo pone en modo espera
|
||||||
Player::Config config_player2;
|
Player::Config config_player2{
|
||||||
config_player2.id = Player::Id::PLAYER2;
|
.id = Player::Id::PLAYER2,
|
||||||
config_player2.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2);
|
.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2),
|
||||||
config_player2.y = Y;
|
.y = Y,
|
||||||
config_player2.demo = demo_.enabled;
|
.demo = demo_.enabled,
|
||||||
config_player2.play_area = ¶m.game.play_area.rect;
|
.play_area = ¶m.game.play_area.rect,
|
||||||
config_player2.texture = player_textures_.at(1);
|
.texture = player_textures_.at(1),
|
||||||
config_player2.animations = player_animations_;
|
.animations = player_animations_,
|
||||||
config_player2.hi_score_table = &Options::settings.hi_score_table;
|
.hi_score_table = &Options::settings.hi_score_table,
|
||||||
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
|
.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1),
|
||||||
config_player2.stage_info = stage_manager_.get();
|
.stage_info = stage_manager_.get()};
|
||||||
|
|
||||||
auto player2 = std::make_unique<Player>(config_player2);
|
auto player2 = std::make_unique<Player>(config_player2);
|
||||||
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
|
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
|
||||||
player2->setName(Lang::getText("[SCOREBOARD] 2"));
|
player2->setName(Lang::getText("[SCOREBOARD] 2"));
|
||||||
player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance);
|
player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance);
|
||||||
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
|
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
|
||||||
player2->setPlayingState(Player::State::WAITING);
|
player2->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER2) ? STATE : Player::State::WAITING);
|
||||||
players_.push_back(std::move(player2));
|
|
||||||
|
|
||||||
// Activa el jugador que coincide con el "player_id" o ambos si es "0"
|
// Añade los jugadores al vector de forma que el jugador 1 se pinte por delante del jugador 2
|
||||||
if (player_id == Player::Id::BOTH_PLAYERS) {
|
players_.push_back(std::move(player2));
|
||||||
// Activa ambos jugadores
|
players_.push_back(std::move(player1));
|
||||||
getPlayer(Player::Id::PLAYER1)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
getPlayer(Player::Id::PLAYER2)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
} else {
|
|
||||||
// Activa el jugador elegido
|
|
||||||
auto player = getPlayer(player_id);
|
|
||||||
player->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
sendPlayerToTheFront(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registra los jugadores en Options
|
// Registra los jugadores en Options
|
||||||
for (const auto &player : players_) {
|
for (const auto &player : players_) {
|
||||||
@@ -1662,7 +1674,8 @@ void Game::updateDemo() {
|
|||||||
|
|
||||||
// Activa el fundido antes de acabar con los datos de la demo
|
// Activa el fundido antes de acabar con los datos de la demo
|
||||||
if (demo_.counter == TOTAL_DEMO_DATA - 200) {
|
if (demo_.counter == TOTAL_DEMO_DATA - 200) {
|
||||||
fade_out_->setType(Fade::Type::RANDOM_SQUARE);
|
fade_out_->setType(Fade::Type::RANDOM_SQUARE2);
|
||||||
|
fade_out_->setPostDuration(param.fade.post_duration_ms);
|
||||||
fade_out_->activate();
|
fade_out_->activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1819,6 +1832,19 @@ void Game::checkAndUpdateBalloonSpeed() {
|
|||||||
void Game::setState(State state) {
|
void Game::setState(State state) {
|
||||||
state_ = state;
|
state_ = state;
|
||||||
counter_ = 0;
|
counter_ = 0;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case State::COMPLETED: // Para la música y elimina todos los globos e items
|
||||||
|
stopMusic(); // Detiene la música
|
||||||
|
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
|
||||||
|
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
|
||||||
|
destroyAllItems(); // Destruye todos los items
|
||||||
|
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
|
||||||
|
tabe_->disableSpawning(); // Deshabilita la creacion de Tabes
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::playSound(const std::string &name) const {
|
void Game::playSound(const std::string &name) const {
|
||||||
@@ -1880,20 +1906,17 @@ void Game::handleDebugEvents(const SDL_Event &event) {
|
|||||||
static int formation_id_ = 0;
|
static int formation_id_ = 0;
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
||||||
switch (event.key.key) {
|
switch (event.key.key) {
|
||||||
case SDLK_1: // Crea una powerball
|
case SDLK_1: { // Crea una powerball
|
||||||
{
|
|
||||||
balloon_manager_->createPowerBall();
|
balloon_manager_->createPowerBall();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_2: // Activa o desactiva la aparición de globos
|
case SDLK_2: { // Activa o desactiva la aparición de globos
|
||||||
{
|
|
||||||
static bool deploy_balloons_ = true;
|
static bool deploy_balloons_ = true;
|
||||||
deploy_balloons_ = !deploy_balloons_;
|
deploy_balloons_ = !deploy_balloons_;
|
||||||
balloon_manager_->enableBalloonDeployment(deploy_balloons_);
|
balloon_manager_->enableBalloonDeployment(deploy_balloons_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_3: // Activa el modo para pasar el juego automaticamente
|
case SDLK_3: { // Activa el modo para pasar el juego automaticamente
|
||||||
{
|
|
||||||
auto_pop_balloons_ = !auto_pop_balloons_;
|
auto_pop_balloons_ = !auto_pop_balloons_;
|
||||||
Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)});
|
Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)});
|
||||||
if (auto_pop_balloons_) {
|
if (auto_pop_balloons_) {
|
||||||
@@ -1903,24 +1926,20 @@ void Game::handleDebugEvents(const SDL_Event &event) {
|
|||||||
balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_);
|
balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_4: // Suelta un item
|
case SDLK_4: { // Suelta un item
|
||||||
{
|
|
||||||
createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40);
|
createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_5: // 5.000
|
case SDLK_5: { // 5.000
|
||||||
{
|
|
||||||
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
||||||
createItemText(X, game_text_textures_.at(2));
|
createItemText(X, game_text_textures_.at(2));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_6: // Crea un mensaje
|
case SDLK_6: { // Crea un mensaje
|
||||||
{
|
|
||||||
createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready"));
|
createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_7: // 100.000
|
case SDLK_7: { // 100.000
|
||||||
{
|
|
||||||
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
||||||
createItemText(X, game_text_textures_.at(6));
|
createItemText(X, game_text_textures_.at(6));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -254,12 +254,11 @@ class Game {
|
|||||||
|
|
||||||
// --- Sistema de globos y enemigos ---
|
// --- Sistema de globos y enemigos ---
|
||||||
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
|
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
|
||||||
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
|
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
|
||||||
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
|
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
|
||||||
|
|
||||||
// --- Gestión de fases y progresión ---
|
// --- Gestión de fases y progresión ---
|
||||||
void updateStage(); // Verifica y actualiza cambio de fase
|
void updateStage(); // Verifica y actualiza cambio de fase
|
||||||
void setTotalPower(); // Calcula poder total necesario para completar el juego
|
|
||||||
void initDifficultyVars(); // Inicializa variables de dificultad
|
void initDifficultyVars(); // Inicializa variables de dificultad
|
||||||
|
|
||||||
// --- Sistema de amenaza ---
|
// --- Sistema de amenaza ---
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include "audio.h" // Para Audio
|
#include "audio.h" // Para Audio
|
||||||
#include "background.h" // Para Background
|
#include "background.h" // Para Background
|
||||||
#include "color.h" // Para Color, easeOutQuint, NO_TEXT_COLOR
|
#include "color.h" // Para Color, easeOutQuint, Colors::NO_COLOR_MOD
|
||||||
#include "fade.h" // Para Fade, FadeMode, FadeType
|
#include "fade.h" // Para Fade, FadeMode, FadeType
|
||||||
#include "global_events.h" // Para check
|
#include "global_events.h" // Para check
|
||||||
#include "global_inputs.h" // Para check
|
#include "global_inputs.h" // Para check
|
||||||
@@ -191,7 +191,7 @@ void HiScoreTable::createSprites() {
|
|||||||
// Crea los sprites para las entradas en la tabla de puntuaciones
|
// Crea los sprites para las entradas en la tabla de puntuaciones
|
||||||
const int ANIMATION = rand() % 4;
|
const int ANIMATION = rand() % 4;
|
||||||
const std::string SAMPLE_LINE(ENTRY_LENGTH + 3, ' ');
|
const std::string SAMPLE_LINE(ENTRY_LENGTH + 3, ' ');
|
||||||
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR));
|
auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(Text::SHADOW, SAMPLE_LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT));
|
||||||
const auto ENTRY_WIDTH = sample_entry->getWidth();
|
const auto ENTRY_WIDTH = sample_entry->getWidth();
|
||||||
for (int i = 0; i < MAX_NAMES; ++i) {
|
for (int i = 0; i < MAX_NAMES; ++i) {
|
||||||
const auto TABLE_POSITION = format(i + 1) + ". ";
|
const auto TABLE_POSITION = format(i + 1) + ". ";
|
||||||
@@ -204,7 +204,7 @@ void HiScoreTable::createSprites() {
|
|||||||
}
|
}
|
||||||
const auto LINE = TABLE_POSITION + Options::settings.hi_score_table.at(i).name + dots + SCORE + ONE_CC;
|
const auto LINE = TABLE_POSITION + Options::settings.hi_score_table.at(i).name + dots + SCORE + ONE_CC;
|
||||||
|
|
||||||
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(Text::SHADOW, LINE, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)));
|
entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(Text::SHADOW, LINE, 1, Colors::NO_COLOR_MOD, 1, Colors::SHADOW_TEXT)));
|
||||||
const int DEFAULT_POS_X = (backbuffer_width - ENTRY_WIDTH) / 2;
|
const int DEFAULT_POS_X = (backbuffer_width - ENTRY_WIDTH) / 2;
|
||||||
const int POS_X = (i < 9) ? DEFAULT_POS_X : DEFAULT_POS_X - entry_text->getCharacterSize();
|
const int POS_X = (i < 9) ? DEFAULT_POS_X : DEFAULT_POS_X - entry_text->getCharacterSize();
|
||||||
const int POS_Y = (i * SPACE_BETWEEN_LINES) + FIRST_LINE + SPACE_BETWEEN_HEADER;
|
const int POS_Y = (i * SPACE_BETWEEN_LINES) + FIRST_LINE + SPACE_BETWEEN_HEADER;
|
||||||
@@ -271,8 +271,8 @@ void HiScoreTable::updateSprites() {
|
|||||||
// Inicializa el fade
|
// Inicializa el fade
|
||||||
void HiScoreTable::initFade() {
|
void HiScoreTable::initFade() {
|
||||||
fade_->setColor(param.fade.color);
|
fade_->setColor(param.fade.color);
|
||||||
fade_->setType(Fade::Type::RANDOM_SQUARE);
|
fade_->setType(Fade::Type::RANDOM_SQUARE2);
|
||||||
fade_->setPostDuration(param.fade.post_duration);
|
fade_->setPostDuration(param.fade.post_duration_ms);
|
||||||
fade_->setMode(fade_mode_);
|
fade_->setMode(fade_mode_);
|
||||||
fade_->activate();
|
fade_->activate();
|
||||||
}
|
}
|
||||||
@@ -291,7 +291,7 @@ void HiScoreTable::initBackground() {
|
|||||||
background_->setTransition(0.0F);
|
background_->setTransition(0.0F);
|
||||||
background_->setSunProgression(1.0F);
|
background_->setSunProgression(1.0F);
|
||||||
background_->setMoonProgression(0.0F);
|
background_->setMoonProgression(0.0F);
|
||||||
background_fade_color_ = GREEN_SKY_COLOR;
|
background_fade_color_ = Colors::GREEN_SKY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ void HiScoreTable::initBackground() {
|
|||||||
background_->setTransition(0.0F);
|
background_->setTransition(0.0F);
|
||||||
background_->setSunProgression(0.65F);
|
background_->setSunProgression(0.65F);
|
||||||
background_->setMoonProgression(0.0F);
|
background_->setMoonProgression(0.0F);
|
||||||
background_fade_color_ = PINK_SKY_COLOR;
|
background_fade_color_ = Colors::PINK_SKY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +311,7 @@ void HiScoreTable::initBackground() {
|
|||||||
background_->setTransition(0.0F);
|
background_->setTransition(0.0F);
|
||||||
background_->setSunProgression(0.0F);
|
background_->setSunProgression(0.0F);
|
||||||
background_->setMoonProgression(0.0F);
|
background_->setMoonProgression(0.0F);
|
||||||
background_fade_color_ = BLUE_SKY_COLOR;
|
background_fade_color_ = Colors::BLUE_SKY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "audio.h" // Para Audio
|
#include "audio.h" // Para Audio
|
||||||
#include "color.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C...
|
#include "color.h" // Para Color, Colors::SHADOW_TEXT, Zone, NO_TEXT_C...
|
||||||
#include "fade.h" // Para Fade, FadeMode, FadeType
|
#include "fade.h" // Para Fade, FadeMode, FadeType
|
||||||
#include "global_events.h" // Para check
|
#include "global_events.h" // Para check
|
||||||
#include "global_inputs.h" // Para check
|
#include "global_inputs.h" // Para check
|
||||||
@@ -44,7 +44,7 @@ Instructions::Instructions()
|
|||||||
tiled_bg_->setColor(param.title.bg_color);
|
tiled_bg_->setColor(param.title.bg_color);
|
||||||
fade_->setColor(param.fade.color);
|
fade_->setColor(param.fade.color);
|
||||||
fade_->setType(Fade::Type::FULLSCREEN);
|
fade_->setType(Fade::Type::FULLSCREEN);
|
||||||
fade_->setPostDuration(param.fade.post_duration);
|
fade_->setPostDuration(param.fade.post_duration_ms);
|
||||||
fade_->setMode(Fade::Mode::IN);
|
fade_->setMode(Fade::Mode::IN);
|
||||||
fade_->activate();
|
fade_->activate();
|
||||||
|
|
||||||
@@ -150,8 +150,8 @@ void Instructions::fillTexture() {
|
|||||||
}
|
}
|
||||||
const int ANCHOR_ITEM = (param.game.width - (length + X_OFFSET)) / 2;
|
const int ANCHOR_ITEM = (param.game.width - (length + X_OFFSET)) / 2;
|
||||||
|
|
||||||
auto caption_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, ORANGE_TEXT_COLOR, SHADOW_TEXT_COLOR);
|
auto caption_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::ORANGE_TEXT, Colors::SHADOW_TEXT);
|
||||||
auto text_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, NO_TEXT_COLOR, SHADOW_TEXT_COLOR);
|
auto text_style = Text::Style(Text::CENTER | Text::COLOR | Text::SHADOW, Colors::NO_COLOR_MOD, Colors::SHADOW_TEXT);
|
||||||
|
|
||||||
// Escribe el texto de las instrucciones
|
// Escribe el texto de las instrucciones
|
||||||
text_->writeStyle(param.game.game_area.center_x, FIRST_LINE, Lang::getText("[INSTRUCTIONS] 01"), caption_style);
|
text_->writeStyle(param.game.game_area.center_x, FIRST_LINE, Lang::getText("[INSTRUCTIONS] 01"), caption_style);
|
||||||
@@ -167,11 +167,11 @@ void Instructions::fillTexture() {
|
|||||||
text_->writeStyle(param.game.game_area.center_x, ANCHOR2, Lang::getText("[INSTRUCTIONS] 06"), caption_style);
|
text_->writeStyle(param.game.game_area.center_x, ANCHOR2, Lang::getText("[INSTRUCTIONS] 06"), caption_style);
|
||||||
|
|
||||||
const int ANCHOR3 = ANCHOR2 + SPACE_POST_HEADER;
|
const int ANCHOR3 = ANCHOR2 + SPACE_POST_HEADER;
|
||||||
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 0), Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR);
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 0), Lang::getText("[INSTRUCTIONS] 07"), Colors::SHADOW_TEXT);
|
||||||
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 1), Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR);
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 1), Lang::getText("[INSTRUCTIONS] 08"), Colors::SHADOW_TEXT);
|
||||||
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 2), Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR);
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 2), Lang::getText("[INSTRUCTIONS] 09"), Colors::SHADOW_TEXT);
|
||||||
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 3), Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR);
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 3), Lang::getText("[INSTRUCTIONS] 10"), Colors::SHADOW_TEXT);
|
||||||
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 4), Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR);
|
text_->writeShadowed(ANCHOR_ITEM + X_OFFSET, ANCHOR3 + (SPACE_BETWEEN_ITEM_LINES * 4), Lang::getText("[INSTRUCTIONS] 11"), Colors::SHADOW_TEXT);
|
||||||
|
|
||||||
// Deja el renderizador como estaba
|
// Deja el renderizador como estaba
|
||||||
SDL_SetRenderTarget(renderer_, temp);
|
SDL_SetRenderTarget(renderer_, temp);
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ struct Line { // Almacena información de línea animada
|
|||||||
|
|
||||||
// Constructor de Line
|
// Constructor de Line
|
||||||
Line(int y, float x, int direction)
|
Line(int y, float x, int direction)
|
||||||
: y(y), x(x), direction(direction) {}
|
: y(y),
|
||||||
|
x(x),
|
||||||
|
direction(direction) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clase Instructions
|
// Clase Instructions
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "audio.h" // Para Audio
|
#include "audio.h" // Para Audio
|
||||||
#include "color.h" // Para NO_TEXT_COLOR, TITLE_SHADOW_TEXT_COLOR
|
#include "color.h" // Para Colors::NO_COLOR_MOD, Colors::TITLE_SHADOW_TEXT
|
||||||
#include "fade.h" // Para Fade, FadeType
|
#include "fade.h" // Para Fade, FadeType
|
||||||
#include "game_logo.h" // Para GameLogo
|
#include "game_logo.h" // Para GameLogo
|
||||||
#include "global_events.h" // Para check
|
#include "global_events.h" // Para check
|
||||||
@@ -49,8 +49,8 @@ Title::Title()
|
|||||||
game_logo_->enable();
|
game_logo_->enable();
|
||||||
mini_logo_sprite_->setX(param.game.game_area.center_x - (mini_logo_sprite_->getWidth() / 2));
|
mini_logo_sprite_->setX(param.game.game_area.center_x - (mini_logo_sprite_->getWidth() / 2));
|
||||||
fade_->setColor(param.fade.color);
|
fade_->setColor(param.fade.color);
|
||||||
fade_->setType(Fade::Type::RANDOM_SQUARE);
|
fade_->setType(Fade::Type::RANDOM_SQUARE2);
|
||||||
fade_->setPostDuration(param.fade.post_duration);
|
fade_->setPostDuration(param.fade.post_duration_ms);
|
||||||
initPlayers();
|
initPlayers();
|
||||||
|
|
||||||
// Asigna valores a otras variables
|
// Asigna valores a otras variables
|
||||||
@@ -438,9 +438,9 @@ void Title::renderStartPrompt() {
|
|||||||
param.title.press_start_position,
|
param.title.press_start_position,
|
||||||
Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"),
|
Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"),
|
||||||
1,
|
1,
|
||||||
NO_TEXT_COLOR,
|
Colors::NO_COLOR_MOD,
|
||||||
1,
|
1,
|
||||||
TITLE_SHADOW_TEXT_COLOR);
|
Colors::TITLE_SHADOW_TEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,9 +455,9 @@ void Title::renderCopyright() {
|
|||||||
anchor_.copyright_text,
|
anchor_.copyright_text,
|
||||||
std::string(TEXT_COPYRIGHT),
|
std::string(TEXT_COPYRIGHT),
|
||||||
1,
|
1,
|
||||||
NO_TEXT_COLOR,
|
Colors::NO_COLOR_MOD,
|
||||||
1,
|
1,
|
||||||
TITLE_SHADOW_TEXT_COLOR);
|
Colors::TITLE_SHADOW_TEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class Sprite {
|
|||||||
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
|
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
|
||||||
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
|
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
|
||||||
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
|
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
|
||||||
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
|
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
|
||||||
[[nodiscard]] auto getActiveTextureIndex() const -> size_t { return texture_index_; } // Obtiene el índice de la textura activa
|
[[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
|
||||||
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
|
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
|
|||||||
@@ -210,4 +210,14 @@ void Tabe::disable() {
|
|||||||
// Detiene/activa el timer
|
// Detiene/activa el timer
|
||||||
void Tabe::pauseTimer(bool value) {
|
void Tabe::pauseTimer(bool value) {
|
||||||
timer_.setPaused(value);
|
timer_.setPaused(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deshabilita el spawning permanentemente
|
||||||
|
void Tabe::disableSpawning() {
|
||||||
|
timer_.setSpawnDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Habilita el spawning nuevamente
|
||||||
|
void Tabe::enableSpawning() {
|
||||||
|
timer_.setSpawnDisabled(false);
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ class Tabe {
|
|||||||
void setState(State state); // Establece el estado
|
void setState(State state); // Establece el estado
|
||||||
auto tryToGetBonus() -> bool; // Intenta obtener el bonus
|
auto tryToGetBonus() -> bool; // Intenta obtener el bonus
|
||||||
void pauseTimer(bool value); // Detiene/activa el timer
|
void pauseTimer(bool value); // Detiene/activa el timer
|
||||||
|
void disableSpawning(); // Deshabilita el spawning permanentemente
|
||||||
|
void enableSpawning(); // Habilita el spawning nuevamente
|
||||||
|
|
||||||
// --- Getters ---
|
// --- Getters ---
|
||||||
auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión
|
auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión
|
||||||
@@ -54,7 +56,8 @@ class Tabe {
|
|||||||
Uint32 current_time; // Tiempo actual
|
Uint32 current_time; // Tiempo actual
|
||||||
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
|
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
|
||||||
Uint32 last_time; // Tiempo de la última actualización
|
Uint32 last_time; // Tiempo de la última actualización
|
||||||
bool is_paused{false}; // Indica si el temporizador está pausado
|
bool is_paused{false}; // Indica si el temporizador está pausado (por pausa de juego)
|
||||||
|
bool spawn_disabled{false}; // Indica si el spawning está deshabilitado permanentemente
|
||||||
|
|
||||||
// Constructor - los parámetros min_time y max_time están en mintos
|
// Constructor - los parámetros min_time y max_time están en mintos
|
||||||
Timer(float min_time, float max_time)
|
Timer(float min_time, float max_time)
|
||||||
@@ -75,8 +78,8 @@ class Tabe {
|
|||||||
void update() {
|
void update() {
|
||||||
current_time = SDL_GetTicks();
|
current_time = SDL_GetTicks();
|
||||||
|
|
||||||
// Solo actualizar si no está pausado
|
// Solo actualizar si no está pausado (ni por juego ni por spawn deshabilitado)
|
||||||
if (!is_paused) {
|
if (!is_paused && !spawn_disabled) {
|
||||||
delta_time = current_time - last_time;
|
delta_time = current_time - last_time;
|
||||||
|
|
||||||
if (time_until_next_spawn > delta_time) {
|
if (time_until_next_spawn > delta_time) {
|
||||||
@@ -101,9 +104,20 @@ class Tabe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pausa o reanuda el spawning
|
||||||
|
void setSpawnDisabled(bool disabled) {
|
||||||
|
if (spawn_disabled != disabled) {
|
||||||
|
spawn_disabled = disabled;
|
||||||
|
// Al reactivar, actualizar last_time para evitar saltos
|
||||||
|
if (!disabled) {
|
||||||
|
last_time = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Indica si el temporizador ha finalizado
|
// Indica si el temporizador ha finalizado
|
||||||
[[nodiscard]] auto shouldSpawn() const -> bool {
|
[[nodiscard]] auto shouldSpawn() const -> bool {
|
||||||
return time_until_next_spawn == 0 && !is_paused;
|
return time_until_next_spawn == 0 && !is_paused && !spawn_disabled;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
219
source/text.cpp
@@ -4,14 +4,16 @@
|
|||||||
|
|
||||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, endl, ifstream
|
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, endl, ifstream
|
||||||
#include <iostream> // Para cerr
|
#include <iostream> // Para cerr
|
||||||
|
#include <sstream> // Para istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
|
|
||||||
#include "color.h" // Para Color
|
#include "color.h" // Para Color
|
||||||
#include "screen.h" // Para Screen
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "sprite.h" // Para Sprite
|
#include "screen.h" // Para Screen
|
||||||
#include "texture.h" // Para Texture
|
#include "sprite.h" // Para Sprite
|
||||||
#include "utils.h" // Para getFileName, printWithDots
|
#include "texture.h" // Para Texture
|
||||||
|
#include "utils.h" // Para getFileName, printWithDots
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -52,6 +54,47 @@ Text::Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Text::
|
|||||||
fixed_width_ = false;
|
fixed_width_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constructor con textura blanca opcional
|
||||||
|
Text::Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Texture> &white_texture, const std::string &text_file) {
|
||||||
|
// Carga los offsets desde el fichero
|
||||||
|
auto tf = loadFile(text_file);
|
||||||
|
|
||||||
|
// Inicializa variables desde la estructura
|
||||||
|
box_height_ = tf->box_height;
|
||||||
|
box_width_ = tf->box_width;
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
offset_[i].x = tf->offset[i].x;
|
||||||
|
offset_[i].y = tf->offset[i].y;
|
||||||
|
offset_[i].w = tf->offset[i].w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea los objetos
|
||||||
|
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
|
||||||
|
white_sprite_ = std::make_unique<Sprite>(white_texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
|
||||||
|
|
||||||
|
// Inicializa variables
|
||||||
|
fixed_width_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor con textura blanca opcional
|
||||||
|
Text::Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Texture> &white_texture, const std::shared_ptr<Text::File> &text_file) {
|
||||||
|
// Inicializa variables desde la estructura
|
||||||
|
box_height_ = text_file->box_height;
|
||||||
|
box_width_ = text_file->box_width;
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
offset_[i].x = text_file->offset[i].x;
|
||||||
|
offset_[i].y = text_file->offset[i].y;
|
||||||
|
offset_[i].w = text_file->offset[i].w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea los objetos
|
||||||
|
sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
|
||||||
|
white_sprite_ = std::make_unique<Sprite>(white_texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)});
|
||||||
|
|
||||||
|
// Inicializa variables
|
||||||
|
fixed_width_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Escribe texto en pantalla
|
// Escribe texto en pantalla
|
||||||
void Text::write(int x, int y, const std::string &text, int kerning, int length) {
|
void Text::write(int x, int y, const std::string &text, int kerning, int length) {
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
@@ -114,8 +157,30 @@ auto Text::writeToTexture(const std::string &text, int zoom, int kerning, int le
|
|||||||
auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color text_color, Uint8 shadow_distance, Color shadow_color, int length) -> std::shared_ptr<Texture> {
|
auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color text_color, Uint8 shadow_distance, Color shadow_color, int length) -> std::shared_ptr<Texture> {
|
||||||
auto *renderer = Screen::get()->getRenderer();
|
auto *renderer = Screen::get()->getRenderer();
|
||||||
auto texture = std::make_shared<Texture>(renderer);
|
auto texture = std::make_shared<Texture>(renderer);
|
||||||
auto width = Text::length(text, kerning) + shadow_distance;
|
|
||||||
auto height = box_height_ + shadow_distance;
|
// Calcula las dimensiones considerando los efectos
|
||||||
|
auto base_width = Text::length(text, kerning);
|
||||||
|
auto base_height = box_height_;
|
||||||
|
auto width = base_width;
|
||||||
|
auto height = base_height;
|
||||||
|
auto offset_x = 0;
|
||||||
|
auto offset_y = 0;
|
||||||
|
|
||||||
|
const auto STROKED = ((flags & Text::STROKE) == Text::STROKE);
|
||||||
|
const auto SHADOWED = ((flags & Text::SHADOW) == Text::SHADOW);
|
||||||
|
|
||||||
|
if (STROKED) {
|
||||||
|
// Para stroke, el texto se expande en todas las direcciones por shadow_distance
|
||||||
|
width = base_width + (shadow_distance * 2);
|
||||||
|
height = base_height + (shadow_distance * 2);
|
||||||
|
offset_x = shadow_distance;
|
||||||
|
offset_y = shadow_distance;
|
||||||
|
} else if (SHADOWED) {
|
||||||
|
// Para shadow, solo se añade espacio a la derecha y abajo
|
||||||
|
width = base_width + shadow_distance;
|
||||||
|
height = base_height + shadow_distance;
|
||||||
|
}
|
||||||
|
|
||||||
auto *temp = SDL_GetRenderTarget(renderer);
|
auto *temp = SDL_GetRenderTarget(renderer);
|
||||||
|
|
||||||
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
@@ -123,7 +188,7 @@ auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, C
|
|||||||
texture->setAsRenderTarget(renderer);
|
texture->setAsRenderTarget(renderer);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
writeDX(flags, 0, 0, text, kerning, text_color, shadow_distance, shadow_color, length);
|
writeDX(flags, offset_x, offset_y, text, kerning, text_color, shadow_distance, shadow_color, length);
|
||||||
SDL_SetRenderTarget(renderer, temp);
|
SDL_SetRenderTarget(renderer, temp);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
@@ -131,9 +196,79 @@ auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, C
|
|||||||
|
|
||||||
// Escribe el texto con colores
|
// Escribe el texto con colores
|
||||||
void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int length) {
|
void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int length) {
|
||||||
sprite_->getTexture()->setColor(color.r, color.g, color.b);
|
writeColoredWithSprite(sprite_.get(), x, y, text, color, kerning, length);
|
||||||
write(x, y, text, kerning, length);
|
}
|
||||||
sprite_->getTexture()->setColor(255, 255, 255);
|
|
||||||
|
// Escribe el texto con colores usando un sprite específico
|
||||||
|
void Text::writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning, int length) {
|
||||||
|
int shift = 0;
|
||||||
|
const std::string_view VISIBLE_TEXT = (length == -1) ? std::string_view(text) : std::string_view(text).substr(0, length);
|
||||||
|
|
||||||
|
auto *texture = sprite->getTexture().get();
|
||||||
|
|
||||||
|
// Guarda el alpha original y aplica el nuevo
|
||||||
|
Uint8 original_alpha;
|
||||||
|
SDL_GetTextureAlphaMod(texture->getSDLTexture(), &original_alpha);
|
||||||
|
texture->setAlpha(color.a);
|
||||||
|
texture->setColor(color.r, color.g, color.b);
|
||||||
|
|
||||||
|
sprite->setY(y);
|
||||||
|
for (const auto CH : VISIBLE_TEXT) {
|
||||||
|
const auto INDEX = static_cast<unsigned char>(CH);
|
||||||
|
|
||||||
|
if (INDEX < offset_.size()) {
|
||||||
|
sprite->setSpriteClip(offset_[INDEX].x, offset_[INDEX].y, box_width_, box_height_);
|
||||||
|
sprite->setX(x + shift);
|
||||||
|
sprite->render();
|
||||||
|
shift += offset_[INDEX].w + kerning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restaura los valores originales
|
||||||
|
texture->setColor(255, 255, 255);
|
||||||
|
texture->setAlpha(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escribe stroke con alpha correcto usando textura temporal
|
||||||
|
void Text::writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length) {
|
||||||
|
auto *renderer = Screen::get()->getRenderer();
|
||||||
|
auto *original_target = SDL_GetRenderTarget(renderer);
|
||||||
|
|
||||||
|
// Calcula dimensiones de la textura temporal
|
||||||
|
auto text_width = Text::length(text, kerning);
|
||||||
|
auto text_height = box_height_;
|
||||||
|
auto temp_width = text_width + (shadow_distance * 2);
|
||||||
|
auto temp_height = text_height + (shadow_distance * 2);
|
||||||
|
|
||||||
|
// Crea textura temporal
|
||||||
|
auto temp_texture = std::make_shared<Texture>(renderer);
|
||||||
|
temp_texture->createBlank(temp_width, temp_height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
|
temp_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
|
// Renderiza el stroke en la textura temporal
|
||||||
|
temp_texture->setAsRenderTarget(renderer);
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
|
// Selecciona el sprite apropiado para el stroke
|
||||||
|
auto *stroke_sprite = white_sprite_ ? white_sprite_.get() : sprite_.get();
|
||||||
|
|
||||||
|
// Renderiza stroke sin alpha (sólido) en textura temporal
|
||||||
|
Color solid_color = Color(stroke_color.r, stroke_color.g, stroke_color.b, 255);
|
||||||
|
for (int dist = 1; dist <= shadow_distance; ++dist) {
|
||||||
|
for (int dy = -dist; dy <= dist; ++dy) {
|
||||||
|
for (int dx = -dist; dx <= dist; ++dx) {
|
||||||
|
writeColoredWithSprite(stroke_sprite, shadow_distance + dx, shadow_distance + dy, text, solid_color, kerning, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restaura render target original
|
||||||
|
SDL_SetRenderTarget(renderer, original_target);
|
||||||
|
|
||||||
|
// Renderiza la textura temporal con el alpha deseado
|
||||||
|
temp_texture->setAlpha(stroke_color.a);
|
||||||
|
temp_texture->render(x - shadow_distance, y - shadow_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escribe el texto con sombra
|
// Escribe el texto con sombra
|
||||||
@@ -160,14 +295,28 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerni
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (SHADOWED) {
|
if (SHADOWED) {
|
||||||
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, length);
|
if (white_sprite_) {
|
||||||
|
writeColoredWithSprite(white_sprite_.get(), x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, length);
|
||||||
|
} else {
|
||||||
|
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (STROKED) {
|
if (STROKED) {
|
||||||
for (int dist = 1; dist <= shadow_distance; ++dist) {
|
if (shadow_color.a < 255) {
|
||||||
for (int dy = -dist; dy <= dist; ++dy) {
|
// Usa textura temporal para alpha correcto
|
||||||
for (int dx = -dist; dx <= dist; ++dx) {
|
writeStrokeWithAlpha(x, y, text, kerning, shadow_color, shadow_distance, length);
|
||||||
writeColored(x + dx, y + dy, text, shadow_color, kerning, length);
|
} else {
|
||||||
|
// Método tradicional para stroke sólido
|
||||||
|
for (int dist = 1; dist <= shadow_distance; ++dist) {
|
||||||
|
for (int dy = -dist; dy <= dist; ++dy) {
|
||||||
|
for (int dx = -dist; dx <= dist; ++dx) {
|
||||||
|
if (white_sprite_) {
|
||||||
|
writeColoredWithSprite(white_sprite_.get(), x + dx, y + dy, text, shadow_color, kerning, length);
|
||||||
|
} else {
|
||||||
|
writeColored(x + dx, y + dy, text, shadow_color, kerning, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,25 +374,41 @@ auto Text::loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>
|
|||||||
tf->box_height = 0;
|
tf->box_height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abre el fichero para leer los valores
|
// Intenta cargar desde ResourceHelper primero
|
||||||
std::ifstream file(file_path);
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
std::istringstream stream;
|
||||||
|
bool using_resource_data = false;
|
||||||
|
|
||||||
if (file.is_open() && file.good()) {
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
|
||||||
|
|
||||||
|
if ((using_resource_data && stream.good()) || (!using_resource_data && file.is_open() && file.good())) {
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
|
|
||||||
// Lee los dos primeros valores del fichero
|
// Lee los dos primeros valores del fichero
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
tf->box_width = std::stoi(buffer);
|
tf->box_width = std::stoi(buffer);
|
||||||
|
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
tf->box_height = std::stoi(buffer);
|
tf->box_height = std::stoi(buffer);
|
||||||
|
|
||||||
// lee el resto de datos del fichero
|
// lee el resto de datos del fichero
|
||||||
auto index = 32;
|
auto index = 32;
|
||||||
auto line_read = 0;
|
auto line_read = 0;
|
||||||
while (std::getline(file, buffer)) {
|
while (std::getline(input_stream, buffer)) {
|
||||||
// Almacena solo las lineas impares
|
// Almacena solo las lineas impares
|
||||||
if (line_read % 2 == 1) {
|
if (line_read % 2 == 1) {
|
||||||
tf->offset[index++].w = std::stoi(buffer);
|
tf->offset[index++].w = std::stoi(buffer);
|
||||||
@@ -254,9 +419,11 @@ auto Text::loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>
|
|||||||
line_read++;
|
line_read++;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cierra el fichero
|
// Cierra el fichero si se usó
|
||||||
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
||||||
file.close();
|
if (!using_resource_data && file.is_open()) {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// El fichero no se puede abrir
|
// El fichero no se puede abrir
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ class Text {
|
|||||||
// --- Constructores y destructor ---
|
// --- Constructores y destructor ---
|
||||||
Text(const std::shared_ptr<Texture> &texture, const std::string &text_file);
|
Text(const std::shared_ptr<Texture> &texture, const std::string &text_file);
|
||||||
Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Text::File> &text_file);
|
Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Text::File> &text_file);
|
||||||
|
Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Texture> &white_texture, const std::string &text_file);
|
||||||
|
Text(const std::shared_ptr<Texture> &texture, const std::shared_ptr<Texture> &white_texture, const std::shared_ptr<Text::File> &text_file);
|
||||||
~Text() = default;
|
~Text() = default;
|
||||||
|
|
||||||
// --- Métodos de escritura en pantalla ---
|
// --- Métodos de escritura en pantalla ---
|
||||||
@@ -81,9 +83,14 @@ class Text {
|
|||||||
// --- Métodos estáticos ---
|
// --- Métodos estáticos ---
|
||||||
static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero
|
static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero
|
||||||
|
|
||||||
|
// --- Métodos privados ---
|
||||||
|
void writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning = 1, int length = -1); // Escribe con un sprite específico
|
||||||
|
void writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1); // Escribe stroke con alpha correcto
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto
|
std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto
|
||||||
|
std::unique_ptr<Sprite> white_sprite_ = nullptr; // Objeto con los gráficos en blanco para efectos
|
||||||
|
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
std::array<Offset, 128> offset_ = {}; // Vector con las posiciones y ancho de cada letra
|
std::array<Offset, 128> offset_ = {}; // Vector con las posiciones y ancho de cada letra
|
||||||
|
|||||||
@@ -12,9 +12,10 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.h" // Para getFileName, Color, printWithDots
|
#include "color.h" // Para getFileName, Color, printWithDots
|
||||||
#include "external/gif.h" // Para Gif
|
#include "external/gif.h" // Para Gif
|
||||||
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -64,7 +65,19 @@ auto Texture::loadFromFile(const std::string &file_path) -> bool {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int orig_format;
|
int orig_format;
|
||||||
unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
unsigned char *data = nullptr;
|
||||||
|
|
||||||
|
// Intentar cargar desde ResourceHelper primero
|
||||||
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
data = stbi_load_from_memory(resource_data.data(), resource_data.size(), &width, &height, &orig_format, req_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
if (data == nullptr) {
|
||||||
|
data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
||||||
|
}
|
||||||
|
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
|
||||||
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
||||||
@@ -212,22 +225,30 @@ auto Texture::loadSurface(const std::string &file_path) -> std::shared_ptr<Surfa
|
|||||||
// Libera la superficie actual
|
// Libera la superficie actual
|
||||||
unloadSurface();
|
unloadSurface();
|
||||||
|
|
||||||
// Abrir el archivo usando std::ifstream para manejo automático del recurso
|
std::vector<Uint8> buffer;
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
|
||||||
if (!file) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtener el tamaño del archivo
|
// Intentar cargar desde ResourceHelper primero
|
||||||
std::streamsize size = file.tellg();
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
file.seekg(0, std::ios::beg);
|
if (!resource_data.empty()) {
|
||||||
|
buffer = resource_data;
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
|
||||||
// Leer el contenido del archivo en un buffer
|
// Obtener el tamaño del archivo
|
||||||
std::vector<Uint8> buffer(size);
|
std::streamsize size = file.tellg();
|
||||||
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
file.seekg(0, std::ios::beg);
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
|
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
// Leer el contenido del archivo en un buffer
|
||||||
|
buffer.resize(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());
|
||||||
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear un objeto Gif y llamar a la función loadGif
|
// Crear un objeto Gif y llamar a la función loadGif
|
||||||
@@ -283,24 +304,33 @@ void Texture::setPaletteColor(int palette, int index, Uint32 color) {
|
|||||||
auto Texture::loadPaletteFromFile(const std::string &file_path) -> Palette {
|
auto Texture::loadPaletteFromFile(const std::string &file_path) -> Palette {
|
||||||
Palette palette;
|
Palette palette;
|
||||||
|
|
||||||
// Abrir el archivo GIF
|
std::vector<Uint8> buffer;
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
|
||||||
if (!file) {
|
// Intentar cargar desde ResourceHelper primero
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
if (!resource_data.empty()) {
|
||||||
|
buffer = resource_data;
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener el tamaño del archivo y leerlo en un buffer
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
buffer.resize(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());
|
||||||
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
|
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
|
||||||
|
|
||||||
// Obtener el tamaño del archivo y leerlo en un buffer
|
|
||||||
std::streamsize size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
std::vector<Uint8> buffer(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());
|
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
||||||
@@ -346,16 +376,33 @@ 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)
|
||||||
|
|
||||||
std::ifstream file(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!file.is_open()) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
throw std::runtime_error("No se pudo abrir el archivo .pal");
|
std::istringstream stream;
|
||||||
|
bool using_resource_data = false;
|
||||||
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("No se pudo abrir el archivo .pal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
int line_number = 0;
|
int line_number = 0;
|
||||||
int color_index = 0;
|
int color_index = 0;
|
||||||
|
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(input_stream, line)) {
|
||||||
++line_number;
|
++line_number;
|
||||||
|
|
||||||
// Ignorar las tres primeras líneas del archivo
|
// Ignorar las tres primeras líneas del archivo
|
||||||
@@ -380,6 +427,8 @@ auto Texture::readPalFile(const std::string &file_path) -> Palette {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
if (!using_resource_data && file.is_open()) {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,9 @@ struct Surface {
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays)
|
Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays)
|
||||||
: data(std::move(pixels)), w(width), h(height) {}
|
: data(std::move(pixels)),
|
||||||
|
w(width),
|
||||||
|
h(height) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clase Texture: gestiona texturas, paletas y renderizado
|
// Clase Texture: gestiona texturas, paletas y renderizado
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class MenuOption {
|
|||||||
|
|
||||||
// --- Constructor y destructor ---
|
// --- Constructor y destructor ---
|
||||||
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
|
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
|
||||||
: caption_(std::move(caption)), group_(group), hidden_(hidden) {}
|
: caption_(std::move(caption)),
|
||||||
|
group_(group),
|
||||||
|
hidden_(hidden) {}
|
||||||
virtual ~MenuOption() = default;
|
virtual ~MenuOption() = default;
|
||||||
|
|
||||||
// --- Getters ---
|
// --- Getters ---
|
||||||
@@ -52,7 +54,8 @@ class MenuOption {
|
|||||||
class BoolOption : public MenuOption {
|
class BoolOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var)
|
BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var)
|
||||||
: MenuOption(cap, grp), linked_variable_(var) {}
|
: MenuOption(cap, grp),
|
||||||
|
linked_variable_(var) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
||||||
[[nodiscard]] auto getValueAsString() const -> std::string override {
|
[[nodiscard]] auto getValueAsString() const -> std::string override {
|
||||||
@@ -74,7 +77,11 @@ class BoolOption : public MenuOption {
|
|||||||
class IntOption : public MenuOption {
|
class IntOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step)
|
IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step)
|
||||||
: MenuOption(cap, grp), linked_variable_(var), min_value_(min), max_value_(max), step_value_(step) {}
|
: MenuOption(cap, grp),
|
||||||
|
linked_variable_(var),
|
||||||
|
min_value_(min),
|
||||||
|
max_value_(max),
|
||||||
|
step_value_(step) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
||||||
[[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); }
|
[[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); }
|
||||||
@@ -150,7 +157,8 @@ class ListOption : public MenuOption {
|
|||||||
class FolderOption : public MenuOption {
|
class FolderOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target)
|
FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target)
|
||||||
: MenuOption(cap, grp), target_group_(target) {}
|
: MenuOption(cap, grp),
|
||||||
|
target_group_(target) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
||||||
[[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; }
|
[[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; }
|
||||||
@@ -162,7 +170,8 @@ class FolderOption : public MenuOption {
|
|||||||
class ActionOption : public MenuOption {
|
class ActionOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false)
|
ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false)
|
||||||
: MenuOption(cap, grp, hidden), action_(std::move(action)) {}
|
: MenuOption(cap, grp, hidden),
|
||||||
|
action_(std::move(action)) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
||||||
void executeAction() override {
|
void executeAction() override {
|
||||||
@@ -183,7 +192,11 @@ class ActionListOption : public MenuOption {
|
|||||||
using ActionExecutor = std::function<void()>;
|
using ActionExecutor = std::function<void()>;
|
||||||
|
|
||||||
ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false)
|
ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false)
|
||||||
: MenuOption(caption, group, hidden), options_(std::move(options)), value_getter_(std::move(getter)), value_setter_(std::move(setter)), action_executor_(std::move(action_executor)) {
|
: MenuOption(caption, group, hidden),
|
||||||
|
options_(std::move(options)),
|
||||||
|
value_getter_(std::move(getter)),
|
||||||
|
value_setter_(std::move(setter)),
|
||||||
|
action_executor_(std::move(action_executor)) {
|
||||||
updateCurrentIndex();
|
updateCurrentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ void MenuRenderer::ShowHideAnimation::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
|
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
|
||||||
: element_text_(std::move(element_text)), title_text_(std::move(title_text)) {
|
: element_text_(std::move(element_text)),
|
||||||
|
title_text_(std::move(title_text)) {
|
||||||
initializeMaxSizes();
|
initializeMaxSizes();
|
||||||
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
|
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
|
||||||
}
|
}
|
||||||
@@ -345,7 +346,7 @@ void MenuRenderer::updateColorCounter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto MenuRenderer::getAnimatedSelectedColor() const -> Color {
|
auto MenuRenderer::getAnimatedSelectedColor() const -> Color {
|
||||||
static auto color_cycle_ = generateMirroredCycle(param.service_menu.selected_color, ColorCycleStyle::HUE_WAVE);
|
static auto color_cycle_ = Colors::generateMirroredCycle(param.service_menu.selected_color, ColorCycleStyle::HUE_WAVE);
|
||||||
return color_cycle_.at(color_counter_ % color_cycle_.size());
|
return color_cycle_.at(color_counter_ % color_cycle_.size());
|
||||||
}
|
}
|
||||||
auto MenuRenderer::setRect(SDL_FRect rect) -> SDL_FRect {
|
auto MenuRenderer::setRect(SDL_FRect rect) -> SDL_FRect {
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ class Notifier {
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
explicit Notification()
|
explicit Notification()
|
||||||
: texture(nullptr), sprite(nullptr), rect{0, 0, 0, 0} {}
|
: texture(nullptr),
|
||||||
|
sprite(nullptr),
|
||||||
|
rect{0, 0, 0, 0} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
|
|||||||
@@ -49,16 +49,16 @@ void ServiceMenu::toggle() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playBackSound();
|
|
||||||
|
|
||||||
if (!enabled_) { // Si está cerrado, abrir
|
if (!enabled_) { // Si está cerrado, abrir
|
||||||
reset();
|
reset();
|
||||||
Options::gamepad_manager.assignAndLinkGamepads();
|
Options::gamepad_manager.assignAndLinkGamepads();
|
||||||
renderer_->show(this);
|
renderer_->show(this);
|
||||||
setEnabledInternal(true);
|
setEnabledInternal(true);
|
||||||
|
playSelectSound();
|
||||||
} else { // Si está abierto, cerrar
|
} else { // Si está abierto, cerrar
|
||||||
renderer_->hide();
|
renderer_->hide();
|
||||||
setEnabledInternal(false);
|
setEnabledInternal(false);
|
||||||
|
playBackSound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +518,7 @@ void ServiceMenu::adjustListValues() {
|
|||||||
void ServiceMenu::playAdjustSound() { Audio::get()->playSound("service_menu_adjust.wav", Audio::Group::INTERFACE); }
|
void ServiceMenu::playAdjustSound() { Audio::get()->playSound("service_menu_adjust.wav", Audio::Group::INTERFACE); }
|
||||||
void ServiceMenu::playMoveSound() { Audio::get()->playSound("service_menu_move.wav", Audio::Group::INTERFACE); }
|
void ServiceMenu::playMoveSound() { Audio::get()->playSound("service_menu_move.wav", Audio::Group::INTERFACE); }
|
||||||
void ServiceMenu::playSelectSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); }
|
void ServiceMenu::playSelectSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); }
|
||||||
void ServiceMenu::playBackSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); }
|
void ServiceMenu::playBackSound() { Audio::get()->playSound("service_menu_back.wav", Audio::Group::INTERFACE); }
|
||||||
|
|
||||||
// Devuelve el nombre del grupo como string para el título
|
// Devuelve el nombre del grupo como string para el título
|
||||||
auto ServiceMenu::settingsGroupToString(SettingsGroup group) -> std::string {
|
auto ServiceMenu::settingsGroupToString(SettingsGroup group) -> std::string {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
// Constructor: inicializa el renderizador, el texto y el color del mensaje
|
// Constructor: inicializa el renderizador, el texto y el color del mensaje
|
||||||
UIMessage::UIMessage(std::shared_ptr<Text> text_renderer, std::string message_text, const Color &color)
|
UIMessage::UIMessage(std::shared_ptr<Text> text_renderer, std::string message_text, const Color &color)
|
||||||
: text_renderer_(std::move(text_renderer)), text_(std::move(message_text)), color_(color) {}
|
: text_renderer_(std::move(text_renderer)),
|
||||||
|
text_(std::move(message_text)),
|
||||||
|
color_(color) {}
|
||||||
|
|
||||||
// Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba
|
// Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba
|
||||||
void UIMessage::show() {
|
void UIMessage::show() {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string> // Para basic_string, allocator, string, operator==, operator+, char_traits
|
#include <string> // Para basic_string, allocator, string, operator==, operator+, char_traits
|
||||||
|
|
||||||
#include "lang.h" // Para getText
|
#include "lang.h" // Para getText
|
||||||
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
Overrides overrides = Overrides();
|
Overrides overrides = Overrides();
|
||||||
@@ -323,8 +324,17 @@ void printWithDots(const std::string &text1, const std::string &text2, const std
|
|||||||
auto loadDemoDataFromFile(const std::string &file_path) -> DemoData {
|
auto loadDemoDataFromFile(const std::string &file_path) -> DemoData {
|
||||||
DemoData dd;
|
DemoData dd;
|
||||||
|
|
||||||
// Indicador de éxito en la carga
|
SDL_IOStream *file = nullptr;
|
||||||
auto *file = SDL_IOFromFile(file_path.c_str(), "r+b");
|
|
||||||
|
// Intentar cargar desde ResourceHelper primero
|
||||||
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
file = SDL_IOFromConstMem(resource_data.data(), resource_data.size());
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
file = SDL_IOFromFile(file_path.c_str(), "r+b");
|
||||||
|
}
|
||||||
|
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
|||||||
@@ -22,9 +22,14 @@ struct Overrides {
|
|||||||
|
|
||||||
struct Circle {
|
struct Circle {
|
||||||
int x, y, r; // Coordenadas y radio
|
int x, y, r; // Coordenadas y radio
|
||||||
Circle() : x(0), y(0), r(0) {}
|
Circle()
|
||||||
|
: x(0),
|
||||||
|
y(0),
|
||||||
|
r(0) {}
|
||||||
Circle(int x_coord, int y_coord, int radius)
|
Circle(int x_coord, int y_coord, int radius)
|
||||||
: x(x_coord), y(y_coord), r(radius) {}
|
: x(x_coord),
|
||||||
|
y(y_coord),
|
||||||
|
r(radius) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DemoKeys {
|
struct DemoKeys {
|
||||||
@@ -36,7 +41,12 @@ struct DemoKeys {
|
|||||||
Uint8 fire_right;
|
Uint8 fire_right;
|
||||||
|
|
||||||
explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0)
|
explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0)
|
||||||
: left(l), right(r), no_input(ni), fire(f), fire_left(fl), fire_right(fr) {}
|
: left(l),
|
||||||
|
right(r),
|
||||||
|
no_input(ni),
|
||||||
|
fire(f),
|
||||||
|
fire_left(fl),
|
||||||
|
fire_right(fr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Tipos ---
|
// --- Tipos ---
|
||||||
@@ -49,9 +59,16 @@ struct Demo {
|
|||||||
DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo
|
DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo
|
||||||
std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo
|
std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo
|
||||||
|
|
||||||
Demo() : enabled(false), recording(false), counter(0) {}
|
Demo()
|
||||||
|
: enabled(false),
|
||||||
|
recording(false),
|
||||||
|
counter(0) {}
|
||||||
Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d)
|
Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d)
|
||||||
: enabled(e), recording(r), counter(c), keys(k), data(d) {}
|
: enabled(e),
|
||||||
|
recording(r),
|
||||||
|
counter(c),
|
||||||
|
keys(k),
|
||||||
|
data(d) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Zone {
|
struct Zone {
|
||||||
|
|||||||
77
tools/Makefile
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Makefile para herramientas de Coffee Crisis Arcade Edition
|
||||||
|
# =========================================================
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
CXX := g++
|
||||||
|
CXXFLAGS := -std=c++17 -Wall -Os -I../
|
||||||
|
SOURCES := pack_resources.cpp ../source/resource_pack.cpp
|
||||||
|
TARGET := pack_resources
|
||||||
|
CLEAN_FILES := pack_resources *.pack *.o
|
||||||
|
|
||||||
|
# Detectar sistema operativo
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
DETECTED_OS := Windows
|
||||||
|
TARGET := $(TARGET).exe
|
||||||
|
CLEAN_CMD := del /Q
|
||||||
|
FixPath = $(subst /,\\,$1)
|
||||||
|
else
|
||||||
|
DETECTED_OS := $(shell uname -s)
|
||||||
|
CLEAN_CMD := rm -f
|
||||||
|
FixPath = $1
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Reglas principales
|
||||||
|
.PHONY: all pack_tool clean help test_pack
|
||||||
|
|
||||||
|
# Compilar herramienta de empaquetado
|
||||||
|
all: pack_tool
|
||||||
|
|
||||||
|
pack_tool:
|
||||||
|
@echo "Compilando herramienta de empaquetado para $(DETECTED_OS)..."
|
||||||
|
$(CXX) $(CXXFLAGS) $(SOURCES) -o $(TARGET)
|
||||||
|
@echo "✓ Herramienta compilada: $(TARGET)"
|
||||||
|
|
||||||
|
# Limpiar archivos generados
|
||||||
|
clean:
|
||||||
|
@echo "Limpiando archivos generados..."
|
||||||
|
$(CLEAN_CMD) $(call FixPath,$(CLEAN_FILES))
|
||||||
|
@echo "✓ Archivos limpiados"
|
||||||
|
|
||||||
|
# Crear pack de recursos de prueba
|
||||||
|
test_pack: pack_tool
|
||||||
|
@echo "Creando pack de recursos de prueba..."
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
.\$(TARGET) ..\data test_resources.pack
|
||||||
|
else
|
||||||
|
./$(TARGET) ../data test_resources.pack
|
||||||
|
endif
|
||||||
|
@echo "✓ Pack de prueba creado: test_resources.pack"
|
||||||
|
|
||||||
|
# Crear pack de recursos final
|
||||||
|
create_pack: pack_tool
|
||||||
|
@echo "Creando pack de recursos final..."
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
.\$(TARGET) ..\data ..\resources.pack
|
||||||
|
else
|
||||||
|
./$(TARGET) ../data ../resources.pack
|
||||||
|
endif
|
||||||
|
@echo "✓ Pack final creado: ../resources.pack"
|
||||||
|
|
||||||
|
# Mostrar ayuda
|
||||||
|
help:
|
||||||
|
@echo "Makefile para herramientas de Coffee Crisis Arcade Edition"
|
||||||
|
@echo "========================================================="
|
||||||
|
@echo ""
|
||||||
|
@echo "Comandos disponibles:"
|
||||||
|
@echo " all - Compilar herramienta de empaquetado (por defecto)"
|
||||||
|
@echo " pack_tool - Compilar herramienta de empaquetado"
|
||||||
|
@echo " test_pack - Crear pack de recursos de prueba"
|
||||||
|
@echo " create_pack - Crear pack de recursos final"
|
||||||
|
@echo " clean - Limpiar archivos generados"
|
||||||
|
@echo " help - Mostrar esta ayuda"
|
||||||
|
@echo ""
|
||||||
|
@echo "Ejemplos de uso:"
|
||||||
|
@echo " make # Compilar herramienta"
|
||||||
|
@echo " make test_pack # Crear pack de prueba"
|
||||||
|
@echo " make create_pack # Crear pack final"
|
||||||
|
@echo " make clean # Limpiar archivos"
|
||||||