Compare commits

18 Commits

Author SHA1 Message Date
960ee367df fix: els globos apareixien un frame mal situats al crearse desde un pare 2025-08-15 13:09:07 +02:00
a7519fc372 afegit AnimatedSprite::getCurrentAnimationFrame()
fix: player2.gif faltava el outline
noves veus i arreglo d'altres
style: fitxer de config
2025-08-15 11:09:17 +02:00
a983269080 animacions noves de recuperació per als jugadors 2025-08-15 06:38:52 +02:00
3964503f1c game fix: la velocitat dels globos dins de la fase actual muntava al primer globo explotat
game fix: al trencar una powerball ja no eixien mes globos
style: renombrades variables i funcions
2025-08-14 20:41:44 +02:00
8fcb7d1eb5 eliminat un warning en systemes unix 2025-08-14 18:05:36 +02:00
3e68afa4be game.cpp: llevat codi comentat mort de feia temps 2025-08-14 13:58:24 +02:00
ca6edcccc0 Afegida animacioneta pa quan continues 2025-08-14 12:55:29 +02:00
a388005968 Afegit lletreret de "gracies" al continuar 2025-08-14 12:37:39 +02:00
ea7628259a la IA havia trencat el recorregut de la lluna 2025-08-14 12:20:34 +02:00
a13e024934 fix: la IA havia trencat el indicador de fases restants 2025-08-14 11:36:29 +02:00
4cc5102d70 Stage ja carrega desde fitxer la informació de les fases 2025-08-14 11:14:54 +02:00
b2139d8e06 treballant en Background i Stage 2025-08-14 09:53:01 +02:00
ea3e704d34 posant ordre en Stage i Background 2025-08-13 14:04:24 +02:00
d5ab5748a7 actualitzat makefile per a linux desktop 2025-08-11 21:39:43 +02:00
e950eb335d YE! ACTUALIZAT MAKEFILE PA WINDOWS ALTRA VOLTA. PUTA IA 2025-08-11 20:30:59 +02:00
e667063767 actualitzat makefile per a linux 2025-08-11 20:28:16 +02:00
3534e4cc54 actualitzat makefile per a windows 2025-08-11 20:27:40 +02:00
685b04c840 afegits nous iconos per a la aplicació
afegit script en python per a crear els iconos
2025-08-11 19:12:17 +02:00
42 changed files with 1423 additions and 518 deletions

171
Makefile
View File

@@ -11,7 +11,22 @@ APP_NAME := Coffee Crisis Arcade Edition
RELEASE_FOLDER := ccae_release
RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME)
RESOURCE_FILE := release/coffee.res
VERSION := 2025-08-10
# Versión automática basada en la fecha actual (específica por SO)
ifeq ($(OS),Windows_NT)
VERSION := $(shell powershell -Command "Get-Date -Format 'yyyy-MM-dd'")
else
VERSION := $(shell date +%Y-%m-%d)
endif
# Variables específicas para Windows (usando APP_NAME)
ifeq ($(OS),Windows_NT)
WIN_TARGET_FILE := $(DIR_BIN)$(APP_NAME)
WIN_RELEASE_FILE := $(RELEASE_FOLDER)/$(APP_NAME)
else
WIN_TARGET_FILE := $(TARGET_FILE)
WIN_RELEASE_FILE := $(RELEASE_FILE)
endif
# Nombres para los ficheros de lanzamiento
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
@@ -120,20 +135,24 @@ endif
# Reglas para compilación
windows:
@echo off
@echo Compilando para Windows con nombre: "$(APP_NAME).exe"
windres release/coffee.rc -O coff -o $(RESOURCE_FILE)
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE).exe"
strip -s -R .comment -R .gnu.version "$(TARGET_FILE).exe" --strip-unneeded
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_TARGET_FILE).exe"
strip -s -R .comment -R .gnu.version "$(WIN_TARGET_FILE).exe" --strip-unneeded
windows_rec:
@echo off
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRECORDING $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)_rec.exe"
@echo Compilando version de grabacion para Windows: "$(APP_NAME)_rec.exe"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DRECORDING $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_TARGET_FILE)_rec.exe"
windows_debug:
@echo off
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_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"
windows_release:
@echo off
@echo Creando release para Windows - Version: $(VERSION)
# Crea carpeta temporal 'RELEASE_FOLDER'
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
@@ -149,23 +168,27 @@ windows_release:
# Compila
windres release/coffee.rc -O coff -o $(RESOURCE_FILE)
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE).exe"
strip -s -R .comment -R .gnu.version "$(RELEASE_FILE).exe" --strip-unneeded
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_RELEASE_FILE).exe"
strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
# Crea el fichero .zip
powershell if (Test-Path "$(WINDOWS_RELEASE)") {Remove-Item "$(WINDOWS_RELEASE)"}
powershell Compress-Archive -Path "$(RELEASE_FOLDER)"/* -DestinationPath "$(WINDOWS_RELEASE)"
@echo Release creado: $(WINDOWS_RELEASE)
# Elimina la carpeta temporal 'RELEASE_FOLDER'
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
macos:
@echo "Compilando para macOS: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
macos_debug:
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
macos_release:
@echo "Creando release para macOS - Version: $(VERSION)"
# Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)"
$(RMDIR) Frameworks
@@ -202,6 +225,7 @@ ifdef ENABLE_MACOS_X86_64
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
$(RMFILE) tmp.dmg
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
endif
# Compila la versión para procesadores Apple Silicon
@@ -214,19 +238,23 @@ endif
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
$(RMFILE) tmp.dmg
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
# Elimina las carpetas temporales
$(RMDIR) Frameworks
$(RMDIR) "$(RELEASE_FOLDER)"
linux:
@echo "Compilando para Linux: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
strip -s -R .comment -R .gnu.version "$(TARGET_FILE)" --strip-unneeded
linux_debug:
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
linux_release:
@echo "Creando release para Linux - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -245,18 +273,117 @@ linux_release:
# Empaqueta ficheros
$(RMFILE) "$(LINUX_RELEASE)"
tar -czvf "$(LINUX_RELEASE)" -C "$(RELEASE_FOLDER)" .
@echo "Release creado: $(LINUX_RELEASE)"
# Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)"
linux_release_desktop:
@echo "Creando release con integracion desktop para Linux - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
# Crea la estructura de directorios estándar para Linux
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)"
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin"
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications"
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps"
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)"
# Copia ficheros del juego
cp -R data "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
# Compila el ejecutable
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)"
strip -s -R .comment -R .gnu.version "$(RELEASE_FOLDER)/$(TARGET_NAME)/bin/$(TARGET_NAME)" --strip-unneeded
# Crea el archivo .desktop
@echo '[Desktop Entry]' > "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Version=1.0' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Type=Application' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Name=$(APP_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Comment=Arcade action game - defend Earth from alien invasion!' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Exec=/opt/$(TARGET_NAME)/bin/$(TARGET_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Icon=$(TARGET_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Path=/opt/$(TARGET_NAME)/share/$(TARGET_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Terminal=false' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'StartupNotify=true' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Categories=Game;ArcadeGame;' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
@echo 'Keywords=arcade;action;shooter;retro;' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop"
# Copia el icono (si existe) y lo redimensiona si es necesario
@if [ -f "release/icon.png" ]; then \
if command -v magick >/dev/null 2>&1; then \
magick "release/icon.png" -resize 256x256 "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png"; \
echo "Icono redimensionado de release/icon.png (usando ImageMagick)"; \
elif command -v convert >/dev/null 2>&1; then \
convert "release/icon.png" -resize 256x256 "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png"; \
echo "Icono redimensionado de release/icon.png (usando ImageMagick legacy)"; \
elif command -v ffmpeg >/dev/null 2>&1; then \
ffmpeg -i "release/icon.png" -vf scale=256:256 "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png" -y -loglevel quiet; \
echo "Icono redimensionado de release/icon.png (usando ffmpeg)"; \
else \
cp "release/icon.png" "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png"; \
echo "Icono copiado sin redimensionar (instalar ImageMagick o ffmpeg para redimensionado automatico)"; \
fi; \
elif [ -f "release/coffee.png" ]; then \
cp "release/coffee.png" "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png"; \
echo "Icono copiado desde release/coffee.png"; \
else \
echo "Advertencia: No se encontró release/icon.png ni release/coffee.png - crear icono manualmente"; \
fi
# Crea script de instalación
@echo '#!/bin/bash' > "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'echo "Instalando $(APP_NAME)..."' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo mkdir -p /opt/$(TARGET_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp -R bin /opt/$(TARGET_NAME)/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp -R share /opt/$(TARGET_NAME)/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp LICENSE /opt/$(TARGET_NAME)/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp README.md /opt/$(TARGET_NAME)/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo mkdir -p /usr/share/applications' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo mkdir -p /usr/share/icons/hicolor/256x256/apps' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp /opt/$(TARGET_NAME)/share/applications/$(TARGET_NAME).desktop /usr/share/applications/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo cp /opt/$(TARGET_NAME)/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png /usr/share/icons/hicolor/256x256/apps/' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo update-desktop-database /usr/share/applications 2>/dev/null || true' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'sudo gtk-update-icon-cache /usr/share/icons/hicolor 2>/dev/null || true' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'echo "$(APP_NAME) instalado correctamente!"' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
@echo 'echo "Ya puedes encontrarlo en el menu de aplicaciones en la categoria Juegos."' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
chmod +x "$(RELEASE_FOLDER)/$(TARGET_NAME)/install.sh"
# Crea script de desinstalación
@echo '#!/bin/bash' > "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'echo "Desinstalando $(APP_NAME)..."' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'sudo rm -rf /opt/$(TARGET_NAME)' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'sudo rm -f /usr/share/applications/$(TARGET_NAME).desktop' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'sudo rm -f /usr/share/icons/hicolor/256x256/apps/$(TARGET_NAME).png' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'sudo update-desktop-database /usr/share/applications 2>/dev/null || true' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'sudo gtk-update-icon-cache /usr/share/icons/hicolor 2>/dev/null || true' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
@echo 'echo "$(APP_NAME) desinstalado correctamente."' >> "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
chmod +x "$(RELEASE_FOLDER)/$(TARGET_NAME)/uninstall.sh"
# Empaqueta ficheros
$(RMFILE) "$(TARGET_NAME)-$(VERSION)-linux-desktop.tar.gz"
tar -czvf "$(TARGET_NAME)-$(VERSION)-linux-desktop.tar.gz" -C "$(RELEASE_FOLDER)" .
@echo "Release con integracion desktop creado: $(TARGET_NAME)-$(VERSION)-linux-desktop.tar.gz"
@echo "Para instalar: extraer y ejecutar ./$(TARGET_NAME)/install.sh"
# Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)"
raspi:
@echo "Compilando para Raspberry Pi: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(TARGET_FILE)
strip -s -R .comment -R .gnu.version $(TARGET_FILE) --strip-unneeded
raspi_debug:
@echo "Compilando version debug para Raspberry Pi: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
raspi_release:
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"
@@ -275,11 +402,13 @@ raspi_release:
# Empaqueta ficheros
$(RMFILE) "$(RASPI_RELEASE)"
tar -czvf "$(RASPI_RELEASE)" -C "$(RELEASE_FOLDER)" .
@echo "Release creado: $(RASPI_RELEASE)"
# Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)"
anbernic:
@echo "Compilando para Anbernic: $(TARGET_NAME)"
# Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic
@@ -294,6 +423,32 @@ anbernic:
# Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake)
no_audio:
@echo "Compilando sin audio: $(TARGET_NAME)_no_audio"
$(CXX) $(filter-out source/external/jail_audio.cpp,$(APP_SOURCES)) $(INCLUDES) -DNO_AUDIO $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)_no_audio"
.PHONY: windows windows_rec windows_debug windows_release macos macos_debug macos_release linux linux_debug linux_release raspi raspi_debug raspi_release anbernic no_audio
# Regla para mostrar la versión actual
show_version:
@echo "Version actual: $(VERSION)"
# Regla de ayuda
help:
@echo "Makefile para Coffee Crisis Arcade Edition"
@echo "Comandos disponibles:"
@echo " windows - Compilar para Windows"
@echo " windows_debug - Compilar debug para Windows"
@echo " windows_release - Crear release completo para Windows"
@echo " linux - Compilar para Linux"
@echo " linux_debug - Compilar debug para Linux"
@echo " linux_release - Crear release basico para Linux"
@echo " linux_release_desktop - Crear release con integracion desktop para Linux"
@echo " macos - Compilar para macOS"
@echo " macos_debug - Compilar debug para macOS"
@echo " macos_release - Crear release completo para macOS"
@echo " raspi - Compilar para Raspberry Pi"
@echo " raspi_release - Crear release completo para Raspberry Pi"
@echo " anbernic - Compilar para Anbernic"
@echo " no_audio - Compilar sin sistema de audio"
@echo " show_version - Mostrar version actual ($(VERSION))"
@echo " help - Mostrar esta ayuda"
.PHONY: windows windows_rec windows_debug windows_release macos macos_debug macos_release linux linux_debug linux_release linux_release_desktop raspi raspi_debug raspi_release anbernic no_audio show_version help

View File

@@ -1,4 +1,4 @@
# Coffee Crisis - Asset Configuration
# Coffee Crisis Arcade Edition - Asset Configuration
# Formato: TIPO|RUTA [|OPCIONES]
# Opciones: optional, absolute (separadas por comas)
# Variables: ${PREFIX}, ${SYSTEM_FOLDER}
@@ -9,33 +9,35 @@ DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
# Archivos de configuración del juego
DATA|${PREFIX}/data/config/formations.txt
DATA|${PREFIX}/data/config/gamecontrollerdb.txt
DATA|${PREFIX}/data/config/param_320x240.txt
DATA|${PREFIX}/data/config/param_320x256.txt
DATA|${PREFIX}/data/config/pools.txt
DATA|${PREFIX}/data/config/stages.txt
DEMODATA|${PREFIX}/data/config/demo1.bin
DEMODATA|${PREFIX}/data/config/demo2.bin
DATA|${PREFIX}/data/config/gamecontrollerdb.txt
DATA|${PREFIX}/data/config/formations.txt
DATA|${PREFIX}/data/config/pools.txt
# Música
MUSIC|${PREFIX}/data/music/credits.ogg
MUSIC|${PREFIX}/data/music/intro.ogg
MUSIC|${PREFIX}/data/music/playing.ogg
MUSIC|${PREFIX}/data/music/title.ogg
MUSIC|${PREFIX}/data/music/credits.ogg
# Sonidos
SOUND|${PREFIX}/data/sound/balloon_pop0.wav
SOUND|${PREFIX}/data/sound/balloon_pop1.wav
SOUND|${PREFIX}/data/sound/balloon_pop2.wav
SOUND|${PREFIX}/data/sound/balloon_pop3.wav
SOUND|${PREFIX}/data/sound/balloon_bounce0.wav
SOUND|${PREFIX}/data/sound/balloon_bounce1.wav
SOUND|${PREFIX}/data/sound/balloon_bounce2.wav
SOUND|${PREFIX}/data/sound/balloon_bounce3.wav
SOUND|${PREFIX}/data/sound/balloon_pop0.wav
SOUND|${PREFIX}/data/sound/balloon_pop1.wav
SOUND|${PREFIX}/data/sound/balloon_pop2.wav
SOUND|${PREFIX}/data/sound/balloon_pop3.wav
SOUND|${PREFIX}/data/sound/bullet.wav
SOUND|${PREFIX}/data/sound/clock.wav
SOUND|${PREFIX}/data/sound/coffee_out.wav
SOUND|${PREFIX}/data/sound/continue_clock.wav
SOUND|${PREFIX}/data/sound/credit.wav
SOUND|${PREFIX}/data/sound/debian_drop.wav
SOUND|${PREFIX}/data/sound/debian_pickup.wav
SOUND|${PREFIX}/data/sound/hi_score_achieved.wav
@@ -54,7 +56,9 @@ SOUND|${PREFIX}/data/sound/tabe_hit.wav
SOUND|${PREFIX}/data/sound/tabe.wav
SOUND|${PREFIX}/data/sound/title.wav
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
SOUND|${PREFIX}/data/sound/voice_brbrbr.wav
SOUND|${PREFIX}/data/sound/voice_coffee.wav
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
SOUND|${PREFIX}/data/sound/voice_get_ready.wav
SOUND|${PREFIX}/data/sound/voice_no.wav
SOUND|${PREFIX}/data/sound/voice_power_up.wav
@@ -62,50 +66,50 @@ SOUND|${PREFIX}/data/sound/voice_thankyou.wav
SOUND|${PREFIX}/data/sound/walk.wav
# Shaders
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
DATA|${PREFIX}/data/shaders/crtpi_240.glsl
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
# Texturas - Balloons
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon2.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon3.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png
# Texturas - Explosiones
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion0.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion1.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion2.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion3.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png
# Texturas - Power Ball
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png
ANIMATION|${PREFIX}/data/gfx/balloon/powerball.ani
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png
# Texturas - Bala
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png
ANIMATION|${PREFIX}/data/gfx/bullet/bullet.ani
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png
# Texturas - Tabe
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png
ANIMATION|${PREFIX}/data/gfx/tabe/tabe.ani
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png
# Texturas - Juego
BITMAP|${PREFIX}/data/gfx/game/game_buildings.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds1.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds2.png
BITMAP|${PREFIX}/data/gfx/game/game_grass.png
BITMAP|${PREFIX}/data/gfx/game/game_moon.png
BITMAP|${PREFIX}/data/gfx/game/game_power_meter.png
BITMAP|${PREFIX}/data/gfx/game/game_sky_colors.png
BITMAP|${PREFIX}/data/gfx/game/game_sun.png
BITMAP|${PREFIX}/data/gfx/game/game_moon.png
# Texturas - Intro
BITMAP|${PREFIX}/data/gfx/intro/intro1.png
@@ -116,75 +120,75 @@ BITMAP|${PREFIX}/data/gfx/intro/intro5.png
BITMAP|${PREFIX}/data/gfx/intro/intro6.png
# Texturas - Logo
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames.png
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames_mini.png
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames.png
BITMAP|${PREFIX}/data/gfx/logo/logo_since_1998.png
# Texturas - Items
BITMAP|${PREFIX}/data/gfx/item/item_points1_disk.png
ANIMATION|${PREFIX}/data/gfx/item/item_clock.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee_machine.ani
ANIMATION|${PREFIX}/data/gfx/item/item_coffee.ani
ANIMATION|${PREFIX}/data/gfx/item/item_debian.ani
ANIMATION|${PREFIX}/data/gfx/item/item_points1_disk.ani
BITMAP|${PREFIX}/data/gfx/item/item_points2_gavina.png
ANIMATION|${PREFIX}/data/gfx/item/item_points2_gavina.ani
BITMAP|${PREFIX}/data/gfx/item/item_points3_pacmar.png
ANIMATION|${PREFIX}/data/gfx/item/item_points3_pacmar.ani
BITMAP|${PREFIX}/data/gfx/item/item_clock.png
ANIMATION|${PREFIX}/data/gfx/item/item_clock.ani
BITMAP|${PREFIX}/data/gfx/item/item_coffee.png
ANIMATION|${PREFIX}/data/gfx/item/item_coffee.ani
BITMAP|${PREFIX}/data/gfx/item/item_debian.png
ANIMATION|${PREFIX}/data/gfx/item/item_debian.ani
BITMAP|${PREFIX}/data/gfx/item/item_coffee_machine.png
ANIMATION|${PREFIX}/data/gfx/item/item_coffee_machine.ani
BITMAP|${PREFIX}/data/gfx/item/item_coffee.png
BITMAP|${PREFIX}/data/gfx/item/item_debian.png
BITMAP|${PREFIX}/data/gfx/item/item_points1_disk.png
BITMAP|${PREFIX}/data/gfx/item/item_points2_gavina.png
BITMAP|${PREFIX}/data/gfx/item/item_points3_pacmar.png
# Texturas - Titulo
ANIMATION|${PREFIX}/data/gfx/title/title_dust.ani
BITMAP|${PREFIX}/data/gfx/title/title_arcade_edition.png
BITMAP|${PREFIX}/data/gfx/title/title_bg_tile.png
BITMAP|${PREFIX}/data/gfx/title/title_coffee.png
BITMAP|${PREFIX}/data/gfx/title/title_crisis.png
BITMAP|${PREFIX}/data/gfx/title/title_arcade_edition.png
BITMAP|${PREFIX}/data/gfx/title/title_dust.png
ANIMATION|${PREFIX}/data/gfx/title/title_dust.ani
# Texturas - Jugador 1
BITMAP|${PREFIX}/data/gfx/player/player1_power.png
BITMAP|${PREFIX}/data/gfx/player/player1.gif
PALETTE|${PREFIX}/data/gfx/player/player1_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player1_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player1_invencible.pal
BITMAP|${PREFIX}/data/gfx/player/player1_power.png
# Texturas - Jugador 2
BITMAP|${PREFIX}/data/gfx/player/player2_power.png
BITMAP|${PREFIX}/data/gfx/player/player2.gif
PALETTE|${PREFIX}/data/gfx/player/player2_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player2_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal
BITMAP|${PREFIX}/data/gfx/player/player2_power.png
# Animaciones del jugador
ANIMATION|${PREFIX}/data/gfx/player/player.ani
ANIMATION|${PREFIX}/data/gfx/player/player_power.ani
ANIMATION|${PREFIX}/data/gfx/player/player.ani
# Texturas - Golpe del jugador
BITMAP|${PREFIX}/data/gfx/player/hit.png
# Fuentes de texto
BITMAP|${PREFIX}/data/font/8bithud.png
FONT|${PREFIX}/data/font/8bithud.txt
BITMAP|${PREFIX}/data/font/aseprite.png
FONT|${PREFIX}/data/font/aseprite.txt
BITMAP|${PREFIX}/data/font/smb2.png
BITMAP|${PREFIX}/data/font/smb2_grad.png
FONT|${PREFIX}/data/font/smb2.txt
BITMAP|${PREFIX}/data/font/04b_25.png
FONT|${PREFIX}/data/font/04b_25.txt
BITMAP|${PREFIX}/data/font/04b_25_2x.png
FONT|${PREFIX}/data/font/04b_25_2x.txt
BITMAP|${PREFIX}/data/font/04b_25_metal.png
BITMAP|${PREFIX}/data/font/04b_25_grey.png
BITMAP|${PREFIX}/data/font/04b_25_flat.png
BITMAP|${PREFIX}/data/font/04b_25_reversed.png
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
BITMAP|${PREFIX}/data/font/04b_25_flat.png
BITMAP|${PREFIX}/data/font/04b_25_grey.png
BITMAP|${PREFIX}/data/font/04b_25_metal.png
BITMAP|${PREFIX}/data/font/04b_25_reversed_2x.png
BITMAP|${PREFIX}/data/font/04b_25_reversed.png
BITMAP|${PREFIX}/data/font/04b_25.png
BITMAP|${PREFIX}/data/font/8bithud.png
BITMAP|${PREFIX}/data/font/aseprite.png
BITMAP|${PREFIX}/data/font/smb2_grad.png
BITMAP|${PREFIX}/data/font/smb2.png
FONT|${PREFIX}/data/font/04b_25_2x.txt
FONT|${PREFIX}/data/font/04b_25.txt
FONT|${PREFIX}/data/font/8bithud.txt
FONT|${PREFIX}/data/font/aseprite.txt
FONT|${PREFIX}/data/font/smb2.txt
# Idiomas
LANG|${PREFIX}/data/lang/es_ES.json
LANG|${PREFIX}/data/lang/ba_BA.json
LANG|${PREFIX}/data/lang/en_UK.json
LANG|${PREFIX}/data/lang/ba_BA.json
LANG|${PREFIX}/data/lang/es_ES.json

19
data/config/stages.txt Normal file
View File

@@ -0,0 +1,19 @@
# Archivo de configuración de fases
# Formato: power_to_complete,min_menace,max_menace,name
# Líneas que empiezan con # son comentarios y se ignoran
# Fases iniciales - Tutorial y aprendizaje
200, 11, 19, Tutorial
300, 15, 23, Primeros pasos
# Fases intermedias - Incremento de dificultad
600, 19, 27, Intensificación
600, 19, 27, Persistencia
600, 23, 31, Desafío medio
600, 23, 31, Resistencia
# Fases avanzadas - Desafío final
650, 27, 35, Aproximación final
750, 27, 35, Penúltimo obstáculo
850, 31, 39, Clímax
950, 35, 47, Maestría

View File

@@ -120,9 +120,16 @@ loop=0
frames=47,48,49,50,51,52,53
[/animation]
[animation]
name=recover
speed=3
loop=-1
frames=54,54,54,54,55,56,57,58,58,58,59,60,61,58,59,60,61,58,59,60,61,62,62,62,62
[/animation]
[animation]
name=hello
speed=3
loop=-1
frames=54,55,56,57,58,59,60,61,62,63,64,64,64,64,64,64,64,64,64,64,64,64,64,65,66,67,68,69,70,71,72,73,73,72,71,70,70,71,72,73,73,72,71,70,70,71,72,73,73,72,71,70,70,71,72,73,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54
frames=63,64,65,66,67,68,69,70,71,72,73,73,73,73,73,73,73,73,73,73,73,73,73,74,75,76,77,78,79,80,81,82,82,81,80,79,79,80,81,82,82,81,80,79,79,80,81,82,82,81,80,79,79,80,81,82,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63
[/animation]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -26,6 +26,7 @@
"[GAME_TEXT] 6": "Temps!",
"[GAME_TEXT] 7": "Endavant!",
"[GAME_TEXT] 8": "1.000.000 de punts!",
"[GAME_TEXT] THANK_YOU": "Gracies!",
"[HIGHSCORE_TABLE] CAPTION": "Millors puntuacions",

View File

@@ -25,6 +25,7 @@
"[GAME_TEXT] 6": "Stop!",
"[GAME_TEXT] 7": "Get Ready!",
"[GAME_TEXT] 8": "1,000,000 points!",
"[GAME_TEXT] THANK_YOU": "Thank you!",
"[HIGHSCORE_TABLE] CAPTION": "Best scores",

View File

@@ -25,6 +25,7 @@
"[GAME_TEXT] 6": "Tiempo!",
"[GAME_TEXT] 7": "Adelante!",
"[GAME_TEXT] 8": "1.000.000 de puntos!",
"[GAME_TEXT] THANK_YOU": "Gracias!",
"[HIGHSCORE_TABLE] CAPTION": "Mejores puntuaciones",

BIN
data/sound/credit.wav Normal file

Binary file not shown.

BIN
data/sound/voice_brbrbr.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

150
release/create_icons.py Normal file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python3
import os
import sys
import shutil
import subprocess
from pathlib import Path
def check_dependencies():
"""Verifica que ImageMagick esté instalado"""
try:
subprocess.run(['magick', '--version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("Error: ImageMagick no está instalado o no se encuentra en el PATH")
print("Instala ImageMagick desde: https://imagemagick.org/script/download.php")
sys.exit(1)
# Verificar iconutil solo en macOS
if sys.platform == 'darwin':
try:
# iconutil no tiene --version, mejor usar which o probar con -h
result = subprocess.run(['which', 'iconutil'], capture_output=True, check=True)
if result.returncode == 0:
print("✓ iconutil disponible - se crearán archivos .ico e .icns")
except (subprocess.CalledProcessError, FileNotFoundError):
print("Error: iconutil no está disponible (solo funciona en macOS)")
print("Solo se creará el archivo .ico")
else:
print(" Sistema no-macOS detectado - solo se creará archivo .ico")
def create_icons(input_file):
"""Crea archivos .icns e .ico a partir de un PNG"""
# Verificar que el archivo existe
if not os.path.isfile(input_file):
print(f"Error: El archivo {input_file} no existe.")
return False
# Obtener información del archivo
file_path = Path(input_file)
file_dir = file_path.parent
file_name = file_path.stem # Nombre sin extensión
file_extension = file_path.suffix
if file_extension.lower() not in ['.png', '.jpg', '.jpeg', '.bmp', '.tiff']:
print(f"Advertencia: {file_extension} puede no ser compatible. Se recomienda usar PNG.")
# Crear archivo .ico usando el método simplificado
ico_output = file_dir / f"{file_name}.ico"
try:
print(f"Creando {ico_output}...")
subprocess.run([
'magick', str(input_file),
'-define', 'icon:auto-resize=256,128,64,48,32,16',
str(ico_output)
], check=True)
print(f"✓ Archivo .ico creado: {ico_output}")
except subprocess.CalledProcessError as e:
print(f"Error creando archivo .ico: {e}")
return False
# Crear archivo .icns (solo en macOS)
if sys.platform == 'darwin':
try:
# Crear carpeta temporal para iconset
temp_folder = file_dir / "icon.iconset"
# Eliminar carpeta temporal si existe
if temp_folder.exists():
shutil.rmtree(temp_folder)
# Crear carpeta temporal
temp_folder.mkdir(parents=True)
# Definir los tamaños y nombres de archivo para .icns
icon_sizes = [
(16, "icon_16x16.png"),
(32, "icon_16x16@2x.png"),
(32, "icon_32x32.png"),
(64, "icon_32x32@2x.png"),
(128, "icon_128x128.png"),
(256, "icon_128x128@2x.png"),
(256, "icon_256x256.png"),
(512, "icon_256x256@2x.png"),
(512, "icon_512x512.png"),
(1024, "icon_512x512@2x.png")
]
print("Generando imágenes para .icns...")
# Crear cada tamaño de imagen
for size, output_name in icon_sizes:
output_path = temp_folder / output_name
subprocess.run([
'magick', str(input_file),
'-resize', f'{size}x{size}',
str(output_path)
], check=True)
# Crear archivo .icns usando iconutil
icns_output = file_dir / f"{file_name}.icns"
print(f"Creando {icns_output}...")
subprocess.run([
'iconutil', '-c', 'icns',
str(temp_folder),
'-o', str(icns_output)
], check=True)
# Limpiar carpeta temporal
if temp_folder.exists():
shutil.rmtree(temp_folder)
print(f"✓ Archivo .icns creado: {icns_output}")
except subprocess.CalledProcessError as e:
print(f"Error creando archivo .icns: {e}")
# Limpiar carpeta temporal en caso de error
if temp_folder.exists():
shutil.rmtree(temp_folder)
return False
else:
print(" Archivo .icns no creado (solo disponible en macOS)")
return True
def main():
"""Función principal"""
# Verificar argumentos
if len(sys.argv) != 2:
print(f"Uso: {sys.argv[0]} ARCHIVO")
print("Ejemplo: python3 create_icons.py imagen.png")
sys.exit(0)
input_file = sys.argv[1]
# Verificar dependencias
check_dependencies()
# Crear iconos
if create_icons(input_file):
print("\n✅ Proceso completado exitosamente")
else:
print("\n❌ El proceso falló")
sys.exit(1)
if __name__ == "__main__":
main()

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 438 KiB

View File

@@ -52,7 +52,7 @@ AnimatedSprite::AnimatedSprite(std::shared_ptr<Texture> texture, const Animation
}
// Obtiene el índice de la animación a partir del nombre
auto AnimatedSprite::getIndex(const std::string& name) -> int {
auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
auto iterator = animation_indices_.find(name);
if (iterator != animation_indices_.end()) {
// Si se encuentra la animación en el mapa, devuelve su índice
@@ -101,7 +101,7 @@ auto AnimatedSprite::animationIsCompleted() -> bool {
// Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
const auto NEW_ANIMATION = getIndex(name);
const auto NEW_ANIMATION = getAnimationIndex(name);
if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION;

View File

@@ -56,17 +56,18 @@ class AnimatedSprite : public MovingSprite {
void update() override; // Actualiza la animación
// --- Control de animaciones ---
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
void resetAnimation(); // Reinicia la animación actual
void setAnimationSpeed(size_t value); // Establece la velocidad de la animación
auto getAnimationSpeed() const -> size_t { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
void animtionPause() { animations_[current_animation_].paused = true; } // Detiene la animación
void animationResume() { animations_[current_animation_].paused = false; } // Reanuda la animación
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice
void resetAnimation(); // Reinicia la animación actual
void setAnimationSpeed(size_t value); // Establece la velocidad de la animación
auto getAnimationSpeed() const -> size_t { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual
void animtionPause() { animations_[current_animation_].paused = true; } // Detiene la animación
void animationResume() { animations_[current_animation_].paused = false; } // Reanuda la animación
auto getCurrentAnimationFrame() const -> size_t { return animations_[current_animation_].current_frame; } // Obtiene el numero de frame de la animación actual
// --- Consultas ---
auto animationIsCompleted() -> bool; // Comprueba si la animación ha terminado
auto getIndex(const std::string& name) -> int; // Obtiene el índice de una animación por nombre
auto animationIsCompleted() -> bool; // Comprueba si la animación ha terminado
auto getAnimationIndex(const std::string& name) -> int; // Obtiene el índice de una animación por nombre
protected:
// --- Datos de animación ---

View File

@@ -14,8 +14,12 @@
#include "texture.h" // Para Texture
// Constructor
Background::Background()
: renderer_(Screen::get()->getRenderer()),
Background::Background(float total_progress_to_complete)
: total_progress_to_complete_(total_progress_to_complete),
progress_per_stage_(total_progress_to_complete_ / STAGES),
sun_completion_progress_(total_progress_to_complete_ * SUN_COMPLETION_FACTOR),
renderer_(Screen::get()->getRenderer()),
buildings_texture_(Resource::get()->getTexture("game_buildings.png")),
top_clouds_texture_(Resource::get()->getTexture("game_clouds1.png")),
@@ -28,81 +32,16 @@ Background::Background()
rect_(SDL_FRect{0, 0, static_cast<float>(gradients_texture_->getWidth() / 2), static_cast<float>(gradients_texture_->getHeight() / 2)}),
src_rect_({0, 0, 320, 240}),
dst_rect_({0, 0, 320, 240}),
base_(rect_.h),
attenuate_color_(Color(param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b)),
alpha_color_text_(param.background.attenuate_color.a),
alpha_color_text_temp_(param.background.attenuate_color.a)
{
// Precalcula rutas
createSunPath();
createMoonPath();
// Inicializa variables
{
gradient_rect_[0] = {0, 0, rect_.w, rect_.h};
gradient_rect_[1] = {rect_.w, 0, rect_.w, rect_.h};
gradient_rect_[2] = {0, rect_.h, rect_.w, rect_.h};
gradient_rect_[3] = {rect_.w, rect_.h, rect_.w, rect_.h};
const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4;
const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4;
for (int i = 0; i < 4; ++i) {
top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT};
bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT};
}
}
// Crea los sprites
{
const float TOP_CLOUDS_Y = base_ - 165;
const float BOTTOM_CLOUDS_Y = base_ - 101;
top_clouds_sprite_a_ = std::make_unique<MovingSprite>(top_clouds_texture_, (SDL_FRect){0, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
top_clouds_sprite_b_ = std::make_unique<MovingSprite>(top_clouds_texture_, (SDL_FRect){rect_.w, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
bottom_clouds_sprite_a_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){0, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
bottom_clouds_sprite_b_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){rect_.w, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
buildings_sprite_ = std::make_unique<Sprite>(buildings_texture_);
gradient_sprite_ = std::make_unique<Sprite>(gradients_texture_, 0, 0, rect_.w, rect_.h);
grass_sprite_ = std::make_unique<Sprite>(grass_texture_, 0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2);
sun_sprite_ = std::make_unique<Sprite>(sun_texture_);
moon_sprite_ = std::make_unique<Sprite>(moon_texture_);
}
// Inicializa objetos
{
constexpr float TOP_CLOUDS_SPEED = 0.1F;
constexpr float BOTTOM_CLOUDS_SPEED = 0.05F;
top_clouds_sprite_a_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
top_clouds_sprite_a_->setVelX(-TOP_CLOUDS_SPEED);
top_clouds_sprite_b_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
top_clouds_sprite_b_->setVelX(-TOP_CLOUDS_SPEED);
bottom_clouds_sprite_a_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
bottom_clouds_sprite_a_->setVelX(-BOTTOM_CLOUDS_SPEED);
bottom_clouds_sprite_b_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
bottom_clouds_sprite_b_->setVelX(-BOTTOM_CLOUDS_SPEED);
buildings_sprite_->setY(base_ - buildings_sprite_->getHeight());
grass_sprite_->setY(base_ - grass_sprite_->getHeight());
sun_sprite_->setPosition(sun_path_.front());
moon_sprite_->setPosition(moon_path_.front());
}
// Crea la textura para componer el fondo
canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND);
// Crea la textura para atenuar el fondo
color_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(color_texture_, SDL_BLENDMODE_BLEND);
setColor(attenuate_color_);
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_);
alpha_color_texture_(param.background.attenuate_color.a),
previous_alpha_color_texture_(param.background.attenuate_color.a),
base_(rect_.h) {
initializePaths();
initializeRects();
initializeSprites();
initializeSpriteProperties();
initializeTextures();
}
// Destructor
@@ -111,9 +50,88 @@ Background::~Background() {
SDL_DestroyTexture(color_texture_);
}
// Inicializa las rutas del sol y la luna
void Background::initializePaths() {
createSunPath();
createMoonPath();
}
// Inicializa los rectángulos de gradientes y nubes
void Background::initializeRects() {
gradient_rect_[0] = {0, 0, rect_.w, rect_.h};
gradient_rect_[1] = {rect_.w, 0, rect_.w, rect_.h};
gradient_rect_[2] = {0, rect_.h, rect_.w, rect_.h};
gradient_rect_[3] = {rect_.w, rect_.h, rect_.w, rect_.h};
const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4;
const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4;
for (int i = 0; i < 4; ++i) {
top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT};
bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT};
}
}
// Crea los sprites
void Background::initializeSprites() {
const float TOP_CLOUDS_Y = base_ - 165;
const float BOTTOM_CLOUDS_Y = base_ - 101;
top_clouds_sprite_a_ = std::make_unique<MovingSprite>(top_clouds_texture_, (SDL_FRect){0, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
top_clouds_sprite_b_ = std::make_unique<MovingSprite>(top_clouds_texture_, (SDL_FRect){rect_.w, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
bottom_clouds_sprite_a_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){0, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
bottom_clouds_sprite_b_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){rect_.w, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
buildings_sprite_ = std::make_unique<Sprite>(buildings_texture_);
gradient_sprite_ = std::make_unique<Sprite>(gradients_texture_, 0, 0, rect_.w, rect_.h);
grass_sprite_ = std::make_unique<Sprite>(grass_texture_, 0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2);
sun_sprite_ = std::make_unique<Sprite>(sun_texture_);
moon_sprite_ = std::make_unique<Sprite>(moon_texture_);
}
// Configura las propiedades iniciales de los sprites
void Background::initializeSpriteProperties() {
constexpr float TOP_CLOUDS_SPEED = 0.1F;
constexpr float BOTTOM_CLOUDS_SPEED = 0.05F;
top_clouds_sprite_a_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
top_clouds_sprite_a_->setVelX(-TOP_CLOUDS_SPEED);
top_clouds_sprite_b_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
top_clouds_sprite_b_->setVelX(-TOP_CLOUDS_SPEED);
bottom_clouds_sprite_a_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
bottom_clouds_sprite_a_->setVelX(-BOTTOM_CLOUDS_SPEED);
bottom_clouds_sprite_b_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
bottom_clouds_sprite_b_->setVelX(-BOTTOM_CLOUDS_SPEED);
buildings_sprite_->setY(base_ - buildings_sprite_->getHeight());
grass_sprite_->setY(base_ - grass_sprite_->getHeight());
sun_sprite_->setPosition(sun_path_.front());
moon_sprite_->setPosition(moon_path_.front());
}
// Inicializa las texturas de renderizado
void Background::initializeTextures() {
canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND);
color_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(color_texture_, SDL_BLENDMODE_BLEND);
setColor(attenuate_color_);
SDL_SetTextureAlphaMod(color_texture_, alpha_color_texture_);
}
// Actualiza la lógica del objeto
void Background::update() {
// Actualiza el valor de alpha_
// Actualiza la progresión y calcula transiciones
if (!manual_mode_) {
updateProgression();
}
// Actualiza el valor de alpha
updateAlphaColorTexture();
// Actualiza las nubes
@@ -122,10 +140,10 @@ void Background::update() {
// Calcula el frame de la hierba
grass_sprite_->setSpriteClip(0, (10 * (counter_ / 20 % 2)), 320, 10);
// Calcula el valor de alpha_
// Calcula el valor de alpha
alpha_ = std::max((255 - (int)(255 * transition_)), 0);
// Mueve el sol
// Mueve el sol y la luna según la progresión
sun_sprite_->setPosition(sun_path_.at(sun_index_));
moon_sprite_->setPosition(moon_path_.at(moon_index_));
@@ -136,6 +154,190 @@ void Background::update() {
fillCanvas();
}
// Incrementa la progresión interna
void Background::incrementProgress(float amount) {
if (state_ == State::NORMAL) {
float old_progress = progress_;
progress_ += amount;
progress_ = std::min(progress_, total_progress_to_complete_);
// Notifica el cambio si hay callback y el progreso cambió
if (progress_callback_ && progress_ != old_progress) {
progress_callback_(progress_);
}
}
}
// Establece la progresión absoluta
void Background::setProgress(float absolute_progress) {
float old_progress = progress_;
progress_ = std::clamp(absolute_progress, 0.0f, total_progress_to_complete_);
// Notifica el cambio si hay callback y el progreso cambió
if (progress_callback_ && progress_ != old_progress) {
progress_callback_(progress_);
}
}
// Cambia el estado del fondo
void Background::setState(State new_state) {
state_ = new_state;
}
// Reinicia la progresión
void Background::reset() {
float old_progress = progress_;
progress_ = 0.0f;
state_ = State::NORMAL;
manual_mode_ = false;
gradient_number_ = 0;
transition_ = 0.0f;
sun_index_ = 0;
moon_index_ = 0;
// Notifica el cambio si hay callback
if (progress_callback_ && progress_ != old_progress) {
progress_callback_(progress_);
}
}
// Activa/desactiva el modo manual
void Background::setManualMode(bool manual) {
manual_mode_ = manual;
}
// Establece callback para sincronización automática
void Background::setProgressCallback(ProgressCallback callback) {
progress_callback_ = callback;
}
// Elimina el callback
void Background::removeProgressCallback() {
progress_callback_ = nullptr;
}
// Ajusta la velocidad de las nubes
void Background::setCloudsSpeed(float value) {
clouds_speed_ = value;
// En modo manual, aplicar la velocidad directamente
// Las nubes inferiores van a la mitad de velocidad que las superiores
top_clouds_sprite_a_->setVelX(value);
top_clouds_sprite_b_->setVelX(value);
bottom_clouds_sprite_a_->setVelX(value / 2.0f);
bottom_clouds_sprite_b_->setVelX(value / 2.0f);
}
// Establece el degradado de fondo
void Background::setGradientNumber(int value) {
gradient_number_ = value % STAGES;
}
// Ajusta la transición entre texturas
void Background::setTransition(float value) {
transition_ = std::clamp(value, 0.0F, 1.0F);
}
// Establece la posición del sol
void Background::setSunProgression(float progress) {
progress = std::clamp(progress, 0.0F, 1.0F);
sun_index_ = static_cast<size_t>(progress * (sun_path_.size() - 1));
}
// Establece la posición de la luna
void Background::setMoonProgression(float progress) {
progress = std::clamp(progress, 0.0F, 1.0F);
moon_index_ = static_cast<size_t>(progress * (moon_path_.size() - 1));
}
// Actualiza la progresión y calcula las transiciones
void Background::updateProgression() {
// Si el juego está completado, reduce la progresión gradualmente
if (state_ == State::COMPLETED) {
if (progress_ > MINIMUM_COMPLETED_PROGRESS) {
progress_ -= COMPLETED_REDUCTION_RATE;
} else {
progress_ = MINIMUM_COMPLETED_PROGRESS;
}
}
// Calcula la transición de los diferentes fondos
const float gradient_number_float = std::min(progress_ / progress_per_stage_, 3.0F);
const float percent = gradient_number_float - static_cast<int>(gradient_number_float);
gradient_number_ = static_cast<size_t>(gradient_number_float);
transition_ = percent;
// Calcula la posición del sol
const float sun_progression = std::min(progress_ / sun_completion_progress_, 1.0f);
sun_index_ = static_cast<size_t>(sun_progression * (sun_path_.size() - 1));
// Calcula la posición de la luna
const float moon_progression = std::min(progress_ / total_progress_to_complete_, 1.0f);
moon_index_ = static_cast<size_t>(moon_progression * (moon_path_.size() - 1));
// Actualiza la velocidad de las nubes
updateCloudsSpeed();
}
// Actualiza la velocidad de las nubes según el estado y progresión
void Background::updateCloudsSpeed() {
// Cálculo de velocidad según progreso
constexpr float CLOUDS_INITIAL_SPEED = 0.05F;
constexpr float CLOUDS_FINAL_SPEED = 2.00F - CLOUDS_INITIAL_SPEED;
// Velocidad base según progreso (de -0.05 a -2.00)
float base_clouds_speed = (-CLOUDS_INITIAL_SPEED) +
(-CLOUDS_FINAL_SPEED * (progress_ / total_progress_to_complete_));
// En estado completado, las nubes se ralentizan gradualmente
if (state_ == State::COMPLETED) {
float completion_factor = (progress_ - MINIMUM_COMPLETED_PROGRESS) /
(total_progress_to_complete_ - MINIMUM_COMPLETED_PROGRESS);
completion_factor = std::max(0.1f, completion_factor);
base_clouds_speed *= completion_factor;
}
// Aplicar velocidades diferentes para nubes superiores e inferiores
const float top_clouds_speed = base_clouds_speed;
const float bottom_clouds_speed = base_clouds_speed / 2.0f;
// Aplicar las velocidades a los sprites correspondientes
top_clouds_sprite_a_->setVelX(top_clouds_speed);
top_clouds_sprite_b_->setVelX(top_clouds_speed);
bottom_clouds_sprite_a_->setVelX(bottom_clouds_speed);
bottom_clouds_sprite_b_->setVelX(bottom_clouds_speed);
// Guardar la velocidad principal
clouds_speed_ = top_clouds_speed;
}
// Actualiza las nubes
void Background::updateClouds() {
// Mueve las nubes
top_clouds_sprite_a_->update();
top_clouds_sprite_b_->update();
bottom_clouds_sprite_a_->update();
bottom_clouds_sprite_b_->update();
// Calcula el offset de las nubes
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) {
top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth());
}
if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) {
top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth());
}
if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) {
bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth());
}
if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) {
bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth());
}
}
// Dibuja el gradiente de fondo
void Background::renderGradient() {
// Dibuja el gradiente de detras
@@ -221,21 +423,6 @@ void Background::render() {
SDL_RenderTexture(renderer_, color_texture_, &src_rect_, &dst_rect_);
}
// Ajusta el valor de la variable
void Background::setCloudsSpeed(float value) {
clouds_speed_ = value;
}
// Ajusta el valor de la variable
void Background::setGradientNumber(int value) {
gradient_number_ = value % 4;
}
// Ajusta el valor de la variable
void Background::setTransition(float value) {
transition_ = std::clamp(value, 0.0F, 1.0F);
}
// Establece la posición del objeto
void Background::setPos(SDL_FRect pos) {
dst_rect_ = pos;
@@ -247,7 +434,7 @@ void Background::setPos(SDL_FRect pos) {
src_rect_.h = pos.h;
}
// Establece el color_ de atenuación
// Establece el color de atenuación
void Background::setColor(Color color) {
attenuate_color_ = color;
@@ -264,52 +451,20 @@ void Background::setColor(Color color) {
// Establece la transparencia de la atenuación
void Background::setAlpha(int alpha) {
// Evita que se asignen valores fuera de rango
alpha_ = std::clamp(alpha, 0, 255);
alpha = std::clamp(alpha, 0, 255);
// Guarda el valor actual y establece el nuevo valor
alpha_color_text_temp_ = alpha_color_text_;
alpha_color_text_ = alpha_;
previous_alpha_color_texture_ = alpha_color_texture_;
alpha_color_texture_ = alpha;
}
// Actualiza el valor de alpha_
// Actualiza el valor de alpha
void Background::updateAlphaColorTexture() {
if (alpha_color_text_ == alpha_color_text_temp_) {
if (alpha_color_texture_ == previous_alpha_color_texture_) {
return;
}
alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_;
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_);
}
// Actualiza las nubes
void Background::updateClouds() {
// Aplica la velocidad calculada a las nubes
top_clouds_sprite_a_->setVelX(clouds_speed_);
top_clouds_sprite_b_->setVelX(clouds_speed_);
bottom_clouds_sprite_a_->setVelX(clouds_speed_ / 2);
bottom_clouds_sprite_b_->setVelX(clouds_speed_ / 2);
// Mueve las nubes
top_clouds_sprite_a_->update();
top_clouds_sprite_b_->update();
bottom_clouds_sprite_a_->update();
bottom_clouds_sprite_b_->update();
// Calcula el offset de las nubes
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) {
top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth());
}
if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) {
top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth());
}
if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) {
bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth());
}
if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) {
bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth());
}
alpha_color_texture_ > previous_alpha_color_texture_ ? ++previous_alpha_color_texture_ : --previous_alpha_color_texture_;
SDL_SetTextureAlphaMod(color_texture_, previous_alpha_color_texture_);
}
// Precalcula el vector con el recorrido del sol
@@ -343,26 +498,22 @@ void Background::createMoonPath() {
const float CENTER_Y = base_ - 50;
constexpr float RADIUS = 140;
// Generar puntos de la curva desde 0 a 90 grados
constexpr double STEP = 0.01;
const int NUM_STEPS = static_cast<int>((M_PI / 2 - 0) / STEP) + 1;
const int NUM_STEPS = static_cast<int>((M_PI / 2) / STEP) + 1;
constexpr float FREEZE_PERCENTAGE = 0.2f; // Porcentaje final del recorrido que se mantiene fijo
const int FREEZE_START_INDEX = static_cast<int>(NUM_STEPS * (1.0f - FREEZE_PERCENTAGE));
for (int i = 0; i < NUM_STEPS; ++i) {
double theta = 0 + i * STEP;
double theta = i * STEP;
float x = CENTER_X + (RADIUS * cos(theta));
float y = CENTER_Y - (RADIUS * sin(theta));
moon_path_.push_back({x, y});
if (i >= FREEZE_START_INDEX && !moon_path_.empty()) {
moon_path_.push_back(moon_path_.back()); // Repite el último punto válido
} else {
moon_path_.push_back({x, y});
}
}
}
// Establece la posición del sol
void Background::setSunProgression(float progress) {
progress = std::clamp(progress, 0.0F, 1.0F);
sun_index_ = static_cast<size_t>(progress * (sun_path_.size() - 1));
}
// Establece la posición de la luna
void Background::setMoonProgression(float progress) {
progress = std::clamp(progress, 0.0F, 1.0F);
moon_index_ = static_cast<size_t>(progress * (moon_path_.size() - 1));
}

View File

@@ -2,10 +2,11 @@
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer
#include <array> // Para array
#include <cstddef> // Para size_t
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include <array> // Para array
#include <cstddef> // Para size_t
#include <functional> // Para std::function
#include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include "color.h" // Para Color
@@ -16,21 +17,29 @@ class Texture;
/*
Esta clase gestiona el fondo que aparece en la sección jugable.
Usa una textura compuesta y una capa superior con un color sólido cuya opacidad es ajustable.
Su área total está definida por "rect", pero solo se pinta la región "srcRect" en la pantalla en "dstRect".
Maneja internamente su progresión a través de diferentes estados del día/noche,
controlando las transiciones entre gradientes, posiciones del sol/luna y velocidad de nubes.
Estados:
- NORMAL: Progresión normal del día
- COMPLETED: Reducción gradual de la actividad (nubes más lentas)
Métodos clave:
- setCloudsSpeed(float value) -> Define la velocidad de las nubes
- setGradientNumber(int value) -> Ajusta el índice del color de cielo
- setTransition(float value) -> Configura la transición entre texturas
- setColor(Color color) -> Aplica un color de atenuación
- setAlpha(int alpha) -> Ajusta la transparencia de la capa de atenuación
- incrementProgress() -> Avanza la progresión del fondo
- setState() -> Cambia el estado del fondo
- setColor/setAlpha -> Efectos de atenuación
*/
class Background {
public:
// Enumeración de estados
enum class State {
NORMAL,
COMPLETED
};
// Constructor y Destructor
Background();
Background(float total_progress_to_complete = 6100.0f);
~Background();
// Actualización y renderizado
@@ -40,20 +49,46 @@ class Background {
// Configuración de posición
void setPos(SDL_FRect pos); // Establece la posición del objeto
// Configuración de animaciones y efectos
void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes
void setGradientNumber(int value); // Establece el degradado de fondo a usar
void setTransition(float value); // Ajusta la transición entre texturas de fondo
// Control de progresión
void incrementProgress(float amount = 1.0f); // Incrementa la progresión interna
void setProgress(float absolute_progress); // Establece la progresión absoluta
void setState(State new_state); // Cambia el estado del fondo
void reset(); // Reinicia la progresión
// Sistema de callback para sincronización automática
using ProgressCallback = std::function<void(float)>;
void setProgressCallback(ProgressCallback callback); // Establece callback para sincronización
void removeProgressCallback(); // Elimina el callback
// Configuración manual (para uso fuera del juego principal)
void setManualMode(bool manual); // Activa/desactiva el modo manual
void setCloudsSpeed(float value); // Ajusta la velocidad de las nubes
void setGradientNumber(int value); // Establece el degradado de fondo
void setTransition(float value); // Ajusta la transición entre texturas
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
// Configuración de efectos visuales
void setColor(Color color); // Establece el color de atenuación
void setAlpha(int alpha); // Ajusta la transparencia del fondo
// Configuración del sol y la luna
void setSunProgression(float progress); // Establece la posición del sol
void setMoonProgression(float progress); // Establece la posición de la luna
// Getters para información del estado
float getProgress() const { return progress_; }
State getState() const { return state_; }
int getCurrentGradient() const { return static_cast<int>(gradient_number_); }
private:
// Constantes de configuración
static constexpr size_t STAGES = 4;
static constexpr float COMPLETED_REDUCTION_RATE = 25.0f;
static constexpr float MINIMUM_COMPLETED_PROGRESS = 200.0f;
static constexpr float SUN_COMPLETION_FACTOR = 0.5f; // El sol completa su recorrido a la mitad del progreso total
// Configuración paramétrica
const float total_progress_to_complete_;
const float progress_per_stage_;
const float sun_completion_progress_;
// Objetos y punteros
SDL_Renderer *renderer_; // Renderizador de la ventana
@@ -81,34 +116,51 @@ class Background {
SDL_Texture *canvas_; // Textura para componer el fondo
SDL_Texture *color_texture_; // Textura para atenuar el fondo
// Variables de control
std::array<SDL_FRect, 4> gradient_rect_;
std::array<SDL_FRect, 4> top_clouds_rect_;
std::array<SDL_FRect, 4> bottom_clouds_rect_;
int gradient_number_ = 0;
int alpha_ = 0;
float clouds_speed_ = 0;
float transition_ = 0;
int counter_ = 0;
SDL_FRect rect_;
SDL_FRect src_rect_;
SDL_FRect dst_rect_;
int base_;
Color attenuate_color_;
int alpha_color_text_;
int alpha_color_text_temp_;
std::vector<SDL_FPoint> sun_path_;
std::vector<SDL_FPoint> moon_path_;
size_t sun_index_ = 0;
size_t moon_index_ = 0;
// Variables de estado y progresión
State state_ = State::NORMAL;
float progress_ = 0.0f; // Progresión interna (0 a total_progress_to_complete_)
bool manual_mode_ = false; // Si está en modo manual, no actualiza automáticamente
ProgressCallback progress_callback_; // Callback para notificar cambios de progreso
// Variables de renderizado
SDL_FRect rect_; // Tamaño del objeto
SDL_FRect src_rect_; // Parte del objeto para copiar en pantalla
SDL_FRect dst_rect_; // Posición en pantalla donde se copia el objeto
std::array<SDL_FRect, STAGES> gradient_rect_; // Fondos degradados
std::array<SDL_FRect, 4> top_clouds_rect_; // Nubes superiores
std::array<SDL_FRect, 4> bottom_clouds_rect_; // Nubes inferiores
Color attenuate_color_; // Color de atenuación
std::vector<SDL_FPoint> sun_path_; // Recorrido del sol
std::vector<SDL_FPoint> moon_path_; // Recorrido de la luna
float clouds_speed_ = 0; // Velocidad de las nubes
float transition_ = 0; // Porcentaje de transición
size_t gradient_number_ = 0; // Índice de fondo degradado
size_t counter_ = 0; // Contador interno
size_t alpha_color_texture_ = 0; // Transparencia de atenuación
size_t previous_alpha_color_texture_ = 0; // Transparencia anterior
size_t sun_index_ = 0; // Índice del recorrido del sol
size_t moon_index_ = 0; // Índice del recorrido de la luna
int base_ = 0; // Posición base del fondo
Uint8 alpha_ = 0; // Transparencia entre fases
// Métodos internos
void renderGradient(); // Dibuja el gradiente de fondo
void renderTopClouds(); // Dibuja las nubes superiores
void renderBottomClouds(); // Dibuja las nubes inferiores
void fillCanvas(); // Compone todos los elementos en la textura
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación
void updateClouds(); // Actualiza el movimiento de las nubes
void createSunPath(); // Precalcula el recorrido del sol
void createMoonPath(); // Precalcula el recorrido de la luna
};
void initializePaths(); // Inicializa las rutas del sol y la luna
void initializeRects(); // Inicializa los rectángulos de gradientes y nubes
void initializeSprites(); // Crea los sprites
void initializeSpriteProperties(); // Configura las propiedades iniciales de los sprites
void initializeTextures(); // Inicializa las texturas de renderizado
void updateProgression(); // Actualiza la progresión y calcula transiciones
void updateCloudsSpeed(); // Actualiza la velocidad de las nubes según el estado
void renderGradient(); // Dibuja el gradiente de fondo
void renderTopClouds(); // Dibuja las nubes superiores
void renderBottomClouds(); // Dibuja las nubes inferiores
void fillCanvas(); // Compone todos los elementos en la textura
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación
void updateClouds(); // Actualiza el movimiento de las nubes
void createSunPath(); // Precalcula el recorrido del sol
void createMoonPath(); // Precalcula el recorrido de la luna
};

View File

@@ -95,10 +95,9 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
// Centra el globo en la posición X
void Balloon::alignTo(int x) {
x_ = static_cast<float>(x - (w_ / 2));
const int MIN_X = play_area_.x;
const int MAX_X = play_area_.w - w_;
x_ = std::clamp(x_, static_cast<float>(MIN_X), static_cast<float>(MAX_X));
const float MIN_X = play_area_.x;
const float MAX_X = play_area_.w - w_;
x_ = std::clamp(x - (w_ / 2), MIN_X, MAX_X);
}
// Pinta el globo en la pantalla

View File

@@ -12,13 +12,14 @@
#include "param.h" // Para Param, ParamGame, param
#include "resource.h" // Para Resource
#include "screen.h" // Para Screen
#include "stage.h" // Para addPower
#include "stage_interface.h" // Para IStageInfo
#include "utils.h"
// Constructor
BalloonManager::BalloonManager()
BalloonManager::BalloonManager(IStageInfo *stage_info)
: explosions_(std::make_unique<Explosions>()),
balloon_formations_(std::make_unique<BalloonFormations>()) { init(); }
balloon_formations_(std::make_unique<BalloonFormations>()),
stage_info_(stage_info) { init(); }
// Inicializa
void BalloonManager::init() {
@@ -110,7 +111,7 @@ void BalloonManager::deployRandomFormation(int stage) {
}
// Reinicia el contador para el próximo despliegue
balloon_deploy_counter_ = 300;
balloon_deploy_counter_ = DEFAULT_BALLOON_DEPLOY_COUNTER;
}
}
}
@@ -174,23 +175,22 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
const auto SIZE = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
const int PARENT_HEIGHT = balloon->getHeight();
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
const int Y = balloon->getPosY() + (PARENT_HEIGHT - CHILD_HEIGHT) / 2;
const int X = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + 2 * (balloon->getWidth() / 3);
const int CHILD_WIDTH = CHILD_HEIGHT;
const float Y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2);
float x = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
const float MIN_X = play_area_.x;
const float MAX_X = play_area_.w - CHILD_WIDTH;
x = std::clamp(x - (CHILD_WIDTH / 2), MIN_X, MAX_X);
// Crea el globo
auto b = createBalloon(0, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0);
auto b = createBalloon(x, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0);
// Establece parametros
b->alignTo(X);
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
// Herencia de estados
if (balloon->isStopped()) {
b->stop();
}
if (balloon->isUsingReversedColor()) {
b->useReverseColor();
}
if (balloon->isStopped()) { b->stop(); }
if (balloon->isUsingReversedColor()) { b->useReverseColor(); }
}
}
@@ -227,7 +227,7 @@ void BalloonManager::setBalloonSpeed(float speed) {
// Explosiona un globo. Lo destruye y crea otros dos si es el caso
auto BalloonManager::popBalloon(std::shared_ptr<Balloon> balloon) -> int {
Stage::addPower(1);
stage_info_->addPower(1);
int score = 0;
if (balloon->getType() == Balloon::Type::POWERBALL) {
@@ -274,7 +274,7 @@ auto BalloonManager::destroyBalloon(std::shared_ptr<Balloon> &balloon) -> int {
}
// Aumenta el poder de la fase
Stage::addPower(balloon->getPower());
stage_info_->addPower(balloon->getPower());
// Destruye el globo
explosions_->add(balloon->getPosX(), balloon->getPosY(), static_cast<int>(balloon->getSize()));

View File

@@ -12,6 +12,7 @@
#include "balloon_formations.h" // Para BalloonFormations
#include "explosions.h" // Para Explosions
#include "param.h" // Para Param, ParamGame, param
#include "stage_interface.h" // Para IStageInfo
#include "utils.h" // Para Zone
class Texture;
@@ -22,7 +23,7 @@ using Balloons = std::vector<std::shared_ptr<Balloon>>;
class BalloonManager {
public:
// Constructor y Destructor
BalloonManager();
BalloonManager(IStageInfo *stage_info);
~BalloonManager() = default;
// Actualización y Renderizado
@@ -80,6 +81,8 @@ class BalloonManager {
[[nodiscard]] auto getNumBalloons() const -> int { return balloons_.size(); }
private:
static const int DEFAULT_BALLOON_DEPLOY_COUNTER = 300;
Balloons balloons_; // Vector con los globos activos
std::unique_ptr<Explosions> explosions_; // Objeto para gestionar explosiones
std::unique_ptr<BalloonFormations> balloon_formations_; // Objeto para manejar formaciones enemigas
@@ -103,6 +106,7 @@ class BalloonManager {
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
IStageInfo *stage_info_; // Informacion de la pantalla actual
// Metodos privados
void init();

View File

@@ -41,7 +41,7 @@ void handleInputEvents(const SDL_Event &event) {
}
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event) {
void handle(const SDL_Event &event) {
switch (event.type) {
case SDL_EVENT_QUIT: // Evento de salida de la aplicación
Section::name = Section::Name::QUIT;

View File

@@ -4,5 +4,5 @@
namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego
void check(const SDL_Event &event);
void handle(const SDL_Event &event);
} // namespace GlobalEvents

View File

@@ -101,14 +101,11 @@ auto saveToFile() -> bool {
// Opciones de ventana
file << "## WINDOW\n";
file << "\n";
file << "window.zoom=" << window.zoom << "\n";
// Opciones de video
file << "## VIDEO\n";
file << "\n## VIDEO\n";
file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n";
file << "\n";
file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n";
file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n";
@@ -117,9 +114,8 @@ auto saveToFile() -> bool {
file << "video.shaders=" << boolToString(video.shaders) << "\n";
// Opciones de audio
file << "\n\n## AUDIO\n";
file << "\n## AUDIO\n";
file << "## volume [0 .. 100]\n";
file << "\n";
file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
file << "audio.volume=" << audio.volume << "\n";
@@ -129,10 +125,9 @@ auto saveToFile() -> bool {
file << "audio.sound.volume=" << audio.sound.volume << "\n";
// Opciones del juego
file << "\n\n## GAME\n";
file << "\n## GAME\n";
file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
file << "## game.difficulty [" << static_cast<int>(Difficulty::Code::EASY) << ": easy, " << static_cast<int>(Difficulty::Code::NORMAL) << ": normal, " << static_cast<int>(Difficulty::Code::HARD) << ": hard]\n";
file << "\n";
file << "game.language=" << static_cast<int>(settings.language) << "\n";
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
@@ -140,11 +135,11 @@ auto saveToFile() -> bool {
file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
// Opciones de mandos
file << "\n\n## CONTROLLERS\n";
file << "\n## CONTROLLERS\n";
gamepad_manager.saveToFile(file);
// Opciones de teclado
file << "\n\n## KEYBOARD\n";
file << "\n## KEYBOARD\n";
file << "keyboard.player=" << static_cast<int>(keyboard.player_id) << "\n";
// Cierra el fichero

View File

@@ -14,6 +14,7 @@
#include "param.h" // Para Param, ParamGame, param
#include "scoreboard.h" // Para Scoreboard
#include "stage.h" // Para power_can_be_added
#include "stage_interface.h" // Para IStageInfo
#include "texture.h" // Para Texture
#ifdef _DEBUG
#include <iostream>
@@ -26,6 +27,7 @@ Player::Player(const Config &config)
enter_name_(std::make_unique<EnterName>()),
hi_score_table_(config.hi_score_table),
glowing_entry_(config.glowing_entry),
stage_info_(config.stage_info),
play_area_(*config.play_area),
id_(config.id),
default_pos_x_(config.x),
@@ -179,6 +181,9 @@ void Player::move() {
case State::WAITING:
handleWaitingMovement();
break;
case State::RECOVER:
handleRecoverMovement();
break;
default:
break;
}
@@ -196,6 +201,11 @@ void Player::handlePlayingMovement() {
shiftSprite();
}
void Player::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_brbrbr.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
}
void Player::handleRollingMovement() {
handleRollingBoundaryCollision();
handleRollingGroundCollision();
@@ -463,6 +473,9 @@ void Player::setAnimation() {
player_sprite_->setFlip(flipMode);
break;
}
case State::RECOVER:
player_sprite_->setCurrentAnimation("recover");
break;
case State::WAITING:
case State::GAME_OVER:
player_sprite_->setCurrentAnimation("hello");
@@ -588,10 +601,10 @@ void Player::update() {
}
// Incrementa la puntuación del jugador
void Player::addScore(int score, int last_hi_score_entry) {
void Player::addScore(int score, int lowest_hi_score_entry) {
if (isPlaying()) {
score_ += score;
qualifies_for_high_score_ = score_ > last_hi_score_entry;
qualifies_for_high_score_ = score_ > lowest_hi_score_entry;
}
}
@@ -625,6 +638,9 @@ void Player::setPlayingState(State state) {
playing_state_ = state;
switch (playing_state_) {
case State::RECOVER: {
break;
}
case State::RESPAWNING: {
playSound("voice_thankyou.wav");
setPlayingState(State::PLAYING);
@@ -635,7 +651,7 @@ void Player::setPlayingState(State state) {
init();
setInvulnerable(true);
setScoreboardMode(Scoreboard::Mode::SCORE);
Stage::power_can_be_added = true;
stage_info_->canCollectPower();
break;
}
case State::CONTINUE: {
@@ -690,7 +706,7 @@ void Player::setPlayingState(State state) {
case State::TITLE_ANIMATION: {
// Activa la animación de rodar
player_sprite_->setCurrentAnimation("walk");
playSound("voice_thankyou.wav");
playSound("voice_credit_thankyou.wav");
break;
}
case State::TITLE_HIDDEN: {
@@ -929,7 +945,6 @@ void Player::playSound(const std::string &name) const {
// Indica si se puede dibujar el objeto
auto Player::isRenderable() const -> bool {
// return !isGameOver() && !isTitleHidden();
return !isTitleHidden();
};
@@ -947,4 +962,9 @@ void Player::addScoreToScoreBoard() const {
}
manager->saveToFile(Asset::get()->get("score.bin"));
}
void Player::addCredit() {
++credits_used_;
playSound("credit.wav");
}

View File

@@ -12,6 +12,7 @@
#include "input.h" // Para Input
#include "manage_hiscore_table.h" // Para Table
#include "scoreboard.h" // Para Scoreboard
#include "stage_interface.h" // Para IStageInfo
#include "utils.h" // Para Circle
class Texture;
@@ -71,7 +72,8 @@ class Player {
CREDITS, // Estado para mostrar los créditos del juego
TITLE_ANIMATION, // Animacion para el titulo
TITLE_HIDDEN, // Animacion para el titulo
RESPAWNING, // Tras continuar y volver al juego
RECOVER, // Al aceptar continuar
RESPAWNING, // Tras continuar y dar las gracias, otorga inmunidad y vuelve al juego
};
struct Config {
@@ -82,8 +84,9 @@ class Player {
SDL_FRect *play_area; // Usamos puntero para mantener la referencia
std::vector<std::shared_ptr<Texture>> texture;
std::vector<std::vector<std::string>> animations;
Table *hi_score_table; // También como puntero para referencia
int *glowing_entry; // Puntero para mantener la referencia
Table *hi_score_table; // También como puntero para referencia
int *glowing_entry; // Puntero para mantener la referencia
IStageInfo *stage_info; // Puntero para el gestor de pantallas
};
// --- Constructor y destructor ---
@@ -111,9 +114,9 @@ class Player {
void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador ---
void addScore(int score, int last_hi_score_entry); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador
void addScore(int score, int lowest_hi_score_entry); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador
// --- Estados de juego ---
void setPlayingState(State state); // Cambia el estado de juego
@@ -181,8 +184,8 @@ class Player {
void setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; }
void setScoreMultiplier(float value) { score_multiplier_ = value; }
void setWalkingState(State state) { walking_state_ = state; }
void addCredit() { ++credits_used_; }
void addCredit();
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; }
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
@@ -204,6 +207,7 @@ class Player {
std::shared_ptr<Input::Gamepad> gamepad_ = nullptr; // Dispositivo asociado
Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones
int *glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar
IStageInfo *stage_info_; // Informacion de la pantalla actual
std::string name_; // Nombre del jugador
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
@@ -277,6 +281,7 @@ class Player {
void transitionToCooling(); // Cambia el estado actual al de enfriamiento (por ejemplo, tras una ráfaga o sobrecalentamiento)
void completeCooling(); // Finaliza el proceso de enfriamiento y restablece el estado listo para disparar
void handlePlayingMovement(); // Gestiona el movimiento del personaje u objeto durante el estado de juego activo
void handleRecoverMovement(); // Comprueba si ha acabado la animación
void handleRollingMovement(); // Actualiza la lógica de movimiento de "rodar" (posiblemente tras impacto o acción especial)
void handleRollingBoundaryCollision(); // Detecta y maneja colisiones del objeto rodante con los límites de la pantalla
void handleRollingGroundCollision(); // Gestiona la interacción del objeto rodante con el suelo (rebotes, frenado, etc.)

View File

@@ -241,7 +241,7 @@ void Scoreboard::renderGameOverMode() {
void Scoreboard::renderStageInfoMode() {
// STAGE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + std::to_string(stage_), 1, text_color1_);
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
// POWERMETER
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);

View File

@@ -35,7 +35,7 @@ constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner";
// Constructor
Credits::Credits()
: balloon_manager_(std::make_unique<BalloonManager>()),
: balloon_manager_(std::make_unique<BalloonManager>(nullptr)),
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
@@ -124,7 +124,7 @@ void Credits::render() {
void Credits::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -42,7 +42,7 @@
#include "texture.h" // Para Texture
#include "ui/service_menu.h" // Para ServiceMenu
#ifdef _DEBUG
#include <iostream> // Para Notifier
#include <iostream> // Para std::cout
#include "ui/notifier.h" // Para Notifier
#endif
@@ -52,12 +52,13 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
: renderer_(Screen::get()->getRenderer()),
screen_(Screen::get()),
input_(Input::get()),
background_(std::make_unique<Background>()),
canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)),
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
stage_manager_(std::make_unique<StageManager>()),
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
background_(std::make_unique<Background>(stage_manager_->getTotalPowerNeededToCompleteGame())),
fade_in_(std::make_unique<Fade>()),
fade_out_(std::make_unique<Fade>()),
balloon_manager_(std::make_unique<BalloonManager>()),
tabe_(std::make_unique<Tabe>()),
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
// Pasa variables
@@ -66,8 +67,9 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
// Otras variables
Section::name = Section::Name::GAME;
Section::options = Section::Options::NONE;
Stage::init();
Stage::number = current_stage;
stage_manager_->initialize(Asset::get()->get("stages.txt"));
stage_manager_->setPowerChangeCallback([this](int amount) { background_->incrementProgress(amount); });
stage_manager_->jumpToStage(current_stage);
// Asigna texturas y animaciones
setResources();
@@ -99,7 +101,6 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
initDifficultyVars();
initDemo(player_id);
initPaths();
setTotalPower();
// Registra callbacks
ServiceMenu::get()->setStateChangeCallback([this](bool is_active) {
@@ -108,15 +109,6 @@ Game::Game(Player::Id player_id, int current_stage, bool demo)
pause_manager_->setServiceMenuPause(is_active);
}
});
#ifdef _DEBUG
// Si se empieza en una fase que no es la primera
if (!demo_.enabled) {
for (int i = 0; i < Stage::number; ++i) {
Stage::total_power += Stage::get(i).power_to_complete;
}
}
#endif
}
Game::~Game() {
@@ -262,33 +254,60 @@ void Game::renderPlayers() {
// Comprueba si hay cambio de fase y actualiza las variables
void Game::updateStage() {
if (Stage::power >= Stage::get(Stage::number).power_to_complete) {
// Cambio de fase
Stage::power = Stage::get(Stage::number).power_to_complete - Stage::power;
++Stage::number;
playSound("stage_change.wav");
balloon_manager_->resetBalloonSpeed();
screen_->flash(FLASH_COLOR, 3);
screen_->shake();
if (!stage_manager_->isCurrentStageCompleted()) {
return; // No hay cambio de fase
}
// Escribe el texto por pantalla
if (Stage::number < 10) {
std::vector<Path> paths = {paths_.at(2), paths_.at(3)};
if (Stage::number == 9) {
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
} else {
auto text = Resource::get()->getText("04b_25_2x");
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") + std::to_string(10 - Stage::number) + Lang::getText("[GAME_TEXT] 2A");
createMessage(paths, text->writeToTexture(CAPTION, 1, -4));
}
}
// Calcular poder sobrante antes de avanzar
int power_overflow = 0;
auto current_stage = stage_manager_->getCurrentStage();
if (current_stage.has_value()) {
int current_power = stage_manager_->getCurrentPower();
int power_needed = current_stage->getPowerToComplete();
power_overflow = current_power - power_needed; // Poder que sobra
}
// Modifica el color de fondo al llegar a la Fase 10
if (Stage::number == 9) {
background_->setColor(Color(0xdd, 0x19, 0x1d).DARKEN());
background_->setAlpha(96);
// Intentar avanzar a la siguiente fase
if (!stage_manager_->advanceToNextStage()) {
// No se pudo avanzar (probablemente juego completado)
return;
}
// Aplicar el poder sobrante a la nueva fase
if (power_overflow > 0) {
stage_manager_->addPower(power_overflow);
}
// Efectos de cambio de fase
playSound("stage_change.wav");
balloon_manager_->resetBalloonSpeed();
screen_->flash(FLASH_COLOR, 3);
screen_->shake();
// Obtener datos de la nueva fase
size_t current_stage_index = stage_manager_->getCurrentStageIndex();
size_t total_stages = stage_manager_->getTotalStages();
// Escribir texto por pantalla
if (current_stage_index < total_stages) { // No es la última fase
std::vector<Path> paths = {paths_.at(2), paths_.at(3)};
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
} else {
auto text = Resource::get()->getText("04b_25_2x");
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
std::to_string(total_stages - current_stage_index) +
Lang::getText("[GAME_TEXT] 2A");
createMessage(paths, text->writeToTexture(CAPTION, 1, -4));
}
}
// Modificar color de fondo en la última fase
if (current_stage_index == total_stages - 1) { // Última fase
background_->setColor(Color(0xdd, 0x19, 0x1d).DARKEN());
background_->setAlpha(96);
}
}
// Actualiza el estado de fin de la partida
@@ -330,11 +349,9 @@ void Game::updateGameStateGameOver() {
if (fade_out_->hasEnded()) {
if (game_completed_counter_ > 0) {
// Los jugadores han completado el juego
Section::name = Section::Name::CREDITS;
Section::name = Section::Name::CREDITS; // Los jugadores han completado el juego
} else {
// La partida ha terminado con la derrota de los jugadores
Section::name = Section::Name::HI_SCORE_TABLE;
Section::name = Section::Name::HI_SCORE_TABLE; // La partida ha terminado con la derrota de los jugadores
}
Section::options = Section::Options::HI_SCORE_AFTER_PLAYING;
if (Options::audio.enabled) {
@@ -362,13 +379,11 @@ void Game::updateGameStateCompleted() {
// Para la música y elimina todos los globos e items
if (game_completed_counter_ == 0) {
stopMusic();
Stage::number = 9; // Deja el valor dentro de los limites
stopMusic(); // Detiene la música
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
playSound("power_ball_explosion.wav");
destroyAllItems(); // Destruye todos los items
Stage::power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos los globos
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
destroyAllItems(); // Destruye todos los items
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
}
// Comienza las celebraciones
@@ -409,8 +424,10 @@ void Game::updateGameStateCompleted() {
// Comprueba el estado del juego
void Game::checkState() {
if (state_ != State::COMPLETED && Stage::number == 10) {
// if (state_ != State::COMPLETED && Stage::number == 10) {
if (state_ != State::COMPLETED && stage_manager_->isGameCompleted()) {
setState(State::COMPLETED);
background_->setState(Background::State::COMPLETED);
}
if (state_ != State::GAME_OVER && allPlayersAreGameOver()) {
@@ -523,11 +540,11 @@ void Game::checkBulletCollision() {
}
if (checkBulletTabeCollision(bullet)) {
break; // Exit early if bullet hit Tabe
break;
}
if (checkBulletBalloonCollision(bullet)) {
break; // Exit early if bullet hit balloon
break;
}
}
}
@@ -608,13 +625,12 @@ void Game::handleItemDrop(std::shared_ptr<Balloon> balloon, std::shared_ptr<Play
// Maneja la destrucción del globo y puntuación
void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) {
evaluateAndSetMenace();
if (player->isPlaying()) {
auto const SCORE = balloon_manager_->popBalloon(balloon) * player->getScoreMultiplier() * difficulty_score_multiplier_;
player->addScore(SCORE, Options::settings.hi_score_table.back().score);
player->incScoreMultiplier();
}
setMenace();
updateHiScore();
}
@@ -852,8 +868,7 @@ void Game::renderPathSprites() {
// Acciones a realizar cuando el jugador colisiona con un globo
void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_ptr<Balloon> &balloon) {
if (!player->isPlaying() || player->isInvulnerable()) {
// Si no está jugando o tiene inmunidad, no hace nada
return;
return; // Si no está jugando o tiene inmunidad, no hace nada
}
// Si tiene cafes
@@ -876,7 +891,7 @@ void Game::handlePlayerCollision(std::shared_ptr<Player> &player, std::shared_pt
player->setPlayingState(Player::State::ROLLING);
sendPlayerToTheBack(player);
if (allPlayersAreNotPlaying()) {
Stage::power_can_be_added = false; // No se puede subir poder de fase si no hay nadie jugando
stage_manager_->disablePowerCollection();
}
}
}
@@ -961,36 +976,12 @@ void Game::updateGameStates() {
// Actualiza el fondo
void Game::updateBackground() {
// Si el juego está completado, se reduce la velocidad de las nubes
if (state_ == State::COMPLETED) {
Stage::total_power = (Stage::total_power > 200) ? (Stage::total_power - 25) : 200;
}
// Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego
constexpr float CLOUDS_INITIAL_SPEED = 0.05F;
constexpr float CLOUDS_FINAL_SPEED = 2.00F - CLOUDS_INITIAL_SPEED;
const float CLOUDS_SPEED = (-CLOUDS_INITIAL_SPEED) + (-CLOUDS_FINAL_SPEED * (static_cast<float>(Stage::total_power) / total_power_to_complete_game_));
background_->setCloudsSpeed(CLOUDS_SPEED);
// Calcula la transición de los diferentes fondos
constexpr float NUM = 1525.0F; // total_power_to_complete div 4
const float GRADIENT_NUMBER = std::min(Stage::total_power / NUM, 3.0F);
const float PERCENT = GRADIENT_NUMBER - static_cast<int>(GRADIENT_NUMBER);
background_->setGradientNumber(static_cast<int>(GRADIENT_NUMBER));
background_->setTransition(PERCENT);
// Calcula la posición del sol
constexpr float SUN_FINAL_POWER = NUM * 2;
background_->setSunProgression(Stage::total_power / SUN_FINAL_POWER);
background_->setMoonProgression(Stage::total_power / static_cast<float>(total_power_to_complete_game_));
// Actualiza el objeto
background_->update();
}
// Dibuja los elementos de la zona de juego en su textura
void Game::fillCanvas() {
// Dibujamos el contenido de la zona de juego en su textura
// Dibuja el contenido de la zona de juego en su textura
auto *temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, canvas_);
@@ -1029,7 +1020,7 @@ void Game::run() {
checkInput();
#endif
update();
checkEvents(); // Tiene que ir antes del render
handleEvents(); // Tiene que ir antes del render
render();
}
}
@@ -1090,7 +1081,7 @@ void Game::initPaths() {
// Actualiza las variables de ayuda
void Game::updateHelper() {
// Solo ofrece ayuda cuando la amenaza es elevada
if (menace_current_ > 15) {
if (menace_ > 15) {
helper_.need_coffee = true;
helper_.need_coffee_machine = true;
for (const auto &player : players_) {
@@ -1135,7 +1126,7 @@ auto Game::allPlayersAreNotPlaying() -> bool {
}
// Comprueba los eventos que hay en cola
void Game::checkEvents() {
void Game::handleEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
@@ -1152,9 +1143,9 @@ void Game::checkEvents() {
}
#ifdef _DEBUG
checkDebugEvents(event);
handleDebugEvents(event);
#endif
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}
@@ -1166,12 +1157,11 @@ void Game::updateScoreboard() {
}
// Resto de marcador
scoreboard_->setStage(Stage::number + 1);
scoreboard_->setPower((float)Stage::power / (float)Stage::get(Stage::number).power_to_complete);
scoreboard_->setStage(stage_manager_->getCurrentStageIndex() + 1);
scoreboard_->setPower(stage_manager_->getCurrentStageProgressFraction());
scoreboard_->setHiScore(hi_score_.score);
scoreboard_->setHiScoreName(hi_score_.name);
// Lógica del marcador
scoreboard_->update();
}
@@ -1411,7 +1401,7 @@ void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire
// Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
player->setPlayingState(Player::State::RESPAWNING);
player->setPlayingState(Player::State::RECOVER);
player->addCredit();
sendPlayerToTheFront(player);
}
@@ -1488,12 +1478,8 @@ void Game::initDemo(Player::Id player_id) {
constexpr auto NUM_DEMOS = 3;
const auto DEMO = rand() % NUM_DEMOS;
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
Stage::number = STAGES[DEMO];
}
// Actualiza el numero de globos explotados según la fase del modo demostración
for (int i = 0; i < Stage::number; ++i) {
Stage::total_power += Stage::get(i).power_to_complete;
// Stage::number = STAGES[DEMO];
stage_manager_->jumpToStage(STAGES.at(DEMO));
}
// Activa o no al otro jugador
@@ -1529,14 +1515,6 @@ void Game::initDemo(Player::Id player_id) {
demo_.counter = 0;
}
// Calcula el poder total necesario para completar el juego
void Game::setTotalPower() {
total_power_to_complete_game_ = 0;
for (const auto &stage : Stage::stages) {
total_power_to_complete_game_ += stage.power_to_complete;
}
}
// Inicializa el marcador
void Game::initScoreboard() {
scoreboard_->setPos(param.scoreboard.rect);
@@ -1596,6 +1574,7 @@ void Game::initPlayers(Player::Id player_id) {
config_player1.animations = player_animations_;
config_player1.hi_score_table = &Options::settings.hi_score_table;
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
config_player1.stage_info = stage_manager_.get();
auto player1 = std::make_unique<Player>(config_player1);
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
@@ -1616,6 +1595,7 @@ void Game::initPlayers(Player::Id player_id) {
config_player2.animations = player_animations_;
config_player2.hi_score_table = &Options::settings.hi_score_table;
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
config_player2.stage_info = stage_manager_.get();
auto player2 = std::make_unique<Player>(config_player2);
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
@@ -1720,7 +1700,7 @@ void Game::updateGameStateFadeIn() {
if (fade_in_->hasEnded()) {
setState(State::ENTERING_PLAYER);
balloon_manager_->createTwoBigBalloons();
evaluateAndSetMenace();
setMenace();
}
}
@@ -1755,7 +1735,8 @@ void Game::updateGameStateShowingGetReadyMessage() {
void Game::updateGameStatePlaying() {
#ifdef _DEBUG
if (auto_pop_balloons_) {
Stage::addPower(5);
// Stage::addPower(5);
stage_manager_->addPower(5);
}
#endif
updatePlayers();
@@ -1789,25 +1770,31 @@ void Game::cleanVectors() {
// Gestiona el nivel de amenaza
void Game::updateMenace() {
if (state_ == State::PLAYING) {
const auto STAGE = Stage::get(Stage::number);
const float PERCENT = Stage::power / STAGE.power_to_complete;
const int DIFFERENCE = STAGE.max_menace - STAGE.min_menace;
if (state_ != State::PLAYING) {
return;
}
// Aumenta el nivel de amenaza en función de la puntuación
menace_threshold_ = STAGE.min_menace + (DIFFERENCE * PERCENT);
auto current_stage = stage_manager_->getCurrentStage();
if (!current_stage.has_value()) {
return;
}
// Si el nivel de amenza es inferior al umbral
if (menace_current_ < menace_threshold_) {
balloon_manager_->deployRandomFormation(Stage::number); // Crea una formación aleatoria de globos
evaluateAndSetMenace(); // Recalcula el nivel de amenaza con el nuevo globo
}
const auto &stage = current_stage.value();
const double fraction = stage_manager_->getCurrentStageProgressFraction();
const int difference = stage.getMaxMenace() - stage.getMinMenace();
// Aumenta el nivel de amenaza en función del progreso de la fase
menace_threshold_ = stage.getMinMenace() + (difference * fraction);
if (menace_ < menace_threshold_) {
balloon_manager_->deployRandomFormation(stage_manager_->getCurrentStageIndex());
setMenace();
}
}
// Calcula y establece el valor de amenaza en funcion de los globos activos
void Game::evaluateAndSetMenace() {
menace_current_ = balloon_manager_->getMenace();
void Game::setMenace() {
menace_ = balloon_manager_->getMenace();
}
// Actualiza la velocidad de los globos en funcion del poder acumulado de la fase
@@ -1816,7 +1803,7 @@ void Game::checkAndUpdateBalloonSpeed() {
return;
}
const float PERCENT = static_cast<float>(Stage::power) / Stage::get(Stage::number).power_to_complete;
const float PERCENT = stage_manager_->getCurrentStageProgressFraction();
constexpr std::array<float, 4> THRESHOLDS = {0.2F, 0.4F, 0.6F, 0.8F};
for (size_t i = 0; i < std::size(THRESHOLDS); ++i) {
@@ -1890,13 +1877,13 @@ void Game::onPauseStateChanged(bool is_paused) {
#ifdef _DEBUG
// Comprueba los eventos en el modo DEBUG
void Game::checkDebugEvents(const SDL_Event &event) {
void Game::handleDebugEvents(const SDL_Event &event) {
static int formation_id_ = 0;
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
switch (event.key.key) {
case SDLK_1: // Crea una powerball
{
// balloon_manager_->createPowerBall();
balloon_manager_->createPowerBall();
// throwCoffee(players_.at(0)->getPosX() + (players_.at(0)->getWidth() / 2), players_.at(0)->getPosY() + (players_.at(0)->getHeight() / 2));
break;
}

View File

@@ -13,6 +13,7 @@
#include "path_sprite.h" // Para PathSprite, Path
#include "player.h" // Para Player
#include "smart_sprite.h" // Para SmartSprite
#include "stage.h" // Para StageManager
#include "utils.h" // Para Demo
class Background;
@@ -100,8 +101,6 @@ class Game {
Input *input_; // Manejador de entrada
Scoreboard *scoreboard_; // Objeto para dibujar el marcador
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
SDL_Texture *canvas_; // Textura para dibujar la zona de juego
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores
@@ -119,9 +118,11 @@ class Game {
std::vector<std::vector<std::string>> player_animations_; // Vector con las animaciones del jugador
std::unique_ptr<PauseManager> pause_manager_; // Objeto para gestionar la pausa
std::unique_ptr<StageManager> stage_manager_; // Objeto para gestionar las fases
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego
std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades
std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades
std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos
std::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor
std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados
@@ -141,8 +142,7 @@ class Game {
int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más globos
int game_over_counter_ = GAME_OVER_COUNTER; // Contador para el estado de fin de partida
int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido
int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases
int menace_current_ = 0; // Nivel de amenaza actual
int menace_ = 0; // Nivel de amenaza actual
int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
State state_ = State::FADE_IN; // Estado
std::vector<std::shared_ptr<Player>> players_to_put_at_back_;
@@ -156,7 +156,7 @@ class Game {
// --- Ciclo principal del juego ---
void update(); // Actualiza la lógica principal del juego
void render(); // Renderiza todos los elementos del juego
void checkEvents(); // Procesa los eventos del sistema en cola
void handleEvents(); // Procesa los eventos del sistema en cola
void checkState(); // Verifica y actualiza el estado actual del juego
void setState(State state); // Cambia el estado del juego
void cleanVectors(); // Limpia vectores de elementos deshabilitados
@@ -263,8 +263,8 @@ class Game {
void initDifficultyVars(); // Inicializa variables de dificultad
// --- Sistema de amenaza ---
void updateMenace(); // Gestiona el nivel de amenaza del juego
void evaluateAndSetMenace(); // Calcula y establece amenaza según globos activos
void updateMenace(); // Gestiona el nivel de amenaza del juego
void setMenace(); // Calcula y establece amenaza según globos activos
// --- Puntuación y marcador ---
void updateHiScore(); // Actualiza el récord máximo si es necesario
@@ -300,6 +300,6 @@ class Game {
// --- Depuración (solo en modo DEBUG) ---
#ifdef _DEBUG
void checkDebugEvents(const SDL_Event &event); // Comprueba los eventos en el modo DEBUG
void handleDebugEvents(const SDL_Event &event); // Comprueba los eventos en el modo DEBUG
#endif
};

View File

@@ -108,7 +108,7 @@ void HiScoreTable::fillTexture() {
void HiScoreTable::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -245,7 +245,7 @@ void Instructions::render() {
void Instructions::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -47,7 +47,7 @@ Intro::Intro()
void Intro::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -68,7 +68,7 @@ Logo::~Logo() {
void Logo::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -117,7 +117,7 @@ void Title::checkEvents() {
handleKeyDownEvent(event);
}
GlobalEvents::check(event);
GlobalEvents::handle(event);
}
}

View File

@@ -1,43 +1,300 @@
#include "stage.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <algorithm> // Para min
#include <vector> // Para vector
// Implementación de StageData
StageData::StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name)
: power_to_complete_(power_to_complete), min_menace_(min_menace),
max_menace_(max_menace), name_(name), status_(StageStatus::LOCKED) {}
namespace Stage {
std::vector<Stage> stages; // Variable con los datos de cada pantalla
int power = 0; // Poder acumulado en la fase
int total_power = 0; // Poder total necesario para completar el juego
int number = 0; // Fase actual
bool power_can_be_added = true; // Habilita la recolecta de poder
// Devuelve una fase
auto get(int index) -> Stage { return stages.at(std::min(9, index)); }
// Inicializa las variables del namespace Stage
void init() {
stages.clear();
stages.emplace_back(200, 7 + (4 * 1), 7 + (4 * 3));
stages.emplace_back(300, 7 + (4 * 2), 7 + (4 * 4));
stages.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5));
stages.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5));
stages.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6));
stages.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6));
stages.emplace_back(650, 7 + (4 * 5), 7 + (4 * 7));
stages.emplace_back(750, 7 + (4 * 5), 7 + (4 * 7));
stages.emplace_back(850, 7 + (4 * 6), 7 + (4 * 8));
stages.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10));
power = 0;
total_power = 0;
number = 0;
// Implementación de StageManager
StageManager::StageManager()
: current_power_(0), total_power_(0), current_stage_index_(0),
power_collection_state_(PowerCollectionState::ENABLED),
power_change_callback_(nullptr) {
initialize();
}
// Añade poder
void addPower(int amount) {
if (power_can_be_added) {
power += amount;
total_power += amount;
void StageManager::initialize() {
stages_.clear();
createDefaultStages();
reset();
}
void StageManager::initialize(const std::string& stages_file) {
stages_.clear();
// Intentar cargar desde archivo, si falla usar valores predeterminados
if (!loadStagesFromFile(stages_file)) {
createDefaultStages();
}
reset();
}
} // namespace Stage
void StageManager::reset() {
current_power_ = 0;
total_power_ = 0;
current_stage_index_ = 0;
power_collection_state_ = PowerCollectionState::ENABLED;
updateStageStatuses();
}
void StageManager::createDefaultStages() {
// Crear las 10 fases predeterminadas con dificultad progresiva
stages_.emplace_back(200, 7 + (4 * 1), 7 + (4 * 3), "Tutorial");
stages_.emplace_back(300, 7 + (4 * 2), 7 + (4 * 4), "Primeros pasos");
stages_.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5), "Intensificación");
stages_.emplace_back(600, 7 + (4 * 3), 7 + (4 * 5), "Persistencia");
stages_.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6), "Desafío medio");
stages_.emplace_back(600, 7 + (4 * 4), 7 + (4 * 6), "Resistencia");
stages_.emplace_back(650, 7 + (4 * 5), 7 + (4 * 7), "Aproximación final");
stages_.emplace_back(750, 7 + (4 * 5), 7 + (4 * 7), "Penúltimo obstáculo");
stages_.emplace_back(850, 7 + (4 * 6), 7 + (4 * 8), "Clímax");
stages_.emplace_back(950, 7 + (4 * 7), 7 + (4 * 10), "Maestría");
}
bool StageManager::loadStagesFromFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
return false; // No se pudo abrir el archivo
}
std::string line;
while (std::getline(file, line)) {
// Ignorar líneas vacías y comentarios (líneas que empiezan con #)
if (line.empty() || line[0] == '#') {
continue;
}
// Parsear línea: power_to_complete,min_menace,max_menace,name
std::stringstream ss(line);
std::string token;
std::vector<std::string> tokens;
// Dividir por comas
while (std::getline(ss, token, ',')) {
// Eliminar espacios en blanco al inicio y final
token.erase(0, token.find_first_not_of(" \t"));
token.erase(token.find_last_not_of(" \t") + 1);
tokens.push_back(token);
}
// Verificar que tenemos exactamente 4 campos
if (tokens.size() != 4) {
// Error de formato, continuar con la siguiente línea
continue;
}
try {
// Convertir a enteros los primeros tres campos
int power_to_complete = std::stoi(tokens[0]);
int min_menace = std::stoi(tokens[1]);
int max_menace = std::stoi(tokens[2]);
std::string name = tokens[3];
// Validar valores
if (power_to_complete <= 0 || min_menace < 0 || max_menace < min_menace) {
continue; // Valores inválidos, saltar línea
}
// Crear y añadir la fase
stages_.emplace_back(power_to_complete, min_menace, max_menace, name);
} catch (const std::exception&) {
// Error de conversión, continuar con la siguiente línea
continue;
}
}
file.close();
// Verificar que se cargó al menos una fase
return !stages_.empty();
}
bool StageManager::advanceToNextStage() {
if (!isCurrentStageCompleted() || current_stage_index_ >= stages_.size() - 1) {
return false;
}
current_stage_index_++;
current_power_ = 0; // Reiniciar poder para la nueva fase
updateStageStatuses();
return true;
}
bool StageManager::jumpToStage(size_t target_stage_index) {
if (!validateStageIndex(target_stage_index)) {
return false;
}
// Calcular el poder acumulado hasta la fase objetivo
int accumulated_power = 0;
for (size_t i = 0; i < target_stage_index; ++i) {
accumulated_power += stages_[i].getPowerToComplete();
}
// Actualizar estado
current_stage_index_ = target_stage_index;
current_power_ = 0; // Comenzar la fase objetivo sin poder
total_power_ = accumulated_power; // Poder total como si se hubieran completado las anteriores
updateStageStatuses();
return true;
}
bool StageManager::subtractPower(int amount) {
if (amount <= 0 || current_power_ < amount) {
return false;
}
current_power_ -= amount;
updateStageStatuses();
return true;
}
void StageManager::enablePowerCollection() {
power_collection_state_ = PowerCollectionState::ENABLED;
}
void StageManager::disablePowerCollection() {
power_collection_state_ = PowerCollectionState::DISABLED;
}
std::optional<StageData> StageManager::getCurrentStage() const {
return getStage(current_stage_index_);
}
std::optional<StageData> StageManager::getStage(size_t index) const {
if (!validateStageIndex(index)) {
return std::nullopt;
}
return stages_[index];
}
bool StageManager::isCurrentStageCompleted() const {
auto current_stage = getCurrentStage();
if (!current_stage.has_value()) {
return false;
}
return current_power_ >= current_stage->getPowerToComplete();
}
bool StageManager::isGameCompleted() const {
return current_stage_index_ >= stages_.size() - 1 && isCurrentStageCompleted();
}
double StageManager::getProgressPercentage() const {
if (stages_.empty()) return 0.0;
int total_power_needed = getTotalPowerNeededToCompleteGame();
if (total_power_needed == 0) return 100.0;
return (static_cast<double>(total_power_) / total_power_needed) * 100.0;
}
double StageManager::getCurrentStageProgressPercentage() const {
return getCurrentStageProgressFraction() * 100.0;
}
double StageManager::getCurrentStageProgressFraction() const {
auto current_stage = getCurrentStage();
if (!current_stage.has_value()) {
return 0.0;
}
int power_needed = current_stage->getPowerToComplete();
if (power_needed == 0) {
return 1.0;
}
// Devuelve una fracción entre 0.0 y 1.0
double fraction = static_cast<double>(current_power_) / power_needed;
return std::min(fraction, 1.0);
}
int StageManager::getPowerNeededForCurrentStage() const {
auto current_stage = getCurrentStage();
if (!current_stage.has_value()) {
return 0;
}
return std::max(0, current_stage->getPowerToComplete() - current_power_);
}
int StageManager::getTotalPowerNeededToCompleteGame() const {
int total_power_needed = 0;
for (const auto& stage : stages_) {
total_power_needed += stage.getPowerToComplete();
}
return total_power_needed;
}
// Implementación de la interfaz IStageInfo
bool StageManager::canCollectPower() const {
return power_collection_state_ == PowerCollectionState::ENABLED;
}
void StageManager::addPower(int amount) {
if (amount <= 0 || !canCollectPower()) {
return;
}
current_power_ += amount;
total_power_ += amount;
// Ejecutar callback si está registrado
if (power_change_callback_) {
power_change_callback_(amount);
}
// Verificar si se completó la fase actual
if (isCurrentStageCompleted()) {
auto current_stage = getCurrentStage();
if (current_stage.has_value()) {
stages_[current_stage_index_].setStatus(StageStatus::COMPLETED);
}
}
updateStageStatuses();
}
int StageManager::getCurrentMenaceLevel() const {
auto current_stage = getCurrentStage();
if (!current_stage.has_value()) {
return 0;
}
return current_stage->getMinMenace();
}
// Gestión de callbacks
void StageManager::setPowerChangeCallback(PowerChangeCallback callback) {
power_change_callback_ = callback;
}
void StageManager::removePowerChangeCallback() {
power_change_callback_ = nullptr;
}
// Métodos privados
bool StageManager::validateStageIndex(size_t index) const {
return index < stages_.size();
}
void StageManager::updateStageStatuses() {
// Actualizar el estado de cada fase según su posición relativa a la actual
for (size_t i = 0; i < stages_.size(); ++i) {
if (i < current_stage_index_) {
stages_[i].setStatus(StageStatus::COMPLETED);
} else if (i == current_stage_index_) {
stages_[i].setStatus(StageStatus::IN_PROGRESS);
} else {
stages_[i].setStatus(StageStatus::LOCKED);
}
}
}

View File

@@ -1,33 +1,111 @@
#pragma once
#include <vector> // Para vector
#include "stage_interface.h"
#include <vector>
#include <optional>
#include <string>
#include <functional>
/*
Namespace Stage: gestiona los datos y operaciones de las fases del juego.
Permite consultar y modificar el poder necesario, la amenaza y el estado de cada fase.
*/
namespace Stage {
// --- Estructura con los datos de una fase ---
struct Stage {
int power_to_complete; // Cantidad de poder que se necesita para completar la fase
int min_menace; // Umbral mínimo de amenaza de la fase
int max_menace; // Umbral máximo de amenaza de la fase
// Constructor
Stage(int power_to_complete, int min_menace, int max_menace)
: power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {}
// --- Estados posibles para la recolección de poder ---
enum class PowerCollectionState {
ENABLED, // Recolección habilitada
DISABLED // Recolección deshabilitada
};
// --- Variables globales del estado de las fases ---
extern std::vector<Stage> stages; // Vector con los datos de cada pantalla
extern int power; // Poder acumulado en la fase actual
extern int total_power; // Poder total necesario para completar el juego
extern int number; // Índice de la fase actual
extern bool power_can_be_added; // Indica si se puede añadir poder a la fase
// --- Estados posibles para una fase del juego ---
enum class StageStatus {
LOCKED, // Fase bloqueada
IN_PROGRESS, // Fase en progreso
COMPLETED // Fase completada
};
// --- Funciones principales ---
auto get(int index) -> Stage; // Devuelve una fase por índice
void init(); // Inicializa las variables del namespace Stage
void addPower(int amount); // Añade poder a la fase actual
} // namespace Stage
// --- Representa los datos de una fase del juego ---
class StageData {
private:
int power_to_complete_; // Poder necesario para completar la fase
int min_menace_; // Nivel mínimo de amenaza
int max_menace_; // Nivel máximo de amenaza
std::string name_; // Nombre de la fase
StageStatus status_; // Estado actual de la fase
public:
// Constructor de una fase
StageData(int power_to_complete, int min_menace, int max_menace, const std::string& name = "");
// --- Getters ---
int getPowerToComplete() const { return power_to_complete_; }
int getMinMenace() const { return min_menace_; }
int getMaxMenace() const { return max_menace_; }
const std::string& getName() const { return name_; }
StageStatus getStatus() const { return status_; }
// --- Setters ---
void setStatus(StageStatus status) { status_ = status; }
// --- Utilidades ---
bool isCompleted() const { return status_ == StageStatus::COMPLETED; }
};
// --- Gestor principal del sistema de fases del juego ---
class StageManager : public IStageInfo {
private:
std::vector<StageData> stages_; // Lista de todas las fases
int current_power_; // Poder actual en la fase activa
int total_power_; // Poder total acumulado en todo el juego
size_t current_stage_index_; // Índice de la fase actual
PowerCollectionState power_collection_state_; // Estado de recolección de poder
public:
using PowerChangeCallback = std::function<void(int)>;
StageManager();
// --- Métodos principales del juego ---
void initialize(); // Inicializa el gestor de fases
void initialize(const std::string& stages_file); // Inicializa con archivo personalizado
void reset(); // Reinicia el progreso del juego
bool advanceToNextStage(); // Avanza a la siguiente fase
// --- Gestión de poder ---
bool subtractPower(int amount); // Resta poder de la fase actual
void enablePowerCollection(); // Habilita la recolección de poder
void disablePowerCollection(); // Deshabilita la recolección de poder
// --- Navegación ---
bool jumpToStage(size_t target_stage_index); // Salta a una fase específica
// --- Consultas de estado ---
std::optional<StageData> getCurrentStage() const; // Obtiene la fase actual
std::optional<StageData> getStage(size_t index) const; // Obtiene una fase específica
size_t getCurrentStageIndex() const { return current_stage_index_; }
int getCurrentPower() const { return current_power_; }
int getTotalPower() const { return total_power_; }
int getTotalPowerNeededToCompleteGame() const; // Poder total necesario para completar el juego
size_t getTotalStages() const { return stages_.size(); }
// --- Seguimiento de progreso ---
bool isCurrentStageCompleted() const; // Verifica si la fase actual está completada
bool isGameCompleted() const; // Verifica si el juego está completado
double getProgressPercentage() const; // Progreso total del juego (0-100%)
double getCurrentStageProgressPercentage() const; // Progreso de la fase actual (0-100%)
double getCurrentStageProgressFraction() const; // Progreso de la fase actual (0.0-1.0)
int getPowerNeededForCurrentStage() const; // Poder restante para completar la fase actual
// --- Gestión de callbacks ---
void setPowerChangeCallback(PowerChangeCallback callback); // Establece callback para cambios de poder
void removePowerChangeCallback(); // Elimina callback de cambios de poder
// --- Implementación de la interfaz IStageInfo ---
bool canCollectPower() const override;
void addPower(int amount) override;
int getCurrentMenaceLevel() const override;
private:
PowerChangeCallback power_change_callback_; // Callback para notificar cambios de poder
// --- Métodos privados ---
void createDefaultStages(); // Crea las fases predeterminadas del juego
bool loadStagesFromFile(const std::string& filename); // Carga fases desde archivo
bool validateStageIndex(size_t index) const; // Valida que un índice de fase sea válido
void updateStageStatuses(); // Actualiza los estados de todas las fases
};

18
source/stage_interface.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
/**
* Interfaz para acceso a información de fases.
* Proporciona una API mínima para componentes que necesitan interactuar con datos de fases
* sin requerir acceso a toda la funcionalidad de StageManager.
*/
class IStageInfo {
public:
virtual ~IStageInfo() = default;
// Interfaz de recolección de poder
virtual bool canCollectPower() const = 0;
virtual void addPower(int amount) = 0;
// Ajuste de comportamiento del gameplay
virtual int getCurrentMenaceLevel() const = 0;
};