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_FOLDER := ccae_release
RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME) RELEASE_FILE := $(RELEASE_FOLDER)/$(TARGET_NAME)
RESOURCE_FILE := release/coffee.res 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 # Nombres para los ficheros de lanzamiento
WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip WINDOWS_RELEASE := $(TARGET_NAME)-$(VERSION)-win32-x64.zip
@@ -120,20 +135,24 @@ endif
# Reglas para compilación # Reglas para compilación
windows: windows:
@echo off @echo off
@echo Compilando para Windows con nombre: "$(APP_NAME).exe"
windres release/coffee.rc -O coff -o $(RESOURCE_FILE) windres release/coffee.rc -O coff -o $(RESOURCE_FILE)
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE).exe" $(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_TARGET_FILE).exe"
strip -s -R .comment -R .gnu.version "$(TARGET_FILE).exe" --strip-unneeded strip -s -R .comment -R .gnu.version "$(WIN_TARGET_FILE).exe" --strip-unneeded
windows_rec: windows_rec:
@echo off @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: windows_debug:
@echo off @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: windows_release:
@echo off @echo off
@echo Creando release para Windows - Version: $(VERSION)
# Crea carpeta temporal 'RELEASE_FOLDER' # Crea carpeta temporal 'RELEASE_FOLDER'
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force} powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
@@ -149,23 +168,27 @@ windows_release:
# Compila # Compila
windres release/coffee.rc -O coff -o $(RESOURCE_FILE) windres release/coffee.rc -O coff -o $(RESOURCE_FILE)
$(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(RELEASE_FILE).exe" $(CXX) $(APP_SOURCES) $(RESOURCE_FILE) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(WIN_RELEASE_FILE).exe"
strip -s -R .comment -R .gnu.version "$(RELEASE_FILE).exe" --strip-unneeded strip -s -R .comment -R .gnu.version "$(WIN_RELEASE_FILE).exe" --strip-unneeded
# Crea el fichero .zip # Crea el fichero .zip
powershell if (Test-Path "$(WINDOWS_RELEASE)") {Remove-Item "$(WINDOWS_RELEASE)"} powershell if (Test-Path "$(WINDOWS_RELEASE)") {Remove-Item "$(WINDOWS_RELEASE)"}
powershell Compress-Archive -Path "$(RELEASE_FOLDER)"/* -DestinationPath "$(WINDOWS_RELEASE)" powershell Compress-Archive -Path "$(RELEASE_FOLDER)"/* -DestinationPath "$(WINDOWS_RELEASE)"
@echo Release creado: $(WINDOWS_RELEASE)
# Elimina la carpeta temporal 'RELEASE_FOLDER' # Elimina la carpeta temporal 'RELEASE_FOLDER'
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force} powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
macos: macos:
@echo "Compilando para macOS: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)" $(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
macos_debug: macos_debug:
@echo "Compilando version debug para macOS: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug" $(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
macos_release: macos_release:
@echo "Creando release para macOS - Version: $(VERSION)"
# Elimina datos de compilaciones anteriores # Elimina datos de compilaciones anteriores
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
$(RMDIR) Frameworks $(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 create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)" hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_INTEL_RELEASE)"
$(RMFILE) tmp.dmg $(RMFILE) tmp.dmg
@echo "Release Intel creado: $(MACOS_INTEL_RELEASE)"
endif endif
# Compila la versión para procesadores Apple Silicon # Compila la versión para procesadores Apple Silicon
@@ -214,19 +238,23 @@ endif
hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)" hdiutil create tmp.dmg -ov -volname "$(APP_NAME)" -fs HFS+ -srcfolder "$(RELEASE_FOLDER)"
hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)" hdiutil convert tmp.dmg -format UDZO -o "$(MACOS_APPLE_SILICON_RELEASE)"
$(RMFILE) tmp.dmg $(RMFILE) tmp.dmg
@echo "Release Apple Silicon creado: $(MACOS_APPLE_SILICON_RELEASE)"
# Elimina las carpetas temporales # Elimina las carpetas temporales
$(RMDIR) Frameworks $(RMDIR) Frameworks
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
linux: linux:
@echo "Compilando para Linux: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)" $(CXX) $(APP_SOURCES) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) -o "$(TARGET_FILE)"
strip -s -R .comment -R .gnu.version "$(TARGET_FILE)" --strip-unneeded strip -s -R .comment -R .gnu.version "$(TARGET_FILE)" --strip-unneeded
linux_debug: linux_debug:
@echo "Compilando version debug para Linux: $(TARGET_NAME)_debug"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug" $(CXX) $(APP_SOURCES) $(INCLUDES) -DDEBUG -DVERBOSE $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
linux_release: linux_release:
@echo "Creando release para Linux - Version: $(VERSION)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -245,18 +273,117 @@ linux_release:
# Empaqueta ficheros # Empaqueta ficheros
$(RMFILE) "$(LINUX_RELEASE)" $(RMFILE) "$(LINUX_RELEASE)"
tar -czvf "$(LINUX_RELEASE)" -C "$(RELEASE_FOLDER)" . 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 # Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
raspi: raspi:
@echo "Compilando para Raspberry Pi: $(TARGET_NAME)"
$(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(TARGET_FILE) $(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(TARGET_FILE)
strip -s -R .comment -R .gnu.version $(TARGET_FILE) --strip-unneeded strip -s -R .comment -R .gnu.version $(TARGET_FILE) --strip-unneeded
raspi_debug: 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" $(CXX) $(APP_SOURCES) $(INCLUDES) -DVERBOSE -DDEBUG $(CXXFLAGS_DEBUG) $(LDFLAGS) -o "$(TARGET_FILE)_debug"
raspi_release: raspi_release:
@echo "Creando release para Raspberry Pi - Version: $(VERSION)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
@@ -275,11 +402,13 @@ raspi_release:
# Empaqueta ficheros # Empaqueta ficheros
$(RMFILE) "$(RASPI_RELEASE)" $(RMFILE) "$(RASPI_RELEASE)"
tar -czvf "$(RASPI_RELEASE)" -C "$(RELEASE_FOLDER)" . tar -czvf "$(RASPI_RELEASE)" -C "$(RELEASE_FOLDER)" .
@echo "Release creado: $(RASPI_RELEASE)"
# Elimina la carpeta temporal # Elimina la carpeta temporal
$(RMDIR) "$(RELEASE_FOLDER)" $(RMDIR) "$(RELEASE_FOLDER)"
anbernic: anbernic:
@echo "Compilando para Anbernic: $(TARGET_NAME)"
# Elimina carpetas previas # Elimina carpetas previas
$(RMDIR) "$(RELEASE_FOLDER)"_anbernic $(RMDIR) "$(RELEASE_FOLDER)"_anbernic
@@ -294,6 +423,32 @@ anbernic:
# Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake) # Opción para deshabilitar audio (equivalente a la opción DISABLE_AUDIO de CMake)
no_audio: no_audio:
@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" $(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] # Formato: TIPO|RUTA [|OPCIONES]
# Opciones: optional, absolute (separadas por comas) # Opciones: optional, absolute (separadas por comas)
# Variables: ${PREFIX}, ${SYSTEM_FOLDER} # Variables: ${PREFIX}, ${SYSTEM_FOLDER}
@@ -9,33 +9,35 @@ DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
# Archivos de configuración del juego # Archivos de configuración del juego
DATA|${PREFIX}/data/config/formations.txt
DATA|${PREFIX}/data/config/gamecontrollerdb.txt
DATA|${PREFIX}/data/config/param_320x240.txt DATA|${PREFIX}/data/config/param_320x240.txt
DATA|${PREFIX}/data/config/param_320x256.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/demo1.bin
DEMODATA|${PREFIX}/data/config/demo2.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 # Música
MUSIC|${PREFIX}/data/music/credits.ogg
MUSIC|${PREFIX}/data/music/intro.ogg MUSIC|${PREFIX}/data/music/intro.ogg
MUSIC|${PREFIX}/data/music/playing.ogg MUSIC|${PREFIX}/data/music/playing.ogg
MUSIC|${PREFIX}/data/music/title.ogg MUSIC|${PREFIX}/data/music/title.ogg
MUSIC|${PREFIX}/data/music/credits.ogg
# Sonidos # 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_bounce0.wav
SOUND|${PREFIX}/data/sound/balloon_bounce1.wav SOUND|${PREFIX}/data/sound/balloon_bounce1.wav
SOUND|${PREFIX}/data/sound/balloon_bounce2.wav SOUND|${PREFIX}/data/sound/balloon_bounce2.wav
SOUND|${PREFIX}/data/sound/balloon_bounce3.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/bullet.wav
SOUND|${PREFIX}/data/sound/clock.wav SOUND|${PREFIX}/data/sound/clock.wav
SOUND|${PREFIX}/data/sound/coffee_out.wav SOUND|${PREFIX}/data/sound/coffee_out.wav
SOUND|${PREFIX}/data/sound/continue_clock.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_drop.wav
SOUND|${PREFIX}/data/sound/debian_pickup.wav SOUND|${PREFIX}/data/sound/debian_pickup.wav
SOUND|${PREFIX}/data/sound/hi_score_achieved.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/tabe.wav
SOUND|${PREFIX}/data/sound/title.wav SOUND|${PREFIX}/data/sound/title.wav
SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav SOUND|${PREFIX}/data/sound/voice_aw_aw_aw.wav
SOUND|${PREFIX}/data/sound/voice_brbrbr.wav
SOUND|${PREFIX}/data/sound/voice_coffee.wav SOUND|${PREFIX}/data/sound/voice_coffee.wav
SOUND|${PREFIX}/data/sound/voice_credit_thankyou.wav
SOUND|${PREFIX}/data/sound/voice_get_ready.wav SOUND|${PREFIX}/data/sound/voice_get_ready.wav
SOUND|${PREFIX}/data/sound/voice_no.wav SOUND|${PREFIX}/data/sound/voice_no.wav
SOUND|${PREFIX}/data/sound/voice_power_up.wav SOUND|${PREFIX}/data/sound/voice_power_up.wav
@@ -62,50 +66,50 @@ SOUND|${PREFIX}/data/sound/voice_thankyou.wav
SOUND|${PREFIX}/data/sound/walk.wav SOUND|${PREFIX}/data/sound/walk.wav
# Shaders # Shaders
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
DATA|${PREFIX}/data/shaders/crtpi_240.glsl DATA|${PREFIX}/data/shaders/crtpi_240.glsl
DATA|${PREFIX}/data/shaders/crtpi_256.glsl
# Texturas - Balloons # Texturas - Balloons
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon2.ani ANIMATION|${PREFIX}/data/gfx/balloon/balloon2.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png
ANIMATION|${PREFIX}/data/gfx/balloon/balloon3.ani ANIMATION|${PREFIX}/data/gfx/balloon/balloon3.ani
BITMAP|${PREFIX}/data/gfx/balloon/balloon0.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon1.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon2.png
BITMAP|${PREFIX}/data/gfx/balloon/balloon3.png
# Texturas - Explosiones # Texturas - Explosiones
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion0.ani ANIMATION|${PREFIX}/data/gfx/balloon/explosion0.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion1.ani ANIMATION|${PREFIX}/data/gfx/balloon/explosion1.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion2.ani ANIMATION|${PREFIX}/data/gfx/balloon/explosion2.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png
ANIMATION|${PREFIX}/data/gfx/balloon/explosion3.ani ANIMATION|${PREFIX}/data/gfx/balloon/explosion3.ani
BITMAP|${PREFIX}/data/gfx/balloon/explosion0.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion1.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion2.png
BITMAP|${PREFIX}/data/gfx/balloon/explosion3.png
# Texturas - Power Ball # Texturas - Power Ball
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png
ANIMATION|${PREFIX}/data/gfx/balloon/powerball.ani ANIMATION|${PREFIX}/data/gfx/balloon/powerball.ani
BITMAP|${PREFIX}/data/gfx/balloon/powerball.png
# Texturas - Bala # Texturas - Bala
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png
ANIMATION|${PREFIX}/data/gfx/bullet/bullet.ani ANIMATION|${PREFIX}/data/gfx/bullet/bullet.ani
BITMAP|${PREFIX}/data/gfx/bullet/bullet.png
# Texturas - Tabe # Texturas - Tabe
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png
ANIMATION|${PREFIX}/data/gfx/tabe/tabe.ani ANIMATION|${PREFIX}/data/gfx/tabe/tabe.ani
BITMAP|${PREFIX}/data/gfx/tabe/tabe.png
# Texturas - Juego # Texturas - Juego
BITMAP|${PREFIX}/data/gfx/game/game_buildings.png BITMAP|${PREFIX}/data/gfx/game/game_buildings.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds1.png BITMAP|${PREFIX}/data/gfx/game/game_clouds1.png
BITMAP|${PREFIX}/data/gfx/game/game_clouds2.png BITMAP|${PREFIX}/data/gfx/game/game_clouds2.png
BITMAP|${PREFIX}/data/gfx/game/game_grass.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_power_meter.png
BITMAP|${PREFIX}/data/gfx/game/game_sky_colors.png BITMAP|${PREFIX}/data/gfx/game/game_sky_colors.png
BITMAP|${PREFIX}/data/gfx/game/game_sun.png BITMAP|${PREFIX}/data/gfx/game/game_sun.png
BITMAP|${PREFIX}/data/gfx/game/game_moon.png
# Texturas - Intro # Texturas - Intro
BITMAP|${PREFIX}/data/gfx/intro/intro1.png 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 BITMAP|${PREFIX}/data/gfx/intro/intro6.png
# Texturas - Logo # 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_mini.png
BITMAP|${PREFIX}/data/gfx/logo/logo_jailgames.png
BITMAP|${PREFIX}/data/gfx/logo/logo_since_1998.png BITMAP|${PREFIX}/data/gfx/logo/logo_since_1998.png
# Texturas - Items # 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 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 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 ANIMATION|${PREFIX}/data/gfx/item/item_points3_pacmar.ani
BITMAP|${PREFIX}/data/gfx/item/item_clock.png 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 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 # 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_bg_tile.png
BITMAP|${PREFIX}/data/gfx/title/title_coffee.png BITMAP|${PREFIX}/data/gfx/title/title_coffee.png
BITMAP|${PREFIX}/data/gfx/title/title_crisis.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 BITMAP|${PREFIX}/data/gfx/title/title_dust.png
ANIMATION|${PREFIX}/data/gfx/title/title_dust.ani
# Texturas - Jugador 1 # Texturas - Jugador 1
BITMAP|${PREFIX}/data/gfx/player/player1_power.png
BITMAP|${PREFIX}/data/gfx/player/player1.gif BITMAP|${PREFIX}/data/gfx/player/player1.gif
PALETTE|${PREFIX}/data/gfx/player/player1_coffee1.pal PALETTE|${PREFIX}/data/gfx/player/player1_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player1_coffee2.pal PALETTE|${PREFIX}/data/gfx/player/player1_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player1_invencible.pal PALETTE|${PREFIX}/data/gfx/player/player1_invencible.pal
BITMAP|${PREFIX}/data/gfx/player/player1_power.png
# Texturas - Jugador 2 # Texturas - Jugador 2
BITMAP|${PREFIX}/data/gfx/player/player2_power.png
BITMAP|${PREFIX}/data/gfx/player/player2.gif BITMAP|${PREFIX}/data/gfx/player/player2.gif
PALETTE|${PREFIX}/data/gfx/player/player2_coffee1.pal PALETTE|${PREFIX}/data/gfx/player/player2_coffee1.pal
PALETTE|${PREFIX}/data/gfx/player/player2_coffee2.pal PALETTE|${PREFIX}/data/gfx/player/player2_coffee2.pal
PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal PALETTE|${PREFIX}/data/gfx/player/player2_invencible.pal
BITMAP|${PREFIX}/data/gfx/player/player2_power.png
# Animaciones del jugador # Animaciones del jugador
ANIMATION|${PREFIX}/data/gfx/player/player.ani
ANIMATION|${PREFIX}/data/gfx/player/player_power.ani ANIMATION|${PREFIX}/data/gfx/player/player_power.ani
ANIMATION|${PREFIX}/data/gfx/player/player.ani
# Texturas - Golpe del jugador # Texturas - Golpe del jugador
BITMAP|${PREFIX}/data/gfx/player/hit.png BITMAP|${PREFIX}/data/gfx/player/hit.png
# Fuentes de texto # 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 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_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_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 # Idiomas
LANG|${PREFIX}/data/lang/es_ES.json
LANG|${PREFIX}/data/lang/en_UK.json
LANG|${PREFIX}/data/lang/ba_BA.json LANG|${PREFIX}/data/lang/ba_BA.json
LANG|${PREFIX}/data/lang/en_UK.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 frames=47,48,49,50,51,52,53
[/animation] [/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] [animation]
name=hello name=hello
speed=3 speed=3
loop=-1 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] [/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] 6": "Temps!",
"[GAME_TEXT] 7": "Endavant!", "[GAME_TEXT] 7": "Endavant!",
"[GAME_TEXT] 8": "1.000.000 de punts!", "[GAME_TEXT] 8": "1.000.000 de punts!",
"[GAME_TEXT] THANK_YOU": "Gracies!",
"[HIGHSCORE_TABLE] CAPTION": "Millors puntuacions", "[HIGHSCORE_TABLE] CAPTION": "Millors puntuacions",

View File

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

View File

@@ -25,6 +25,7 @@
"[GAME_TEXT] 6": "Tiempo!", "[GAME_TEXT] 6": "Tiempo!",
"[GAME_TEXT] 7": "Adelante!", "[GAME_TEXT] 7": "Adelante!",
"[GAME_TEXT] 8": "1.000.000 de puntos!", "[GAME_TEXT] 8": "1.000.000 de puntos!",
"[GAME_TEXT] THANK_YOU": "Gracias!",
"[HIGHSCORE_TABLE] CAPTION": "Mejores puntuaciones", "[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 // 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); auto iterator = animation_indices_.find(name);
if (iterator != animation_indices_.end()) { if (iterator != animation_indices_.end()) {
// Si se encuentra la animación en el mapa, devuelve su índice // Si se encuentra la animación en el mapa, devuelve su índice
@@ -101,7 +101,7 @@ auto AnimatedSprite::animationIsCompleted() -> bool {
// Establece la animacion actual // Establece la animacion actual
void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) { 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) { if (current_animation_ != NEW_ANIMATION) {
const auto OLD_ANIMATION = current_animation_; const auto OLD_ANIMATION = current_animation_;
current_animation_ = NEW_ANIMATION; current_animation_ = NEW_ANIMATION;

View File

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

View File

@@ -14,8 +14,12 @@
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // Constructor
Background::Background() Background::Background(float total_progress_to_complete)
: renderer_(Screen::get()->getRenderer()), : 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")), buildings_texture_(Resource::get()->getTexture("game_buildings.png")),
top_clouds_texture_(Resource::get()->getTexture("game_clouds1.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)}), 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}), src_rect_({0, 0, 320, 240}),
dst_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)), 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)
{ alpha_color_texture_(param.background.attenuate_color.a),
// Precalcula rutas previous_alpha_color_texture_(param.background.attenuate_color.a),
createSunPath(); base_(rect_.h) {
createMoonPath(); initializePaths();
initializeRects();
// Inicializa variables initializeSprites();
{ initializeSpriteProperties();
gradient_rect_[0] = {0, 0, rect_.w, rect_.h}; initializeTextures();
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_);
} }
// Destructor // Destructor
@@ -111,9 +50,88 @@ Background::~Background() {
SDL_DestroyTexture(color_texture_); 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 // Actualiza la lógica del objeto
void Background::update() { 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(); updateAlphaColorTexture();
// Actualiza las nubes // Actualiza las nubes
@@ -122,10 +140,10 @@ void Background::update() {
// Calcula el frame de la hierba // Calcula el frame de la hierba
grass_sprite_->setSpriteClip(0, (10 * (counter_ / 20 % 2)), 320, 10); 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); 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_)); sun_sprite_->setPosition(sun_path_.at(sun_index_));
moon_sprite_->setPosition(moon_path_.at(moon_index_)); moon_sprite_->setPosition(moon_path_.at(moon_index_));
@@ -136,6 +154,190 @@ void Background::update() {
fillCanvas(); 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 // Dibuja el gradiente de fondo
void Background::renderGradient() { void Background::renderGradient() {
// Dibuja el gradiente de detras // Dibuja el gradiente de detras
@@ -221,21 +423,6 @@ void Background::render() {
SDL_RenderTexture(renderer_, color_texture_, &src_rect_, &dst_rect_); 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 // Establece la posición del objeto
void Background::setPos(SDL_FRect pos) { void Background::setPos(SDL_FRect pos) {
dst_rect_ = pos; dst_rect_ = pos;
@@ -247,7 +434,7 @@ void Background::setPos(SDL_FRect pos) {
src_rect_.h = pos.h; src_rect_.h = pos.h;
} }
// Establece el color_ de atenuación // Establece el color de atenuación
void Background::setColor(Color color) { void Background::setColor(Color color) {
attenuate_color_ = color; attenuate_color_ = color;
@@ -264,52 +451,20 @@ void Background::setColor(Color color) {
// Establece la transparencia de la atenuación // Establece la transparencia de la atenuación
void Background::setAlpha(int alpha) { void Background::setAlpha(int alpha) {
// Evita que se asignen valores fuera de rango // 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 // Guarda el valor actual y establece el nuevo valor
alpha_color_text_temp_ = alpha_color_text_; previous_alpha_color_texture_ = alpha_color_texture_;
alpha_color_text_ = alpha_; alpha_color_texture_ = alpha;
} }
// Actualiza el valor de alpha_ // Actualiza el valor de alpha
void Background::updateAlphaColorTexture() { void Background::updateAlphaColorTexture() {
if (alpha_color_text_ == alpha_color_text_temp_) { if (alpha_color_texture_ == previous_alpha_color_texture_) {
return; return;
} }
alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_; alpha_color_texture_ > previous_alpha_color_texture_ ? ++previous_alpha_color_texture_ : --previous_alpha_color_texture_;
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_); SDL_SetTextureAlphaMod(color_texture_, previous_alpha_color_texture_);
}
// 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());
}
} }
// Precalcula el vector con el recorrido del sol // Precalcula el vector con el recorrido del sol
@@ -343,26 +498,22 @@ void Background::createMoonPath() {
const float CENTER_Y = base_ - 50; const float CENTER_Y = base_ - 50;
constexpr float RADIUS = 140; constexpr float RADIUS = 140;
// Generar puntos de la curva desde 0 a 90 grados
constexpr double STEP = 0.01; 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) { 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 x = CENTER_X + (RADIUS * cos(theta));
float y = CENTER_Y - (RADIUS * sin(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 <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer
#include <array> // Para array #include <array> // Para array
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <memory> // Para unique_ptr, shared_ptr #include <functional> // Para std::function
#include <vector> // Para vector #include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector
#include "color.h" // Para Color #include "color.h" // Para Color
@@ -16,21 +17,29 @@ class Texture;
/* /*
Esta clase gestiona el fondo que aparece en la sección jugable. 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. Maneja internamente su progresión a través de diferentes estados del día/noche,
Su área total está definida por "rect", pero solo se pinta la región "srcRect" en la pantalla en "dstRect". 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: Métodos clave:
- setCloudsSpeed(float value) -> Define la velocidad de las nubes - incrementProgress() -> Avanza la progresión del fondo
- setGradientNumber(int value) -> Ajusta el índice del color de cielo - setState() -> Cambia el estado del fondo
- setTransition(float value) -> Configura la transición entre texturas - setColor/setAlpha -> Efectos de atenuación
- setColor(Color color) -> Aplica un color de atenuación
- setAlpha(int alpha) -> Ajusta la transparencia de la capa de atenuación
*/ */
class Background { class Background {
public: public:
// Enumeración de estados
enum class State {
NORMAL,
COMPLETED
};
// Constructor y Destructor // Constructor y Destructor
Background(); Background(float total_progress_to_complete = 6100.0f);
~Background(); ~Background();
// Actualización y renderizado // Actualización y renderizado
@@ -40,20 +49,46 @@ class Background {
// Configuración de posición // Configuración de posición
void setPos(SDL_FRect pos); // Establece la posición del objeto void setPos(SDL_FRect pos); // Establece la posición del objeto
// Configuración de animaciones y efectos // Control de progresión
void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes void incrementProgress(float amount = 1.0f); // Incrementa la progresión interna
void setGradientNumber(int value); // Establece el degradado de fondo a usar void setProgress(float absolute_progress); // Establece la progresión absoluta
void setTransition(float value); // Ajusta la transición entre texturas de fondo 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 // Configuración de efectos visuales
void setColor(Color color); // Establece el color de atenuación void setColor(Color color); // Establece el color de atenuación
void setAlpha(int alpha); // Ajusta la transparencia del fondo void setAlpha(int alpha); // Ajusta la transparencia del fondo
// Configuración del sol y la luna // Getters para información del estado
void setSunProgression(float progress); // Establece la posición del sol float getProgress() const { return progress_; }
void setMoonProgression(float progress); // Establece la posición de la luna State getState() const { return state_; }
int getCurrentGradient() const { return static_cast<int>(gradient_number_); }
private: 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 // Objetos y punteros
SDL_Renderer *renderer_; // Renderizador de la ventana SDL_Renderer *renderer_; // Renderizador de la ventana
@@ -81,34 +116,51 @@ class Background {
SDL_Texture *canvas_; // Textura para componer el fondo SDL_Texture *canvas_; // Textura para componer el fondo
SDL_Texture *color_texture_; // Textura para atenuar el fondo SDL_Texture *color_texture_; // Textura para atenuar el fondo
// Variables de control // Variables de estado y progresión
std::array<SDL_FRect, 4> gradient_rect_; State state_ = State::NORMAL;
std::array<SDL_FRect, 4> top_clouds_rect_; float progress_ = 0.0f; // Progresión interna (0 a total_progress_to_complete_)
std::array<SDL_FRect, 4> bottom_clouds_rect_; bool manual_mode_ = false; // Si está en modo manual, no actualiza automáticamente
int gradient_number_ = 0; ProgressCallback progress_callback_; // Callback para notificar cambios de progreso
int alpha_ = 0;
float clouds_speed_ = 0; // Variables de renderizado
float transition_ = 0; SDL_FRect rect_; // Tamaño del objeto
int counter_ = 0; SDL_FRect src_rect_; // Parte del objeto para copiar en pantalla
SDL_FRect rect_; SDL_FRect dst_rect_; // Posición en pantalla donde se copia el objeto
SDL_FRect src_rect_; std::array<SDL_FRect, STAGES> gradient_rect_; // Fondos degradados
SDL_FRect dst_rect_; std::array<SDL_FRect, 4> top_clouds_rect_; // Nubes superiores
int base_; std::array<SDL_FRect, 4> bottom_clouds_rect_; // Nubes inferiores
Color attenuate_color_; Color attenuate_color_; // Color de atenuación
int alpha_color_text_;
int alpha_color_text_temp_; std::vector<SDL_FPoint> sun_path_; // Recorrido del sol
std::vector<SDL_FPoint> sun_path_; std::vector<SDL_FPoint> moon_path_; // Recorrido de la luna
std::vector<SDL_FPoint> moon_path_;
size_t sun_index_ = 0; float clouds_speed_ = 0; // Velocidad de las nubes
size_t moon_index_ = 0; 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 // Métodos internos
void renderGradient(); // Dibuja el gradiente de fondo void initializePaths(); // Inicializa las rutas del sol y la luna
void renderTopClouds(); // Dibuja las nubes superiores void initializeRects(); // Inicializa los rectángulos de gradientes y nubes
void renderBottomClouds(); // Dibuja las nubes inferiores void initializeSprites(); // Crea los sprites
void fillCanvas(); // Compone todos los elementos en la textura void initializeSpriteProperties(); // Configura las propiedades iniciales de los sprites
void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación void initializeTextures(); // Inicializa las texturas de renderizado
void updateClouds(); // Actualiza el movimiento de las nubes void updateProgression(); // Actualiza la progresión y calcula transiciones
void createSunPath(); // Precalcula el recorrido del sol void updateCloudsSpeed(); // Actualiza la velocidad de las nubes según el estado
void createMoonPath(); // Precalcula el recorrido de la luna 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 // Centra el globo en la posición X
void Balloon::alignTo(int x) { void Balloon::alignTo(int x) {
x_ = static_cast<float>(x - (w_ / 2)); const float MIN_X = play_area_.x;
const int MIN_X = play_area_.x; const float MAX_X = play_area_.w - w_;
const int MAX_X = play_area_.w - w_; x_ = std::clamp(x - (w_ / 2), MIN_X, MAX_X);
x_ = std::clamp(x_, static_cast<float>(MIN_X), static_cast<float>(MAX_X));
} }
// Pinta el globo en la pantalla // Pinta el globo en la pantalla

View File

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

View File

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

View File

@@ -4,5 +4,5 @@
namespace GlobalEvents { namespace GlobalEvents {
// Comprueba los eventos que se pueden producir en cualquier sección del juego // 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 } // namespace GlobalEvents

View File

@@ -101,14 +101,11 @@ auto saveToFile() -> bool {
// Opciones de ventana // Opciones de ventana
file << "## WINDOW\n"; file << "## WINDOW\n";
file << "\n";
file << "window.zoom=" << window.zoom << "\n"; file << "window.zoom=" << window.zoom << "\n";
// Opciones de video // 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 << "## 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.fullscreen=" << boolToString(video.fullscreen) << "\n";
file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\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"; file << "video.shaders=" << boolToString(video.shaders) << "\n";
// Opciones de audio // Opciones de audio
file << "\n\n## AUDIO\n"; file << "\n## AUDIO\n";
file << "## volume [0 .. 100]\n"; file << "## volume [0 .. 100]\n";
file << "\n";
file << "audio.enabled=" << boolToString(audio.enabled) << "\n"; file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
file << "audio.volume=" << audio.volume << "\n"; file << "audio.volume=" << audio.volume << "\n";
@@ -129,10 +125,9 @@ auto saveToFile() -> bool {
file << "audio.sound.volume=" << audio.sound.volume << "\n"; file << "audio.sound.volume=" << audio.sound.volume << "\n";
// Opciones del juego // Opciones del juego
file << "\n\n## GAME\n"; file << "\n## GAME\n";
file << "## game.language [0: spanish, 1: valencian, 2: english]\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 << "## 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.language=" << static_cast<int>(settings.language) << "\n";
file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\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"; file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";
// Opciones de mandos // Opciones de mandos
file << "\n\n## CONTROLLERS\n"; file << "\n## CONTROLLERS\n";
gamepad_manager.saveToFile(file); gamepad_manager.saveToFile(file);
// Opciones de teclado // Opciones de teclado
file << "\n\n## KEYBOARD\n"; file << "\n## KEYBOARD\n";
file << "keyboard.player=" << static_cast<int>(keyboard.player_id) << "\n"; file << "keyboard.player=" << static_cast<int>(keyboard.player_id) << "\n";
// Cierra el fichero // Cierra el fichero

View File

@@ -14,6 +14,7 @@
#include "param.h" // Para Param, ParamGame, param #include "param.h" // Para Param, ParamGame, param
#include "scoreboard.h" // Para Scoreboard #include "scoreboard.h" // Para Scoreboard
#include "stage.h" // Para power_can_be_added #include "stage.h" // Para power_can_be_added
#include "stage_interface.h" // Para IStageInfo
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
#ifdef _DEBUG #ifdef _DEBUG
#include <iostream> #include <iostream>
@@ -26,6 +27,7 @@ Player::Player(const Config &config)
enter_name_(std::make_unique<EnterName>()), enter_name_(std::make_unique<EnterName>()),
hi_score_table_(config.hi_score_table), hi_score_table_(config.hi_score_table),
glowing_entry_(config.glowing_entry), glowing_entry_(config.glowing_entry),
stage_info_(config.stage_info),
play_area_(*config.play_area), play_area_(*config.play_area),
id_(config.id), id_(config.id),
default_pos_x_(config.x), default_pos_x_(config.x),
@@ -179,6 +181,9 @@ void Player::move() {
case State::WAITING: case State::WAITING:
handleWaitingMovement(); handleWaitingMovement();
break; break;
case State::RECOVER:
handleRecoverMovement();
break;
default: default:
break; break;
} }
@@ -196,6 +201,11 @@ void Player::handlePlayingMovement() {
shiftSprite(); shiftSprite();
} }
void Player::handleRecoverMovement() {
if (player_sprite_->getCurrentAnimationFrame() == 10) { playSound("voice_brbrbr.wav"); }
if (player_sprite_->animationIsCompleted()) { setPlayingState(State::RESPAWNING); }
}
void Player::handleRollingMovement() { void Player::handleRollingMovement() {
handleRollingBoundaryCollision(); handleRollingBoundaryCollision();
handleRollingGroundCollision(); handleRollingGroundCollision();
@@ -463,6 +473,9 @@ void Player::setAnimation() {
player_sprite_->setFlip(flipMode); player_sprite_->setFlip(flipMode);
break; break;
} }
case State::RECOVER:
player_sprite_->setCurrentAnimation("recover");
break;
case State::WAITING: case State::WAITING:
case State::GAME_OVER: case State::GAME_OVER:
player_sprite_->setCurrentAnimation("hello"); player_sprite_->setCurrentAnimation("hello");
@@ -588,10 +601,10 @@ void Player::update() {
} }
// Incrementa la puntuación del jugador // 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()) { if (isPlaying()) {
score_ += score; 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; playing_state_ = state;
switch (playing_state_) { switch (playing_state_) {
case State::RECOVER: {
break;
}
case State::RESPAWNING: { case State::RESPAWNING: {
playSound("voice_thankyou.wav"); playSound("voice_thankyou.wav");
setPlayingState(State::PLAYING); setPlayingState(State::PLAYING);
@@ -635,7 +651,7 @@ void Player::setPlayingState(State state) {
init(); init();
setInvulnerable(true); setInvulnerable(true);
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
Stage::power_can_be_added = true; stage_info_->canCollectPower();
break; break;
} }
case State::CONTINUE: { case State::CONTINUE: {
@@ -690,7 +706,7 @@ void Player::setPlayingState(State state) {
case State::TITLE_ANIMATION: { case State::TITLE_ANIMATION: {
// Activa la animación de rodar // Activa la animación de rodar
player_sprite_->setCurrentAnimation("walk"); player_sprite_->setCurrentAnimation("walk");
playSound("voice_thankyou.wav"); playSound("voice_credit_thankyou.wav");
break; break;
} }
case State::TITLE_HIDDEN: { case State::TITLE_HIDDEN: {
@@ -929,7 +945,6 @@ void Player::playSound(const std::string &name) const {
// Indica si se puede dibujar el objeto // Indica si se puede dibujar el objeto
auto Player::isRenderable() const -> bool { auto Player::isRenderable() const -> bool {
// return !isGameOver() && !isTitleHidden();
return !isTitleHidden(); return !isTitleHidden();
}; };
@@ -948,3 +963,8 @@ void Player::addScoreToScoreBoard() const {
manager->saveToFile(Asset::get()->get("score.bin")); 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 "input.h" // Para Input
#include "manage_hiscore_table.h" // Para Table #include "manage_hiscore_table.h" // Para Table
#include "scoreboard.h" // Para Scoreboard #include "scoreboard.h" // Para Scoreboard
#include "stage_interface.h" // Para IStageInfo
#include "utils.h" // Para Circle #include "utils.h" // Para Circle
class Texture; class Texture;
@@ -71,7 +72,8 @@ class Player {
CREDITS, // Estado para mostrar los créditos del juego CREDITS, // Estado para mostrar los créditos del juego
TITLE_ANIMATION, // Animacion para el titulo TITLE_ANIMATION, // Animacion para el titulo
TITLE_HIDDEN, // 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 { struct Config {
@@ -82,8 +84,9 @@ class Player {
SDL_FRect *play_area; // Usamos puntero para mantener la referencia SDL_FRect *play_area; // Usamos puntero para mantener la referencia
std::vector<std::shared_ptr<Texture>> texture; std::vector<std::shared_ptr<Texture>> texture;
std::vector<std::vector<std::string>> animations; std::vector<std::vector<std::string>> animations;
Table *hi_score_table; // También como puntero para referencia Table *hi_score_table; // También como puntero para referencia
int *glowing_entry; // Puntero para mantener la referencia int *glowing_entry; // Puntero para mantener la referencia
IStageInfo *stage_info; // Puntero para el gestor de pantallas
}; };
// --- Constructor y destructor --- // --- Constructor y destructor ---
@@ -111,9 +114,9 @@ class Player {
void updateCooldown(); // Actualiza el cooldown de disparo void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador --- // --- Puntuación y marcador ---
void addScore(int score, int last_hi_score_entry); // Añade puntos void addScore(int score, int lowest_hi_score_entry); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador void decScoreMultiplier(); // Decrementa el multiplicador
// --- Estados de juego --- // --- Estados de juego ---
void setPlayingState(State state); // Cambia el estado 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 setScoreBoardPanel(Scoreboard::Id panel) { scoreboard_panel_ = panel; }
void setScoreMultiplier(float value) { score_multiplier_ = value; } void setScoreMultiplier(float value) { score_multiplier_ = value; }
void setWalkingState(State state) { walking_state_ = state; } void setWalkingState(State state) { walking_state_ = state; }
void addCredit() { ++credits_used_; }
void addCredit();
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; } void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; }
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; } [[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; } void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
@@ -204,6 +207,7 @@ class Player {
std::shared_ptr<Input::Gamepad> gamepad_ = nullptr; // Dispositivo asociado std::shared_ptr<Input::Gamepad> gamepad_ = nullptr; // Dispositivo asociado
Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones Table *hi_score_table_ = nullptr; // Tabla de máximas puntuaciones
int *glowing_entry_ = nullptr; // Entrada de la tabla de puntuaciones para hacerla brillar 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 name_; // Nombre del jugador
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones 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 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 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 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 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 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.) 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() { void Scoreboard::renderStageInfoMode() {
// STAGE // STAGE
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + std::to_string(stage_), 1, text_color1_); text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
// POWERMETER // POWERMETER
power_meter_sprite_->setSpriteClip(0, 0, 40, 7); power_meter_sprite_->setSpriteClip(0, 0, 40, 7);

View File

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

View File

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

View File

@@ -13,6 +13,7 @@
#include "path_sprite.h" // Para PathSprite, Path #include "path_sprite.h" // Para PathSprite, Path
#include "player.h" // Para Player #include "player.h" // Para Player
#include "smart_sprite.h" // Para SmartSprite #include "smart_sprite.h" // Para SmartSprite
#include "stage.h" // Para StageManager
#include "utils.h" // Para Demo #include "utils.h" // Para Demo
class Background; class Background;
@@ -100,8 +101,6 @@ class Game {
Input *input_; // Manejador de entrada Input *input_; // Manejador de entrada
Scoreboard *scoreboard_; // Objeto para dibujar el marcador 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 SDL_Texture *canvas_; // Textura para dibujar la zona de juego
std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores 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::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<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_in_; // Objeto para renderizar fades
std::unique_ptr<Fade> fade_out_; // 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::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor
std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados 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_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 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 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_ = 0; // Nivel de amenaza actual
int menace_current_ = 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 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 State state_ = State::FADE_IN; // Estado
std::vector<std::shared_ptr<Player>> players_to_put_at_back_; std::vector<std::shared_ptr<Player>> players_to_put_at_back_;
@@ -156,7 +156,7 @@ class Game {
// --- Ciclo principal del juego --- // --- Ciclo principal del juego ---
void update(); // Actualiza la lógica principal del juego void update(); // Actualiza la lógica principal del juego
void render(); // Renderiza todos los elementos 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 checkState(); // Verifica y actualiza el estado actual del juego
void setState(State state); // Cambia el estado del juego void setState(State state); // Cambia el estado del juego
void cleanVectors(); // Limpia vectores de elementos deshabilitados void cleanVectors(); // Limpia vectores de elementos deshabilitados
@@ -263,8 +263,8 @@ class Game {
void initDifficultyVars(); // Inicializa variables de dificultad void initDifficultyVars(); // Inicializa variables de dificultad
// --- Sistema de amenaza --- // --- Sistema de amenaza ---
void updateMenace(); // Gestiona el nivel de amenaza del juego void updateMenace(); // Gestiona el nivel de amenaza del juego
void evaluateAndSetMenace(); // Calcula y establece amenaza según globos activos void setMenace(); // Calcula y establece amenaza según globos activos
// --- Puntuación y marcador --- // --- Puntuación y marcador ---
void updateHiScore(); // Actualiza el récord máximo si es necesario void updateHiScore(); // Actualiza el récord máximo si es necesario
@@ -300,6 +300,6 @@ class Game {
// --- Depuración (solo en modo DEBUG) --- // --- Depuración (solo en modo DEBUG) ---
#ifdef _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 #endif
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,43 +1,300 @@
#include "stage.h" #include "stage.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <algorithm> // Para min // Implementación de StageData
#include <vector> // Para vector 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 { // Implementación de StageManager
StageManager::StageManager()
std::vector<Stage> stages; // Variable con los datos de cada pantalla : current_power_(0), total_power_(0), current_stage_index_(0),
int power = 0; // Poder acumulado en la fase power_collection_state_(PowerCollectionState::ENABLED),
int total_power = 0; // Poder total necesario para completar el juego power_change_callback_(nullptr) {
int number = 0; // Fase actual initialize();
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;
} }
// Añade poder void StageManager::initialize() {
void addPower(int amount) { stages_.clear();
if (power_can_be_added) { createDefaultStages();
power += amount; reset();
total_power += amount; }
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();
}
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);
}
} }
} }
} // namespace Stage

View File

@@ -1,33 +1,111 @@
#pragma once #pragma once
#include <vector> // Para vector #include "stage_interface.h"
#include <vector>
#include <optional>
#include <string>
#include <functional>
/* // --- Estados posibles para la recolección de poder ---
Namespace Stage: gestiona los datos y operaciones de las fases del juego. enum class PowerCollectionState {
Permite consultar y modificar el poder necesario, la amenaza y el estado de cada fase. ENABLED, // Recolección habilitada
*/ DISABLED // Recolección deshabilitada
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) {}
}; };
// --- Variables globales del estado de las fases --- // --- Estados posibles para una fase del juego ---
extern std::vector<Stage> stages; // Vector con los datos de cada pantalla enum class StageStatus {
extern int power; // Poder acumulado en la fase actual LOCKED, // Fase bloqueada
extern int total_power; // Poder total necesario para completar el juego IN_PROGRESS, // Fase en progreso
extern int number; // Índice de la fase actual COMPLETED // Fase completada
extern bool power_can_be_added; // Indica si se puede añadir poder a la fase };
// --- Funciones principales --- // --- Representa los datos de una fase del juego ---
auto get(int index) -> Stage; // Devuelve una fase por índice class StageData {
void init(); // Inicializa las variables del namespace Stage private:
void addPower(int amount); // Añade poder a la fase actual int power_to_complete_; // Poder necesario para completar la fase
} // namespace Stage 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;
};