Compare commits

33 Commits

Author SHA1 Message Date
d2be113c4e corregit makefile 2025-10-25 09:29:01 +02:00
052bb063df Corregit resource_helper i millora DMG amb create-dmg 2025-10-25 00:20:35 +02:00
9d974438b3 millorat makefile per a que el dmg es veja millor 2025-10-24 23:31:30 +02:00
b9fe376f2a eliminat el fallback a disc en les versions de release 2025-10-24 23:21:27 +02:00
0c05f6d5b6 corregida la versio release de macos per a funcionar correctament amb resources.pack 2025-10-24 23:11:48 +02:00
39125ee57c nou icon 2025-10-24 21:17:29 +02:00
7ebeeebaaf arreglat include en pack_resources.cpp 2025-10-24 18:38:02 +02:00
c6c7b63f6d ultim linter 2025-10-24 18:36:00 +02:00
7959baeed9 linter 2025-10-24 18:22:15 +02:00
5d1c6c6d99 linter 2025-10-24 18:11:51 +02:00
145d1e3fc0 canvis en run_clang-tidy.sh 2025-10-24 17:58:53 +02:00
4679255d60 linter 2025-10-24 17:50:31 +02:00
3cfb65320c linter: opengl_shader.cpp 2025-10-24 17:21:17 +02:00
636e4d932a linter: varios 2025-10-24 17:12:57 +02:00
9979f31b4a linter: manage_hiscore_table 2025-10-24 14:10:38 +02:00
6190b35349 integrat jail_audio en la carrega de resources.pack 2025-10-24 13:59:03 +02:00
fd4136a882 linter: varios 2025-10-24 13:45:56 +02:00
5362c5b022 linter; manage_hiscore i resource_loader 2025-10-24 12:06:53 +02:00
357b5d5977 linter: renombrar variables 2025-10-24 10:33:18 +02:00
f50ad68f10 linter: resource_pack 2025-10-24 10:27:56 +02:00
155a7d038d ajustades constants en credits.cpp 2025-10-24 10:13:10 +02:00
4b732c189c migrat correctament a delta_time credits.cpp i eliminat un bug que feia que si no pasaves a ma el final, no acabara mai 2025-10-24 10:10:55 +02:00
1c3d59d678 migrat hiscore_table a delta_time de manera correcta
corregida deformació subpixel de la textura en instructions i hiscore_table
2025-10-24 08:49:09 +02:00
1acbe1d097 migrat instructions a delta_time pur 2025-10-23 18:59:37 +02:00
245524c021 fix: detecta fitxers de puntuació corruptes 2025-10-23 18:47:38 +02:00
b4624a9223 arreglos en makefiles i cmakes 2025-10-23 18:32:17 +02:00
28e3e1ee0a style: si ja havies posat nom, el carrusel apareix directament en el caracter d'acabar 2025-10-23 17:46:06 +02:00
e250ca048f calibrats els pulsos al gust 2025-10-23 14:56:33 +02:00
5bf96b9aba afegit "pulsos" a scoreboard 2025-10-23 14:44:51 +02:00
ac6f521288 fix: la logica de Rects dels credits 2025-10-22 21:02:09 +02:00
5a5c06efd1 fix: si vas a posar nom i el nom ja està ple, ja aplica les restriccions i posa el carrusel com toca 2025-10-22 19:58:47 +02:00
ef1a514c9b style: ja pots mouret pel carrusel de posar nom mantenint la direccio apretada (ja no vas lletra a lletra) 2025-10-22 19:55:02 +02:00
ce54b10abb afegida classe Cooldown 2025-10-22 19:54:27 +02:00
49 changed files with 1377 additions and 754 deletions

7
.gitignore vendored
View File

@@ -18,4 +18,9 @@ debug.txt
cppcheck-result*
desktop.ini
ccae_release/
resources.pack
Frameworks/
resources.pack
# Herramienta pack_resources (todas las plataformas)
tools/pack_resources
tools/pack_resources.exe

171
Makefile
View File

@@ -22,7 +22,7 @@ else
PACK_CXX := $(CXX)
endif
PACK_SOURCES := $(DIR_TOOLS)pack_resources.cpp $(DIR_SOURCES)resource_pack.cpp
PACK_INCLUDES := -I$(DIR_ROOT)
PACK_INCLUDES := -I$(DIR_ROOT) -I$(DIR_BUILD)
# Versión automática basada en la fecha actual (específica por SO)
ifeq ($(OS),Windows_NT)
@@ -49,45 +49,41 @@ RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
# Lista completa de archivos fuente (basada en CMakeLists.txt)
APP_SOURCES := \
source/animated_sprite.cpp \
source/asset.cpp \
source/audio.cpp \
source/background.cpp \
source/balloon_formations.cpp \
source/balloon_manager.cpp \
source/balloon.cpp \
source/bullet.cpp \
source/color.cpp \
source/define_buttons.cpp \
source/difficulty.cpp \
source/director.cpp \
source/enter_name.cpp \
source/explosions.cpp \
source/external/gif.cpp \
source/external/jail_audio.cpp \
source/external/jail_shader.cpp \
source/fade.cpp \
source/game_logo.cpp \
source/global_events.cpp \
source/global_inputs.cpp \
source/input_types.cpp \
source/input.cpp \
source/item.cpp \
source/lang.cpp \
source/main.cpp \
source/manage_hiscore_table.cpp \
source/mouse.cpp \
source/moving_sprite.cpp \
source/options.cpp \
source/param.cpp \
source/path_sprite.cpp \
source/player.cpp \
source/resource.cpp \
source/resource_helper.cpp \
source/resource_loader.cpp \
source/resource_pack.cpp \
source/scoreboard.cpp \
source/screen.cpp \
source/text.cpp \
source/writer.cpp \
source/ui/menu_option.cpp \
source/ui/menu_renderer.cpp \
source/ui/notifier.cpp \
source/ui/service_menu.cpp \
source/ui/ui_message.cpp \
source/ui/window_message.cpp \
source/balloon_formations.cpp \
source/balloon_manager.cpp \
source/balloon.cpp \
source/bullet.cpp \
source/bullet_manager.cpp \
source/enter_name.cpp \
source/explosions.cpp \
source/game_logo.cpp \
source/item.cpp \
source/manage_hiscore_table.cpp \
source/player.cpp \
source/scoreboard.cpp \
source/tabe.cpp \
source/sections/credits.cpp \
source/sections/game.cpp \
source/sections/hiscore_table.cpp \
@@ -95,26 +91,32 @@ APP_SOURCES := \
source/sections/intro.cpp \
source/sections/logo.cpp \
source/sections/title.cpp \
source/shutdown.cpp \
source/animated_sprite.cpp \
source/background.cpp \
source/fade.cpp \
source/moving_sprite.cpp \
source/path_sprite.cpp \
source/smart_sprite.cpp \
source/sprite.cpp \
source/stage.cpp \
source/system_utils.cpp \
source/tabe.cpp \
source/text.cpp \
source/texture.cpp \
source/tiled_bg.cpp \
source/ui/menu_option.cpp \
source/ui/menu_renderer.cpp \
source/ui/notifier.cpp \
source/ui/service_menu.cpp \
source/ui/ui_message.cpp \
source/ui/window_message.cpp \
source/color.cpp \
source/demo.cpp \
source/define_buttons.cpp \
source/difficulty.cpp \
source/input_types.cpp \
source/mouse.cpp \
source/options.cpp \
source/shutdown.cpp \
source/stage.cpp \
source/system_utils.cpp \
source/utils.cpp \
source/writer.cpp
source/external/jail_audio.cpp \
source/external/gif.cpp \
source/rendering/opengl/opengl_shader.cpp
# Includes
INCLUDES := -Isource -Isource/external
INCLUDES := -Isource -Isource/external -Isource/rendering -I$(DIR_BUILD)
# Variables según el sistema operativo
ifeq ($(OS),Windows_NT)
@@ -148,9 +150,9 @@ else
endif
# Reglas para herramienta de empaquetado y resources.pack
$(PACK_TOOL): $(PACK_SOURCES)
$(PACK_TOOL): FORCE
@echo "Compilando herramienta de empaquetado..."
$(PACK_CXX) -std=c++17 -Wall -Os $(PACK_INCLUDES) $(PACK_SOURCES) -o $(PACK_TOOL)
$(PACK_CXX) -std=c++20 -Wall -Os $(PACK_INCLUDES) $(PACK_SOURCES) -o $(PACK_TOOL)
@echo "✓ Herramienta de empaquetado lista: $(PACK_TOOL)"
pack_tool: $(PACK_TOOL)
@@ -178,7 +180,9 @@ windows_debug:
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_debug.exe"
windows_release: resources.pack
windows_release:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo off
@echo Creando release para Windows - Version: $(VERSION)
@@ -197,7 +201,7 @@ windows_release: resources.pack
# Compila
windres release/coffee.rc -O coff -o $(RESOURCE_FILE)
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_RELEASE_FILE).exe"
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_RELEASE_FILE).exe"
strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
# Crea el fichero .zip
@@ -216,8 +220,14 @@ macos_debug:
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
macos_release: resources.pack
macos_release:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Creando release para macOS - Version: $(VERSION)"
# Verificar e instalar create-dmg si es necesario
@which create-dmg > /dev/null || (echo "Instalando create-dmg..." && brew install create-dmg)
# Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)"
$(RMDIR) Frameworks
@@ -241,33 +251,52 @@ macos_release: resources.pack
cp LICENSE "$(RELEASE_FOLDER)"
cp README.md "$(RELEASE_FOLDER)"
# Crea enlaces
ln -s /Applications "$(RELEASE_FOLDER)"/Applications
# Compila la versión para procesadores Intel
ifdef ENABLE_MACOS_X86_64
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.15
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target x86_64-apple-macos10.15
# Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
# Empaqueta el .dmg de la versión Intel
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
$(RMFILE) tmp.dmg
# Empaqueta el .dmg de la versión Intel con create-dmg
@echo "Creando DMG Intel con iconos de 96x96..."
create-dmg \
--volname "$(APP_NAME)" \
--window-pos 200 120 \
--window-size 720 300 \
--icon-size 96 \
--text-size 12 \
--icon "$(APP_NAME).app" 278 102 \
--icon "LICENSE" 441 102 \
--icon "README.md" 604 102 \
--app-drop-link 115 102 \
--hide-extension "$(APP_NAME).app" \
"$(MACOS_INTEL_RELEASE)" \
"$(RELEASE_FOLDER)" || true
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
endif
# Compila la versión para procesadores Apple Silicon
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
$(CXX) $(APP_SOURCES) $(INCLUDES) -DMACOS_BUNDLE -DRELEASE_BUILD -DSDL_DISABLE_IMMINTRIN_H $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/MacOS/$(TARGET_NAME)" -rpath @executable_path/../Frameworks/ -target arm64-apple-macos11
# Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
# Empaqueta el .dmg de la versión Apple Silicon
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
$(RMFILE) tmp.dmg
# Empaqueta el .dmg de la versión Apple Silicon con create-dmg
@echo "Creando DMG Apple Silicon con iconos de 96x96..."
create-dmg \
--volname "$(APP_NAME)" \
--window-pos 200 120 \
--window-size 720 300 \
--icon-size 96 \
--text-size 12 \
--icon "$(APP_NAME).app" 278 102 \
--icon "LICENSE" 441 102 \
--icon "README.md" 604 102 \
--app-drop-link 115 102 \
--hide-extension "$(APP_NAME).app" \
"$(MACOS_APPLE_SILICON_RELEASE)" \
"$(RELEASE_FOLDER)" || true
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
# Elimina las carpetas temporales
@@ -283,7 +312,9 @@ linux_debug:
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
linux_release: resources.pack
linux_release:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Creando release para Linux - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -298,7 +329,7 @@ linux_release: resources.pack
cp README.md "$(RELEASE_FOLDER)"
# Compila
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE)"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE)"
strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded
# Empaqueta ficheros
@@ -309,7 +340,9 @@ linux_release: resources.pack
# Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)"
linux_release_desktop: resources.pack
linux_release_desktop:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -328,7 +361,7 @@ linux_release_desktop: resources.pack
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
# Compila el ejecutable
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)"
strip -s -R .comment -R .gnu.version "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)" --strip-unneeded
# Crea el archivo .desktop
@@ -414,7 +447,9 @@ raspi_debug:
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
raspi_release: resources.pack
raspi_release:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -429,7 +464,7 @@ raspi_release: resources.pack
cp README.md "$(RELEASE_FOLDER)"
# Compila
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE)"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE)"
strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded
# Empaqueta ficheros
@@ -440,7 +475,9 @@ raspi_release: resources.pack
# Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)"
anbernic: resources.pack
anbernic:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Compilando para Anbernic: $(TARGET_NAME)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
@@ -453,7 +490,7 @@ anbernic: resources.pack
cp resources.pack "$(RELEASE_FOLDER)"_anbernic
# Compila
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRELEASE_BUILD -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
# Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake)
no_audio:
@@ -487,4 +524,6 @@ help:
@echo " show_version - Mostrar version actual ($(VERSION))"
@echo " help - Mostrar esta ayuda"
.PHONY: windows windows_rec windows_debug windows_release macos macos_debug macos_release linux linux_debug linux_release linux_release_desktop raspi raspi_debug raspi_release anbernic no_audio show_version help pack_tool
.PHONY: windows windows_rec windows_debug windows_release macos macos_debug macos_release linux linux_debug linux_release linux_release_desktop raspi raspi_debug raspi_release anbernic no_audio show_version help pack_tool resources.pack
FORCE:

View File

@@ -18,191 +18,191 @@ DATA|${PREFIX}/config/pools.txt
DATA|${PREFIX}/config/stages.txt
# Archivos con los datos de la demo
DEMODATA|${PREFIX}/data/demo/demo1.bin
DEMODATA|${PREFIX}/data/demo/demo2.bin
DEMODATA|${PREFIX}/data/demo/demo3.bin
DEMODATA|/data/demo/demo1.bin
DEMODATA|/data/demo/demo2.bin
DEMODATA|/data/demo/demo3.bin
# Música
MUSIC|${PREFIX}/data/music/congratulations.ogg
MUSIC|${PREFIX}/data/music/credits.ogg
MUSIC|${PREFIX}/data/music/intro.ogg
MUSIC|${PREFIX}/data/music/playing.ogg
MUSIC|${PREFIX}/data/music/title.ogg
MUSIC|/data/music/congratulations.ogg
MUSIC|/data/music/credits.ogg
MUSIC|/data/music/intro.ogg
MUSIC|/data/music/playing.ogg
MUSIC|/data/music/title.ogg
# Sonidos
SOUND|${PREFIX}/data/sound/balloon_bounce0.wav
SOUND|${PREFIX}/data/sound/balloon_bounce1.wav
SOUND|${PREFIX}/data/sound/balloon_bounce2.wav
SOUND|${PREFIX}/data/sound/balloon_bounce3.wav
SOUND|${PREFIX}/data/sound/balloon_pop0.wav
SOUND|${PREFIX}/data/sound/balloon_pop1.wav
SOUND|${PREFIX}/data/sound/balloon_pop2.wav
SOUND|${PREFIX}/data/sound/balloon_pop3.wav
SOUND|${PREFIX}/data/sound/bullet1p.wav
SOUND|${PREFIX}/data/sound/bullet2p.wav
SOUND|${PREFIX}/data/sound/clock.wav
SOUND|${PREFIX}/data/sound/coffee_out.wav
SOUND|${PREFIX}/data/sound/continue_clock.wav
SOUND|${PREFIX}/data/sound/credit.wav
SOUND|${PREFIX}/data/sound/debian_drop.wav
SOUND|${PREFIX}/data/sound/debian_pickup.wav
SOUND|${PREFIX}/data/sound/hi_score_achieved.wav
SOUND|${PREFIX}/data/sound/item_drop.wav
SOUND|${PREFIX}/data/sound/item_pickup.wav
SOUND|${PREFIX}/data/sound/jump.wav
SOUND|${PREFIX}/data/sound/logo.wav
SOUND|${PREFIX}/data/sound/name_input_accept.wav
SOUND|${PREFIX}/data/sound/notify.wav
SOUND|${PREFIX}/data/sound/player_collision.wav
SOUND|${PREFIX}/data/sound/power_ball_explosion.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_select.wav
SOUND|${PREFIX}/data/sound/stage_change.wav
SOUND|${PREFIX}/data/sound/tabe_hit.wav
SOUND|${PREFIX}/data/sound/tabe.wav
SOUND|${PREFIX}/data/sound/title.wav
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
SOUND|${PREFIX}/data/sound/voice_coffee.wav
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
SOUND|${PREFIX}/data/sound/voice_game_over.wav
SOUND|${PREFIX}/data/sound/voice_get_ready.wav
SOUND|${PREFIX}/data/sound/voice_no.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/walk.wav
SOUND|/data/sound/balloon_bounce0.wav
SOUND|/data/sound/balloon_bounce1.wav
SOUND|/data/sound/balloon_bounce2.wav
SOUND|/data/sound/balloon_bounce3.wav
SOUND|/data/sound/balloon_pop0.wav
SOUND|/data/sound/balloon_pop1.wav
SOUND|/data/sound/balloon_pop2.wav
SOUND|/data/sound/balloon_pop3.wav
SOUND|/data/sound/bullet1p.wav
SOUND|/data/sound/bullet2p.wav
SOUND|/data/sound/clock.wav
SOUND|/data/sound/coffee_out.wav
SOUND|/data/sound/continue_clock.wav
SOUND|/data/sound/credit.wav
SOUND|/data/sound/debian_drop.wav
SOUND|/data/sound/debian_pickup.wav
SOUND|/data/sound/hi_score_achieved.wav
SOUND|/data/sound/item_drop.wav
SOUND|/data/sound/item_pickup.wav
SOUND|/data/sound/jump.wav
SOUND|/data/sound/logo.wav
SOUND|/data/sound/name_input_accept.wav
SOUND|/data/sound/notify.wav
SOUND|/data/sound/player_collision.wav
SOUND|/data/sound/power_ball_explosion.wav
SOUND|/data/sound/service_menu_adjust.wav
SOUND|/data/sound/service_menu_back.wav
SOUND|/data/sound/service_menu_move.wav
SOUND|/data/sound/service_menu_select.wav
SOUND|/data/sound/stage_change.wav
SOUND|/data/sound/tabe_hit.wav
SOUND|/data/sound/tabe.wav
SOUND|/data/sound/title.wav
SOUND|/data/sound/voice_aw_aw_aw.wav
SOUND|/data/sound/voice_coffee.wav
SOUND|/data/sound/voice_credit_thankyou.wav
SOUND|/data/sound/voice_game_over.wav
SOUND|/data/sound/voice_get_ready.wav
SOUND|/data/sound/voice_no.wav
SOUND|/data/sound/voice_power_up.wav
SOUND|/data/sound/voice_recover.wav
SOUND|/data/sound/voice_thankyou.wav
SOUND|/data/sound/walk.wav
# Shaders OpenGL Desktop 3.3 (Windows/Linux)
DATA|${PREFIX}/data/shaders/crtpi_vertex.glsl
DATA|${PREFIX}/data/shaders/crtpi_fragment.glsl
DATA|/data/shaders/crtpi_vertex.glsl
DATA|/data/shaders/crtpi_fragment.glsl
# Shaders OpenGL ES 3.0 (Raspberry Pi) - opcionales
DATA|${PREFIX}/data/shaders/crtpi_vertex_es.glsl|optional
DATA|${PREFIX}/data/shaders/crtpi_fragment_es.glsl|optional
DATA|/data/shaders/crtpi_vertex_es.glsl|optional
DATA|/data/shaders/crtpi_fragment_es.glsl|optional
# Texturas - Balloons
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon2.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon3.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png
ANIMATION|/data/gfx/balloon/balloon0.ani
ANIMATION|/data/gfx/balloon/balloon1.ani
ANIMATION|/data/gfx/balloon/balloon2.ani
ANIMATION|/data/gfx/balloon/balloon3.ani
BITMAP|/data/gfx/balloon/balloon0.png
BITMAP|/data/gfx/balloon/balloon1.png
BITMAP|/data/gfx/balloon/balloon2.png
BITMAP|/data/gfx/balloon/balloon3.png
# Texturas - Explosiones
ANIMATION|${PREFIX}/data/gfx/balloon/explosion0.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion1.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion2.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion3.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png
ANIMATION|/data/gfx/balloon/explosion0.ani
ANIMATION|/data/gfx/balloon/explosion1.ani
ANIMATION|/data/gfx/balloon/explosion2.ani
ANIMATION|/data/gfx/balloon/explosion3.ani
BITMAP|/data/gfx/balloon/explosion0.png
BITMAP|/data/gfx/balloon/explosion1.png
BITMAP|/data/gfx/balloon/explosion2.png
BITMAP|/data/gfx/balloon/explosion3.png
# Texturas - Power Ball
ANIMATION|${PREFIX}/data/gfx/balloon/powerball.ani
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png
ANIMATION|/data/gfx/balloon/powerball.ani
BITMAP|/data/gfx/balloon/powerball.png
# Texturas - Bala
ANIMATION|${PREFIX}/data/gfx/bullet/bullet.ani
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png
ANIMATION|/data/gfx/bullet/bullet.ani
BITMAP|/data/gfx/bullet/bullet.png
# Texturas - Tabe
ANIMATION|${PREFIX}/data/gfx/tabe/tabe.ani
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png
ANIMATION|/data/gfx/tabe/tabe.ani
BITMAP|/data/gfx/tabe/tabe.png
# Texturas - Juego
BITMAP|${PREFIX}/data/gfx/game/game_buildings.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds1.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds2.png
ANIMATION|${PREFIX}/data/gfx/game/game_grass.ani
BITMAP|${PREFIX}/data/gfx/game/game_grass.png
BITMAP|${PREFIX}/data/gfx/game/game_moon.png
BITMAP|${PREFIX}/data/gfx/game/game_power_meter.png
BITMAP|${PREFIX}/data/gfx/game/game_sky_colors.png
BITMAP|${PREFIX}/data/gfx/game/game_sun.png
BITMAP|/data/gfx/game/game_buildings.png
BITMAP|/data/gfx/game/game_clouds1.png
BITMAP|/data/gfx/game/game_clouds2.png
ANIMATION|/data/gfx/game/game_grass.ani
BITMAP|/data/gfx/game/game_grass.png
BITMAP|/data/gfx/game/game_moon.png
BITMAP|/data/gfx/game/game_power_meter.png
BITMAP|/data/gfx/game/game_sky_colors.png
BITMAP|/data/gfx/game/game_sun.png
# Texturas - Intro
BITMAP|${PREFIX}/data/gfx/intro/intro1.png
BITMAP|${PREFIX}/data/gfx/intro/intro2.png
BITMAP|${PREFIX}/data/gfx/intro/intro3.png
BITMAP|${PREFIX}/data/gfx/intro/intro4.png
BITMAP|${PREFIX}/data/gfx/intro/intro5.png
BITMAP|${PREFIX}/data/gfx/intro/intro6.png
BITMAP|/data/gfx/intro/intro1.png
BITMAP|/data/gfx/intro/intro2.png
BITMAP|/data/gfx/intro/intro3.png
BITMAP|/data/gfx/intro/intro4.png
BITMAP|/data/gfx/intro/intro5.png
BITMAP|/data/gfx/intro/intro6.png
# Texturas - Logo
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames_mini.png
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames.png
BITMAP|${PREFIX}/data/gfx/logo/logo_since_1998.png
BITMAP|/data/gfx/logo/logo_jailgames_mini.png
BITMAP|/data/gfx/logo/logo_jailgames.png
BITMAP|/data/gfx/logo/logo_since_1998.png
# Texturas - Items
ANIMATION|${PREFIX}/data/gfx/item/item_clock.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee_machine.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee.ani
ANIMATION|${PREFIX}/data/gfx/item/item_debian.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points1_disk.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points2_gavina.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points3_pacmar.ani
BITMAP|${PREFIX}/data/gfx/item/item_clock.png
BITMAP|${PREFIX}/data/gfx/item/item_coffee_machine.png
BITMAP|${PREFIX}/data/gfx/item/item_coffee.png
BITMAP|${PREFIX}/data/gfx/item/item_debian.png
BITMAP|${PREFIX}/data/gfx/item/item_points1_disk.png
BITMAP|${PREFIX}/data/gfx/item/item_points2_gavina.png
BITMAP|${PREFIX}/data/gfx/item/item_points3_pacmar.png
ANIMATION|/data/gfx/item/item_clock.ani
ANIMATION|/data/gfx/item/item_coffee_machine.ani
ANIMATION|/data/gfx/item/item_coffee.ani
ANIMATION|/data/gfx/item/item_debian.ani
ANIMATION|/data/gfx/item/item_points1_disk.ani
ANIMATION|/data/gfx/item/item_points2_gavina.ani
ANIMATION|/data/gfx/item/item_points3_pacmar.ani
BITMAP|/data/gfx/item/item_clock.png
BITMAP|/data/gfx/item/item_coffee_machine.png
BITMAP|/data/gfx/item/item_coffee.png
BITMAP|/data/gfx/item/item_debian.png
BITMAP|/data/gfx/item/item_points1_disk.png
BITMAP|/data/gfx/item/item_points2_gavina.png
BITMAP|/data/gfx/item/item_points3_pacmar.png
# Texturas - Titulo
ANIMATION|${PREFIX}/data/gfx/title/title_dust.ani
BITMAP|${PREFIX}/data/gfx/title/title_arcade_edition.png
BITMAP|${PREFIX}/data/gfx/title/title_bg_tile.png
BITMAP|${PREFIX}/data/gfx/title/title_coffee.png
BITMAP|${PREFIX}/data/gfx/title/title_crisis.png
BITMAP|${PREFIX}/data/gfx/title/title_dust.png
ANIMATION|/data/gfx/title/title_dust.ani
BITMAP|/data/gfx/title/title_arcade_edition.png
BITMAP|/data/gfx/title/title_bg_tile.png
BITMAP|/data/gfx/title/title_coffee.png
BITMAP|/data/gfx/title/title_crisis.png
BITMAP|/data/gfx/title/title_dust.png
# Texturas - Jugador 1
BITMAP|${PREFIX}/data/gfx/player/player1_power.png
BITMAP|${PREFIX}/data/gfx/player/player1.gif
PALETTE|${PREFIX}/data/gfx/player/player1_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player1_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player1_invencible.pal
BITMAP|/data/gfx/player/player1_power.png
BITMAP|/data/gfx/player/player1.gif
PALETTE|/data/gfx/player/player1_coffee1.pal
PALETTE|/data/gfx/player/player1_coffee2.pal
PALETTE|/data/gfx/player/player1_invencible.pal
# Texturas - Jugador 2
BITMAP|${PREFIX}/data/gfx/player/player2_power.png
BITMAP|${PREFIX}/data/gfx/player/player2.gif
PALETTE|${PREFIX}/data/gfx/player/player2_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player2_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal
BITMAP|/data/gfx/player/player2_power.png
BITMAP|/data/gfx/player/player2.gif
PALETTE|/data/gfx/player/player2_coffee1.pal
PALETTE|/data/gfx/player/player2_coffee2.pal
PALETTE|/data/gfx/player/player2_invencible.pal
# Animaciones del jugador
ANIMATION|${PREFIX}/data/gfx/player/player_power.ani
ANIMATION|${PREFIX}/data/gfx/player/player1.ani
ANIMATION|${PREFIX}/data/gfx/player/player2.ani
ANIMATION|/data/gfx/player/player_power.ani
ANIMATION|/data/gfx/player/player1.ani
ANIMATION|/data/gfx/player/player2.ani
# Fuentes de texto
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.png
BITMAP|${PREFIX}/data/font/04b_25_grey.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.png
BITMAP|${PREFIX}/data/font/04b_25_white.png
BITMAP|${PREFIX}/data/font/04b_25.png
BITMAP|${PREFIX}/data/font/8bithud.png
BITMAP|${PREFIX}/data/font/aseprite.png
BITMAP|${PREFIX}/data/font/smb2_grad.png
BITMAP|${PREFIX}/data/font/smb2.png
FONT|${PREFIX}/data/font/04b_25_2x.txt
FONT|${PREFIX}/data/font/04b_25.txt
FONT|${PREFIX}/data/font/8bithud.txt
FONT|${PREFIX}/data/font/aseprite.txt
FONT|${PREFIX}/data/font/smb2.txt
BITMAP|/data/font/04b_25_2x.png
BITMAP|/data/font/04b_25_2x_white.png
BITMAP|/data/font/04b_25_flat_2x.png
BITMAP|/data/font/04b_25_flat.png
BITMAP|/data/font/04b_25_grey.png
BITMAP|/data/font/04b_25_metal.png
BITMAP|/data/font/04b_25_reversed_2x.png
BITMAP|/data/font/04b_25_reversed.png
BITMAP|/data/font/04b_25_white.png
BITMAP|/data/font/04b_25.png
BITMAP|/data/font/8bithud.png
BITMAP|/data/font/aseprite.png
BITMAP|/data/font/smb2_grad.png
BITMAP|/data/font/smb2.png
FONT|/data/font/04b_25_2x.txt
FONT|/data/font/04b_25.txt
FONT|/data/font/8bithud.txt
FONT|/data/font/aseprite.txt
FONT|/data/font/smb2.txt
# Idiomas
LANG|${PREFIX}/data/lang/ba_BA.json
LANG|${PREFIX}/data/lang/en_UK.json
LANG|${PREFIX}/data/lang/es_ES.json
LANG|/data/lang/ba_BA.json
LANG|/data/lang/en_UK.json
LANG|/data/lang/es_ES.json

View File

@@ -1,7 +1,18 @@
#!/bin/bash
# Script para ejecutar clang-tidy en múltiples directorios
# Uso: ./run_clang-tidy.sh
# Uso: ./run_clang-tidy.sh [--fix]
# --fix: Aplica las correcciones automáticamente (opcional)
# Detectar si se pasó el parámetro --fix
FIX_FLAG=""
if [[ "$1" == "--fix" ]]; then
FIX_FLAG="--fix"
echo "Modo: Aplicando correcciones automáticamente (--fix)"
else
echo "Modo: Solo análisis (sin --fix)"
fi
echo
# Lista de rutas donde ejecutar clang-tidy
PATHS=(
@@ -31,8 +42,8 @@ process_directory() {
cd "$dir" || return 1
# Buscar archivos .cpp, .h, .hpp solo en el nivel actual (no subdirectorios)
find . -maxdepth 1 \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) | \
xargs -P4 -I{} bash -c 'echo "Procesando: {}"; clang-tidy {} -p '"$BUILD_DIR"' --fix'
find . -maxdepth 1 \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) -print0 | \
xargs -0 -P4 -I{} bash -c 'echo "Procesando: {}"; clang-tidy {} -p '"$BUILD_DIR"' '"$FIX_FLAG"
echo "=== Completado: $dir ==="
echo

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 504 KiB

View File

@@ -197,8 +197,12 @@ auto Asset::check() const -> bool {
// Comprueba que existe un fichero
auto Asset::checkFile(const std::string& path) const -> bool {
// Construir ruta del pack usando executable_path_
// Construir ruta del pack usando executable_path_ (misma lógica que Director::init)
#ifdef MACOS_BUNDLE
std::string pack_path = executable_path_ + "../Resources/resources.pack";
#else
std::string pack_path = executable_path_ + "resources.pack";
#endif
bool pack_exists = std::filesystem::exists(pack_path);
if (pack_exists) {

View File

@@ -4,7 +4,7 @@
#include <fstream>
#include <iostream>
bool AssetIntegrated::resource_pack_enabled_ = false;
bool AssetIntegrated::resource_pack_enabled = false;
void AssetIntegrated::initWithResourcePack(const std::string& executable_path,
const std::string& resource_pack_path) {
@@ -14,29 +14,29 @@ void AssetIntegrated::initWithResourcePack(const std::string& 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;
resource_pack_enabled = true;
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << '\n';
} else {
resource_pack_enabled_ = false;
std::cout << "Asset system initialized in fallback mode (filesystem)" << std::endl;
resource_pack_enabled = false;
std::cout << "Asset system initialized in fallback mode (filesystem)" << '\n';
}
}
std::vector<uint8_t> AssetIntegrated::loadFile(const std::string& filename) {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
auto AssetIntegrated::loadFile(const std::string& filename) -> std::vector<uint8_t> {
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;
std::string relative_path = 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/"
size_t data_pos = filename.find("data/");
if (data_pos != std::string::npos) {
relative_path = filename.substr(data_pos + 5); // +5 para saltar "data/"
}
auto data = loader.loadResource(relativePath);
auto data = loader.loadResource(relative_path);
if (!data.empty()) {
return data;
}
@@ -45,34 +45,34 @@ std::vector<uint8_t> AssetIntegrated::loadFile(const std::string& filename) {
// 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;
std::cerr << "Error: Could not open file: " << filename << '\n';
return {};
}
std::streamsize fileSize = file.tellg();
std::streamsize file_size = 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;
std::vector<uint8_t> data(file_size);
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "Error: Could not read file: " << filename << '\n';
return {};
}
return data;
}
bool AssetIntegrated::fileExists(const std::string& filename) const {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
auto AssetIntegrated::fileExists(const std::string& filename) -> bool {
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);
std::string relative_path = filename;
size_t data_pos = filename.find("data/");
if (data_pos != std::string::npos) {
relative_path = filename.substr(data_pos + 5);
}
if (loader.resourceExists(relativePath)) {
if (loader.resourceExists(relative_path)) {
return true;
}
}
@@ -81,24 +81,24 @@ bool AssetIntegrated::fileExists(const std::string& filename) const {
return std::filesystem::exists(filename);
}
std::string AssetIntegrated::getSystemPath(const std::string& filename) const {
auto AssetIntegrated::getSystemPath(const std::string& filename) -> std::string {
// Los archivos de sistema/config siempre van al filesystem
return filename;
}
bool AssetIntegrated::shouldUseResourcePack(const std::string& filepath) const {
auto AssetIntegrated::shouldUseResourcePack(const std::string& filepath) -> bool {
// 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) {
filepath.starts_with("config/")) {
return false;
}
if (filepath.find("/data/") != std::string::npos ||
filepath.find("data/") == 0) {
filepath.starts_with("data/")) {
return true;
}

View File

@@ -13,17 +13,17 @@ class AssetIntegrated : public Asset {
const std::string& resource_pack_path = "resources.pack");
// Carga un archivo usando ResourceLoader como primera opción
auto loadFile(const std::string& filename) -> std::vector<uint8_t>;
static auto loadFile(const std::string& filename) -> std::vector<uint8_t>;
// Verifica si un archivo existe (pack o filesystem)
auto fileExists(const std::string& filename) const -> bool;
static auto fileExists(const std::string& filename) -> bool;
// Obtiene la ruta completa para archivos del sistema/config
auto getSystemPath(const std::string& filename) const -> std::string;
static auto getSystemPath(const std::string& filename) -> std::string;
private:
static bool resource_pack_enabled;
// Determina si un archivo debe cargarse del pack o del filesystem
auto shouldUseResourcePack(const std::string& filepath) const -> bool;
static auto shouldUseResourcePack(const std::string& filepath) -> bool;
};

View File

@@ -514,10 +514,10 @@ void Background::updateAlphaColorTexture(float delta_time) {
// 4. Actualiza el valor entero solo si ha cambiado lo suficiente
// Usamos std::round para un redondeo más natural.
const auto new_alpha = static_cast<size_t>(std::round(current_alpha_float_));
const auto NEW_ALPHA = static_cast<size_t>(std::round(current_alpha_float_));
if (new_alpha != previous_alpha_color_texture_) {
previous_alpha_color_texture_ = new_alpha;
if (NEW_ALPHA != previous_alpha_color_texture_) {
previous_alpha_color_texture_ = NEW_ALPHA;
// SDL espera un Uint8 (0-255), así que hacemos un cast seguro.
SDL_SetTextureAlphaMod(color_texture_, static_cast<Uint8>(previous_alpha_color_texture_));
}

View File

@@ -3,9 +3,9 @@
#include <SDL3/SDL.h> // Para SDL_FRect
#include <functional> // Para function
#include <list> // Para list
#include <memory> // Para shared_ptr
#include <vector> // Para vector
#include <list> // Para list
#include "bullet.hpp" // for Bullet
@@ -71,6 +71,6 @@ class BulletManager {
OutOfBoundsCallback out_of_bounds_callback_; // Callback para balas fuera de límites
// --- Métodos internos ---
void processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float delta_time); // Procesa actualización individual
auto isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) const -> bool; // Verifica si la bala está fuera de límites
void processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float delta_time); // Procesa actualización individual
[[nodiscard]] auto isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) const -> bool; // Verifica si la bala está fuera de límites
};

48
source/cooldown.hpp Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <algorithm> // Para std::max
class Cooldown {
public:
Cooldown(float first_delay_s = 0.0F, float repeat_delay_s = 0.0F)
: first_delay_s_(first_delay_s), repeat_delay_s_(repeat_delay_s) {}
// Llamar cada frame con delta en segundos (float)
void update(float delta_s) {
if (remaining_s_ <= 0.0F) {
remaining_s_ = 0.0F;
return;
}
remaining_s_ -= delta_s;
remaining_s_ = std::max(remaining_s_, 0.0F);
}
// Llamar cuando el input está activo. Devuelve true si debe ejecutarse la acción ahora.
auto tryConsumeOnHeld() -> bool {
if (remaining_s_ > 0.0F) {
return false;
}
float delay = held_before_ ? repeat_delay_s_ : first_delay_s_;
remaining_s_ = delay;
held_before_ = true;
return true;
}
// Llamar cuando el input se suelta
void onReleased() {
held_before_ = false;
remaining_s_ = 0.0F;
}
[[nodiscard]] auto empty() const -> bool { return remaining_s_ == 0.0F; }
// Fuerza un valor en segundos (útil para tests o resets)
void forceSet(float seconds) { remaining_s_ = seconds > 0.0F ? seconds : 0.0F; }
private:
float first_delay_s_;
float repeat_delay_s_;
float remaining_s_{0.0F};
bool held_before_{false};
};

View File

@@ -81,11 +81,22 @@ void Director::init() {
// Configuración inicial de parametros
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
// Determinar ruta del pack según la plataforma
#ifdef MACOS_BUNDLE
ResourceHelper::initializeResourceSystem(executable_path_ + "../Resources/resources.pack");
std::string pack_path = executable_path_ + "../Resources/resources.pack";
#else
ResourceHelper::initializeResourceSystem(executable_path_ + "resources.pack");
std::string pack_path = executable_path_ + "resources.pack";
#endif
// Inicializar sistema de recursos con o sin fallback según el tipo de build
#ifdef RELEASE_BUILD
// Release: Sin fallback - Solo resources.pack (estricto)
ResourceHelper::initializeResourceSystem(pack_path, false);
#else
// Desarrollo: Con fallback - Puede usar data/ si falta el pack (flexible)
ResourceHelper::initializeResourceSystem(pack_path, true);
#endif
loadAssets(); // Crea el índice de archivos
Logger::section("INIT INPUT");

View File

@@ -12,6 +12,11 @@ EnterName::EnterName()
void EnterName::init(const std::string& name) {
name_ = sanitizeName(name);
selected_index_ = 0;
// Si el nombre está completo, cambia el caracter seleccionado a el caracter de finalizar
if (!nameIsEmpty()) {
forceEndCharSelected();
}
}
// Incrementa el índice del carácter seleccionado
@@ -62,7 +67,7 @@ auto EnterName::getSelectedCharacter(int offset) const -> std::string {
index += size;
}
return std::string(1, character_list_[index]);
return {1, character_list_[index]};
}
// Devuelve el carrusel completo de caracteres centrado en el seleccionado

View File

@@ -130,12 +130,7 @@ void Fade::updatePostState() {
}
// Mantener el estado final del fade
Uint8 post_alpha = a_;
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
} else {
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
}
Uint8 post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
cleanBackbuffer(r_, g_, b_, post_alpha);
}
@@ -145,7 +140,7 @@ void Fade::updateFullscreenFade() {
float progress = std::min(static_cast<float>(elapsed_time) / fading_duration_, 1.0F);
// Modifica la transparencia basada en el progreso
Uint8 current_alpha = static_cast<Uint8>(progress * 255.0f);
auto current_alpha = static_cast<Uint8>(progress * 255.0F);
a_ = (mode_ == Mode::OUT) ? current_alpha : 255 - current_alpha;
SDL_SetTextureAlphaMod(backbuffer_, a_);
@@ -162,10 +157,10 @@ void Fade::updateCenterFade() {
float progress = std::min(static_cast<float>(elapsed_time) / fading_duration_, 1.0F);
// Calcula la altura de las barras
float rect_height = progress * (param.game.height / 2.0f);
float rect_height = progress * (param.game.height / 2.0F);
if (mode_ == Mode::IN) {
rect_height = (param.game.height / 2.0f) - rect_height;
rect_height = (param.game.height / 2.0F) - rect_height;
}
rect1_.h = rect_height;
@@ -228,14 +223,14 @@ void Fade::updateRandomSquare2Fade() {
squares_to_activate = total_squares;
}
for (int i = 0; i < squares_to_activate; ++i) {
if (square_age_[i] == -1) square_age_[i] = elapsed_time;
if (square_age_[i] == -1) {square_age_[i] = elapsed_time;}
}
} else {
squares_to_activate = total_squares;
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
int squares_starting_transition = std::min(total_squares, std::max(1, static_cast<int>(activation_progress * total_squares)));
for (int i = 0; i < squares_starting_transition; ++i) {
if (square_age_[i] == -1) square_age_[i] = elapsed_time;
if (square_age_[i] == -1) {square_age_[i] = elapsed_time;}
}
}
@@ -452,8 +447,8 @@ void Fade::activate() {
// Ahora, inicializamos la modulación de alfa correctamente:
// - IN: Empieza opaco (255) y se desvanece a transparente.
// - OUT: Empieza transparente (0) y se desvanece a opaco.
const Uint8 initial_alpha = (mode_ == Mode::IN) ? 255 : 0;
SDL_SetTextureAlphaMod(backbuffer_, initial_alpha);
const Uint8 INITIAL_ALPHA = (mode_ == Mode::IN) ? 255 : 0;
SDL_SetTextureAlphaMod(backbuffer_, INITIAL_ALPHA);
break;
}
@@ -509,7 +504,7 @@ void Fade::activate() {
case Type::VENETIAN: {
square_.clear();
rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = (mode_ == Mode::OUT) ? 0.0f : static_cast<float>(param.fade.venetian_size)};
rect1_ = {.x = 0, .y = 0, .w = param.game.width, .h = (mode_ == Mode::OUT) ? 0.0F : param.fade.venetian_size};
const int MAX = param.game.height / param.fade.venetian_size;
for (int i = 0; i < MAX; ++i) {
rect1_.y = i * param.fade.venetian_size;
@@ -546,7 +541,11 @@ void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
// Calcula el valor del estado del fade
auto Fade::calculateValue(int min, int max, int current) -> int {
if (current <= min) return 0;
if (current >= max) return 100;
if (current <= min) {
return 0;
}
if (current >= max) {
return 100;
}
return static_cast<int>(100.0 * (current - min) / (max - min));
}

View File

@@ -3,6 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO, SDL_GetError, SDL_IOFromFile, SDL_LogError, SDL_LogCategory, SDL_LogInfo
#include <algorithm> // Para __sort_fn, sort
#include <array> // Para array
#include <functional> // Para identity
#include <iterator> // Para distance
#include <ranges> // Para __find_if_fn, find_if
@@ -101,49 +102,181 @@ void ManageHiScoreTable::sort() {
// Carga la tabla desde un fichero
auto ManageHiScoreTable::loadFromFile(const std::string& file_path) -> bool {
clear();
auto success = true;
auto* file = SDL_IOFromFile(file_path.c_str(), "rb");
if (file != nullptr) {
table_.clear(); // Limpia la tabla actual
if (file == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
clear();
return false;
}
// Lee el número de entradas en la tabla
int table_size = 0;
SDL_ReadIO(file, &table_size, sizeof(int));
// Validar header (magic number + version + table size)
if (!validateMagicNumber(file, file_path)) {
SDL_CloseIO(file);
clear();
return false;
}
// Lee los datos de cada entrada
for (int i = 0; i < table_size; ++i) {
HiScoreEntry entry;
if (!validateVersion(file, file_path)) {
SDL_CloseIO(file);
clear();
return false;
}
// Lee la puntuación
SDL_ReadIO(file, &entry.score, sizeof(int));
int table_size = 0;
if (!readTableSize(file, file_path, table_size)) {
SDL_CloseIO(file);
clear();
return false;
}
// Lee el tamaño del nombre y luego el nombre
int name_size = 0;
SDL_ReadIO(file, &name_size, sizeof(int));
// Leer todas las entradas
Table temp_table;
bool success = true;
for (int i = 0; i < table_size; ++i) {
HiScoreEntry entry;
if (!readEntry(file, file_path, i, entry)) {
success = false;
break;
}
temp_table.push_back(entry);
}
std::vector<char> name_buffer(name_size + 1);
SDL_ReadIO(file, name_buffer.data(), name_size);
name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena
entry.name = std::string(name_buffer.data());
// Verificar checksum
if (success) {
success = verifyChecksum(file, file_path, temp_table);
}
// Lee el valor de one_credit_complete
int occ_value = 0;
SDL_ReadIO(file, &occ_value, sizeof(int));
entry.one_credit_complete = (occ_value != 0);
SDL_CloseIO(file);
// Añade la entrada a la tabla
table_.push_back(entry);
// Si todo fue bien, actualizar la tabla; si no, usar valores por defecto
if (success) {
table_ = std::move(temp_table);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s (loaded %d entries successfully)", getFileName(file_path).c_str(), table_size);
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "File %s is corrupted - loading default values", getFileName(file_path).c_str());
clear();
}
return success;
}
// Métodos auxiliares privados para loadFromFile
auto ManageHiScoreTable::validateMagicNumber(SDL_IOStream* file, const std::string& file_path) -> bool {
std::array<char, 4> magic;
if (SDL_ReadIO(file, magic.data(), 4) != 4 || magic[0] != 'C' || magic[1] != 'C' || magic[2] != 'A' || magic[3] != 'E') {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid magic number in %s - file may be corrupted or old format", getFileName(file_path).c_str());
return false;
}
return true;
}
auto ManageHiScoreTable::validateVersion(SDL_IOStream* file, const std::string& file_path) -> bool {
int version = 0;
if (SDL_ReadIO(file, &version, sizeof(int)) != sizeof(int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read version in %s", getFileName(file_path).c_str());
return false;
}
if (version != FILE_VERSION) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unsupported file version %d in %s (expected %d)", version, getFileName(file_path).c_str(), FILE_VERSION);
return false;
}
return true;
}
auto ManageHiScoreTable::readTableSize(SDL_IOStream* file, const std::string& file_path, int& table_size) -> bool {
if (SDL_ReadIO(file, &table_size, sizeof(int)) != sizeof(int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read table size in %s", getFileName(file_path).c_str());
return false;
}
if (table_size < 0 || table_size > MAX_TABLE_SIZE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid table size %d in %s (expected 0-%d)", table_size, getFileName(file_path).c_str(), MAX_TABLE_SIZE);
return false;
}
return true;
}
auto ManageHiScoreTable::readEntry(SDL_IOStream* file, const std::string& file_path, int index, HiScoreEntry& entry) -> bool {
// Leer y validar puntuación
if (SDL_ReadIO(file, &entry.score, sizeof(int)) != sizeof(int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read score for entry %d in %s", index, getFileName(file_path).c_str());
return false;
}
if (entry.score < 0 || entry.score > MAX_SCORE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid score %d for entry %d in %s", entry.score, index, getFileName(file_path).c_str());
return false;
}
// Leer y validar tamaño del nombre
int name_size = 0;
if (SDL_ReadIO(file, &name_size, sizeof(int)) != sizeof(int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read name size for entry %d in %s", index, getFileName(file_path).c_str());
return false;
}
if (name_size < 0 || name_size > MAX_NAME_SIZE) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Invalid name size %d for entry %d in %s", name_size, index, getFileName(file_path).c_str());
return false;
}
// Leer el nombre
std::vector<char> name_buffer(name_size + 1);
if (SDL_ReadIO(file, name_buffer.data(), name_size) != static_cast<size_t>(name_size)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read name for entry %d in %s", index, getFileName(file_path).c_str());
return false;
}
name_buffer[name_size] = '\0';
entry.name = std::string(name_buffer.data());
// Leer one_credit_complete
int occ_value = 0;
if (SDL_ReadIO(file, &occ_value, sizeof(int)) != sizeof(int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read one_credit_complete for entry %d in %s", index, getFileName(file_path).c_str());
return false;
}
entry.one_credit_complete = (occ_value != 0);
return true;
}
auto ManageHiScoreTable::verifyChecksum(SDL_IOStream* file, const std::string& file_path, const Table& temp_table) -> bool {
unsigned int stored_checksum = 0;
if (SDL_ReadIO(file, &stored_checksum, sizeof(unsigned int)) != sizeof(unsigned int)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read checksum in %s", getFileName(file_path).c_str());
return false;
}
unsigned int calculated_checksum = calculateChecksum(temp_table);
if (stored_checksum != calculated_checksum) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Checksum mismatch in %s (stored: 0x%08X, calculated: 0x%08X) - file is corrupted",
getFileName(file_path).c_str(), stored_checksum, calculated_checksum);
return false;
}
return true;
}
// Calcula checksum de la tabla
auto ManageHiScoreTable::calculateChecksum(const Table& table) -> unsigned int {
unsigned int checksum = 0x12345678; // Magic seed
for (const auto& entry : table) {
// Checksum del score
checksum = ((checksum << 5) + checksum) + static_cast<unsigned int>(entry.score);
// Checksum del nombre
for (char c : entry.name) {
checksum = ((checksum << 5) + checksum) + static_cast<unsigned int>(c);
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());
SDL_CloseIO(file);
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError());
success = false;
// Checksum de one_credit_complete
checksum = ((checksum << 5) + checksum) + (entry.one_credit_complete ? 1U : 0U);
}
return success;
return checksum;
}
// Guarda la tabla en un fichero
@@ -152,6 +285,14 @@ auto ManageHiScoreTable::saveToFile(const std::string& file_path) -> bool {
auto* file = SDL_IOFromFile(file_path.c_str(), "w+b");
if (file != nullptr) {
// Escribe magic number "CCAE"
constexpr std::array<char, 4> MAGIC = {'C', 'C', 'A', 'E'};
SDL_WriteIO(file, MAGIC.data(), 4);
// Escribe versión del formato
int version = FILE_VERSION;
SDL_WriteIO(file, &version, sizeof(int));
// Guarda el número de entradas en la tabla
int table_size = static_cast<int>(table_.size());
SDL_WriteIO(file, &table_size, sizeof(int));
@@ -173,6 +314,10 @@ auto ManageHiScoreTable::saveToFile(const std::string& file_path) -> bool {
SDL_WriteIO(file, &occ_value, sizeof(int));
}
// Calcula y escribe el checksum
unsigned int checksum = calculateChecksum(table_);
SDL_WriteIO(file, &checksum, sizeof(unsigned int));
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str());
Logger::info("Writing file: " + getFileName(file_path));
SDL_CloseIO(file);

View File

@@ -1,5 +1,7 @@
#pragma once
#include <SDL3/SDL.h> // Para SDL_IOStream
#include <string> // Para std::string
#include <vector> // Para std::vector
@@ -24,6 +26,10 @@ class ManageHiScoreTable {
public:
// --- Constantes ---
static constexpr int NO_ENTRY = -1;
static constexpr int FILE_VERSION = 1;
static constexpr int MAX_TABLE_SIZE = 100;
static constexpr int MAX_NAME_SIZE = 50;
static constexpr int MAX_SCORE = 999999999;
// --- Constructor y destructor ---
explicit ManageHiScoreTable(Table& table) // Constructor con referencia a tabla
@@ -41,5 +47,13 @@ class ManageHiScoreTable {
Table& table_; // Referencia a la tabla con los records
// --- Métodos privados ---
void sort(); // Ordena la tabla
void sort(); // Ordena la tabla
static auto calculateChecksum(const Table& table) -> unsigned int; // Calcula checksum de la tabla
// Métodos auxiliares para loadFromFile
static auto validateMagicNumber(SDL_IOStream* file, const std::string& file_path) -> bool;
static auto validateVersion(SDL_IOStream* file, const std::string& file_path) -> bool;
static auto readTableSize(SDL_IOStream* file, const std::string& file_path, int& table_size) -> bool;
static auto readEntry(SDL_IOStream* file, const std::string& file_path, int index, HiScoreEntry& entry) -> bool;
static auto verifyChecksum(SDL_IOStream* file, const std::string& file_path, const Table& temp_table) -> bool;
};

View File

@@ -55,62 +55,70 @@ void PathSprite::render() {
}
}
// Determina el tipo de centrado basado en el tipo de path
auto PathSprite::determineCenteringType(const Path& path, bool centered) -> PathCentered {
if (!centered) {
return PathCentered::NONE;
}
if (path.is_point_path) {
// Lógica de centrado para paths por PUNTOS
if (!path.spots.empty()) {
// Si X es constante, es un path Vertical, centramos en X
return (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y;
}
return PathCentered::NONE;
}
// Lógica de centrado para paths GENERADOS
// Si el tipo es Vertical, centramos en X
return (path.type == PathType::VERTICAL) ? PathCentered::ON_X : PathCentered::ON_Y;
}
// Aplica centrado en el eje X (para paths verticales)
void PathSprite::centerPathOnX(Path& path, float offset) {
if (path.is_point_path) {
const float X_BASE = !path.spots.empty() ? path.spots.front().x : 0.0F;
const float X = X_BASE - offset;
for (auto& spot : path.spots) {
spot.x = X;
}
} else {
// Es un path generado, ajustamos la posición fija (que es X)
path.fixed_pos -= offset;
}
}
// Aplica centrado en el eje Y (para paths horizontales)
void PathSprite::centerPathOnY(Path& path, float offset) {
if (path.is_point_path) {
const float Y_BASE = !path.spots.empty() ? path.spots.front().y : 0.0F;
const float Y = Y_BASE - offset;
for (auto& spot : path.spots) {
spot.y = Y;
}
} else {
// Es un path generado, ajustamos la posición fija (que es Y)
path.fixed_pos -= offset;
}
}
// Añade un recorrido
void PathSprite::addPath(Path path, bool centered) {
PathCentered path_centered = PathCentered::NONE;
if (centered) {
if (path.is_point_path) {
// Lógica de centrado para paths por PUNTOS (como antes)
if (!path.spots.empty()) {
// Si X es constante, es un path Vertical, centramos en X
path_centered = (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y;
}
} else {
// Lógica de centrado para paths GENERADOS (por duración)
// Si el tipo es Vertical, centramos en X
path_centered = (path.type == PathType::VERTICAL) ? PathCentered::ON_X : PathCentered::ON_Y;
}
}
PathCentered path_centered = determineCenteringType(path, centered);
switch (path_centered) {
case PathCentered::ON_X: {
// Centrar en el eje X (para paths Verticales)
const float X_OFFSET = pos_.w / 2.0F; // Asume que pos_.w está inicializado por el constructor de Sprite
if (path.is_point_path) {
const float X_BASE = !path.spots.empty() ? path.spots.front().x : 0.0F;
const float X = X_BASE - X_OFFSET;
for (auto& spot : path.spots) {
spot.x = X;
}
} else {
// Es un path generado, ajustamos la posición fija (que es X)
path.fixed_pos -= X_OFFSET;
}
paths_.emplace_back(std::move(path)); // Usamos std::move
case PathCentered::ON_X:
centerPathOnX(path, pos_.w / 2.0F);
break;
}
case PathCentered::ON_Y: {
// Centrar en el eje Y (para paths Horizontales)
const float Y_OFFSET = pos_.h / 2.0F; // Asume que pos_.h está inicializado
if (path.is_point_path) {
const float Y_BASE = !path.spots.empty() ? path.spots.front().y : 0.0F;
const float Y = Y_BASE - Y_OFFSET;
for (auto& spot : path.spots) {
spot.y = Y;
}
} else {
// Es un path generado, ajustamos la posición fija (que es Y)
path.fixed_pos -= Y_OFFSET;
}
paths_.emplace_back(std::move(path)); // Usamos std::move
case PathCentered::ON_Y:
centerPathOnY(path, pos_.h / 2.0F);
break;
}
default:
// Sin centrado
paths_.emplace_back(std::move(path)); // Usamos std::move
case PathCentered::NONE:
break;
}
paths_.emplace_back(std::move(path));
}
// Añade un recorrido generado (en segundos)

View File

@@ -93,4 +93,9 @@ class PathSprite : public Sprite {
// --- Métodos internos ---
void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
// --- Métodos auxiliares para addPath ---
[[nodiscard]] static auto determineCenteringType(const Path& path, bool centered) -> PathCentered; // Determina el tipo de centrado
static void centerPathOnX(Path& path, float offset); // Aplica centrado en el eje X
static void centerPathOnY(Path& path, float offset); // Aplica centrado en el eje Y
};

View File

@@ -9,6 +9,7 @@
#include "animated_sprite.hpp" // Para AnimatedSprite
#include "asset.hpp" // Para Asset
#include "audio.hpp" // Para Audio
#include "cooldown.hpp" // Para Cooldown
#include "input.hpp" // Para Input
#include "input_types.hpp" // Para InputAction
#include "manage_hiscore_table.hpp" // Para ManageHiScoreTable, HiScoreEntry
@@ -22,6 +23,7 @@ Player::Player(const Config& config)
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))),
power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))),
enter_name_(std::make_unique<EnterName>()),
cooldown_(std::make_unique<Cooldown>(0.300F, 0.140F)),
hi_score_table_(config.hi_score_table),
glowing_entry_(config.glowing_entry),
stage_info_(config.stage_info),
@@ -122,57 +124,84 @@ void Player::setInputPlaying(Input::Action action) {
}
}
// Gestiona la adición de un carácter o la confirmación del nombre.
void Player::handleNameCharacterAddition() {
if (enter_name_->endCharSelected()) {
confirmNameEntry();
} else {
enter_name_->addCharacter();
playSound("service_menu_select.wav");
}
}
// Gestiona la eliminación del último carácter.
void Player::handleNameCharacterRemoval() {
if (!enter_name_->nameIsEmpty()) {
enter_name_->removeLastCharacter();
playSound("service_menu_back.wav");
}
}
// Gestiona el movimiento del cursor de selección de letras (izquierda/derecha).
void Player::handleNameSelectionMove(Input::Action action) {
if (isShowingName() || enter_name_->nameIsFull()) {
return; // No hacer nada si se muestra el nombre o si está lleno
}
if (cooldown_->tryConsumeOnHeld()) {
if (action == Input::Action::RIGHT) {
enter_name_->incIndex();
} else {
enter_name_->decIndex();
}
playSound("service_menu_move.wav");
}
}
// Confirma el nombre introducido y cambia el estado del jugador.
void Player::confirmNameEntry() {
last_enter_name_ = getRecordName();
setPlayingState(Player::State::SHOWING_NAME);
playSound("name_input_accept.wav");
}
// Procesa inputs para cuando está introduciendo el nombre
void Player::setInputEnteringName(Input::Action action) {
switch (action) {
case Input::Action::FIRE_LEFT: // Añade una letra
case Input::Action::FIRE_LEFT:
if (isShowingName()) {
passShowingName();
} else {
if (enter_name_->endCharSelected()) {
last_enter_name_ = getRecordName();
setPlayingState(Player::State::SHOWING_NAME);
playSound("name_input_accept.wav");
} else {
enter_name_->addCharacter();
playSound("service_menu_select.wav");
}
handleNameCharacterAddition();
}
break;
case Input::Action::FIRE_CENTER: // Borra una letra
case Input::Action::FIRE_CENTER:
if (isShowingName()) {
passShowingName();
} else {
if (!enter_name_->nameIsEmpty()) {
enter_name_->removeLastCharacter();
playSound("service_menu_back.wav");
}
}
break;
case Input::Action::RIGHT:
if (!enter_name_->nameIsFull()) {
enter_name_->incIndex();
playSound("service_menu_move.wav");
}
break;
case Input::Action::LEFT:
if (!enter_name_->nameIsFull()) {
enter_name_->decIndex();
playSound("service_menu_move.wav");
handleNameCharacterRemoval();
}
break;
case Input::Action::START:
if (isShowingName()) {
passShowingName();
} else {
last_enter_name_ = getRecordName();
setPlayingState(Player::State::SHOWING_NAME);
playSound("name_input_accept.wav");
confirmNameEntry();
}
break;
case Input::Action::RIGHT:
case Input::Action::LEFT:
handleNameSelectionMove(action);
break;
default:
cooldown_->onReleased();
break;
}
name_entry_idle_time_accumulator_ = 0.0F;
}
@@ -543,6 +572,7 @@ void Player::update(float delta_time) {
updateContinueCounter(delta_time); // Sistema de continue
updateEnterNameCounter(delta_time); // Sistema de name entry
updateShowingName(delta_time); // Sistema de showing name
cooldown_->update(delta_time);
}
void Player::passShowingName() {
@@ -874,6 +904,7 @@ void Player::updateContinueCounter(float delta_time) {
if (continue_time_accumulator_ >= CONTINUE_INTERVAL_S) {
continue_time_accumulator_ -= CONTINUE_INTERVAL_S;
decContinueCounter();
if (continue_counter_ >= 0) { Scoreboard::get()->triggerPanelPulse(scoreboard_panel_, 0.4F); }
}
}
}

View File

@@ -9,8 +9,9 @@
#include <utility> // Para move, pair
#include <vector> // Para vector
#include "animated_sprite.hpp" // for AnimatedSprite
#include "bullet.hpp" // for Bullet
#include "animated_sprite.hpp" // for AnimatedSprite
#include "bullet.hpp" // for Bullet
#include "cooldown.hpp"
#include "enter_name.hpp" // for EnterName
#include "input.hpp" // for Input
#include "manage_hiscore_table.hpp" // for Table
@@ -249,6 +250,7 @@ class Player {
std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador
std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope
std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre
std::unique_ptr<Cooldown> cooldown_ = nullptr; // Objeto para gestionar cooldowns de teclado
std::shared_ptr<Input::Gamepad> gamepad_ = nullptr; // Dispositivo asociado
Table* hi_score_table_ = nullptr; // Tabla de máximas puntuaciones
int* glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar
@@ -392,6 +394,12 @@ class Player {
void handleWaitingMovement(float delta_time); // Animación del jugador saludando
void updateWalkingStateForCredits(); // Actualiza estado de caminata en créditos
// --- Introducción de nombre ---
void handleNameCharacterAddition();
void handleNameCharacterRemoval();
void handleNameSelectionMove(Input::Action action);
void confirmNameEntry();
// --- Utilidades de animación ---
[[nodiscard]] auto computeAnimation() const -> std::pair<std::string, SDL_FlipMode>; // Calcula animación de movimiento y disparo
};

View File

@@ -2,6 +2,7 @@
#include <SDL3/SDL.h>
#include <array>
#include <cstring>
#include <stdexcept>
#include <vector>
@@ -15,7 +16,7 @@ OpenGLShader::~OpenGLShader() {
}
#ifndef __APPLE__
bool OpenGLShader::initGLExtensions() {
auto OpenGLShader::initGLExtensions() -> bool {
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
@@ -42,13 +43,13 @@ bool OpenGLShader::initGLExtensions() {
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer");
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glDeleteProgram && glGetUniformLocation && glUniform2f &&
glGenVertexArrays && glBindVertexArray && glDeleteVertexArrays &&
glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers &&
glVertexAttribPointer && glEnableVertexAttribArray;
return (glCreateShader != nullptr) && (glShaderSource != nullptr) && (glCompileShader != nullptr) && (glGetShaderiv != nullptr) &&
(glGetShaderInfoLog != nullptr) && (glDeleteShader != nullptr) && (glAttachShader != nullptr) && (glCreateProgram != nullptr) &&
(glLinkProgram != nullptr) && (glValidateProgram != nullptr) && (glGetProgramiv != nullptr) && (glGetProgramInfoLog != nullptr) &&
(glUseProgram != nullptr) && (glDeleteProgram != nullptr) && (glGetUniformLocation != nullptr) && (glUniform2f != nullptr) &&
(glGenVertexArrays != nullptr) && (glBindVertexArray != nullptr) && (glDeleteVertexArrays != nullptr) &&
(glGenBuffers != nullptr) && (glBindBuffer != nullptr) && (glBufferData != nullptr) && (glDeleteBuffers != nullptr) &&
(glVertexAttribPointer != nullptr) && (glEnableVertexAttribArray != nullptr);
}
#endif
@@ -62,7 +63,7 @@ void OpenGLShader::checkGLError(const char* operation) {
}
}
GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type) {
auto OpenGLShader::compileShader(const std::string& source, GLenum shader_type) -> GLuint {
if (source.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"ERROR: El código fuente del shader está vacío");
@@ -76,8 +77,8 @@ GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type
return 0;
}
const char* sources[1] = {source.c_str()};
glShaderSource(shader_id, 1, sources, nullptr);
std::array<const char*, 1> sources = {source.c_str()};
glShaderSource(shader_id, 1, sources.data(), nullptr);
checkGLError("glShaderSource");
glCompileShader(shader_id);
@@ -105,7 +106,7 @@ GLuint OpenGLShader::compileShader(const std::string& source, GLenum shader_type
return shader_id;
}
GLuint OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) {
auto OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) -> GLuint {
GLuint program = glCreateProgram();
if (program == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -149,28 +150,28 @@ GLuint OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) {
void OpenGLShader::createQuadGeometry() {
// Datos del quad: posición (x, y) + coordenadas de textura (u, v)
// Formato: x, y, u, v
float vertices[] = {
std::array<float, 16> vertices = {
// Posición // TexCoords
-1.0f,
-1.0f,
0.0f,
0.0f, // Inferior izquierda
1.0f,
-1.0f,
1.0f,
0.0f, // Inferior derecha
1.0f,
1.0f,
1.0f,
1.0f, // Superior derecha
-1.0f,
1.0f,
0.0f,
1.0f // Superior izquierda
-1.0F,
-1.0F,
0.0F,
0.0F, // Inferior izquierda
1.0F,
-1.0F,
1.0F,
0.0F, // Inferior derecha
1.0F,
1.0F,
1.0F,
1.0F, // Superior derecha
-1.0F,
1.0F,
0.0F,
1.0F // Superior izquierda
};
// Índices para dibujar el quad con dos triángulos
unsigned int indices[] = {
std::array<unsigned int, 6> indices = {
0,
1,
2, // Primer triángulo
@@ -187,22 +188,22 @@ void OpenGLShader::createQuadGeometry() {
// Generar y configurar VBO
glGenBuffers(1, &vbo_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
checkGLError("glBufferData(VBO)");
// Generar y configurar EBO
glGenBuffers(1, &ebo_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices.data(), GL_STATIC_DRAW);
checkGLError("glBufferData(EBO)");
// Atributo 0: Posición (2 floats)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
checkGLError("glVertexAttribPointer(position)");
// Atributo 1: Coordenadas de textura (2 floats)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), static_cast<const void*>(static_cast<const char*>(nullptr) + 2 * sizeof(float)));
glEnableVertexAttribArray(1);
checkGLError("glVertexAttribPointer(texcoord)");
@@ -211,8 +212,10 @@ void OpenGLShader::createQuadGeometry() {
glBindVertexArray(0);
}
GLuint OpenGLShader::getTextureID(SDL_Texture* texture) {
if (!texture) return 1;
auto OpenGLShader::getTextureID(SDL_Texture* texture) -> GLuint {
if (texture == nullptr) {
return 1;
}
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
GLuint texture_id = 0;
@@ -237,15 +240,15 @@ GLuint OpenGLShader::getTextureID(SDL_Texture* texture) {
return texture_id;
}
bool OpenGLShader::init(SDL_Window* window,
auto OpenGLShader::init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) {
const std::string& fragment_source) -> bool {
window_ = window;
back_buffer_ = texture;
renderer_ = SDL_GetRenderer(window);
if (!renderer_) {
if (renderer_ == nullptr) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error: No se pudo obtener el renderer");
return false;
@@ -276,10 +279,10 @@ bool OpenGLShader::init(SDL_Window* window,
// Verificar que es OpenGL
const char* renderer_name = SDL_GetRendererName(renderer_);
if (!renderer_name || strncmp(renderer_name, "opengl", 6) != 0) {
if ((renderer_name == nullptr) || strncmp(renderer_name, "opengl", 6) != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Renderer no es OpenGL: %s",
renderer_name ? renderer_name : "unknown");
(renderer_name != nullptr) ? renderer_name : "unknown");
return false;
}
@@ -305,8 +308,12 @@ bool OpenGLShader::init(SDL_Window* window,
if (vertex_shader == 0 || fragment_shader == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error al compilar shaders");
if (vertex_shader != 0) glDeleteShader(vertex_shader);
if (fragment_shader != 0) glDeleteShader(fragment_shader);
if (vertex_shader != 0) {
glDeleteShader(vertex_shader);
}
if (fragment_shader != 0) {
glDeleteShader(fragment_shader);
}
return false;
}
@@ -370,15 +377,16 @@ void OpenGLShader::render() {
}
// Obtener tamaño actual de ventana (puede haber cambiado)
int current_width, current_height;
int current_width;
int current_height;
SDL_GetWindowSize(window_, &current_width, &current_height);
// Guardar estados OpenGL
GLint old_program;
glGetIntegerv(GL_CURRENT_PROGRAM, &old_program);
GLint old_viewport[4];
glGetIntegerv(GL_VIEWPORT, old_viewport);
std::array<GLint, 4> old_viewport{};
glGetIntegerv(GL_VIEWPORT, old_viewport.data());
GLboolean was_texture_enabled = glIsEnabled(GL_TEXTURE_2D);
GLint old_texture;
@@ -403,7 +411,8 @@ void OpenGLShader::render() {
checkGLError("glUseProgram");
// Configurar viewport (obtener tamaño lógico de SDL)
int logical_w, logical_h;
int logical_w;
int logical_h;
SDL_RendererLogicalPresentation mode;
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &mode);
@@ -413,14 +422,16 @@ void OpenGLShader::render() {
}
// Calcular viewport considerando aspect ratio
int viewport_x = 0, viewport_y = 0;
int viewport_w = current_width, viewport_h = current_height;
int viewport_x = 0;
int viewport_y = 0;
int viewport_w = current_width;
int viewport_h = current_height;
if (mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) {
int scale_x = current_width / logical_w;
int scale_y = current_height / logical_h;
int scale = (scale_x < scale_y) ? scale_x : scale_y;
if (scale < 1) scale = 1;
scale = std::max(scale, 1);
viewport_w = logical_w * scale;
viewport_h = logical_h * scale;
@@ -444,7 +455,7 @@ void OpenGLShader::render() {
// Dibujar quad usando VAO
glBindVertexArray(vao_);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
checkGLError("glDrawElements");
// Presentar
@@ -453,7 +464,7 @@ void OpenGLShader::render() {
// Restaurar estados OpenGL
glUseProgram(old_program);
glBindTexture(GL_TEXTURE_2D, old_texture);
if (!was_texture_enabled) {
if (was_texture_enabled == 0U) {
glDisable(GL_TEXTURE_2D);
}
glBindVertexArray(old_vao);

View File

@@ -23,24 +23,24 @@ class OpenGLShader : public ShaderBackend {
OpenGLShader() = default;
~OpenGLShader() override;
bool init(SDL_Window* window,
auto init(SDL_Window* window,
SDL_Texture* texture,
const std::string& vertex_source,
const std::string& fragment_source) override;
const std::string& fragment_source) -> bool override;
void render() override;
void setTextureSize(float width, float height) override;
void cleanup() override;
bool isHardwareAccelerated() const override { return is_initialized_; }
void cleanup() final;
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
private:
// Funciones auxiliares
bool initGLExtensions();
GLuint compileShader(const std::string& source, GLenum shader_type);
GLuint linkProgram(GLuint vertex_shader, GLuint fragment_shader);
auto initGLExtensions() -> bool;
auto compileShader(const std::string& source, GLenum shader_type) -> GLuint;
auto linkProgram(GLuint vertex_shader, GLuint fragment_shader) -> GLuint;
void createQuadGeometry();
GLuint getTextureID(SDL_Texture* texture);
void checkGLError(const char* operation);
static auto getTextureID(SDL_Texture* texture) -> GLuint;
static void checkGLError(const char* operation);
// Estado SDL
SDL_Window* window_ = nullptr;
@@ -59,8 +59,8 @@ class OpenGLShader : public ShaderBackend {
// Tamaños
int window_width_ = 0;
int window_height_ = 0;
float texture_width_ = 0.0f;
float texture_height_ = 0.0f;
float texture_width_ = 0.0F;
float texture_height_ = 0.0F;
// Estado
bool is_initialized_ = false;

View File

@@ -26,33 +26,16 @@
struct JA_Music_t; // lines 11-11
struct JA_Sound_t; // lines 12-12
// Helper para cargar archivos de audio desde pack o filesystem
// Helper para cargar archivos de audio desde pack o filesystem en memoria
namespace {
auto createTempAudioFile(const std::string& file_path, std::vector<std::string>& temp_files_tracker) -> std::string {
struct AudioData {
std::vector<uint8_t> data;
std::string filepath;
};
auto loadAudioData(const std::string& file_path) -> AudioData {
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
return AudioData{.data = std::move(resource_data), .filepath = file_path};
}
} // namespace
@@ -92,7 +75,6 @@ Resource::Resource(LoadingMode mode)
// Destructor
Resource::~Resource() {
cleanupTempAudioFiles();
clear();
}
@@ -335,8 +317,12 @@ auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
for (const auto& file : sound_list) {
if (getFileName(file) == name) {
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
return JA_LoadSound(audio_path.c_str());
auto audio_data = loadAudioData(file);
if (!audio_data.data.empty()) {
return JA_LoadSound(audio_data.data.data(), audio_data.data.size());
}
// Fallback a cargar desde disco si no está en pack
return JA_LoadSound(file.c_str());
}
}
return nullptr;
@@ -347,8 +333,12 @@ auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* {
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
for (const auto& file : music_list) {
if (getFileName(file) == name) {
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
return JA_LoadMusic(audio_path.c_str());
auto audio_data = loadAudioData(file);
if (!audio_data.data.empty()) {
return JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
}
// Fallback a cargar desde disco si no está en pack
return JA_LoadMusic(file.c_str());
}
}
return nullptr;
@@ -484,8 +474,15 @@ void Resource::loadSounds() {
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
sounds_.emplace_back(name, JA_LoadSound(audio_path.c_str()));
auto audio_data = loadAudioData(l);
JA_Sound_t* sound = nullptr;
if (!audio_data.data.empty()) {
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
} else {
// Fallback a cargar desde disco si no está en pack
sound = JA_LoadSound(l.c_str());
}
sounds_.emplace_back(name, sound);
Logger::dots("Sound : ", name, "[ LOADED ]");
}
}
@@ -500,8 +497,15 @@ void Resource::loadMusics() {
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
musics_.emplace_back(name, JA_LoadMusic(audio_path.c_str()));
auto audio_data = loadAudioData(l);
JA_Music_t* music = nullptr;
if (!audio_data.data.empty()) {
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
} else {
// Fallback a cargar desde disco si no está en pack
music = JA_LoadMusic(l.c_str());
}
musics_.emplace_back(name, music);
Logger::dots("Music : ", name, "[ LOADED ]");
}
}
@@ -901,18 +905,3 @@ void Resource::updateLoadingProgress(std::string name) {
void Resource::updateProgressBar() {
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();
}

View File

@@ -146,9 +146,6 @@ class Resource {
SDL_FRect loading_wired_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 ---
void loadSounds(); // Carga los sonidos
void loadMusics(); // Carga las músicas
@@ -167,7 +164,6 @@ class Resource {
void load(); // Carga todos los recursos
void clearSounds(); // Vacía el vector de sonidos
void clearMusics(); // Vacía el vector de músicas
void cleanupTempAudioFiles(); // Limpia archivos temporales de audio
// --- Métodos para carga perezosa ---
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido

View File

@@ -10,9 +10,9 @@
namespace ResourceHelper {
static bool resource_system_initialized = false;
auto initializeResourceSystem(const std::string& pack_file) -> bool {
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool {
auto& loader = ResourceLoader::getInstance();
resource_system_initialized = loader.initialize(pack_file, true);
resource_system_initialized = loader.initialize(pack_file, enable_fallback);
if (resource_system_initialized) {
std::cout << "Resource system initialized with pack: " << pack_file << '\n';

View File

@@ -9,7 +9,7 @@
// Helper functions para integrar ResourceLoader con el sistema existente
namespace ResourceHelper {
// Inicializa ResourceLoader (llamar al inicio del programa)
auto initializeResourceSystem(const std::string& pack_file = "resources.pack") -> bool;
auto initializeResourceSystem(const std::string& pack_file = "resources.pack", bool enable_fallback = true) -> bool;
// Cierra ResourceLoader
void shutdownResourceSystem();

View File

@@ -24,22 +24,22 @@ ResourceLoader::~ResourceLoader() {
shutdown();
}
auto ResourceLoader::initialize(const std::string& packFile, bool enable_fallback) -> bool {
auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
shutdown();
fallback_to_files_ = enable_fallback;
pack_path_ = packFile;
pack_path_ = pack_file;
if (std::filesystem::exists(packFile)) {
if (std::filesystem::exists(pack_file)) {
resource_pack_ = new ResourcePack();
if (resource_pack_->loadPack(packFile)) {
std::cout << "Resource pack loaded successfully: " << packFile << '\n';
if (resource_pack_->loadPack(pack_file)) {
std::cout << "Resource pack loaded successfully: " << pack_file << '\n';
std::cout << "Resources available: " << resource_pack_->getResourceCount() << '\n';
return true;
}
delete resource_pack_;
resource_pack_ = nullptr;
std::cerr << "Failed to load resource pack: " << packFile << std::endl;
std::cerr << "Failed to load resource pack: " << pack_file << '\n';
}
if (fallback_to_files_) {
@@ -47,7 +47,7 @@ auto ResourceLoader::initialize(const std::string& packFile, bool enable_fallbac
return true;
}
std::cerr << "Resource pack not found and fallback disabled: " << packFile << '\n';
std::cerr << "Resource pack not found and fallback disabled: " << pack_file << '\n';
return false;
}

View File

@@ -34,6 +34,6 @@ class ResourceLoader {
[[nodiscard]] auto getAvailableResources() const -> std::vector<std::string>;
private:
auto loadFromFile(const std::string& filename) -> std::vector<uint8_t>;
static auto loadFromFile(const std::string& filename) -> std::vector<uint8_t>;
static auto getDataPath(const std::string& filename) -> std::string;
};

View File

@@ -1,6 +1,7 @@
#include "resource_pack.hpp"
#include <algorithm> // Para replace
#include <array> // Para array
#include <filesystem> // Para path, recursive_directory_iterator, directory_entry, exists, relative
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ofstream, operator<<, basic_ios, basic_istream::read, basic_ostream::write, endl, ios, basic_istream, ifstream, operator|, basic_istream::seekg, basic_istream::tellg, ofstream, streamsize
#include <iostream> // Para cerr
@@ -44,9 +45,9 @@ auto ResourcePack::loadPack(const std::string& pack_file) -> bool {
return false;
}
char header[4];
file.read(header, 4);
if (std::string(header, 4) != "CCAE") {
std::array<char, 4> header;
file.read(header.data(), 4);
if (std::string(header.data(), 4) != "CCAE") {
std::cerr << "Error: Invalid pack file format" << '\n';
return false;
}

View File

@@ -21,7 +21,7 @@ class ResourcePack {
static auto calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t;
static void encryptData(std::vector<uint8_t>& data, const std::string& key);
void decryptData(std::vector<uint8_t>& data, const std::string& key);
static void decryptData(std::vector<uint8_t>& data, const std::string& key);
public:
ResourcePack();

View File

@@ -107,27 +107,30 @@ void Scoreboard::setCarouselAnimation(Id id, int selected_index, EnterName* ente
// ===== Inicialización (primera vez) =====
if (carousel_prev_index_.at(idx) == -1) {
carousel_position_.at(idx) = static_cast<float>(selected_index);
carousel_target_.at(idx) = static_cast<float>(selected_index);
carousel_position_.at(idx) = static_cast<float>(selected_index);
carousel_target_.at(idx) = static_cast<float>(selected_index);
carousel_prev_index_.at(idx) = selected_index;
return;
}
int prev_index = carousel_prev_index_.at(idx);
if (selected_index == prev_index) {
return; // nada que hacer
return; // nada que hacer
}
// ===== Bloquear si aún animando =====
if (std::abs(carousel_position_.at(idx) - carousel_target_.at(idx)) > 0.01f) {
if (std::abs(carousel_position_.at(idx) - carousel_target_.at(idx)) > 0.01F) {
return;
}
// ===== Calcular salto circular =====
int delta = selected_index - prev_index;
const int LIST_SIZE = static_cast<int>(enter_name_ptr->getCharacterList().size());
if (delta > LIST_SIZE / 2) delta -= LIST_SIZE;
else if (delta < -LIST_SIZE / 2) delta += LIST_SIZE;
if (delta > LIST_SIZE / 2) {
delta -= LIST_SIZE;
} else if (delta < -LIST_SIZE / 2) {
delta += LIST_SIZE;
}
// ===== Alinear posición actual antes de moverse =====
carousel_position_.at(idx) = std::round(carousel_position_.at(idx));
@@ -141,8 +144,8 @@ void Scoreboard::setCarouselAnimation(Id id, int selected_index, EnterName* ente
} else {
// Movimiento largo → animado pero limitado en tiempo
// Normalizamos el salto para que visualmente tarde como mucho el doble
const float MAX_DURATION_FACTOR = 2.0f; // máximo 2x la duración de una letra
const float SPEED_SCALE = std::min(1.0f, MAX_DURATION_FACTOR / static_cast<float>(ABS_DELTA));
const float MAX_DURATION_FACTOR = 2.0F; // máximo 2x la duración de una letra
const float SPEED_SCALE = std::min(1.0F, MAX_DURATION_FACTOR / static_cast<float>(ABS_DELTA));
// Guardamos el destino real
float target = std::round(carousel_position_.at(idx)) + static_cast<float>(delta);
@@ -257,7 +260,7 @@ void Scoreboard::updateCarouselAnimation(float delta_time) {
} else {
// Forzar al target exacto cuando estamos muy cerca
carousel_position_.at(i) = carousel_target_.at(i);
carousel_speed_scale_.at(i) = 1.0f; // restaurar velocidad normal
carousel_speed_scale_.at(i) = 1.0F; // restaurar velocidad normal
}
}
}
@@ -287,12 +290,41 @@ void Scoreboard::updateTextSlideAnimation(float delta_time) {
}
}
// Actualiza las animaciones de pulso de los paneles
void Scoreboard::updatePanelPulses(float delta_time) {
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
auto& pulse = panel_pulse_.at(i);
if (!pulse.active) {
continue;
}
// Avanzar el tiempo transcurrido
pulse.elapsed_s += delta_time;
// Desactivar el pulso si ha terminado
if (pulse.elapsed_s >= pulse.duration_s) {
pulse.active = false;
pulse.elapsed_s = 0.0F;
}
}
}
// Activa un pulso en el panel especificado
void Scoreboard::triggerPanelPulse(Id id, float duration_s) {
auto idx = static_cast<size_t>(id);
panel_pulse_.at(idx).active = true;
panel_pulse_.at(idx).elapsed_s = 0.0F;
panel_pulse_.at(idx).duration_s = duration_s;
}
// Actualiza la lógica del marcador
void Scoreboard::update(float delta_time) {
updateTimeCounter();
updateNameColorIndex();
updateCarouselAnimation(delta_time);
updateTextSlideAnimation(delta_time);
updatePanelPulses(delta_time);
fillBackgroundTexture(); // Renderizar DESPUÉS de actualizar
}
@@ -334,8 +366,31 @@ void Scoreboard::fillPanelTextures() {
// Cambia el destino del renderizador
SDL_SetRenderTarget(renderer_, panel_texture_.at(i));
// Calcula el color de fondo del panel (puede tener pulso activo)
Color background_color = Color(0, 0, 0, 0); // Transparente por defecto
const auto& pulse = panel_pulse_.at(i);
if (pulse.active) {
// Calcular el progreso del pulso (0.0 a 1.0 y de vuelta a 0.0)
float progress = pulse.elapsed_s / pulse.duration_s;
// Crear curva de ida y vuelta (0 → 1 → 0)
float pulse_intensity;
if (progress < 0.5F) {
pulse_intensity = progress * 2.0F; // 0.0 a 1.0
} else {
pulse_intensity = (1.0F - progress) * 2.0F; // 1.0 a 0.0
}
// Interpolar entre color base y color aclarado
Color target_color = color_.LIGHTEN(PANEL_PULSE_LIGHTEN_AMOUNT);
// Color target_color = color_.INVERSE();
background_color = color_.LERP(target_color, pulse_intensity);
background_color.a = 255; // Opaco durante el pulso
}
// Dibuja el fondo de la textura
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
SDL_SetRenderDrawColor(renderer_, background_color.r, background_color.g, background_color.b, background_color.a);
SDL_RenderClear(renderer_);
renderPanelContent(i);
@@ -685,7 +740,7 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
constexpr int HALF_VISIBLE = CAROUSEL_VISIBLE_LETTERS / 2; // 4 letras a cada lado
// Posición flotante actual del carrusel (índice en la lista de caracteres)
float CAROUSEL_POS = carousel_position_.at(panel_index);
float carousel_pos = carousel_position_.at(panel_index);
const int CHAR_LIST_SIZE = static_cast<int>(char_list.size());
// Calcular ancho promedio de una letra (asumimos ancho uniforme)
@@ -693,17 +748,17 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
const int CHAR_STEP = AVG_CHAR_WIDTH + EXTRA_SPACING;
// --- Corrección visual de residuales flotantes (evita “baile”) ---
float frac = CAROUSEL_POS - std::floor(CAROUSEL_POS);
if (frac > 0.999f || frac < 0.001f) {
CAROUSEL_POS = std::round(CAROUSEL_POS);
frac = 0.0f;
float frac = carousel_pos - std::floor(carousel_pos);
if (frac > 0.999F || frac < 0.001F) {
carousel_pos = std::round(carousel_pos);
frac = 0.0F;
}
const float FRACTIONAL_OFFSET = frac;
const int PIXEL_OFFSET = static_cast<int>(FRACTIONAL_OFFSET * CHAR_STEP + 0.5f);
const int PIXEL_OFFSET = static_cast<int>((FRACTIONAL_OFFSET * CHAR_STEP) + 0.5F);
// Índice base en la lista de caracteres (posición central)
const int BASE_INDEX = static_cast<int>(std::floor(CAROUSEL_POS));
const int BASE_INDEX = static_cast<int>(std::floor(carousel_pos));
// Calcular posición X inicial (centrar las 9 letras visibles)
int start_x = center_x - (HALF_VISIBLE * CHAR_STEP) - (AVG_CHAR_WIDTH / 2) - PIXEL_OFFSET;
@@ -718,24 +773,27 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
}
// --- Calcular distancia circular correcta (corregido el bug de wrap) ---
float normalized_pos = std::fmod(CAROUSEL_POS, static_cast<float>(CHAR_LIST_SIZE));
if (normalized_pos < 0.0f) normalized_pos += static_cast<float>(CHAR_LIST_SIZE);
float normalized_pos = std::fmod(carousel_pos, static_cast<float>(CHAR_LIST_SIZE));
if (normalized_pos < 0.0F) {
normalized_pos += static_cast<float>(CHAR_LIST_SIZE);
}
float diff = std::abs(static_cast<float>(char_index) - normalized_pos);
if (diff > static_cast<float>(CHAR_LIST_SIZE) / 2.0f)
if (diff > static_cast<float>(CHAR_LIST_SIZE) / 2.0F) {
diff = static_cast<float>(CHAR_LIST_SIZE) - diff;
}
const float distance_from_center = diff;
const float DISTANCE_FROM_CENTER = diff;
// --- Seleccionar color con LERP según la distancia ---
Color letter_color;
if (distance_from_center < 0.5F) {
if (DISTANCE_FROM_CENTER < 0.5F) {
// Letra central → transiciona hacia animated_color_
float lerp_to_animated = distance_from_center / 0.5F; // 0.0 a 1.0
float lerp_to_animated = DISTANCE_FROM_CENTER / 0.5F; // 0.0 a 1.0
letter_color = animated_color_.LERP(text_color1_, lerp_to_animated);
} else {
// Letras alejadas → degradan hacia color_ base
float base_lerp = (distance_from_center - 0.5F) / (HALF_VISIBLE - 0.5F);
float base_lerp = (DISTANCE_FROM_CENTER - 0.5F) / (HALF_VISIBLE - 0.5F);
base_lerp = std::min(base_lerp, 1.0F);
const float LERP_FACTOR = base_lerp * 0.85F;
letter_color = text_color1_.LERP(color_, LERP_FACTOR);

View File

@@ -49,6 +49,12 @@ class Scoreboard {
SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador
};
struct PanelPulse {
bool active = false; // Si el pulso está activo
float elapsed_s = 0.0F; // Tiempo transcurrido desde el inicio
float duration_s = 0.5F; // Duración total del pulso
};
// --- Métodos de singleton ---
static void init(); // Crea el objeto Scoreboard
static void destroy(); // Libera el objeto Scoreboard
@@ -74,6 +80,7 @@ class Scoreboard {
void setScore(Id id, int score) { score_.at(static_cast<size_t>(id)) = score; }
void setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast<size_t>(id)) = pos; }
void setStage(int stage) { stage_ = stage; }
void triggerPanelPulse(Id id, float duration_s = 0.5F); // Activa un pulso en el panel especificado
private:
// --- Objetos y punteros ---
@@ -94,6 +101,7 @@ class Scoreboard {
std::array<int, static_cast<int>(Id::SIZE)> carousel_prev_index_ = {}; // Índice previo para detectar cambios
std::array<float, static_cast<int>(Id::SIZE)> text_slide_offset_ = {}; // Progreso de animación de deslizamiento (0.0 a 1.0)
std::array<Panel, static_cast<int>(Id::SIZE)> panel_ = {}; // Lista con todos los paneles del marcador
std::array<PanelPulse, static_cast<int>(Id::SIZE)> panel_pulse_ = {}; // Estado de pulso para cada panel
Colors::Cycle name_color_cycle_; // Ciclo de colores para destacar el nombre una vez introducido
Color animated_color_; // Color actual animado (ciclo automático cada 100ms)
std::string hi_score_name_; // Nombre del jugador con la máxima puntuación
@@ -109,12 +117,13 @@ class Scoreboard {
int time_counter_ = 0; // Contador de segundos
Uint32 name_color_index_ = 0; // Índice actual del color en el ciclo de animación del nombre
Uint64 name_color_last_update_ = 0; // Último tick de actualización del color del nombre
float power_ = 0; // Poder actual de la fase
std::array<float, static_cast<size_t>(Id::SIZE)> carousel_speed_scale_ = {1.0f, 1.0f, 1.0f};
float power_ = 0.0F; // Poder actual de la fase
std::array<float, static_cast<size_t>(Id::SIZE)> carousel_speed_scale_ = {1.0F, 1.0F, 1.0F};
// --- Constantes ---
static constexpr int CAROUSEL_VISIBLE_LETTERS = 9;
static constexpr float TEXT_SLIDE_DURATION = 0.3F; // Duración de la animación de deslizamiento en segundos
static constexpr int PANEL_PULSE_LIGHTEN_AMOUNT = 40; // Cantidad de aclarado para el pulso del panel
// --- Variables de aspecto ---
Color text_color1_, text_color2_; // Colores para los marcadores del texto;
@@ -134,6 +143,7 @@ class Scoreboard {
void updateNameColorIndex(); // Actualiza el índice del color animado del nombre
void updateCarouselAnimation(float delta_time); // Actualiza la animación del carrusel
void updateTextSlideAnimation(float delta_time); // Actualiza la animación de deslizamiento de texto
void updatePanelPulses(float delta_time); // Actualiza las animaciones de pulso de los paneles
void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador
void renderPanelContent(size_t panel_index);
void renderScoreMode(size_t panel_index);

View File

@@ -45,27 +45,8 @@ Credits::Credits()
if (text_texture_ == nullptr) {
throw std::runtime_error("Failed to create SDL texture for text.");
}
Section::name = Section::Name::CREDITS;
balloon_manager_->setPlayArea(play_area_);
fade_in_->setColor(param.fade.color);
fade_in_->setType(Fade::Type::FULLSCREEN);
fade_in_->setPostDuration(800);
fade_in_->setMode(Fade::Mode::IN);
fade_in_->activate();
fade_out_->setColor(0, 0, 0);
fade_out_->setType(Fade::Type::FULLSCREEN);
fade_out_->setPostDuration(7000);
updateRedRect();
tiled_bg_->setColor(Color(255, 96, 96));
tiled_bg_->setSpeed(60.0F);
initPlayers();
SDL_SetTextureBlendMode(text_texture_, SDL_BLENDMODE_BLEND);
fillTextTexture();
steps_ = static_cast<int>(std::abs((top_black_rect_.h - param.game.game_area.center_y - 1) + ((left_black_rect_.w - param.game.game_area.center_x) / 4)));
initVars();
startCredits();
}
// Destructor
@@ -101,9 +82,9 @@ void Credits::run() {
}
}
// Actualiza las variables (time-based)
// Actualiza las variables (time-based puro - sin conversión frame-based)
void Credits::update(float delta_time) {
const float MULTIPLIER = want_to_pass_ ? 4.0F : 1.0F;
const float MULTIPLIER = want_to_pass_ ? FAST_FORWARD_MULTIPLIER : 1.0F;
const float ADJUSTED_DELTA_TIME = delta_time * MULTIPLIER;
static auto* const SCREEN = Screen::get();
@@ -118,10 +99,6 @@ void Credits::update(float delta_time) {
updatePlayers(ADJUSTED_DELTA_TIME);
updateAllFades(ADJUSTED_DELTA_TIME);
// Convertir deltaTime a equivalente de frames (60fps)
const float FRAME_FACTOR = ADJUSTED_DELTA_TIME * 60.0F;
counter_ += FRAME_FACTOR;
fillCanvas();
}
@@ -274,9 +251,7 @@ void Credits::fillCanvas() {
SDL_RenderFillRect(Screen::get()->getRenderer(), &right_black_rect_);
// Dibuja el rectangulo rojo
const Color COLOR = color_.LIGHTEN();
SDL_SetRenderDrawColor(Screen::get()->getRenderer(), COLOR.r, COLOR.g, COLOR.b, 0xFF);
SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_);
drawBorderRect();
// Si el mini_logo está en su destino, lo dibuja encima de lo anterior
if (mini_logo_on_position_) {
@@ -291,7 +266,7 @@ void Credits::fillCanvas() {
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp);
}
// Actualiza el destino de los rectangulos de las texturas (time-based)
// Actualiza el destino de los rectangulos de las texturas (time-based puro)
void Credits::updateTextureDstRects(float delta_time) {
constexpr float TEXTURE_UPDATE_INTERVAL_S = 10.0F / 60.0F; // ~0.167s (cada 10 frames)
credits_state_.texture_accumulator += delta_time;
@@ -305,34 +280,45 @@ void Credits::updateTextureDstRects(float delta_time) {
}
// Comprueba la posición de la textura con el mini_logo
if (mini_logo_rect_dst_.y == mini_logo_final_pos_) {
if (mini_logo_rect_dst_.y <= static_cast<float>(mini_logo_final_pos_)) {
// Forzar posición exacta para evitar problemas de comparación float
mini_logo_rect_dst_.y = static_cast<float>(mini_logo_final_pos_);
mini_logo_on_position_ = true;
// Si el jugador quiere pasar los titulos de credito, el fade se inicia solo
if (want_to_pass_) {
fading_ = true;
}
// Se activa el contador para evitar que la sección sea infinita
if (counter_prevent_endless_ == 1000) {
fading_ = true;
} else {
++counter_prevent_endless_;
}
} else {
--mini_logo_rect_dst_.y;
}
}
// Acumular tiempo desde que el logo llegó a su posición (fuera del if para que se ejecute cada frame)
if (mini_logo_on_position_) {
time_since_logo_positioned_ += delta_time;
// Timeout para evitar que la sección sea infinita
if (time_since_logo_positioned_ >= MAX_TIME_AFTER_LOGO_S) {
fading_ = true;
}
// Si el jugador quiere pasar los titulos de credito, el fade se inicia solo
if (want_to_pass_) {
fading_ = true;
}
}
}
// Tira globos al escenario (time-based)
// Tira globos al escenario (time-based puro)
void Credits::throwBalloons(float delta_time) {
constexpr int SPEED = 200;
constexpr size_t NUM_SETS = 8; // Tamaño del vector SETS
const std::vector<int> SETS = {0, 63, 25, 67, 17, 75, 13, 50};
constexpr float BALLOON_INTERVAL_S = SPEED / 60.0F; // ~3.33s (cada 200 frames)
constexpr float POWERBALL_INTERVAL_S = (SPEED * 4) / 60.0F; // ~13.33s (cada 800 frames)
constexpr float BALLOON_INTERVAL_S = SPEED / 60.0F; // ~3.33s (cada 200 frames)
constexpr float POWERBALL_INTERVAL_S = (SPEED * 4) / 60.0F; // ~13.33s (cada 800 frames)
constexpr float MAX_BALLOON_TIME_S = ((NUM_SETS - 1) * SPEED * 3) / 60.0F; // Tiempo máximo para lanzar globos
if (counter_ > ((SETS.size() - 1) * SPEED) * 3) {
// Acumular tiempo total de globos
elapsed_time_balloons_ += delta_time;
// Detener lanzamiento después del tiempo límite
if (elapsed_time_balloons_ > MAX_BALLOON_TIME_S) {
return;
}
@@ -341,11 +327,11 @@ void Credits::throwBalloons(float delta_time) {
if (credits_state_.balloon_accumulator >= BALLOON_INTERVAL_S) {
credits_state_.balloon_accumulator -= BALLOON_INTERVAL_S;
const int INDEX = (static_cast<int>(counter_ / SPEED)) % SETS.size();
const int INDEX = (static_cast<int>(elapsed_time_balloons_ * 60.0F / SPEED)) % SETS.size();
balloon_manager_->deployFormation(SETS.at(INDEX), -60);
}
if (credits_state_.powerball_accumulator >= POWERBALL_INTERVAL_S && counter_ > 0) {
if (credits_state_.powerball_accumulator >= POWERBALL_INTERVAL_S && elapsed_time_balloons_ > 0.0F) {
credits_state_.powerball_accumulator -= POWERBALL_INTERVAL_S;
balloon_manager_->createPowerBall();
}
@@ -354,8 +340,8 @@ void Credits::throwBalloons(float delta_time) {
// Inicializa los jugadores
void Credits::initPlayers() {
std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores;
std::vector<std::vector<std::string>> player1_animations; // Vector con las animaciones del jugador 1
std::vector<std::vector<std::string>> player2_animations; // Vector con las animaciones del jugador 2
std::vector<std::vector<std::string>> player1_animations; // Vector con las animaciones del jugador 1
std::vector<std::vector<std::string>> player2_animations; // Vector con las animaciones del jugador 2
// Texturas - Player1
std::vector<std::shared_ptr<Texture>> player1_textures;
@@ -423,66 +409,117 @@ void Credits::initPlayers() {
// Actualiza los rectangulos negros (time-based)
void Credits::updateBlackRects(float delta_time) {
static auto current_step_ = static_cast<float>(steps_);
constexpr float BLACK_RECT_INTERVAL_S = 4.0F / 60.0F; // ~0.067s (cada 4 frames)
if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) {
// Si los rectangulos superior e inferior no han llegado al centro
if (!initialized_) { return; }
delta_time = std::max(delta_time, 0.0F);
// Fase vertical: hasta que ambos rects verticales estén exactos en su target
if (!vertical_done_) {
credits_state_.black_rect_accumulator += delta_time;
if (credits_state_.black_rect_accumulator >= BLACK_RECT_INTERVAL_S) {
credits_state_.black_rect_accumulator -= BLACK_RECT_INTERVAL_S;
// Incrementa la altura del rectangulo superior
top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1);
// top
int prev_top_h = static_cast<int>(top_black_rect_.h);
top_black_rect_.h = std::min(top_black_rect_.h + 1.0F,
static_cast<float>(param.game.game_area.center_y - 1));
int top_delta = static_cast<int>(top_black_rect_.h) - prev_top_h;
// Incrementa la altura y modifica la posición del rectangulo inferior
++bottom_black_rect_.h;
bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1, param.game.game_area.center_y + 1);
// bottom
int prev_bottom_h = static_cast<int>(bottom_black_rect_.h);
int prev_bottom_y = static_cast<int>(bottom_black_rect_.y);
bottom_black_rect_.h = bottom_black_rect_.h + 1.0F;
bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1.0F,
static_cast<float>(param.game.game_area.center_y + 1));
int bottom_steps_by_h = static_cast<int>(bottom_black_rect_.h) - prev_bottom_h;
int bottom_steps_by_y = prev_bottom_y - static_cast<int>(bottom_black_rect_.y);
int bottom_steps = std::max({0, bottom_steps_by_h, bottom_steps_by_y});
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
}
} else {
// Si los rectangulos superior e inferior han llegado al centro
if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) {
constexpr int SPEED = 2;
// Si los rectangulos izquierdo y derecho no han llegado al centro
// Incrementa la anchura del rectangulo situado a la izquierda
left_black_rect_.w = std::min(left_black_rect_.w + SPEED, param.game.game_area.center_x);
int steps_done = top_delta + bottom_steps;
if (steps_done > 0) {
current_step_ = std::max(0.0F, current_step_ - static_cast<float>(steps_done));
float vol_f = initial_volume_ * (current_step_ / static_cast<float>(total_steps_));
int vol_i = static_cast<int>(std::clamp(vol_f, 0.0F, static_cast<float>(initial_volume_)));
Audio::get()->setMusicVolume(vol_i); // usa tu API de audio aquí
}
// Incrementa la anchura y modifica la posición del rectangulo situado a la derecha
right_black_rect_.w += SPEED;
right_black_rect_.x = std::max(right_black_rect_.x - SPEED, param.game.game_area.center_x);
--current_step_;
setVolume(static_cast<int>(initial_volume_ * current_step_ / steps_));
} else {
// Si los rectangulos izquierdo y derecho han llegado al centro
setVolume(0);
Audio::get()->stopMusic();
if (counter_pre_fade_ == 400) {
fade_out_->activate();
} else {
// Convertir deltaTime a equivalente de frames
const float FRAME_FACTOR = delta_time * 60.0F;
counter_pre_fade_ += FRAME_FACTOR;
// Si han alcanzado los objetivos, fijarlos exactamente y marcar done
bool top_at_target = static_cast<int>(top_black_rect_.h) == param.game.game_area.center_y - 1.0F;
bool bottom_at_target = static_cast<int>(bottom_black_rect_.y) == param.game.game_area.center_y + 1.0F;
if (top_at_target && bottom_at_target) {
top_black_rect_.h = param.game.game_area.center_y - 1.0F;
bottom_black_rect_.y = param.game.game_area.center_y + 1.0F;
vertical_done_ = true;
}
}
// actualizar border_rect cada frame aunque todavía en fase vertical
updateBorderRect();
return;
}
// Fase horizontal
if (!horizontal_done_) {
int prev_left_w = static_cast<int>(left_black_rect_.w);
left_black_rect_.w = std::min(left_black_rect_.w + static_cast<float>(HORIZONTAL_SPEED),
param.game.game_area.center_x);
int left_gain = static_cast<int>(left_black_rect_.w) - prev_left_w;
int prev_right_x = static_cast<int>(right_black_rect_.x);
right_black_rect_.w = right_black_rect_.w + static_cast<float>(HORIZONTAL_SPEED);
right_black_rect_.x = std::max(right_black_rect_.x - static_cast<float>(HORIZONTAL_SPEED),
param.game.game_area.center_x);
int right_move = prev_right_x - static_cast<int>(right_black_rect_.x);
int steps_done = left_gain + right_move;
if (steps_done > 0) {
current_step_ = std::max(0.0F, current_step_ - static_cast<float>(steps_done));
float vol_f = initial_volume_ * (current_step_ / static_cast<float>(total_steps_));
int vol_i = static_cast<int>(std::clamp(vol_f, 0.0F, static_cast<float>(initial_volume_)));
Audio::get()->setMusicVolume(vol_i); // usa tu API de audio aquí
}
bool left_at_target = static_cast<int>(left_black_rect_.w) == param.game.game_area.center_x;
bool right_at_target = static_cast<int>(right_black_rect_.x) == param.game.game_area.center_x;
if (left_at_target && right_at_target) {
left_black_rect_.w = param.game.game_area.center_x;
right_black_rect_.x = param.game.game_area.center_x;
horizontal_done_ = true;
}
updateBorderRect();
return;
}
// Fase final: ya completado el movimiento de rects
Audio::get()->setMusicVolume(0);
// Audio::get()->stopMusic(); // opcional, si quieres parar la reproducción
// Usar segundos puros en lugar de frames equivalentes
if (counter_pre_fade_ >= PRE_FADE_DELAY_S) {
if (fade_out_) {
fade_out_->activate();
}
} else {
counter_pre_fade_ += delta_time;
}
}
// Actualiza el rectangulo rojo
void Credits::updateRedRect() {
// Actualiza el rectangulo del borde
void Credits::updateBorderRect() {
border_rect_.x = left_black_rect_.x + left_black_rect_.w;
border_rect_.y = top_black_rect_.y + top_black_rect_.h - 1;
border_rect_.w = right_black_rect_.x - border_rect_.x;
border_rect_.h = bottom_black_rect_.y - border_rect_.y + 1;
border_rect_.y = top_black_rect_.y + top_black_rect_.h - 1.0F;
float raw_w = right_black_rect_.x - border_rect_.x;
float raw_h = bottom_black_rect_.y - border_rect_.y + 1.0F;
border_rect_.w = std::max(0.0F, raw_w);
border_rect_.h = std::max(0.0F, raw_h);
}
// Actualiza el estado de fade (time-based)
void Credits::updateAllFades(float delta_time) {
if (fading_) {
updateBlackRects(delta_time);
updateRedRect();
updateBorderRect();
}
fade_in_->update();
@@ -512,7 +549,7 @@ void Credits::resetVolume() const {
void Credits::cycleColors(float delta_time) {
constexpr int UPPER_LIMIT = 140; // Límite superior
constexpr int LOWER_LIMIT = 30; // Límite inferior
// Factor para escalar los valores de incremento.
// Asumimos que los valores originales estaban balanceados para 60 FPS.
const float FRAME_ADJUSTMENT = delta_time * 60.0F;
@@ -527,10 +564,10 @@ void Credits::cycleColors(float delta_time) {
// Ajustar valores de R
credits_state_.r += credits_state_.step_r * FRAME_ADJUSTMENT;
if (credits_state_.r >= UPPER_LIMIT) {
credits_state_.r = UPPER_LIMIT; // Clamp para evitar que se pase
credits_state_.r = UPPER_LIMIT; // Clamp para evitar que se pase
credits_state_.step_r = -credits_state_.step_r; // Cambia de dirección al alcanzar los límites
} else if (credits_state_.r <= LOWER_LIMIT) {
credits_state_.r = LOWER_LIMIT; // Clamp para evitar que se pase
credits_state_.r = LOWER_LIMIT; // Clamp para evitar que se pase
credits_state_.step_r = -credits_state_.step_r;
}
@@ -572,3 +609,118 @@ void Credits::renderPlayers() {
player->render();
}
}
// Inicializa variables
void Credits::initVars() {
// Inicialización segura de rects tal y como los mostraste
top_black_rect_ = {
.x = play_area_.x,
.y = param.game.game_area.rect.y,
.w = play_area_.w,
.h = black_bars_size_};
bottom_black_rect_ = {
.x = play_area_.x,
.y = param.game.game_area.rect.h - black_bars_size_,
.w = play_area_.w,
.h = black_bars_size_};
left_black_rect_ = {
.x = play_area_.x,
.y = param.game.game_area.center_y - 1.0F,
.w = 0.0F,
.h = 2.0F};
right_black_rect_ = {
.x = play_area_.x + play_area_.w,
.y = param.game.game_area.center_y - 1.0F,
.w = 0.0F,
.h = 2.0F};
initialized_ = false;
Section::name = Section::Name::CREDITS;
balloon_manager_->setPlayArea(play_area_);
fade_in_->setColor(param.fade.color);
fade_in_->setType(Fade::Type::FULLSCREEN);
fade_in_->setPostDuration(800);
fade_in_->setMode(Fade::Mode::IN);
fade_in_->activate();
fade_out_->setColor(0, 0, 0);
fade_out_->setType(Fade::Type::FULLSCREEN);
fade_out_->setPostDuration(7000);
updateBorderRect();
tiled_bg_->setColor(Color(255, 96, 96));
tiled_bg_->setSpeed(60.0F);
initPlayers();
SDL_SetTextureBlendMode(text_texture_, SDL_BLENDMODE_BLEND);
fillTextTexture();
steps_ = static_cast<int>(std::abs((top_black_rect_.h - param.game.game_area.center_y - 1) + ((left_black_rect_.w - param.game.game_area.center_x) / 4)));
}
void Credits::startCredits() {
// Guardar iniciales (enteros para contar "pasos" por píxel)
init_top_h_ = static_cast<int>(top_black_rect_.h);
init_bottom_y_ = static_cast<int>(bottom_black_rect_.y);
init_left_w_ = static_cast<int>(left_black_rect_.w);
init_right_x_ = static_cast<int>(right_black_rect_.x);
// Objetivos
int top_target_h = param.game.game_area.center_y - 1;
int bottom_target_y = param.game.game_area.center_y + 1;
int left_target_w = param.game.game_area.center_x;
int right_target_x = param.game.game_area.center_x;
// Pasos verticales
int pasos_top = std::max(0, top_target_h - init_top_h_);
int pasos_bottom = std::max(0, init_bottom_y_ - bottom_target_y);
// Pasos horizontales. right se mueve a velocidad HORIZONTAL_SPEED, contamos pasos como unidades de movimiento equivalentes
int pasos_left = std::max(0, left_target_w - init_left_w_);
int dx_right = std::max(0, init_right_x_ - right_target_x);
int pasos_right = (dx_right + (HORIZONTAL_SPEED - 1)) / HORIZONTAL_SPEED; // ceil
total_steps_ = pasos_top + pasos_bottom + pasos_left + pasos_right;
if (total_steps_ <= 0) {
total_steps_ = 1;
}
current_step_ = static_cast<float>(total_steps_);
// Reiniciar contadores y estado
credits_state_.black_rect_accumulator = 0.0F;
counter_pre_fade_ = 0.0F;
initialized_ = true;
// Asegurar volumen inicial consistente
if (steps_ <= 0) {
steps_ = 1;
}
float vol_f = initial_volume_ * (current_step_ / static_cast<float>(total_steps_));
setVolume(static_cast<int>(std::clamp(vol_f, 0.0F, static_cast<float>(initial_volume_))));
}
// Dibuja el rectángulo del borde si es visible
void Credits::drawBorderRect() {
// Umbral: cualquier valor menor que 1 píxel no se considera visible
constexpr float VISIBLE_THRESHOLD = 1.0F;
if (border_rect_.w < VISIBLE_THRESHOLD || border_rect_.h < VISIBLE_THRESHOLD) {
return; // no dibujar
}
const Color COLOR = color_.LIGHTEN();
SDL_Renderer* rdr = Screen::get()->getRenderer();
SDL_SetRenderDrawColor(rdr, COLOR.r, COLOR.g, COLOR.b, 0xFF);
// Convertir a enteros de forma conservadora para evitar líneas de 1px por redondeo extraño
SDL_Rect r;
r.x = static_cast<int>(std::floor(border_rect_.x + 0.5F));
r.y = static_cast<int>(std::floor(border_rect_.y + 0.5F));
r.w = static_cast<int>(std::max(0.0F, std::floor(border_rect_.w + 0.5F)));
r.h = static_cast<int>(std::max(0.0F, std::floor(border_rect_.h + 0.5F)));
if (r.w > 0 && r.h > 0) {
SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_);
}
}

View File

@@ -29,9 +29,16 @@ class Credits {
// --- Métodos del bucle principal ---
void update(float delta_time); // Actualización principal de la lógica (time-based)
auto calculateDeltaTime() -> float; // Calcula el deltatime
void initVars(); // Inicializa variables
void startCredits(); // Inicializa mas variables
// --- Constantes de clase ---
// --- Constantes de clase (time-based) ---
static constexpr int PLAY_AREA_HEIGHT = 200;
static constexpr float FAST_FORWARD_MULTIPLIER = 6.0F;
static constexpr float BLACK_RECT_INTERVAL_S = 4.0F / 60.0F; // ~0.0667s (cada 4 frames a 60fps)
static constexpr int HORIZONTAL_SPEED = 2;
static constexpr float MAX_TIME_AFTER_LOGO_S = 20.0F;
static constexpr float PRE_FADE_DELAY_S = 8.0F;
// --- Objetos principales ---
std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos
@@ -44,16 +51,27 @@ class Credits {
SDL_Texture* text_texture_; // Textura con el texto de créditos
SDL_Texture* canvas_; // Textura donde se dibuja todo
// --- Temporización y contadores ---
Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
float counter_ = 0; // Contador principal de lógica
float counter_pre_fade_ = 0; // Activación del fundido final
float counter_prevent_endless_ = 0; // Prevención de bucle infinito
// --- Temporización (time-based puro) ---
Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
float elapsed_time_balloons_ = 0.0F; // Tiempo acumulado para lanzamiento de globos (segundos)
float counter_pre_fade_ = 0.0F; // Tiempo antes de activar fundido final (segundos)
float time_since_logo_positioned_ = 0.0F; // Tiempo desde que el logo llegó a su posición (segundos)
float current_step_ = 0.0F;
int total_steps_ = 1;
bool initialized_ = false;
// --- Guardar estados iniciales para cálculo de pasos ---
int init_top_h_ = 0;
int init_bottom_y_ = 0;
int init_left_w_ = 0;
int init_right_x_ = 0;
// --- Variables de estado ---
bool fading_ = false; // Estado del fade final
bool want_to_pass_ = false; // Jugador quiere saltarse créditos
bool mini_logo_on_position_ = false; // Minilogo en posición final
bool vertical_done_ = false;
bool horizontal_done_ = false;
// --- Diseño y posicionamiento ---
float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2; // Tamaño de las barras negras
@@ -127,6 +145,7 @@ class Credits {
void fillTextTexture(); // Crear textura de texto de créditos
void fillCanvas(); // Renderizar todos los sprites y fondos
void renderPlayers(); // Renderiza los jugadores
void drawBorderRect(); // Renderiza el rectangulo del borde
// --- Métodos de lógica del juego ---
void throwBalloons(float delta_time); // Lanzar globos al escenario (time-based)
@@ -138,7 +157,7 @@ class Credits {
// --- Métodos de interfaz ---
void updateBlackRects(); // Actualizar rectángulos negros (letterbox) (frame-based)
void updateBlackRects(float delta_time); // Actualizar rectángulos negros (letterbox) (time-based)
void updateRedRect(); // Actualizar rectángulo rojo (borde)
void updateBorderRect(); // Actualizar rectángulo rojo (borde)
void updateTextureDstRects(); // Actualizar destinos de texturas (frame-based)
void updateTextureDstRects(float delta_time); // Actualizar destinos de texturas (time-based)

View File

@@ -370,7 +370,7 @@ void Game::updateGameStateGameOver(float delta_time) {
}
if (fade_out_->hasEnded()) {
Section::name = Section::Name::HI_SCORE_TABLE;
Section::name = Section::Name::HI_SCORE_TABLE;
Section::options = Section::Options::HI_SCORE_AFTER_PLAYING;
}
}
@@ -551,19 +551,18 @@ void Game::handleTabeHitEffects() {
// Maneja la colisión entre bala y globos
auto Game::checkBulletBalloonCollision(const std::shared_ptr<Bullet>& bullet) -> bool {
for (auto& balloon : balloon_manager_->getBalloons()) {
return std::ranges::any_of(balloon_manager_->getBalloons(), [this, &bullet](auto& balloon) {
if (!balloon->isEnabled() || balloon->isInvulnerable()) {
continue;
return false;
}
if (!checkCollision(balloon->getCollider(), bullet->getCollider())) {
continue;
return false;
}
processBalloonHit(bullet, balloon);
return true;
}
return false;
});
}
// Procesa el impacto en un globo
@@ -1448,12 +1447,12 @@ void Game::handleNameInput(const std::shared_ptr<Player>& player) {
return;
}
if (input_->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
if (input_->checkAction(Input::Action::LEFT, Input::ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setInput(Input::Action::LEFT);
return;
}
if (input_->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
if (input_->checkAction(Input::Action::RIGHT, Input::ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setInput(Input::Action::RIGHT);
return;
}
@@ -1461,7 +1460,10 @@ void Game::handleNameInput(const std::shared_ptr<Player>& player) {
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setInput(Input::Action::START);
updateHiScoreName();
return;
}
player->setInput(Input::Action::NONE);
}
// Inicializa las variables para el modo DEMO
@@ -1958,68 +1960,87 @@ void Game::handleGameOverEvents() {
}
}
// Construye (una vez) el drawList a partir del vector principal
// drawList almacena punteros a los elementos y queda reservado
void Game::buildPlayerDrawList(const Players& elements, Players& drawList) {
drawList.clear();
drawList.reserve(elements.size());
for (const auto& e : elements) drawList.push_back(e); // copia el shared_ptr
std::stable_sort(drawList.begin(), drawList.end(), [](const std::shared_ptr<Player>& a, const std::shared_ptr<Player>& b) {
// Construye (una vez) el draw_list a partir del vector principal
// draw_list almacena punteros a los elementos y queda reservado
void Game::buildPlayerDrawList(const Players& elements, Players& draw_list) {
draw_list.clear();
draw_list.reserve(elements.size());
for (const auto& e : elements) {
draw_list.push_back(e); // copia el shared_ptr
}
std::ranges::stable_sort(draw_list, [](const std::shared_ptr<Player>& a, const std::shared_ptr<Player>& b) {
return a->getZOrder() < b->getZOrder();
});
}
// Actualiza drawList tras cambios en los z_order. Implementación simple:
// reordena drawList según los z_order actuales. Llamar cuando cambian z_order
void Game::updatePlayerDrawList(const Players& elements, Players& drawList) {
// Si drawList está vacío o su tamaño no coincide, reconstruirlo.
if (drawList.size() != elements.size()) {
buildPlayerDrawList(elements, drawList);
// Actualiza draw_list tras cambios en los z_order. Implementación simple:
// reordena draw_list según los z_order actuales. Llamar cuando cambian z_order
void Game::updatePlayerDrawList(const Players& elements, Players& draw_list) {
// Si draw_list está vacío o su tamaño no coincide, reconstruirlo.
if (draw_list.size() != elements.size()) {
buildPlayerDrawList(elements, draw_list);
return;
}
// Dado que apuntan a los mismos elementos, basta ordenar por los z_order actuales.
std::stable_sort(drawList.begin(), drawList.end(), [](const std::shared_ptr<Player>& a, const std::shared_ptr<Player>& b) {
std::ranges::stable_sort(draw_list, [](const std::shared_ptr<Player>& a, const std::shared_ptr<Player>& b) {
return a->getZOrder() < b->getZOrder();
});
}
// Dibuja en el orden definido por drawList
void Game::renderPlayerDrawList(const Players& drawList) {
for (const auto& e : drawList) e->render();
// Dibuja en el orden definido por draw_list
void Game::renderPlayerDrawList(const Players& draw_list) {
for (const auto& e : draw_list) {
e->render();
}
}
// Operaciones sobre z_order que mantienen la invariante y actualizan drawList.
// Operaciones sobre z_order que mantienen la invariante y actualizan draw_list.
auto Game::findPlayerIndex(const Players& elems, const std::shared_ptr<Player>& who) -> size_t {
for (size_t i = 0; i < elems.size(); ++i)
if (elems[i] == who) return static_cast<int>(i); // compara shared_ptr directamente
for (size_t i = 0; i < elems.size(); ++i) {
if (elems[i] == who) {
return static_cast<int>(i); // compara shared_ptr directamente
}
}
return -1;
}
void Game::sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& drawList) {
void Game::sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list) {
int idx = findPlayerIndex(elements, who);
if (idx < 0) return; // no encontrado
const int oldZ = elements[idx]->getZOrder();
if (oldZ <= 0) return;
if (idx < 0) {
return; // no encontrado
}
const int OLD_Z = elements[idx]->getZOrder();
if (OLD_Z <= 0) {
return;
}
for (auto& p : elements) {
int z = p->getZOrder();
if (z < oldZ) p->setZOrder(z + 1);
if (z < OLD_Z) {
p->setZOrder(z + 1);
}
}
elements[idx]->setZOrder(0);
updatePlayerDrawList(elements, drawList);
updatePlayerDrawList(elements, draw_list);
}
void Game::bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& drawList) {
void Game::bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list) {
int idx = findPlayerIndex(elements, who);
if (idx < 0) return; // no encontrado
const int oldZ = elements[idx]->getZOrder();
if (idx < 0) {
return; // no encontrado
}
const int OLD_Z = elements[idx]->getZOrder();
const int N = static_cast<int>(elements.size());
if (oldZ >= N - 1) return;
if (OLD_Z >= N - 1) {
return;
}
for (auto& p : elements) {
int z = p->getZOrder();
if (z > oldZ) p->setZOrder(z - 1);
if (z > OLD_Z) {
p->setZOrder(z - 1);
}
}
elements[idx]->setZOrder(N - 1);
updatePlayerDrawList(elements, drawList);
updatePlayerDrawList(elements, draw_list);
}
#ifdef _DEBUG

View File

@@ -336,12 +336,12 @@ class Game {
void playSound(const std::string& name) const; // Reproduce un efecto de sonido específico
// --- Gestion y dibujado de jugadores en z-order ---
void buildPlayerDrawList(const Players& elements, Players& drawList); // Construye el drawList a partir del vector principal
void updatePlayerDrawList(const Players& elements, Players& drawList); // Actualiza drawList tras cambios en los z_order
void renderPlayerDrawList(const Players& drawList); // Dibuja en el orden definido
static void buildPlayerDrawList(const Players& elements, Players& draw_list); // Construye el draw_list a partir del vector principal
static void updatePlayerDrawList(const Players& elements, Players& draw_list); // Actualiza draw_list tras cambios en los z_order
static void renderPlayerDrawList(const Players& draw_list); // Dibuja en el orden definido
static auto findPlayerIndex(const Players& elems, const std::shared_ptr<Player>& who) -> size_t;
void sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& drawList); // Envia al jugador al fondo de la pantalla
void bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& drawList); // Envia al jugador al frente de la pantalla
static void sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list); // Envia al jugador al fondo de la pantalla
static void bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list); // Envia al jugador al frente de la pantalla
// --- Varios ---
void onPauseStateChanged(bool is_paused);

View File

@@ -74,11 +74,11 @@ void HiScoreTable::render() {
SCREEN->start(); // Prepara para empezar a dibujar en la textura de juego
SCREEN->clean(); // Limpia la pantalla
background_->render(); // Pinta el fondo
float counter_equivalent = elapsed_time_ * 60.0F; // Convertir tiempo a equivalente frame para UI
view_area_.y = std::max(0.0F, param.game.height - counter_equivalent + 100); // Establece la ventana del backbuffer
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_); // Copia el backbuffer al renderizador
fade_->render(); // Renderiza el fade
background_->render(); // Pinta el fondo
float scroll_offset = elapsed_time_ * SCROLL_SPEED_PPS; // Calcula el desplazamiento del scroll usando velocidad en pixels/segundo
view_area_.y = std::round(std::max(0.0F, (param.game.height + 100.0F) - scroll_offset)); // Establece la ventana del backbuffer (redondeado para evitar deformaciones)
SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_); // Copia el backbuffer al renderizador
fade_->render(); // Renderiza el fade
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
}

View File

@@ -38,6 +38,7 @@ class HiScoreTable {
static constexpr float BACKGROUND_CHANGE_S = 150.0F / 60.0F; // Tiempo cambio fondo (≈2.5s)
static constexpr float ANIM_DURATION_S = 80.0F / 60.0F; // Duración animación (≈1.33s)
static constexpr float CLOUDS_SPEED = -6.0F; // Velocidad nubes (pixels/s)
static constexpr float SCROLL_SPEED_PPS = 60.0F; // Velocidad de scroll (60 pixels por segundo)
// --- Objetos y punteros ---
SDL_Renderer* renderer_; // El renderizador de la ventana

View File

@@ -49,8 +49,8 @@ Instructions::Instructions()
fade_->setMode(Fade::Mode::IN);
fade_->activate();
// Inicializa las líneas con un retraso progresivo de 50 ms
lines_ = initializeLines(256);
// Inicializa las líneas con un retraso progresivo
lines_ = initializeLines(256, LINE_START_DELAY_S);
// Rellena la textura de texto
fillTexture();
@@ -278,39 +278,53 @@ void Instructions::run() {
}
// Método para inicializar las líneas
auto Instructions::initializeLines(int height) -> std::vector<Line> {
auto Instructions::initializeLines(int height, float line_delay) -> std::vector<Line> {
std::vector<Line> lines;
for (int y = 0; y < height; y++) {
int direction = (y % 2 == 0) ? -1 : 1; // Pares a la izquierda, impares a la derecha
lines.emplace_back(y, 0.0F, direction);
float delay = y * line_delay; // Retraso progresivo basado en la línea
lines.emplace_back(y, 0.0F, direction, delay);
}
return lines;
}
// Método para mover las líneas con suavizado
auto Instructions::moveLines(std::vector<Line>& lines, int width, float duration, Uint32 start_delay) -> bool {
Uint32 current_time = SDL_GetTicks();
// Método para mover las líneas con suavizado (usando delta_time puro)
auto Instructions::moveLines(std::vector<Line>& lines, int width, float duration, float delta_time) -> bool {
bool all_lines_off_screen = true;
for (auto& line : lines) {
// Establecer start_time en el primer cuadro de animación
if (line.start_time == 0) {
line.start_time = current_time + line.y * start_delay;
// Verificar si la línea ha superado su tiempo de retraso
if (!line.started) {
line.delay_time -= delta_time;
if (line.delay_time <= 0.0F) {
line.started = true;
line.accumulated_time = 0.0F;
} else {
all_lines_off_screen = false; // Aún hay líneas esperando para empezar
continue;
}
}
float elapsed_time = (current_time - line.start_time) / 1000.0F; // Convertir a segundos
if (elapsed_time < 0) {
all_lines_off_screen = false; // Si aún no se debe mover esta línea, no están todas fuera de pantalla
continue;
}
if (elapsed_time >= duration) {
continue; // Si la línea ha salido de los límites, no la muevas más
// Si la línea ya ha completado su movimiento, saltarla
if (line.accumulated_time >= duration) {
continue; // Esta línea ya terminó
}
float t = elapsed_time / duration;
// Acumular tiempo y calcular posición
line.accumulated_time += delta_time;
if (line.accumulated_time >= duration) {
// La línea ha completado su movimiento
line.accumulated_time = duration;
} else {
// La línea aún se está moviendo
all_lines_off_screen = false;
}
// Calcular posición con suavizado
float t = line.accumulated_time / duration;
float smooth_factor = easeInOutQuint(t);
line.x = line.direction * smooth_factor * width;
all_lines_off_screen = false; // Si alguna línea aún se está moviendo, no están todas fuera de pantalla
}
return all_lines_off_screen;
@@ -327,12 +341,14 @@ void Instructions::renderLines(SDL_Renderer* renderer, SDL_Texture* texture, con
// Gestiona la textura con los graficos
void Instructions::updateBackbuffer(float delta_time) {
// Establece la ventana del backbuffer (convertir elapsed_time_ a equivalente de counter)
float counter_equivalent = elapsed_time_ * 60.0F; // Convertir segundos a equivalente frame para UI
view_.y = std::max(0.0F, param.game.height - counter_equivalent + 100);
// Establece la ventana del backbuffer usando velocidad en pixels por segundo
// El scroll comienza desde (param.game.height + 100) y desciende a 0
// IMPORTANTE: Se redondea a entero para evitar deformaciones de textura causadas por sub-pixel rendering
float scroll_offset = elapsed_time_ * SCROLL_SPEED_PPS;
view_.y = std::round(std::max(0.0F, (param.game.height + 100.0F) - scroll_offset));
// Verifica si view_.y == 0 y gestiona el temporizador
if (view_.y == 0) {
if (view_.y == 0.0F) {
if (!start_delay_triggered_) {
// Activa el temporizador si no ha sido activado
start_delay_triggered_ = true;
@@ -341,7 +357,7 @@ void Instructions::updateBackbuffer(float delta_time) {
start_delay_timer_ += delta_time;
if (start_delay_timer_ >= START_DELAY_S) {
// Han pasado los segundos de retraso, mover líneas
all_lines_off_screen_ = moveLines(lines_, 320, LINE_MOVE_DURATION_S, static_cast<Uint32>(LINE_START_DELAY_MS));
all_lines_off_screen_ = moveLines(lines_, 320, LINE_MOVE_DURATION_S, delta_time);
}
}
}

View File

@@ -25,17 +25,20 @@ class TiledBG;
*/
// --- Estructuras ---
struct Line { // Almacena información de línea animada
int y; // Coordenada Y de la línea
float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado)
int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha
Uint32 start_time{0}; // Tiempo de inicio del movimiento
struct Line { // Almacena información de línea animada
int y; // Coordenada Y de la línea
float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado)
int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha
float accumulated_time{0}; // Tiempo acumulado desde que empezó la animación (segundos)
float delay_time{0}; // Tiempo de retraso antes de comenzar la animación (segundos)
bool started{false}; // Indica si la línea ha comenzado a moverse
// Constructor de Line
Line(int y, float x, int direction)
Line(int y, float x, int direction, float delay)
: y(y),
x(x),
direction(direction) {}
direction(direction),
delay_time(delay) {}
};
// Clase Instructions
@@ -53,7 +56,8 @@ class Instructions {
static constexpr float SPRITE_ANIMATION_CYCLE_S = 36.0F / 60.0F; // Ciclo de animación sprites (≈0.6s)
static constexpr float START_DELAY_S = 4.0F; // Retraso antes de mover líneas (4s)
static constexpr float LINE_MOVE_DURATION_S = 1.0F; // Duración movimiento líneas (1s)
static constexpr float LINE_START_DELAY_MS = 5.0F; // Retraso entre líneas (5ms)
static constexpr float LINE_START_DELAY_S = 0.005F; // Retraso entre líneas (5ms = 0.005s)
static constexpr float SCROLL_SPEED_PPS = 60.0F; // Velocidad de scroll (60 pixels por segundo)
// --- Objetos y punteros ---
SDL_Renderer* renderer_; // El renderizador de la ventana
@@ -86,9 +90,9 @@ class Instructions {
void fillBackbuffer(); // Rellena el backbuffer
void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
static auto moveLines(std::vector<Line>& lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas (ya usa tiempo real)
static void renderLines(SDL_Renderer* renderer, SDL_Texture* texture, const std::vector<Line>& lines); // Renderiza las líneas
static auto initializeLines(int height, float line_delay) -> std::vector<Line>; // Inicializa las líneas animadas
static auto moveLines(std::vector<Line>& lines, int width, float duration, float delta_time) -> bool; // Mueve las líneas usando delta_time puro
static void renderLines(SDL_Renderer* renderer, SDL_Texture* texture, const std::vector<Line>& lines); // Renderiza las líneas
void updateBackbuffer(float delta_time); // Gestiona la textura con los gráficos
auto calculateDeltaTime() -> float; // Calcula el tiempo transcurrido desde el último frame
};

View File

@@ -285,6 +285,30 @@ void Text::writeCentered(int x, int y, const std::string& text, int kerning, int
write(x, y, text, kerning, length);
}
// Renderiza sombra del texto
void Text::renderShadow(int x, int y, const std::string& text, Color shadow_color, int kerning, int length, Uint8 shadow_distance) {
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);
}
}
// Renderiza stroke sólido (método tradicional para stroke sin alpha)
void Text::renderSolidStroke(int x, int y, const std::string& text, Color stroke_color, int kerning, int length, Uint8 shadow_distance) {
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, stroke_color, kerning, length);
} else {
writeColored(x + dx, y + dy, text, stroke_color, kerning, length);
}
}
}
}
}
// Escribe texto con extras
void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Color text_color, Uint8 shadow_distance, Color shadow_color, int length) {
const auto CENTERED = ((flags & Text::CENTER) == Text::CENTER);
@@ -297,11 +321,7 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
}
if (SHADOWED) {
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);
}
renderShadow(x, y, text, shadow_color, kerning, length, shadow_distance);
}
if (STROKED) {
@@ -310,17 +330,7 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni
writeStrokeWithAlpha(x, y, text, kerning, shadow_color, shadow_distance, 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);
}
}
}
}
renderSolidStroke(x, y, text, shadow_color, kerning, length, shadow_distance);
}
}

View File

@@ -86,6 +86,8 @@ class Text {
// --- 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
void renderShadow(int x, int y, const std::string& text, Color shadow_color, int kerning, int length, Uint8 shadow_distance); // Renderiza sombra del texto
void renderSolidStroke(int x, int y, const std::string& text, Color stroke_color, int kerning, int length, Uint8 shadow_distance); // Renderiza stroke sólido
private:
// --- Objetos y punteros ---

View File

@@ -51,7 +51,7 @@ void UIMessage::updateAnimation(float delta_time) {
float t = animation_timer_ / ANIMATION_DURATION_S;
// Clamp t entre 0 y 1
t = std::min(t, 1.0f);
t = std::min(t, 1.0F);
if (target_y_ > start_y_) {
// Animación de entrada (ease out cubic)

View File

@@ -3,7 +3,7 @@
# Variables
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Os -I../
CXXFLAGS := -std=c++20 -Wall -Os -I../
SOURCES := pack_resources.cpp ../source/resource_pack.cpp
TARGET := pack_resources
CLEAN_FILES := pack_resources *.pack *.o

Binary file not shown.

View File

@@ -1,4 +1,4 @@
#include "../source/resource_pack.h"
#include "../source/resource_pack.hpp"
#include "../build/version.h" // Para Version::APP_NAME
#include <iostream>
#include <filesystem>