Compare commits

22 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
44 changed files with 934 additions and 745 deletions

7
.gitignore vendored
View File

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

View File

@@ -150,7 +150,7 @@ else
endif endif
# Reglas para herramienta de empaquetado y resources.pack # Reglas para herramienta de empaquetado y resources.pack
$(PACK_TOOL): $(PACK_SOURCES) $(PACK_TOOL): FORCE
@echo "Compilando herramienta de empaquetado..." @echo "Compilando herramienta de empaquetado..."
$(PACK_CXX) -std=c++20 -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)" @echo "✓ Herramienta de empaquetado lista: $(PACK_TOOL)"
@@ -180,7 +180,9 @@ windows_debug:
@echo Compilando version debug para Windows: "$(APP_NAME)_debug.exe" @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" $(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 off
@echo Creando release para Windows - Version: $(VERSION) @echo Creando release para Windows - Version: $(VERSION)
@@ -199,7 +201,7 @@ windows_release: resources.pack
# Compila # Compila
windres release/coffee.rc -O coff -o $(RESOURCE_FILE) 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 strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
# Crea el fichero .zip # Crea el fichero .zip
@@ -218,8 +220,14 @@ macos_debug:
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug" @echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)" @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 # Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
$(RMDIR) Frameworks $(RMDIR) Frameworks
@@ -243,33 +251,52 @@ macos_release: resources.pack
cp LICENSE "$(RELEASE_FOLDER)" cp LICENSE "$(RELEASE_FOLDER)"
cp README.md "$(RELEASE_FOLDER)" cp README.md "$(RELEASE_FOLDER)"
# Crea enlaces
ln -s /Applications "$(RELEASE_FOLDER)"/Applications
# Compila la versión para procesadores Intel # Compila la versión para procesadores Intel
ifdef ENABLE_MACOS_X86_64 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 # Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app" codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
# Empaqueta el .dmg de la versión Intel # Empaqueta el .dmg de la versión Intel con create-dmg
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)" @echo "Creando DMG Intel con iconos de 96x96..."
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)" create-dmg \
$(RMFILE) tmp.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)" @echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
endif endif
# Compila la versión para procesadores Apple Silicon # 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 # Firma la aplicación
codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app" codesign --deep --force --sign - --timestamp=none "$(RELEASE_FOLDER)/$(APP_NAME).app"
# Empaqueta el .dmg de la versión Apple Silicon # Empaqueta el .dmg de la versión Apple Silicon con create-dmg
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)" @echo "Creando DMG Apple Silicon con iconos de 96x96..."
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)" create-dmg \
$(RMFILE) tmp.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)" @echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
# Elimina las carpetas temporales # Elimina las carpetas temporales
@@ -285,7 +312,9 @@ linux_debug:
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug" @echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)" @echo "Creando release para Linux - Version: $(VERSION)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -300,7 +329,7 @@ linux_release: resources.pack
cp README.md "$(RELEASE_FOLDER)" cp README.md "$(RELEASE_FOLDER)"
# Compila # 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 strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded
# Empaqueta ficheros # Empaqueta ficheros
@@ -311,7 +340,9 @@ linux_release: resources.pack
# Elimina la carpeta temporal # Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)" $(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)" @echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -330,7 +361,7 @@ linux_release_desktop: resources.pack
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/" cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
# Compila el ejecutable # 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 strip -s -R .comment -R .gnu.version "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)" --strip-unneeded
# Crea el archivo .desktop # Crea el archivo .desktop
@@ -416,7 +447,9 @@ raspi_debug:
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug" @echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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)" @echo "Creando release para Raspberry Pi - Version: $(VERSION)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -431,7 +464,7 @@ raspi_release: resources.pack
cp README.md "$(RELEASE_FOLDER)" cp README.md "$(RELEASE_FOLDER)"
# Compila # 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 strip -s -R .comment -R .gnu.version "$(RELEASE_FILE)" --strip-unneeded
# Empaqueta ficheros # Empaqueta ficheros
@@ -442,7 +475,9 @@ raspi_release: resources.pack
# Elimina la carpeta temporal # Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
anbernic: resources.pack anbernic:
@$(MAKE) pack_tool
@$(MAKE) resources.pack
@echo "Compilando para Anbernic: $(TARGET_NAME)" @echo "Compilando para Anbernic: $(TARGET_NAME)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic $(RMDIR) "$(RELEASE_FOLDER)"_anbernic
@@ -455,7 +490,7 @@ anbernic: resources.pack
cp resources.pack "$(RELEASE_FOLDER)"_anbernic cp resources.pack "$(RELEASE_FOLDER)"_anbernic
# Compila # Compila
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME) $(CXX) $(APP_SOURCES) $(INCLUDES) -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) # Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake)
no_audio: no_audio:
@@ -489,4 +524,6 @@ help:
@echo " show_version - Mostrar version actual ($(VERSION))" @echo " show_version - Mostrar version actual ($(VERSION))"
@echo " help - Mostrar esta ayuda" @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 DATA|${PREFIX}/config/stages.txt
# Archivos con los datos de la demo # Archivos con los datos de la demo
DEMODATA|${PREFIX}/data/demo/demo1.bin DEMODATA|/data/demo/demo1.bin
DEMODATA|${PREFIX}/data/demo/demo2.bin DEMODATA|/data/demo/demo2.bin
DEMODATA|${PREFIX}/data/demo/demo3.bin DEMODATA|/data/demo/demo3.bin
# Música # Música
MUSIC|${PREFIX}/data/music/congratulations.ogg MUSIC|/data/music/congratulations.ogg
MUSIC|${PREFIX}/data/music/credits.ogg MUSIC|/data/music/credits.ogg
MUSIC|${PREFIX}/data/music/intro.ogg MUSIC|/data/music/intro.ogg
MUSIC|${PREFIX}/data/music/playing.ogg MUSIC|/data/music/playing.ogg
MUSIC|${PREFIX}/data/music/title.ogg MUSIC|/data/music/title.ogg
# Sonidos # Sonidos
SOUND|${PREFIX}/data/sound/balloon_bounce0.wav SOUND|/data/sound/balloon_bounce0.wav
SOUND|${PREFIX}/data/sound/balloon_bounce1.wav SOUND|/data/sound/balloon_bounce1.wav
SOUND|${PREFIX}/data/sound/balloon_bounce2.wav SOUND|/data/sound/balloon_bounce2.wav
SOUND|${PREFIX}/data/sound/balloon_bounce3.wav SOUND|/data/sound/balloon_bounce3.wav
SOUND|${PREFIX}/data/sound/balloon_pop0.wav SOUND|/data/sound/balloon_pop0.wav
SOUND|${PREFIX}/data/sound/balloon_pop1.wav SOUND|/data/sound/balloon_pop1.wav
SOUND|${PREFIX}/data/sound/balloon_pop2.wav SOUND|/data/sound/balloon_pop2.wav
SOUND|${PREFIX}/data/sound/balloon_pop3.wav SOUND|/data/sound/balloon_pop3.wav
SOUND|${PREFIX}/data/sound/bullet1p.wav SOUND|/data/sound/bullet1p.wav
SOUND|${PREFIX}/data/sound/bullet2p.wav SOUND|/data/sound/bullet2p.wav
SOUND|${PREFIX}/data/sound/clock.wav SOUND|/data/sound/clock.wav
SOUND|${PREFIX}/data/sound/coffee_out.wav SOUND|/data/sound/coffee_out.wav
SOUND|${PREFIX}/data/sound/continue_clock.wav SOUND|/data/sound/continue_clock.wav
SOUND|${PREFIX}/data/sound/credit.wav SOUND|/data/sound/credit.wav
SOUND|${PREFIX}/data/sound/debian_drop.wav SOUND|/data/sound/debian_drop.wav
SOUND|${PREFIX}/data/sound/debian_pickup.wav SOUND|/data/sound/debian_pickup.wav
SOUND|${PREFIX}/data/sound/hi_score_achieved.wav SOUND|/data/sound/hi_score_achieved.wav
SOUND|${PREFIX}/data/sound/item_drop.wav SOUND|/data/sound/item_drop.wav
SOUND|${PREFIX}/data/sound/item_pickup.wav SOUND|/data/sound/item_pickup.wav
SOUND|${PREFIX}/data/sound/jump.wav SOUND|/data/sound/jump.wav
SOUND|${PREFIX}/data/sound/logo.wav SOUND|/data/sound/logo.wav
SOUND|${PREFIX}/data/sound/name_input_accept.wav SOUND|/data/sound/name_input_accept.wav
SOUND|${PREFIX}/data/sound/notify.wav SOUND|/data/sound/notify.wav
SOUND|${PREFIX}/data/sound/player_collision.wav SOUND|/data/sound/player_collision.wav
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav SOUND|/data/sound/power_ball_explosion.wav
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav SOUND|/data/sound/service_menu_adjust.wav
SOUND|${PREFIX}/data/sound/service_menu_back.wav SOUND|/data/sound/service_menu_back.wav
SOUND|${PREFIX}/data/sound/service_menu_move.wav SOUND|/data/sound/service_menu_move.wav
SOUND|${PREFIX}/data/sound/service_menu_select.wav SOUND|/data/sound/service_menu_select.wav
SOUND|${PREFIX}/data/sound/stage_change.wav SOUND|/data/sound/stage_change.wav
SOUND|${PREFIX}/data/sound/tabe_hit.wav SOUND|/data/sound/tabe_hit.wav
SOUND|${PREFIX}/data/sound/tabe.wav SOUND|/data/sound/tabe.wav
SOUND|${PREFIX}/data/sound/title.wav SOUND|/data/sound/title.wav
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav SOUND|/data/sound/voice_aw_aw_aw.wav
SOUND|${PREFIX}/data/sound/voice_coffee.wav SOUND|/data/sound/voice_coffee.wav
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav SOUND|/data/sound/voice_credit_thankyou.wav
SOUND|${PREFIX}/data/sound/voice_game_over.wav SOUND|/data/sound/voice_game_over.wav
SOUND|${PREFIX}/data/sound/voice_get_ready.wav SOUND|/data/sound/voice_get_ready.wav
SOUND|${PREFIX}/data/sound/voice_no.wav SOUND|/data/sound/voice_no.wav
SOUND|${PREFIX}/data/sound/voice_power_up.wav SOUND|/data/sound/voice_power_up.wav
SOUND|${PREFIX}/data/sound/voice_recover.wav SOUND|/data/sound/voice_recover.wav
SOUND|${PREFIX}/data/sound/voice_thankyou.wav SOUND|/data/sound/voice_thankyou.wav
SOUND|${PREFIX}/data/sound/walk.wav SOUND|/data/sound/walk.wav
# Shaders OpenGL Desktop 3.3 (Windows/Linux) # Shaders OpenGL Desktop 3.3 (Windows/Linux)
DATA|${PREFIX}/data/shaders/crtpi_vertex.glsl DATA|/data/shaders/crtpi_vertex.glsl
DATA|${PREFIX}/data/shaders/crtpi_fragment.glsl DATA|/data/shaders/crtpi_fragment.glsl
# Shaders OpenGL ES 3.0 (Raspberry Pi) - opcionales # Shaders OpenGL ES 3.0 (Raspberry Pi) - opcionales
DATA|${PREFIX}/data/shaders/crtpi_vertex_es.glsl|optional DATA|/data/shaders/crtpi_vertex_es.glsl|optional
DATA|${PREFIX}/data/shaders/crtpi_fragment_es.glsl|optional DATA|/data/shaders/crtpi_fragment_es.glsl|optional
# Texturas - Balloons # Texturas - Balloons
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani ANIMATION|/data/gfx/balloon/balloon0.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani ANIMATION|/data/gfx/balloon/balloon1.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon2.ani ANIMATION|/data/gfx/balloon/balloon2.ani
ANIMATION|${PREFIX}/data/gfx/balloon/balloon3.ani ANIMATION|/data/gfx/balloon/balloon3.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png BITMAP|/data/gfx/balloon/balloon0.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png BITMAP|/data/gfx/balloon/balloon1.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png BITMAP|/data/gfx/balloon/balloon2.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png BITMAP|/data/gfx/balloon/balloon3.png
# Texturas - Explosiones # Texturas - Explosiones
ANIMATION|${PREFIX}/data/gfx/balloon/explosion0.ani ANIMATION|/data/gfx/balloon/explosion0.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion1.ani ANIMATION|/data/gfx/balloon/explosion1.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion2.ani ANIMATION|/data/gfx/balloon/explosion2.ani
ANIMATION|${PREFIX}/data/gfx/balloon/explosion3.ani ANIMATION|/data/gfx/balloon/explosion3.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png BITMAP|/data/gfx/balloon/explosion0.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png BITMAP|/data/gfx/balloon/explosion1.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png BITMAP|/data/gfx/balloon/explosion2.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png BITMAP|/data/gfx/balloon/explosion3.png
# Texturas - Power Ball # Texturas - Power Ball
ANIMATION|${PREFIX}/data/gfx/balloon/powerball.ani ANIMATION|/data/gfx/balloon/powerball.ani
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png BITMAP|/data/gfx/balloon/powerball.png
# Texturas - Bala # Texturas - Bala
ANIMATION|${PREFIX}/data/gfx/bullet/bullet.ani ANIMATION|/data/gfx/bullet/bullet.ani
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png BITMAP|/data/gfx/bullet/bullet.png
# Texturas - Tabe # Texturas - Tabe
ANIMATION|${PREFIX}/data/gfx/tabe/tabe.ani ANIMATION|/data/gfx/tabe/tabe.ani
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png BITMAP|/data/gfx/tabe/tabe.png
# Texturas - Juego # Texturas - Juego
BITMAP|${PREFIX}/data/gfx/game/game_buildings.png BITMAP|/data/gfx/game/game_buildings.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds1.png BITMAP|/data/gfx/game/game_clouds1.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds2.png BITMAP|/data/gfx/game/game_clouds2.png
ANIMATION|${PREFIX}/data/gfx/game/game_grass.ani ANIMATION|/data/gfx/game/game_grass.ani
BITMAP|${PREFIX}/data/gfx/game/game_grass.png BITMAP|/data/gfx/game/game_grass.png
BITMAP|${PREFIX}/data/gfx/game/game_moon.png BITMAP|/data/gfx/game/game_moon.png
BITMAP|${PREFIX}/data/gfx/game/game_power_meter.png BITMAP|/data/gfx/game/game_power_meter.png
BITMAP|${PREFIX}/data/gfx/game/game_sky_colors.png BITMAP|/data/gfx/game/game_sky_colors.png
BITMAP|${PREFIX}/data/gfx/game/game_sun.png BITMAP|/data/gfx/game/game_sun.png
# Texturas - Intro # Texturas - Intro
BITMAP|${PREFIX}/data/gfx/intro/intro1.png BITMAP|/data/gfx/intro/intro1.png
BITMAP|${PREFIX}/data/gfx/intro/intro2.png BITMAP|/data/gfx/intro/intro2.png
BITMAP|${PREFIX}/data/gfx/intro/intro3.png BITMAP|/data/gfx/intro/intro3.png
BITMAP|${PREFIX}/data/gfx/intro/intro4.png BITMAP|/data/gfx/intro/intro4.png
BITMAP|${PREFIX}/data/gfx/intro/intro5.png BITMAP|/data/gfx/intro/intro5.png
BITMAP|${PREFIX}/data/gfx/intro/intro6.png BITMAP|/data/gfx/intro/intro6.png
# Texturas - Logo # Texturas - Logo
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames_mini.png BITMAP|/data/gfx/logo/logo_jailgames_mini.png
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames.png BITMAP|/data/gfx/logo/logo_jailgames.png
BITMAP|${PREFIX}/data/gfx/logo/logo_since_1998.png BITMAP|/data/gfx/logo/logo_since_1998.png
# Texturas - Items # Texturas - Items
ANIMATION|${PREFIX}/data/gfx/item/item_clock.ani ANIMATION|/data/gfx/item/item_clock.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee_machine.ani ANIMATION|/data/gfx/item/item_coffee_machine.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee.ani ANIMATION|/data/gfx/item/item_coffee.ani
ANIMATION|${PREFIX}/data/gfx/item/item_debian.ani ANIMATION|/data/gfx/item/item_debian.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points1_disk.ani ANIMATION|/data/gfx/item/item_points1_disk.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points2_gavina.ani ANIMATION|/data/gfx/item/item_points2_gavina.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points3_pacmar.ani ANIMATION|/data/gfx/item/item_points3_pacmar.ani
BITMAP|${PREFIX}/data/gfx/item/item_clock.png BITMAP|/data/gfx/item/item_clock.png
BITMAP|${PREFIX}/data/gfx/item/item_coffee_machine.png BITMAP|/data/gfx/item/item_coffee_machine.png
BITMAP|${PREFIX}/data/gfx/item/item_coffee.png BITMAP|/data/gfx/item/item_coffee.png
BITMAP|${PREFIX}/data/gfx/item/item_debian.png BITMAP|/data/gfx/item/item_debian.png
BITMAP|${PREFIX}/data/gfx/item/item_points1_disk.png BITMAP|/data/gfx/item/item_points1_disk.png
BITMAP|${PREFIX}/data/gfx/item/item_points2_gavina.png BITMAP|/data/gfx/item/item_points2_gavina.png
BITMAP|${PREFIX}/data/gfx/item/item_points3_pacmar.png BITMAP|/data/gfx/item/item_points3_pacmar.png
# Texturas - Titulo # Texturas - Titulo
ANIMATION|${PREFIX}/data/gfx/title/title_dust.ani ANIMATION|/data/gfx/title/title_dust.ani
BITMAP|${PREFIX}/data/gfx/title/title_arcade_edition.png BITMAP|/data/gfx/title/title_arcade_edition.png
BITMAP|${PREFIX}/data/gfx/title/title_bg_tile.png BITMAP|/data/gfx/title/title_bg_tile.png
BITMAP|${PREFIX}/data/gfx/title/title_coffee.png BITMAP|/data/gfx/title/title_coffee.png
BITMAP|${PREFIX}/data/gfx/title/title_crisis.png BITMAP|/data/gfx/title/title_crisis.png
BITMAP|${PREFIX}/data/gfx/title/title_dust.png BITMAP|/data/gfx/title/title_dust.png
# Texturas - Jugador 1 # Texturas - Jugador 1
BITMAP|${PREFIX}/data/gfx/player/player1_power.png BITMAP|/data/gfx/player/player1_power.png
BITMAP|${PREFIX}/data/gfx/player/player1.gif BITMAP|/data/gfx/player/player1.gif
PALETTE|${PREFIX}/data/gfx/player/player1_coffee1.pal PALETTE|/data/gfx/player/player1_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player1_coffee2.pal PALETTE|/data/gfx/player/player1_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player1_invencible.pal PALETTE|/data/gfx/player/player1_invencible.pal
# Texturas - Jugador 2 # Texturas - Jugador 2
BITMAP|${PREFIX}/data/gfx/player/player2_power.png BITMAP|/data/gfx/player/player2_power.png
BITMAP|${PREFIX}/data/gfx/player/player2.gif BITMAP|/data/gfx/player/player2.gif
PALETTE|${PREFIX}/data/gfx/player/player2_coffee1.pal PALETTE|/data/gfx/player/player2_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player2_coffee2.pal PALETTE|/data/gfx/player/player2_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal PALETTE|/data/gfx/player/player2_invencible.pal
# Animaciones del jugador # Animaciones del jugador
ANIMATION|${PREFIX}/data/gfx/player/player_power.ani ANIMATION|/data/gfx/player/player_power.ani
ANIMATION|${PREFIX}/data/gfx/player/player1.ani ANIMATION|/data/gfx/player/player1.ani
ANIMATION|${PREFIX}/data/gfx/player/player2.ani ANIMATION|/data/gfx/player/player2.ani
# Fuentes de texto # Fuentes de texto
BITMAP|${PREFIX}/data/font/04b_25_2x.png BITMAP|/data/font/04b_25_2x.png
BITMAP|${PREFIX}/data/font/04b_25_2x_white.png BITMAP|/data/font/04b_25_2x_white.png
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png BITMAP|/data/font/04b_25_flat_2x.png
BITMAP|${PREFIX}/data/font/04b_25_flat.png BITMAP|/data/font/04b_25_flat.png
BITMAP|${PREFIX}/data/font/04b_25_grey.png BITMAP|/data/font/04b_25_grey.png
BITMAP|${PREFIX}/data/font/04b_25_metal.png BITMAP|/data/font/04b_25_metal.png
BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png BITMAP|/data/font/04b_25_reversed_2x.png
BITMAP|${PREFIX}/data/font/04b_25_reversed.png BITMAP|/data/font/04b_25_reversed.png
BITMAP|${PREFIX}/data/font/04b_25_white.png BITMAP|/data/font/04b_25_white.png
BITMAP|${PREFIX}/data/font/04b_25.png BITMAP|/data/font/04b_25.png
BITMAP|${PREFIX}/data/font/8bithud.png BITMAP|/data/font/8bithud.png
BITMAP|${PREFIX}/data/font/aseprite.png BITMAP|/data/font/aseprite.png
BITMAP|${PREFIX}/data/font/smb2_grad.png BITMAP|/data/font/smb2_grad.png
BITMAP|${PREFIX}/data/font/smb2.png BITMAP|/data/font/smb2.png
FONT|${PREFIX}/data/font/04b_25_2x.txt FONT|/data/font/04b_25_2x.txt
FONT|${PREFIX}/data/font/04b_25.txt FONT|/data/font/04b_25.txt
FONT|${PREFIX}/data/font/8bithud.txt FONT|/data/font/8bithud.txt
FONT|${PREFIX}/data/font/aseprite.txt FONT|/data/font/aseprite.txt
FONT|${PREFIX}/data/font/smb2.txt FONT|/data/font/smb2.txt
# Idiomas # Idiomas
LANG|${PREFIX}/data/lang/ba_BA.json LANG|/data/lang/ba_BA.json
LANG|${PREFIX}/data/lang/en_UK.json LANG|/data/lang/en_UK.json
LANG|${PREFIX}/data/lang/es_ES.json LANG|/data/lang/es_ES.json

View File

@@ -1,7 +1,18 @@
#!/bin/bash #!/bin/bash
# Script para ejecutar clang-tidy en múltiples directorios # 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 # Lista de rutas donde ejecutar clang-tidy
PATHS=( PATHS=(
@@ -31,8 +42,8 @@ process_directory() {
cd "$dir" || return 1 cd "$dir" || return 1
# Buscar archivos .cpp, .h, .hpp solo en el nivel actual (no subdirectorios) # Buscar archivos .cpp, .h, .hpp solo en el nivel actual (no subdirectorios)
find . -maxdepth 1 \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) | \ find . -maxdepth 1 \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) -print0 | \
xargs -P4 -I{} bash -c 'echo "Procesando: {}"; clang-tidy {} -p '"$BUILD_DIR"' --fix' xargs -0 -P4 -I{} bash -c 'echo "Procesando: {}"; clang-tidy {} -p '"$BUILD_DIR"' '"$FIX_FLAG"
echo "=== Completado: $dir ===" echo "=== Completado: $dir ==="
echo 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 // Comprueba que existe un fichero
auto Asset::checkFile(const std::string& path) const -> bool { 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"; std::string pack_path = executable_path_ + "resources.pack";
#endif
bool pack_exists = std::filesystem::exists(pack_path); bool pack_exists = std::filesystem::exists(pack_path);
if (pack_exists) { if (pack_exists) {

View File

@@ -4,7 +4,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
bool AssetIntegrated::resource_pack_enabled_ = false; bool AssetIntegrated::resource_pack_enabled = false;
void AssetIntegrated::initWithResourcePack(const std::string& executable_path, void AssetIntegrated::initWithResourcePack(const std::string& executable_path,
const std::string& resource_pack_path) { const std::string& resource_pack_path) {
@@ -14,29 +14,29 @@ void AssetIntegrated::initWithResourcePack(const std::string& executable_path,
// Inicializar ResourceLoader // Inicializar ResourceLoader
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
if (loader.initialize(resource_pack_path, true)) { if (loader.initialize(resource_pack_path, true)) {
resource_pack_enabled_ = true; resource_pack_enabled = true;
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << std::endl; std::cout << "Asset system initialized with resource pack: " << resource_pack_path << '\n';
} else { } else {
resource_pack_enabled_ = false; resource_pack_enabled = false;
std::cout << "Asset system initialized in fallback mode (filesystem)" << std::endl; std::cout << "Asset system initialized in fallback mode (filesystem)" << '\n';
} }
} }
std::vector<uint8_t> AssetIntegrated::loadFile(const std::string& filename) { auto AssetIntegrated::loadFile(const std::string& filename) -> std::vector<uint8_t> {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) { if (shouldUseResourcePack(filename) && resource_pack_enabled) {
// Intentar cargar del pack de recursos // Intentar cargar del pack de recursos
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
// Convertir ruta completa a ruta relativa para el pack // 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 // Si la ruta contiene "data/", extraer la parte relativa
size_t dataPos = filename.find("data/"); size_t data_pos = filename.find("data/");
if (dataPos != std::string::npos) { if (data_pos != std::string::npos) {
relativePath = filename.substr(dataPos + 5); // +5 para saltar "data/" 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()) { if (!data.empty()) {
return data; return data;
} }
@@ -45,34 +45,34 @@ std::vector<uint8_t> AssetIntegrated::loadFile(const std::string& filename) {
// Fallback: cargar del filesystem // Fallback: cargar del filesystem
std::ifstream file(filename, std::ios::binary | std::ios::ate); std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) { if (!file) {
std::cerr << "Error: Could not open file: " << filename << std::endl; std::cerr << "Error: Could not open file: " << filename << '\n';
return {}; return {};
} }
std::streamsize fileSize = file.tellg(); std::streamsize file_size = file.tellg();
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(fileSize); std::vector<uint8_t> data(file_size);
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) { if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "Error: Could not read file: " << filename << std::endl; std::cerr << "Error: Could not read file: " << filename << '\n';
return {}; return {};
} }
return data; return data;
} }
bool AssetIntegrated::fileExists(const std::string& filename) const { auto AssetIntegrated::fileExists(const std::string& filename) -> bool {
if (shouldUseResourcePack(filename) && resource_pack_enabled_) { if (shouldUseResourcePack(filename) && resource_pack_enabled) {
auto& loader = ResourceLoader::getInstance(); auto& loader = ResourceLoader::getInstance();
// Convertir ruta completa a ruta relativa para el pack // Convertir ruta completa a ruta relativa para el pack
std::string relativePath = filename; std::string relative_path = filename;
size_t dataPos = filename.find("data/"); size_t data_pos = filename.find("data/");
if (dataPos != std::string::npos) { if (data_pos != std::string::npos) {
relativePath = filename.substr(dataPos + 5); relative_path = filename.substr(data_pos + 5);
} }
if (loader.resourceExists(relativePath)) { if (loader.resourceExists(relative_path)) {
return true; return true;
} }
} }
@@ -81,24 +81,24 @@ bool AssetIntegrated::fileExists(const std::string& filename) const {
return std::filesystem::exists(filename); 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 // Los archivos de sistema/config siempre van al filesystem
return filename; 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: // Los archivos que NO van al pack:
// - Archivos de config/ (ahora están fuera de data/) // - Archivos de config/ (ahora están fuera de data/)
// - Archivos con absolute=true en assets.txt // - Archivos con absolute=true en assets.txt
// - Archivos de sistema (${SYSTEM_FOLDER}) // - Archivos de sistema (${SYSTEM_FOLDER})
if (filepath.find("/config/") != std::string::npos || if (filepath.find("/config/") != std::string::npos ||
filepath.find("config/") == 0) { filepath.starts_with("config/")) {
return false; return false;
} }
if (filepath.find("/data/") != std::string::npos || if (filepath.find("/data/") != std::string::npos ||
filepath.find("data/") == 0) { filepath.starts_with("data/")) {
return true; return true;
} }

View File

@@ -13,17 +13,17 @@ class AssetIntegrated : public Asset {
const std::string& resource_pack_path = "resources.pack"); const std::string& resource_pack_path = "resources.pack");
// Carga un archivo usando ResourceLoader como primera opción // 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) // 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 // 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: private:
static bool resource_pack_enabled; static bool resource_pack_enabled;
// Determina si un archivo debe cargarse del pack o del filesystem // 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 // 4. Actualiza el valor entero solo si ha cambiado lo suficiente
// Usamos std::round para un redondeo más natural. // 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_) { if (NEW_ALPHA != previous_alpha_color_texture_) {
previous_alpha_color_texture_ = new_alpha; previous_alpha_color_texture_ = NEW_ALPHA;
// SDL espera un Uint8 (0-255), así que hacemos un cast seguro. // SDL espera un Uint8 (0-255), así que hacemos un cast seguro.
SDL_SetTextureAlphaMod(color_texture_, static_cast<Uint8>(previous_alpha_color_texture_)); 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 <SDL3/SDL.h> // Para SDL_FRect
#include <functional> // Para function #include <functional> // Para function
#include <list> // Para list
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <vector> // Para vector #include <vector> // Para vector
#include <list> // Para list
#include "bullet.hpp" // for Bullet #include "bullet.hpp" // for Bullet
@@ -71,6 +71,6 @@ class BulletManager {
OutOfBoundsCallback out_of_bounds_callback_; // Callback para balas fuera de límites OutOfBoundsCallback out_of_bounds_callback_; // Callback para balas fuera de límites
// --- Métodos internos --- // --- Métodos internos ---
void processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float delta_time); // Procesa actualización individual 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 [[nodiscard]] auto isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) const -> bool; // Verifica si la bala está fuera de límites
}; };

View File

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

View File

@@ -44,7 +44,7 @@ Director::Director(int argc, std::span<char*> argv) {
Section::name = Section::Name::GAME; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG #elif _DEBUG
Section::name = Section::Name::HI_SCORE_TABLE; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME #else // NORMAL GAME
Section::name = Section::Name::LOGO; Section::name = Section::Name::LOGO;
@@ -81,11 +81,22 @@ void Director::init() {
// Configuración inicial de parametros // Configuración inicial de parametros
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
// Determinar ruta del pack según la plataforma
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
ResourceHelper::initializeResourceSystem(executable_path_ + "../Resources/resources.pack"); std::string pack_path = executable_path_ + "../Resources/resources.pack";
#else #else
ResourceHelper::initializeResourceSystem(executable_path_ + "resources.pack"); std::string pack_path = executable_path_ + "resources.pack";
#endif #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 loadAssets(); // Crea el índice de archivos
Logger::section("INIT INPUT"); Logger::section("INIT INPUT");

View File

@@ -67,7 +67,7 @@ auto EnterName::getSelectedCharacter(int offset) const -> std::string {
index += size; index += size;
} }
return std::string(1, character_list_[index]); return {1, character_list_[index]};
} }
// Devuelve el carrusel completo de caracteres centrado en el seleccionado // 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 // Mantener el estado final del fade
Uint8 post_alpha = a_; Uint8 post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
} else {
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
}
cleanBackbuffer(r_, g_, b_, post_alpha); 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); float progress = std::min(static_cast<float>(elapsed_time) / fading_duration_, 1.0F);
// Modifica la transparencia basada en el progreso // 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; a_ = (mode_ == Mode::OUT) ? current_alpha : 255 - current_alpha;
SDL_SetTextureAlphaMod(backbuffer_, a_); SDL_SetTextureAlphaMod(backbuffer_, a_);
@@ -162,10 +157,10 @@ void Fade::updateCenterFade() {
float progress = std::min(static_cast<float>(elapsed_time) / fading_duration_, 1.0F); float progress = std::min(static_cast<float>(elapsed_time) / fading_duration_, 1.0F);
// Calcula la altura de las barras // 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) { 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; rect1_.h = rect_height;
@@ -228,14 +223,14 @@ void Fade::updateRandomSquare2Fade() {
squares_to_activate = total_squares; squares_to_activate = total_squares;
} }
for (int i = 0; i < squares_to_activate; ++i) { 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 { } else {
squares_to_activate = total_squares; squares_to_activate = total_squares;
float activation_progress = static_cast<float>(elapsed_time) / activation_time; 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))); 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) { 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: // Ahora, inicializamos la modulación de alfa correctamente:
// - IN: Empieza opaco (255) y se desvanece a transparente. // - IN: Empieza opaco (255) y se desvanece a transparente.
// - OUT: Empieza transparente (0) y se desvanece a opaco. // - OUT: Empieza transparente (0) y se desvanece a opaco.
const Uint8 initial_alpha = (mode_ == Mode::IN) ? 255 : 0; const Uint8 INITIAL_ALPHA = (mode_ == Mode::IN) ? 255 : 0;
SDL_SetTextureAlphaMod(backbuffer_, initial_alpha); SDL_SetTextureAlphaMod(backbuffer_, INITIAL_ALPHA);
break; break;
} }
@@ -509,7 +504,7 @@ void Fade::activate() {
case Type::VENETIAN: { case Type::VENETIAN: {
square_.clear(); 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; const int MAX = param.game.height / param.fade.venetian_size;
for (int i = 0; i < MAX; ++i) { for (int i = 0; i < MAX; ++i) {
rect1_.y = i * param.fade.venetian_size; 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 // Calcula el valor del estado del fade
auto Fade::calculateValue(int min, int max, int current) -> int { auto Fade::calculateValue(int min, int max, int current) -> int {
if (current <= min) return 0; if (current <= min) {
if (current >= max) return 100; return 0;
}
if (current >= max) {
return 100;
}
return static_cast<int>(100.0 * (current - min) / (max - min)); 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 <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 <algorithm> // Para __sort_fn, sort
#include <array> // Para array
#include <functional> // Para identity #include <functional> // Para identity
#include <iterator> // Para distance #include <iterator> // Para distance
#include <ranges> // Para __find_if_fn, find_if #include <ranges> // Para __find_if_fn, find_if
@@ -109,117 +110,41 @@ auto ManageHiScoreTable::loadFromFile(const std::string& file_path) -> bool {
return false; return false;
} }
Table temp_table; // Tabla temporal para no corromper la actual si hay errores // Validar header (magic number + version + table size)
bool success = true; if (!validateMagicNumber(file, file_path)) {
// Validación 1: Verificar magic number "CCAE"
char magic[4];
if (SDL_ReadIO(file, magic, 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());
SDL_CloseIO(file); SDL_CloseIO(file);
clear(); clear();
return false; return false;
} }
// Validación 2: Verificar versión del formato if (!validateVersion(file, file_path)) {
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());
SDL_CloseIO(file); SDL_CloseIO(file);
clear(); clear();
return false; 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);
SDL_CloseIO(file);
clear();
return false;
}
// Validación 3: Leer y validar tamaño de la tabla
int table_size = 0; int table_size = 0;
if (SDL_ReadIO(file, &table_size, sizeof(int)) != sizeof(int)) { if (!readTableSize(file, file_path, table_size)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot read table size in %s", getFileName(file_path).c_str());
SDL_CloseIO(file); SDL_CloseIO(file);
clear(); clear();
return false; return false;
} }
if (table_size < 0 || table_size > MAX_TABLE_SIZE) { // Leer todas las entradas
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); Table temp_table;
SDL_CloseIO(file); bool success = true;
clear();
return false;
}
// Leer cada entrada con validaciones
for (int i = 0; i < table_size; ++i) { for (int i = 0; i < table_size; ++i) {
HiScoreEntry entry; HiScoreEntry entry;
if (!readEntry(file, file_path, i, entry)) {
// Validación 4: 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", i, getFileName(file_path).c_str());
success = false; success = false;
break; break;
} }
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, i, getFileName(file_path).c_str());
success = false;
break;
}
// Validación 5: 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", i, getFileName(file_path).c_str());
success = false;
break;
}
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, i, getFileName(file_path).c_str());
success = false;
break;
}
// 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", i, getFileName(file_path).c_str());
success = false;
break;
}
name_buffer[name_size] = '\0';
entry.name = std::string(name_buffer.data());
// Validación 6: 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", i, getFileName(file_path).c_str());
success = false;
break;
}
entry.one_credit_complete = (occ_value != 0);
temp_table.push_back(entry); temp_table.push_back(entry);
} }
// Validación 7: Verificar checksum // Verificar checksum
if (success) { if (success) {
unsigned int stored_checksum = 0; success = verifyChecksum(file, file_path, temp_table);
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());
success = false;
} else {
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);
success = false;
}
}
} }
SDL_CloseIO(file); SDL_CloseIO(file);
@@ -236,6 +161,104 @@ auto ManageHiScoreTable::loadFromFile(const std::string& file_path) -> bool {
return success; 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 // Calcula checksum de la tabla
auto ManageHiScoreTable::calculateChecksum(const Table& table) -> unsigned int { auto ManageHiScoreTable::calculateChecksum(const Table& table) -> unsigned int {
unsigned int checksum = 0x12345678; // Magic seed unsigned int checksum = 0x12345678; // Magic seed
@@ -250,7 +273,7 @@ auto ManageHiScoreTable::calculateChecksum(const Table& table) -> unsigned int {
} }
// Checksum de one_credit_complete // Checksum de one_credit_complete
checksum = ((checksum << 5) + checksum) + (entry.one_credit_complete ? 1u : 0u); checksum = ((checksum << 5) + checksum) + (entry.one_credit_complete ? 1U : 0U);
} }
return checksum; return checksum;
@@ -263,8 +286,8 @@ auto ManageHiScoreTable::saveToFile(const std::string& file_path) -> bool {
if (file != nullptr) { if (file != nullptr) {
// Escribe magic number "CCAE" // Escribe magic number "CCAE"
const char magic[4] = {'C', 'C', 'A', 'E'}; constexpr std::array<char, 4> MAGIC = {'C', 'C', 'A', 'E'};
SDL_WriteIO(file, magic, 4); SDL_WriteIO(file, MAGIC.data(), 4);
// Escribe versión del formato // Escribe versión del formato
int version = FILE_VERSION; int version = FILE_VERSION;

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <SDL3/SDL.h> // Para SDL_IOStream
#include <string> // Para std::string #include <string> // Para std::string
#include <vector> // Para std::vector #include <vector> // Para std::vector
@@ -45,6 +47,13 @@ class ManageHiScoreTable {
Table& table_; // Referencia a la tabla con los records Table& table_; // Referencia a la tabla con los records
// --- Métodos privados --- // --- 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 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 // Añade un recorrido
void PathSprite::addPath(Path path, bool centered) { void PathSprite::addPath(Path path, bool centered) {
PathCentered path_centered = PathCentered::NONE; PathCentered path_centered = determineCenteringType(path, centered);
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;
}
}
switch (path_centered) { switch (path_centered) {
case PathCentered::ON_X: { case PathCentered::ON_X:
// Centrar en el eje X (para paths Verticales) centerPathOnX(path, pos_.w / 2.0F);
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
break; break;
} case PathCentered::ON_Y:
case PathCentered::ON_Y: { centerPathOnY(path, pos_.h / 2.0F);
// 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
break; break;
} case PathCentered::NONE:
default:
// Sin centrado
paths_.emplace_back(std::move(path)); // Usamos std::move
break; break;
} }
paths_.emplace_back(std::move(path));
} }
// Añade un recorrido generado (en segundos) // Añade un recorrido generado (en segundos)

View File

@@ -93,4 +93,9 @@ class PathSprite : public Sprite {
// --- Métodos internos --- // --- Métodos internos ---
void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza 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

@@ -124,62 +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 // Procesa inputs para cuando está introduciendo el nombre
void Player::setInputEnteringName(Input::Action action) { void Player::setInputEnteringName(Input::Action action) {
switch (action) { switch (action) {
case Input::Action::FIRE_LEFT: // Añade una letra case Input::Action::FIRE_LEFT:
if (isShowingName()) { if (isShowingName()) {
passShowingName(); passShowingName();
} else { } else {
if (enter_name_->endCharSelected()) { handleNameCharacterAddition();
last_enter_name_ = getRecordName();
setPlayingState(Player::State::SHOWING_NAME);
playSound("name_input_accept.wav");
} else {
enter_name_->addCharacter();
playSound("service_menu_select.wav");
}
} }
break; break;
case Input::Action::FIRE_CENTER: // Borra una letra
case Input::Action::FIRE_CENTER:
if (isShowingName()) { if (isShowingName()) {
passShowingName(); passShowingName();
} else { } else {
if (!enter_name_->nameIsEmpty()) { handleNameCharacterRemoval();
enter_name_->removeLastCharacter();
playSound("service_menu_back.wav");
}
}
break;
case Input::Action::RIGHT:
if (!isShowingName() && !enter_name_->nameIsFull()) {
if (cooldown_->tryConsumeOnHeld()) {
enter_name_->incIndex();
playSound("service_menu_move.wav");
}
}
break;
case Input::Action::LEFT:
if (!isShowingName() && !enter_name_->nameIsFull()) {
if (cooldown_->tryConsumeOnHeld()) {
enter_name_->decIndex();
playSound("service_menu_move.wav");
}
} }
break; break;
case Input::Action::START: case Input::Action::START:
if (isShowingName()) { if (isShowingName()) {
passShowingName(); passShowingName();
} else { } else {
last_enter_name_ = getRecordName(); confirmNameEntry();
setPlayingState(Player::State::SHOWING_NAME);
playSound("name_input_accept.wav");
} }
break; break;
case Input::Action::RIGHT:
case Input::Action::LEFT:
handleNameSelectionMove(action);
break;
default: default:
cooldown_->onReleased(); cooldown_->onReleased();
break; break;
} }
name_entry_idle_time_accumulator_ = 0.0F; name_entry_idle_time_accumulator_ = 0.0F;
} }

View File

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

View File

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

View File

@@ -26,33 +26,16 @@
struct JA_Music_t; // lines 11-11 struct JA_Music_t; // lines 11-11
struct JA_Sound_t; // lines 12-12 struct JA_Sound_t; // lines 12-12
// Helper para cargar archivos de audio desde pack o filesystem // Helper para cargar archivos de audio desde pack o filesystem en memoria
namespace { 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); auto resource_data = ResourceHelper::loadFile(file_path);
if (!resource_data.empty()) { return AudioData{.data = std::move(resource_data), .filepath = file_path};
// Crear archivo temporal
std::string temp_dir;
#ifdef _WIN32
temp_dir = std::getenv("TEMP") ? std::getenv("TEMP") : "C:\\temp";
#else
temp_dir = "/tmp";
#endif
std::string temp_path = temp_dir + "/ccae_audio_" + std::to_string(std::hash<std::string>{}(file_path));
std::ofstream temp_file(temp_path, std::ios::binary);
if (!temp_file) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot create temp file %s", temp_path.c_str());
return file_path;
}
temp_file.write(reinterpret_cast<const char*>(resource_data.data()), resource_data.size());
temp_file.close();
// Agregar a la lista de archivos temporales para limpieza posterior
temp_files_tracker.push_back(temp_path);
return temp_path;
}
return file_path; // Usar ruta original si no está en pack
} }
} // namespace } // namespace
@@ -92,7 +75,6 @@ Resource::Resource(LoadingMode mode)
// Destructor // Destructor
Resource::~Resource() { Resource::~Resource() {
cleanupTempAudioFiles();
clear(); clear();
} }
@@ -335,8 +317,12 @@ auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND); auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
for (const auto& file : sound_list) { for (const auto& file : sound_list) {
if (getFileName(file) == name) { if (getFileName(file) == name) {
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_); auto audio_data = loadAudioData(file);
return JA_LoadSound(audio_path.c_str()); 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; 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); auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
for (const auto& file : music_list) { for (const auto& file : music_list) {
if (getFileName(file) == name) { if (getFileName(file) == name) {
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_); auto audio_data = loadAudioData(file);
return JA_LoadMusic(audio_path.c_str()); 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; return nullptr;
@@ -484,8 +474,15 @@ void Resource::loadSounds() {
for (const auto& l : list) { for (const auto& l : list) {
auto name = getFileName(l); auto name = getFileName(l);
updateLoadingProgress(name); updateLoadingProgress(name);
std::string audio_path = createTempAudioFile(l, temp_audio_files_); auto audio_data = loadAudioData(l);
sounds_.emplace_back(name, JA_LoadSound(audio_path.c_str())); 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 ]"); Logger::dots("Sound : ", name, "[ LOADED ]");
} }
} }
@@ -500,8 +497,15 @@ void Resource::loadMusics() {
for (const auto& l : list) { for (const auto& l : list) {
auto name = getFileName(l); auto name = getFileName(l);
updateLoadingProgress(name); updateLoadingProgress(name);
std::string audio_path = createTempAudioFile(l, temp_audio_files_); auto audio_data = loadAudioData(l);
musics_.emplace_back(name, JA_LoadMusic(audio_path.c_str())); 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 ]"); Logger::dots("Music : ", name, "[ LOADED ]");
} }
} }
@@ -901,18 +905,3 @@ void Resource::updateLoadingProgress(std::string name) {
void Resource::updateProgressBar() { void Resource::updateProgressBar() {
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage(); loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
} }
// Limpia archivos temporales de audio
void Resource::cleanupTempAudioFiles() {
for (const auto& temp_path : temp_audio_files_) {
try {
if (std::filesystem::exists(temp_path)) {
std::filesystem::remove(temp_path);
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Removed temp audio file: %s", temp_path.c_str());
}
} catch (const std::exception& e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to remove temp audio file %s: %s", temp_path.c_str(), e.what());
}
}
temp_audio_files_.clear();
}

View File

@@ -146,9 +146,6 @@ class Resource {
SDL_FRect loading_wired_rect_; SDL_FRect loading_wired_rect_;
SDL_FRect loading_full_rect_; SDL_FRect loading_full_rect_;
// --- Archivos temporales ---
std::vector<std::string> temp_audio_files_; // Rutas de archivos temporales de audio para limpieza
// --- Métodos internos de carga y gestión --- // --- Métodos internos de carga y gestión ---
void loadSounds(); // Carga los sonidos void loadSounds(); // Carga los sonidos
void loadMusics(); // Carga las músicas void loadMusics(); // Carga las músicas
@@ -167,7 +164,6 @@ class Resource {
void load(); // Carga todos los recursos void load(); // Carga todos los recursos
void clearSounds(); // Vacía el vector de sonidos void clearSounds(); // Vacía el vector de sonidos
void clearMusics(); // Vacía el vector de músicas void clearMusics(); // Vacía el vector de músicas
void cleanupTempAudioFiles(); // Limpia archivos temporales de audio
// --- Métodos para carga perezosa --- // --- Métodos para carga perezosa ---
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido

View File

@@ -10,9 +10,9 @@
namespace ResourceHelper { namespace ResourceHelper {
static bool resource_system_initialized = false; 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(); 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) { if (resource_system_initialized) {
std::cout << "Resource system initialized with pack: " << pack_file << '\n'; 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 // Helper functions para integrar ResourceLoader con el sistema existente
namespace ResourceHelper { namespace ResourceHelper {
// Inicializa ResourceLoader (llamar al inicio del programa) // 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 // Cierra ResourceLoader
void shutdownResourceSystem(); void shutdownResourceSystem();

View File

@@ -24,22 +24,22 @@ ResourceLoader::~ResourceLoader() {
shutdown(); 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(); shutdown();
fallback_to_files_ = enable_fallback; 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(); resource_pack_ = new ResourcePack();
if (resource_pack_->loadPack(packFile)) { if (resource_pack_->loadPack(pack_file)) {
std::cout << "Resource pack loaded successfully: " << packFile << '\n'; std::cout << "Resource pack loaded successfully: " << pack_file << '\n';
std::cout << "Resources available: " << resource_pack_->getResourceCount() << '\n'; std::cout << "Resources available: " << resource_pack_->getResourceCount() << '\n';
return true; return true;
} }
delete resource_pack_; delete resource_pack_;
resource_pack_ = nullptr; 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_) { if (fallback_to_files_) {
@@ -47,7 +47,7 @@ auto ResourceLoader::initialize(const std::string& packFile, bool enable_fallbac
return true; 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; return false;
} }

View File

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

View File

@@ -1,6 +1,7 @@
#include "resource_pack.hpp" #include "resource_pack.hpp"
#include <algorithm> // Para replace #include <algorithm> // Para replace
#include <array> // Para array
#include <filesystem> // Para path, recursive_directory_iterator, directory_entry, exists, relative #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 <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 #include <iostream> // Para cerr
@@ -44,9 +45,9 @@ auto ResourcePack::loadPack(const std::string& pack_file) -> bool {
return false; return false;
} }
char header[4]; std::array<char, 4> header;
file.read(header, 4); file.read(header.data(), 4);
if (std::string(header, 4) != "CCAE") { if (std::string(header.data(), 4) != "CCAE") {
std::cerr << "Error: Invalid pack file format" << '\n'; std::cerr << "Error: Invalid pack file format" << '\n';
return false; return false;
} }

View File

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

View File

@@ -107,27 +107,30 @@ void Scoreboard::setCarouselAnimation(Id id, int selected_index, EnterName* ente
// ===== Inicialización (primera vez) ===== // ===== Inicialización (primera vez) =====
if (carousel_prev_index_.at(idx) == -1) { if (carousel_prev_index_.at(idx) == -1) {
carousel_position_.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_target_.at(idx) = static_cast<float>(selected_index);
carousel_prev_index_.at(idx) = selected_index; carousel_prev_index_.at(idx) = selected_index;
return; return;
} }
int prev_index = carousel_prev_index_.at(idx); int prev_index = carousel_prev_index_.at(idx);
if (selected_index == prev_index) { if (selected_index == prev_index) {
return; // nada que hacer return; // nada que hacer
} }
// ===== Bloquear si aún animando ===== // ===== 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; return;
} }
// ===== Calcular salto circular ===== // ===== Calcular salto circular =====
int delta = selected_index - prev_index; int delta = selected_index - prev_index;
const int LIST_SIZE = static_cast<int>(enter_name_ptr->getCharacterList().size()); const int LIST_SIZE = static_cast<int>(enter_name_ptr->getCharacterList().size());
if (delta > LIST_SIZE / 2) delta -= LIST_SIZE; if (delta > LIST_SIZE / 2) {
else if (delta < -LIST_SIZE / 2) delta += LIST_SIZE; delta -= LIST_SIZE;
} else if (delta < -LIST_SIZE / 2) {
delta += LIST_SIZE;
}
// ===== Alinear posición actual antes de moverse ===== // ===== Alinear posición actual antes de moverse =====
carousel_position_.at(idx) = std::round(carousel_position_.at(idx)); 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 { } else {
// Movimiento largo → animado pero limitado en tiempo // Movimiento largo → animado pero limitado en tiempo
// Normalizamos el salto para que visualmente tarde como mucho el doble // 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 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 SPEED_SCALE = std::min(1.0F, MAX_DURATION_FACTOR / static_cast<float>(ABS_DELTA));
// Guardamos el destino real // Guardamos el destino real
float target = std::round(carousel_position_.at(idx)) + static_cast<float>(delta); float target = std::round(carousel_position_.at(idx)) + static_cast<float>(delta);
@@ -257,7 +260,7 @@ void Scoreboard::updateCarouselAnimation(float delta_time) {
} else { } else {
// Forzar al target exacto cuando estamos muy cerca // Forzar al target exacto cuando estamos muy cerca
carousel_position_.at(i) = carousel_target_.at(i); 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
} }
} }
} }
@@ -302,7 +305,7 @@ void Scoreboard::updatePanelPulses(float delta_time) {
// Desactivar el pulso si ha terminado // Desactivar el pulso si ha terminado
if (pulse.elapsed_s >= pulse.duration_s) { if (pulse.elapsed_s >= pulse.duration_s) {
pulse.active = false; pulse.active = false;
pulse.elapsed_s = 0.0f; pulse.elapsed_s = 0.0F;
} }
} }
} }
@@ -311,7 +314,7 @@ void Scoreboard::updatePanelPulses(float delta_time) {
void Scoreboard::triggerPanelPulse(Id id, float duration_s) { void Scoreboard::triggerPanelPulse(Id id, float duration_s) {
auto idx = static_cast<size_t>(id); auto idx = static_cast<size_t>(id);
panel_pulse_.at(idx).active = true; panel_pulse_.at(idx).active = true;
panel_pulse_.at(idx).elapsed_s = 0.0f; panel_pulse_.at(idx).elapsed_s = 0.0F;
panel_pulse_.at(idx).duration_s = duration_s; panel_pulse_.at(idx).duration_s = duration_s;
} }
@@ -381,7 +384,7 @@ void Scoreboard::fillPanelTextures() {
// Interpolar entre color base y color aclarado // Interpolar entre color base y color aclarado
Color target_color = color_.LIGHTEN(PANEL_PULSE_LIGHTEN_AMOUNT); Color target_color = color_.LIGHTEN(PANEL_PULSE_LIGHTEN_AMOUNT);
//Color target_color = color_.INVERSE(); // Color target_color = color_.INVERSE();
background_color = color_.LERP(target_color, pulse_intensity); background_color = color_.LERP(target_color, pulse_intensity);
background_color.a = 255; // Opaco durante el pulso background_color.a = 255; // Opaco durante el pulso
} }
@@ -737,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 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) // 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()); const int CHAR_LIST_SIZE = static_cast<int>(char_list.size());
// Calcular ancho promedio de una letra (asumimos ancho uniforme) // Calcular ancho promedio de una letra (asumimos ancho uniforme)
@@ -745,17 +748,17 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
const int CHAR_STEP = AVG_CHAR_WIDTH + EXTRA_SPACING; const int CHAR_STEP = AVG_CHAR_WIDTH + EXTRA_SPACING;
// --- Corrección visual de residuales flotantes (evita “baile”) --- // --- Corrección visual de residuales flotantes (evita “baile”) ---
float frac = CAROUSEL_POS - std::floor(CAROUSEL_POS); float frac = carousel_pos - std::floor(carousel_pos);
if (frac > 0.999f || frac < 0.001f) { if (frac > 0.999F || frac < 0.001F) {
CAROUSEL_POS = std::round(CAROUSEL_POS); carousel_pos = std::round(carousel_pos);
frac = 0.0f; frac = 0.0F;
} }
const float FRACTIONAL_OFFSET = frac; 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) // Í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) // 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; int start_x = center_x - (HALF_VISIBLE * CHAR_STEP) - (AVG_CHAR_WIDTH / 2) - PIXEL_OFFSET;
@@ -770,24 +773,27 @@ void Scoreboard::renderCarousel(size_t panel_index, int center_x, int y) {
} }
// --- Calcular distancia circular correcta (corregido el bug de wrap) --- // --- Calcular distancia circular correcta (corregido el bug de wrap) ---
float normalized_pos = std::fmod(CAROUSEL_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); if (normalized_pos < 0.0F) {
normalized_pos += static_cast<float>(CHAR_LIST_SIZE);
}
float diff = std::abs(static_cast<float>(char_index) - normalized_pos); 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; 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 --- // --- Seleccionar color con LERP según la distancia ---
Color letter_color; Color letter_color;
if (distance_from_center < 0.5F) { if (DISTANCE_FROM_CENTER < 0.5F) {
// Letra central → transiciona hacia animated_color_ // 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); letter_color = animated_color_.LERP(text_color1_, lerp_to_animated);
} else { } else {
// Letras alejadas → degradan hacia color_ base // 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); base_lerp = std::min(base_lerp, 1.0F);
const float LERP_FACTOR = base_lerp * 0.85F; const float LERP_FACTOR = base_lerp * 0.85F;
letter_color = text_color1_.LERP(color_, LERP_FACTOR); letter_color = text_color1_.LERP(color_, LERP_FACTOR);

View File

@@ -51,8 +51,8 @@ class Scoreboard {
struct PanelPulse { struct PanelPulse {
bool active = false; // Si el pulso está activo bool active = false; // Si el pulso está activo
float elapsed_s = 0.0f; // Tiempo transcurrido desde el inicio float elapsed_s = 0.0F; // Tiempo transcurrido desde el inicio
float duration_s = 0.5f; // Duración total del pulso float duration_s = 0.5F; // Duración total del pulso
}; };
// --- Métodos de singleton --- // --- Métodos de singleton ---
@@ -80,7 +80,7 @@ class Scoreboard {
void setScore(Id id, int score) { score_.at(static_cast<size_t>(id)) = score; } 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 setSelectorPos(Id id, int pos) { selector_pos_.at(static_cast<size_t>(id)) = pos; }
void setStage(int stage) { stage_ = stage; } void setStage(int stage) { stage_ = stage; }
void triggerPanelPulse(Id id, float duration_s = 0.5f); // Activa un pulso en el panel especificado void triggerPanelPulse(Id id, float duration_s = 0.5F); // Activa un pulso en el panel especificado
private: private:
// --- Objetos y punteros --- // --- Objetos y punteros ---
@@ -117,8 +117,8 @@ class Scoreboard {
int time_counter_ = 0; // Contador de segundos int time_counter_ = 0; // Contador de segundos
Uint32 name_color_index_ = 0; // Índice actual del color en el ciclo de animación del nombre 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 Uint64 name_color_last_update_ = 0; // Último tick de actualización del color del nombre
float power_ = 0; // Poder actual de la fase 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}; std::array<float, static_cast<size_t>(Id::SIZE)> carousel_speed_scale_ = {1.0F, 1.0F, 1.0F};
// --- Constantes --- // --- Constantes ---
static constexpr int CAROUSEL_VISIBLE_LETTERS = 9; static constexpr int CAROUSEL_VISIBLE_LETTERS = 9;

View File

@@ -82,7 +82,7 @@ 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) { void Credits::update(float delta_time) {
const float MULTIPLIER = want_to_pass_ ? FAST_FORWARD_MULTIPLIER : 1.0F; const float MULTIPLIER = want_to_pass_ ? FAST_FORWARD_MULTIPLIER : 1.0F;
const float ADJUSTED_DELTA_TIME = delta_time * MULTIPLIER; const float ADJUSTED_DELTA_TIME = delta_time * MULTIPLIER;
@@ -99,10 +99,6 @@ void Credits::update(float delta_time) {
updatePlayers(ADJUSTED_DELTA_TIME); updatePlayers(ADJUSTED_DELTA_TIME);
updateAllFades(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(); fillCanvas();
} }
@@ -270,7 +266,7 @@ void Credits::fillCanvas() {
SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); 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) { void Credits::updateTextureDstRects(float delta_time) {
constexpr float TEXTURE_UPDATE_INTERVAL_S = 10.0F / 60.0F; // ~0.167s (cada 10 frames) constexpr float TEXTURE_UPDATE_INTERVAL_S = 10.0F / 60.0F; // ~0.167s (cada 10 frames)
credits_state_.texture_accumulator += delta_time; credits_state_.texture_accumulator += delta_time;
@@ -284,34 +280,45 @@ void Credits::updateTextureDstRects(float delta_time) {
} }
// Comprueba la posición de la textura con el mini_logo // 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; 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 { } else {
--mini_logo_rect_dst_.y; --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) { void Credits::throwBalloons(float delta_time) {
constexpr int SPEED = 200; 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}; 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 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 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; return;
} }
@@ -320,11 +327,11 @@ void Credits::throwBalloons(float delta_time) {
if (credits_state_.balloon_accumulator >= BALLOON_INTERVAL_S) { if (credits_state_.balloon_accumulator >= BALLOON_INTERVAL_S) {
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); 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; credits_state_.powerball_accumulator -= POWERBALL_INTERVAL_S;
balloon_manager_->createPowerBall(); balloon_manager_->createPowerBall();
} }
@@ -402,8 +409,8 @@ void Credits::initPlayers() {
// Actualiza los rectangulos negros (time-based) // Actualiza los rectangulos negros (time-based)
void Credits::updateBlackRects(float delta_time) { void Credits::updateBlackRects(float delta_time) {
if (!initialized_) return; if (!initialized_) { return; }
if (delta_time < 0.0f) delta_time = 0.0f; delta_time = std::max(delta_time, 0.0F);
// Fase vertical: hasta que ambos rects verticales estén exactos en su target // Fase vertical: hasta que ambos rects verticales estén exactos en su target
if (!vertical_done_) { if (!vertical_done_) {
@@ -413,34 +420,34 @@ void Credits::updateBlackRects(float delta_time) {
// top // top
int prev_top_h = static_cast<int>(top_black_rect_.h); int prev_top_h = static_cast<int>(top_black_rect_.h);
top_black_rect_.h = std::min(top_black_rect_.h + 1.0f, top_black_rect_.h = std::min(top_black_rect_.h + 1.0F,
static_cast<float>(param.game.game_area.center_y - 1)); static_cast<float>(param.game.game_area.center_y - 1));
int top_delta = static_cast<int>(top_black_rect_.h) - prev_top_h; int top_delta = static_cast<int>(top_black_rect_.h) - prev_top_h;
// bottom // bottom
int prev_bottom_h = static_cast<int>(bottom_black_rect_.h); int prev_bottom_h = static_cast<int>(bottom_black_rect_.h);
int prev_bottom_y = static_cast<int>(bottom_black_rect_.y); int prev_bottom_y = static_cast<int>(bottom_black_rect_.y);
bottom_black_rect_.h = bottom_black_rect_.h + 1.0f; bottom_black_rect_.h = bottom_black_rect_.h + 1.0F;
bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1.0f, bottom_black_rect_.y = std::max(bottom_black_rect_.y - 1.0F,
static_cast<float>(param.game.game_area.center_y + 1)); 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_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_by_y = prev_bottom_y - static_cast<int>(bottom_black_rect_.y);
int bottom_steps = std::max(0, std::max(bottom_steps_by_h, bottom_steps_by_y)); int bottom_steps = std::max({0, bottom_steps_by_h, bottom_steps_by_y});
int steps_done = top_delta + bottom_steps; int steps_done = top_delta + bottom_steps;
if (steps_done > 0) { if (steps_done > 0) {
current_step_ = std::max(0.0f, current_step_ - static_cast<float>(steps_done)); 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_)); 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_))); 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í Audio::get()->setMusicVolume(vol_i); // usa tu API de audio aquí
} }
// Si han alcanzado los objetivos, fijarlos exactamente y marcar done // 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; 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; 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) { if (top_at_target && bottom_at_target) {
top_black_rect_.h = static_cast<float>(param.game.game_area.center_y - 1); top_black_rect_.h = param.game.game_area.center_y - 1.0F;
bottom_black_rect_.y = static_cast<float>(param.game.game_area.center_y + 1); bottom_black_rect_.y = param.game.game_area.center_y + 1.0F;
vertical_done_ = true; vertical_done_ = true;
} }
} }
@@ -453,28 +460,28 @@ void Credits::updateBlackRects(float delta_time) {
if (!horizontal_done_) { if (!horizontal_done_) {
int prev_left_w = static_cast<int>(left_black_rect_.w); 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), left_black_rect_.w = std::min(left_black_rect_.w + static_cast<float>(HORIZONTAL_SPEED),
static_cast<float>(param.game.game_area.center_x)); param.game.game_area.center_x);
int left_gain = static_cast<int>(left_black_rect_.w) - prev_left_w; int left_gain = static_cast<int>(left_black_rect_.w) - prev_left_w;
int prev_right_x = static_cast<int>(right_black_rect_.x); 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_.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), right_black_rect_.x = std::max(right_black_rect_.x - static_cast<float>(HORIZONTAL_SPEED),
static_cast<float>(param.game.game_area.center_x)); param.game.game_area.center_x);
int right_move = prev_right_x - static_cast<int>(right_black_rect_.x); int right_move = prev_right_x - static_cast<int>(right_black_rect_.x);
int steps_done = left_gain + right_move; int steps_done = left_gain + right_move;
if (steps_done > 0) { if (steps_done > 0) {
current_step_ = std::max(0.0f, current_step_ - static_cast<float>(steps_done)); 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_)); 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_))); 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í 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 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; bool right_at_target = static_cast<int>(right_black_rect_.x) == param.game.game_area.center_x;
if (left_at_target && right_at_target) { if (left_at_target && right_at_target) {
left_black_rect_.w = static_cast<float>(param.game.game_area.center_x); left_black_rect_.w = param.game.game_area.center_x;
right_black_rect_.x = static_cast<float>(param.game.game_area.center_x); right_black_rect_.x = param.game.game_area.center_x;
horizontal_done_ = true; horizontal_done_ = true;
} }
@@ -485,24 +492,27 @@ void Credits::updateBlackRects(float delta_time) {
// Fase final: ya completado el movimiento de rects // Fase final: ya completado el movimiento de rects
Audio::get()->setMusicVolume(0); Audio::get()->setMusicVolume(0);
// Audio::get()->stopMusic(); // opcional, si quieres parar la reproducción // Audio::get()->stopMusic(); // opcional, si quieres parar la reproducción
if (counter_pre_fade_ >= 400.0f) {
if (fade_out_) fade_out_->activate(); // Usar segundos puros en lugar de frames equivalentes
if (counter_pre_fade_ >= PRE_FADE_DELAY_S) {
if (fade_out_) {
fade_out_->activate();
}
} else { } else {
const float frame_increment = delta_time * FRAMES_PER_SECOND; counter_pre_fade_ += delta_time;
counter_pre_fade_ += frame_increment;
} }
} }
// Actualiza el rectangulo del borde // Actualiza el rectangulo del borde
void Credits::updateBorderRect() { void Credits::updateBorderRect() {
border_rect_.x = left_black_rect_.x + left_black_rect_.w; 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_.y = top_black_rect_.y + top_black_rect_.h - 1.0F;
float raw_w = right_black_rect_.x - border_rect_.x; float raw_w = right_black_rect_.x - border_rect_.x;
float raw_h = bottom_black_rect_.y - border_rect_.y + 1.0f; float raw_h = bottom_black_rect_.y - border_rect_.y + 1.0F;
border_rect_.w = std::max(0.0f, raw_w); border_rect_.w = std::max(0.0F, raw_w);
border_rect_.h = std::max(0.0f, raw_h); border_rect_.h = std::max(0.0F, raw_h);
} }
// Actualiza el estado de fade (time-based) // Actualiza el estado de fade (time-based)
@@ -604,25 +614,25 @@ void Credits::renderPlayers() {
void Credits::initVars() { void Credits::initVars() {
// Inicialización segura de rects tal y como los mostraste // Inicialización segura de rects tal y como los mostraste
top_black_rect_ = { top_black_rect_ = {
play_area_.x, .x = play_area_.x,
param.game.game_area.rect.y, .y = param.game.game_area.rect.y,
play_area_.w, .w = play_area_.w,
black_bars_size_}; .h = black_bars_size_};
bottom_black_rect_ = { bottom_black_rect_ = {
play_area_.x, .x = play_area_.x,
param.game.game_area.rect.h - black_bars_size_, .y = param.game.game_area.rect.h - black_bars_size_,
play_area_.w, .w = play_area_.w,
black_bars_size_}; .h = black_bars_size_};
left_black_rect_ = { left_black_rect_ = {
play_area_.x, .x = play_area_.x,
param.game.game_area.center_y - 1, .y = param.game.game_area.center_y - 1.0F,
0, .w = 0.0F,
2}; .h = 2.0F};
right_black_rect_ = { right_black_rect_ = {
play_area_.x + play_area_.w, .x = play_area_.x + play_area_.w,
param.game.game_area.center_y - 1, .y = param.game.game_area.center_y - 1.0F,
0, .w = 0.0F,
2}; .h = 2.0F};
initialized_ = false; initialized_ = false;
@@ -651,10 +661,10 @@ void Credits::initVars() {
void Credits::startCredits() { void Credits::startCredits() {
// Guardar iniciales (enteros para contar "pasos" por píxel) // Guardar iniciales (enteros para contar "pasos" por píxel)
init_top_h = static_cast<int>(top_black_rect_.h); init_top_h_ = static_cast<int>(top_black_rect_.h);
init_bottom_y = static_cast<int>(bottom_black_rect_.y); init_bottom_y_ = static_cast<int>(bottom_black_rect_.y);
init_left_w = static_cast<int>(left_black_rect_.w); init_left_w_ = static_cast<int>(left_black_rect_.w);
init_right_x = static_cast<int>(right_black_rect_.x); init_right_x_ = static_cast<int>(right_black_rect_.x);
// Objetivos // Objetivos
int top_target_h = param.game.game_area.center_y - 1; int top_target_h = param.game.game_area.center_y - 1;
@@ -663,34 +673,38 @@ void Credits::startCredits() {
int right_target_x = param.game.game_area.center_x; int right_target_x = param.game.game_area.center_x;
// Pasos verticales // Pasos verticales
int pasos_top = std::max(0, top_target_h - init_top_h); int pasos_top = std::max(0, top_target_h - init_top_h_);
int pasos_bottom = std::max(0, init_bottom_y - bottom_target_y); 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 // 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 pasos_left = std::max(0, left_target_w - init_left_w_);
int dx_right = std::max(0, init_right_x - right_target_x); int dx_right = std::max(0, init_right_x_ - right_target_x);
int pasos_right = (dx_right + (HORIZONTAL_SPEED - 1)) / HORIZONTAL_SPEED; // ceil int pasos_right = (dx_right + (HORIZONTAL_SPEED - 1)) / HORIZONTAL_SPEED; // ceil
total_steps_ = pasos_top + pasos_bottom + pasos_left + pasos_right; total_steps_ = pasos_top + pasos_bottom + pasos_left + pasos_right;
if (total_steps_ <= 0) total_steps_ = 1; if (total_steps_ <= 0) {
total_steps_ = 1;
}
current_step_ = static_cast<float>(total_steps_); current_step_ = static_cast<float>(total_steps_);
// Reiniciar contadores y estado // Reiniciar contadores y estado
credits_state_.black_rect_accumulator = 0.0f; credits_state_.black_rect_accumulator = 0.0F;
counter_pre_fade_ = 0.0f; counter_pre_fade_ = 0.0F;
initialized_ = true; initialized_ = true;
// Asegurar volumen inicial consistente // Asegurar volumen inicial consistente
if (steps_ <= 0) steps_ = 1; if (steps_ <= 0) {
steps_ = 1;
}
float vol_f = initial_volume_ * (current_step_ / static_cast<float>(total_steps_)); 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_)))); setVolume(static_cast<int>(std::clamp(vol_f, 0.0F, static_cast<float>(initial_volume_))));
} }
// Dibuja el rectángulo del borde si es visible // Dibuja el rectángulo del borde si es visible
void Credits::drawBorderRect() { void Credits::drawBorderRect() {
// Umbral: cualquier valor menor que 1 píxel no se considera visible // Umbral: cualquier valor menor que 1 píxel no se considera visible
constexpr float VISIBLE_THRESHOLD = 1.0f; constexpr float VISIBLE_THRESHOLD = 1.0F;
if (border_rect_.w < VISIBLE_THRESHOLD || border_rect_.h < VISIBLE_THRESHOLD) { if (border_rect_.w < VISIBLE_THRESHOLD || border_rect_.h < VISIBLE_THRESHOLD) {
return; // no dibujar return; // no dibujar
} }
@@ -701,10 +715,10 @@ void Credits::drawBorderRect() {
// Convertir a enteros de forma conservadora para evitar líneas de 1px por redondeo extraño // Convertir a enteros de forma conservadora para evitar líneas de 1px por redondeo extraño
SDL_Rect r; SDL_Rect r;
r.x = static_cast<int>(std::floor(border_rect_.x + 0.5f)); 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.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.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))); r.h = static_cast<int>(std::max(0.0F, std::floor(border_rect_.h + 0.5F)));
if (r.w > 0 && r.h > 0) { if (r.w > 0 && r.h > 0) {
SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_); SDL_RenderRect(Screen::get()->getRenderer(), &border_rect_);

View File

@@ -32,12 +32,13 @@ class Credits {
void initVars(); // Inicializa variables void initVars(); // Inicializa variables
void startCredits(); // Inicializa mas variables void startCredits(); // Inicializa mas variables
// --- Constantes de clase --- // --- Constantes de clase (time-based) ---
static constexpr int PLAY_AREA_HEIGHT = 200; static constexpr int PLAY_AREA_HEIGHT = 200;
static constexpr float FAST_FORWARD_MULTIPLIER = 6.0F; static constexpr float FAST_FORWARD_MULTIPLIER = 6.0F;
static constexpr float BLACK_RECT_INTERVAL_S = 4.0F / 60.0F; // ~0.0667s static constexpr float BLACK_RECT_INTERVAL_S = 4.0F / 60.0F; // ~0.0667s (cada 4 frames a 60fps)
static constexpr float FRAMES_PER_SECOND = 60.0F;
static constexpr int HORIZONTAL_SPEED = 2; 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 --- // --- Objetos principales ---
std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos
@@ -50,20 +51,20 @@ class Credits {
SDL_Texture* text_texture_; // Textura con el texto de créditos SDL_Texture* text_texture_; // Textura con el texto de créditos
SDL_Texture* canvas_; // Textura donde se dibuja todo SDL_Texture* canvas_; // Textura donde se dibuja todo
// --- Temporización y contadores --- // --- Temporización (time-based puro) ---
Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime Uint64 last_time_ = 0; // Último tiempo registrado para deltaTime
float counter_ = 0.0F; // Contador principal de lógica float elapsed_time_balloons_ = 0.0F; // Tiempo acumulado para lanzamiento de globos (segundos)
float counter_pre_fade_ = 0.0F; // Activación del fundido final float counter_pre_fade_ = 0.0F; // Tiempo antes de activar fundido final (segundos)
float counter_prevent_endless_ = 0.0F; // Prevención de bucle infinito float time_since_logo_positioned_ = 0.0F; // Tiempo desde que el logo llegó a su posición (segundos)
float current_step_ = 0.0F; float current_step_ = 0.0F;
int total_steps_ = 1; int total_steps_ = 1;
bool initialized_ = false; bool initialized_ = false;
// --- Guardar estados iniciales para cálculo de pasos --- // --- Guardar estados iniciales para cálculo de pasos ---
int init_top_h = 0; int init_top_h_ = 0;
int init_bottom_y = 0; int init_bottom_y_ = 0;
int init_left_w = 0; int init_left_w_ = 0;
int init_right_x = 0; int init_right_x_ = 0;
// --- Variables de estado --- // --- Variables de estado ---
bool fading_ = false; // Estado del fade final bool fading_ = false; // Estado del fade final

View File

@@ -370,7 +370,7 @@ void Game::updateGameStateGameOver(float delta_time) {
} }
if (fade_out_->hasEnded()) { 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; Section::options = Section::Options::HI_SCORE_AFTER_PLAYING;
} }
} }
@@ -551,19 +551,18 @@ void Game::handleTabeHitEffects() {
// Maneja la colisión entre bala y globos // Maneja la colisión entre bala y globos
auto Game::checkBulletBalloonCollision(const std::shared_ptr<Bullet>& bullet) -> bool { 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()) { if (!balloon->isEnabled() || balloon->isInvulnerable()) {
continue; return false;
} }
if (!checkCollision(balloon->getCollider(), bullet->getCollider())) { if (!checkCollision(balloon->getCollider(), bullet->getCollider())) {
continue; return false;
} }
processBalloonHit(bullet, balloon); processBalloonHit(bullet, balloon);
return true; return true;
} });
return false;
} }
// Procesa el impacto en un globo // Procesa el impacto en un globo
@@ -1961,68 +1960,87 @@ void Game::handleGameOverEvents() {
} }
} }
// Construye (una vez) el drawList a partir del vector principal // Construye (una vez) el draw_list a partir del vector principal
// drawList almacena punteros a los elementos y queda reservado // draw_list almacena punteros a los elementos y queda reservado
void Game::buildPlayerDrawList(const Players& elements, Players& drawList) { void Game::buildPlayerDrawList(const Players& elements, Players& draw_list) {
drawList.clear(); draw_list.clear();
drawList.reserve(elements.size()); draw_list.reserve(elements.size());
for (const auto& e : elements) drawList.push_back(e); // copia el shared_ptr for (const auto& e : elements) {
std::stable_sort(drawList.begin(), drawList.end(), [](const std::shared_ptr<Player>& a, const std::shared_ptr<Player>& b) { 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(); return a->getZOrder() < b->getZOrder();
}); });
} }
// Actualiza drawList tras cambios en los z_order. Implementación simple: // Actualiza draw_list tras cambios en los z_order. Implementación simple:
// reordena drawList según los z_order actuales. Llamar cuando cambian z_order // reordena draw_list según los z_order actuales. Llamar cuando cambian z_order
void Game::updatePlayerDrawList(const Players& elements, Players& drawList) { void Game::updatePlayerDrawList(const Players& elements, Players& draw_list) {
// Si drawList está vacío o su tamaño no coincide, reconstruirlo. // Si draw_list está vacío o su tamaño no coincide, reconstruirlo.
if (drawList.size() != elements.size()) { if (draw_list.size() != elements.size()) {
buildPlayerDrawList(elements, drawList); buildPlayerDrawList(elements, draw_list);
return; return;
} }
// Dado que apuntan a los mismos elementos, basta ordenar por los z_order actuales. // 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(); return a->getZOrder() < b->getZOrder();
}); });
} }
// Dibuja en el orden definido por drawList // Dibuja en el orden definido por draw_list
void Game::renderPlayerDrawList(const Players& drawList) { void Game::renderPlayerDrawList(const Players& draw_list) {
for (const auto& e : drawList) e->render(); 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 { auto Game::findPlayerIndex(const Players& elems, const std::shared_ptr<Player>& who) -> size_t {
for (size_t i = 0; i < elems.size(); ++i) for (size_t i = 0; i < elems.size(); ++i) {
if (elems[i] == who) return static_cast<int>(i); // compara shared_ptr directamente if (elems[i] == who) {
return static_cast<int>(i); // compara shared_ptr directamente
}
}
return -1; 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); int idx = findPlayerIndex(elements, who);
if (idx < 0) return; // no encontrado if (idx < 0) {
const int oldZ = elements[idx]->getZOrder(); return; // no encontrado
if (oldZ <= 0) return; }
const int OLD_Z = elements[idx]->getZOrder();
if (OLD_Z <= 0) {
return;
}
for (auto& p : elements) { for (auto& p : elements) {
int z = p->getZOrder(); int z = p->getZOrder();
if (z < oldZ) p->setZOrder(z + 1); if (z < OLD_Z) {
p->setZOrder(z + 1);
}
} }
elements[idx]->setZOrder(0); 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); int idx = findPlayerIndex(elements, who);
if (idx < 0) return; // no encontrado if (idx < 0) {
const int oldZ = elements[idx]->getZOrder(); return; // no encontrado
}
const int OLD_Z = elements[idx]->getZOrder();
const int N = static_cast<int>(elements.size()); const int N = static_cast<int>(elements.size());
if (oldZ >= N - 1) return; if (OLD_Z >= N - 1) {
return;
}
for (auto& p : elements) { for (auto& p : elements) {
int z = p->getZOrder(); int z = p->getZOrder();
if (z > oldZ) p->setZOrder(z - 1); if (z > OLD_Z) {
p->setZOrder(z - 1);
}
} }
elements[idx]->setZOrder(N - 1); elements[idx]->setZOrder(N - 1);
updatePlayerDrawList(elements, drawList); updatePlayerDrawList(elements, draw_list);
} }
#ifdef _DEBUG #ifdef _DEBUG

View File

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

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); 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 // 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) { 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); 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 (SHADOWED) {
if (white_sprite_) { renderShadow(x, y, text, shadow_color, kerning, length, shadow_distance);
writeColoredWithSprite(white_sprite_.get(), x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, length);
} else {
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, length);
}
} }
if (STROKED) { if (STROKED) {
@@ -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); writeStrokeWithAlpha(x, y, text, kerning, shadow_color, shadow_distance, length);
} else { } else {
// Método tradicional para stroke sólido // Método tradicional para stroke sólido
for (int dist = 1; dist <= shadow_distance; ++dist) { renderSolidStroke(x, y, text, shadow_color, kerning, length, shadow_distance);
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);
}
}
}
}
} }
} }

View File

@@ -86,6 +86,8 @@ class Text {
// --- Métodos privados --- // --- 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 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 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: private:
// --- Objetos y punteros --- // --- Objetos y punteros ---

View File

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

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 "../build/version.h" // Para Version::APP_NAME
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>