Compare commits
38 Commits
2025-08-17
...
19768cb72b
| Author | SHA1 | Date | |
|---|---|---|---|
| 19768cb72b | |||
| 26e0fd7247 | |||
| e2fd470ad3 | |||
| a72ae0a5fc | |||
| 7579594c22 | |||
| 6c702e7e23 | |||
| fb9c78eb49 | |||
| 62f65cbd5a | |||
| 057d3dcfee | |||
| c85336a4d0 | |||
| e4702e4e24 | |||
| 928335576c | |||
| fe950e6f17 | |||
| 6e81b6e60c | |||
| 74f6fe3501 | |||
| dfdb679054 | |||
| 26ed479306 | |||
| 32e9da55ef | |||
| 610083578e | |||
| 7250073d60 | |||
| dfa06870e4 | |||
| 81f3a25143 | |||
| 5aca95f3d2 | |||
| 7b193605e6 | |||
| 089a5b15d7 | |||
| e6a14ca57d | |||
| 467d609c28 | |||
| e03c798000 | |||
| 52d76b7338 | |||
| 83ee9c2649 | |||
| 43788bb01a | |||
| 58cf78e1e3 | |||
| 6bf8490776 | |||
| 8cfe28922c | |||
| 63990c75c2 | |||
| 94dca528ab | |||
| 4b6b89ceb2 | |||
| ed077c1da5 |
@@ -14,6 +14,8 @@ ContinuationIndentWidth: 4
|
|||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: true
|
||||||
BreakConstructorInitializers: BeforeComma
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
PackConstructorInitializers: Never
|
||||||
AllowAllArgumentsOnNextLine: false
|
AllowAllArgumentsOnNextLine: false
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
|||||||
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(make:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,9 @@ set(APP_SOURCES
|
|||||||
source/main.cpp
|
source/main.cpp
|
||||||
source/param.cpp
|
source/param.cpp
|
||||||
source/resource.cpp
|
source/resource.cpp
|
||||||
|
source/resource_helper.cpp
|
||||||
|
source/resource_loader.cpp
|
||||||
|
source/resource_pack.cpp
|
||||||
source/screen.cpp
|
source/screen.cpp
|
||||||
source/text.cpp
|
source/text.cpp
|
||||||
source/writer.cpp
|
source/writer.cpp
|
||||||
|
|||||||
22
LICENSE
@@ -1 +1,21 @@
|
|||||||
GNU General Public License v3.0 only
|
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Coffee Crisis Arcade Edition
|
||||||
|
|
||||||
|
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
|
|
||||||
|
You are free to:
|
||||||
|
- Share — copy and redistribute the material in any medium or format
|
||||||
|
- Adapt — remix, transform, and build upon the material
|
||||||
|
|
||||||
|
Under the following terms:
|
||||||
|
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||||
|
- NonCommercial — You may not use the material for commercial purposes.
|
||||||
|
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||||
|
|
||||||
|
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||||
|
|
||||||
|
To view a copy of this license, visit:
|
||||||
|
https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
25
Makefile
@@ -71,6 +71,9 @@ APP_SOURCES := \
|
|||||||
source/path_sprite.cpp \
|
source/path_sprite.cpp \
|
||||||
source/player.cpp \
|
source/player.cpp \
|
||||||
source/resource.cpp \
|
source/resource.cpp \
|
||||||
|
source/resource_helper.cpp \
|
||||||
|
source/resource_loader.cpp \
|
||||||
|
source/resource_pack.cpp \
|
||||||
source/scoreboard.cpp \
|
source/scoreboard.cpp \
|
||||||
source/screen.cpp \
|
source/screen.cpp \
|
||||||
source/sections/credits.cpp \
|
source/sections/credits.cpp \
|
||||||
@@ -104,7 +107,7 @@ INCLUDES := -Isource -Isource/external
|
|||||||
# Variables según el sistema operativo
|
# Variables según el sistema operativo
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
FixPath = $(subst /,\\,$1)
|
FixPath = $(subst /,\\,$1)
|
||||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -Wl,-subsystem,windows -DWINDOWS_BUILD
|
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -static-libstdc++ -static-libgcc -Wl,-Bstatic -lpthread -Wl,-Bdynamic -Wl,-subsystem,windows -DWINDOWS_BUILD
|
||||||
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
CXXFLAGS_DEBUG := -std=c++20 -Wall -g -D_DEBUG -DWINDOWS_BUILD
|
||||||
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
LDFLAGS := -lmingw32 -lws2_32 -lSDL3 -lopengl32
|
||||||
RM := del /Q
|
RM := del /Q
|
||||||
@@ -158,8 +161,9 @@ windows_release:
|
|||||||
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
|
powershell if (Test-Path "$(RELEASE_FOLDER)") {Remove-Item "$(RELEASE_FOLDER)" -Recurse -Force}
|
||||||
powershell if (-not (Test-Path "$(RELEASE_FOLDER)")) {New-Item "$(RELEASE_FOLDER)" -ItemType Directory}
|
powershell if (-not (Test-Path "$(RELEASE_FOLDER)")) {New-Item "$(RELEASE_FOLDER)" -ItemType Directory}
|
||||||
|
|
||||||
# Copia la carpeta 'data'
|
# Copia la carpeta 'config' y el archivo 'resources.pack'
|
||||||
powershell Copy-Item -Path "data" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
powershell Copy-Item -Path "config" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
||||||
|
powershell Copy-Item -Path "resources.pack" -Destination "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia los ficheros que estan en la raíz del proyecto
|
# Copia los ficheros que estan en la raíz del proyecto
|
||||||
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
||||||
@@ -203,7 +207,8 @@ macos_release:
|
|||||||
$(MKDIR) Frameworks
|
$(MKDIR) Frameworks
|
||||||
|
|
||||||
# Copia carpetas y ficheros
|
# Copia carpetas y ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
cp -R config "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||||
cp -R release/frameworks/SDL3.xcframework Frameworks
|
cp -R release/frameworks/SDL3.xcframework Frameworks
|
||||||
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
cp release/*.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||||
@@ -262,7 +267,8 @@ linux_release:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"
|
cp -R config "$(RELEASE_FOLDER)"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -291,7 +297,8 @@ linux_release_desktop:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)"
|
$(MKDIR) "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)"
|
||||||
|
|
||||||
# Copia ficheros del juego
|
# Copia ficheros del juego
|
||||||
cp -R data "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
cp -R config "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)/$(TARGET_NAME)/share/$(TARGET_NAME)/"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp LICENSE "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
cp README.md "$(RELEASE_FOLDER)/$(TARGET_NAME)/"
|
||||||
|
|
||||||
@@ -391,7 +398,8 @@ raspi_release:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"
|
$(MKDIR) "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"
|
cp -R config "$(RELEASE_FOLDER)"
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"
|
||||||
cp LICENSE "$(RELEASE_FOLDER)"
|
cp LICENSE "$(RELEASE_FOLDER)"
|
||||||
cp README.md "$(RELEASE_FOLDER)"
|
cp README.md "$(RELEASE_FOLDER)"
|
||||||
|
|
||||||
@@ -416,7 +424,8 @@ anbernic:
|
|||||||
$(MKDIR) "$(RELEASE_FOLDER)"_anbernic
|
$(MKDIR) "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
|
||||||
# Copia ficheros
|
# Copia ficheros
|
||||||
cp -R data "$(RELEASE_FOLDER)"_anbernic
|
cp -R config "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
cp resources.pack "$(RELEASE_FOLDER)"_anbernic
|
||||||
|
|
||||||
# Compila
|
# Compila
|
||||||
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
|
$(CXX) $(APP_SOURCES) $(INCLUDES) -DANBERNIC -DNO_SHADERS -DARCADE -DVERBOSE $(CXXFLAGS) $(LDFLAGS) -o $(RELEASE_FOLDER)_anbernic/$(TARGET_NAME)
|
||||||
|
|||||||
@@ -9,15 +9,17 @@ DATA|${SYSTEM_FOLDER}/controllers.json|optional,absolute
|
|||||||
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
DATA|${SYSTEM_FOLDER}/score.bin|optional,absolute
|
||||||
|
|
||||||
# Archivos de configuración del juego
|
# Archivos de configuración del juego
|
||||||
DATA|${PREFIX}/data/config/formations.txt
|
DATA|${PREFIX}/config/formations.txt
|
||||||
DATA|${PREFIX}/data/config/gamecontrollerdb.txt
|
DATA|${PREFIX}/config/gamecontrollerdb.txt
|
||||||
DATA|${PREFIX}/data/config/param_320x240.txt
|
DATA|${PREFIX}/config/param_320x240.txt
|
||||||
DATA|${PREFIX}/data/config/param_320x256.txt
|
DATA|${PREFIX}/config/param_320x256.txt
|
||||||
DATA|${PREFIX}/data/config/param_red.txt
|
DATA|${PREFIX}/config/param_red.txt
|
||||||
DATA|${PREFIX}/data/config/pools.txt
|
DATA|${PREFIX}/config/pools.txt
|
||||||
DATA|${PREFIX}/data/config/stages.txt
|
DATA|${PREFIX}/config/stages.txt
|
||||||
DEMODATA|${PREFIX}/data/config/demo1.bin
|
|
||||||
DEMODATA|${PREFIX}/data/config/demo2.bin
|
# Archivos con los datos de la demo
|
||||||
|
DEMODATA|${PREFIX}/data/demo/demo1.bin
|
||||||
|
DEMODATA|${PREFIX}/data/demo/demo2.bin
|
||||||
|
|
||||||
# Música
|
# Música
|
||||||
MUSIC|${PREFIX}/data/music/credits.ogg
|
MUSIC|${PREFIX}/data/music/credits.ogg
|
||||||
@@ -50,6 +52,7 @@ SOUND|${PREFIX}/data/sound/notify.wav
|
|||||||
SOUND|${PREFIX}/data/sound/player_collision.wav
|
SOUND|${PREFIX}/data/sound/player_collision.wav
|
||||||
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav
|
SOUND|${PREFIX}/data/sound/power_ball_explosion.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav
|
SOUND|${PREFIX}/data/sound/service_menu_adjust.wav
|
||||||
|
SOUND|${PREFIX}/data/sound/service_menu_back.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_move.wav
|
SOUND|${PREFIX}/data/sound/service_menu_move.wav
|
||||||
SOUND|${PREFIX}/data/sound/service_menu_select.wav
|
SOUND|${PREFIX}/data/sound/service_menu_select.wav
|
||||||
SOUND|${PREFIX}/data/sound/stage_change.wav
|
SOUND|${PREFIX}/data/sound/stage_change.wav
|
||||||
@@ -172,6 +175,7 @@ BITMAP|${PREFIX}/data/gfx/player/hit.png
|
|||||||
|
|
||||||
# Fuentes de texto
|
# Fuentes de texto
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_2x.png
|
BITMAP|${PREFIX}/data/font/04b_25_2x.png
|
||||||
|
BITMAP|${PREFIX}/data/font/04b_25_2x_white.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
|
BITMAP|${PREFIX}/data/font/04b_25_flat_2x.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_flat.png
|
BITMAP|${PREFIX}/data/font/04b_25_flat.png
|
||||||
BITMAP|${PREFIX}/data/font/04b_25_grey.png
|
BITMAP|${PREFIX}/data/font/04b_25_grey.png
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
data/font/04b_25_2x_white.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 882 B |
BIN
data/sound/click-2.wav
Normal file
BIN
data/sound/click-3.wav
Normal file
BIN
data/sound/service_menu_back.wav
Normal file
BIN
define_buttons.o
@@ -9,22 +9,44 @@
|
|||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <utility> // Para pair
|
#include <utility> // Para pair
|
||||||
|
|
||||||
#include "texture.h" // Para Texture
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "utils.h" // Para printWithDots
|
#include "texture.h" // Para Texture
|
||||||
|
#include "utils.h" // Para printWithDots
|
||||||
|
|
||||||
// Carga las animaciones en un vector(Animations) desde un fichero
|
// Carga las animaciones en un vector(Animations) desde un fichero
|
||||||
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {
|
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer {
|
||||||
std::ifstream file(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!file) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
std::istringstream stream;
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
bool using_resource_data = false;
|
||||||
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& input_stream = using_resource_data ? stream : static_cast<std::istream&>(file);
|
||||||
|
|
||||||
printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
||||||
|
|
||||||
std::vector<std::string> buffer;
|
std::vector<std::string> buffer;
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(input_stream, line)) {
|
||||||
|
// Eliminar caracteres de retorno de carro (\r) al final de la línea
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
if (!line.empty()) {
|
if (!line.empty()) {
|
||||||
buffer.push_back(line);
|
buffer.push_back(line);
|
||||||
}
|
}
|
||||||
@@ -64,7 +86,7 @@ auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcula el frame correspondiente a la animación
|
// Calcula el frame correspondiente a la animación (frame-based)
|
||||||
void AnimatedSprite::animate() {
|
void AnimatedSprite::animate() {
|
||||||
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
|
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
|
||||||
return;
|
return;
|
||||||
@@ -94,6 +116,39 @@ void AnimatedSprite::animate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula el frame correspondiente a la animación (time-based)
|
||||||
|
void AnimatedSprite::animate(float deltaTime) {
|
||||||
|
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertir speed (frames) a tiempo: speed frames = speed/60 segundos a 60fps
|
||||||
|
float frameTime = static_cast<float>(animations_[current_animation_].speed) / 60.0f;
|
||||||
|
|
||||||
|
// Acumular tiempo transcurrido
|
||||||
|
animations_[current_animation_].time_accumulator += deltaTime;
|
||||||
|
|
||||||
|
// Verificar si es momento de cambiar frame
|
||||||
|
if (animations_[current_animation_].time_accumulator >= frameTime) {
|
||||||
|
animations_[current_animation_].time_accumulator -= frameTime;
|
||||||
|
animations_[current_animation_].current_frame++;
|
||||||
|
|
||||||
|
// Si alcanza el final de la animación
|
||||||
|
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
|
||||||
|
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
|
||||||
|
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size() - 1;
|
||||||
|
animations_[current_animation_].completed = true;
|
||||||
|
} else { // Si hay loop, vuelve al frame indicado
|
||||||
|
animations_[current_animation_].time_accumulator = 0.0f;
|
||||||
|
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar el sprite clip
|
||||||
|
updateSpriteClip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Comprueba si ha terminado la animación
|
// Comprueba si ha terminado la animación
|
||||||
auto AnimatedSprite::animationIsCompleted() -> bool {
|
auto AnimatedSprite::animationIsCompleted() -> bool {
|
||||||
return animations_[current_animation_].completed;
|
return animations_[current_animation_].completed;
|
||||||
@@ -108,10 +163,12 @@ void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
|
|||||||
if (reset) {
|
if (reset) {
|
||||||
animations_[current_animation_].current_frame = 0;
|
animations_[current_animation_].current_frame = 0;
|
||||||
animations_[current_animation_].counter = 0;
|
animations_[current_animation_].counter = 0;
|
||||||
|
animations_[current_animation_].time_accumulator = 0.0f;
|
||||||
animations_[current_animation_].completed = false;
|
animations_[current_animation_].completed = false;
|
||||||
} else {
|
} else {
|
||||||
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
|
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
|
||||||
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
|
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
|
||||||
|
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
|
||||||
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
|
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
|
||||||
}
|
}
|
||||||
updateSpriteClip();
|
updateSpriteClip();
|
||||||
@@ -127,26 +184,35 @@ void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
|
|||||||
if (reset) {
|
if (reset) {
|
||||||
animations_[current_animation_].current_frame = 0;
|
animations_[current_animation_].current_frame = 0;
|
||||||
animations_[current_animation_].counter = 0;
|
animations_[current_animation_].counter = 0;
|
||||||
|
animations_[current_animation_].time_accumulator = 0.0f;
|
||||||
animations_[current_animation_].completed = false;
|
animations_[current_animation_].completed = false;
|
||||||
} else {
|
} else {
|
||||||
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
|
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
|
||||||
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
|
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
|
||||||
|
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
|
||||||
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
|
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
|
||||||
}
|
}
|
||||||
updateSpriteClip();
|
updateSpriteClip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables del objeto
|
// Actualiza las variables del objeto (frame-based)
|
||||||
void AnimatedSprite::update() {
|
void AnimatedSprite::update() {
|
||||||
animate();
|
animate();
|
||||||
MovingSprite::update();
|
MovingSprite::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualiza las variables del objeto (time-based)
|
||||||
|
void AnimatedSprite::update(float deltaTime) {
|
||||||
|
animate(deltaTime);
|
||||||
|
MovingSprite::update(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
// Reinicia la animación
|
// Reinicia la animación
|
||||||
void AnimatedSprite::resetAnimation() {
|
void AnimatedSprite::resetAnimation() {
|
||||||
animations_[current_animation_].current_frame = 0;
|
animations_[current_animation_].current_frame = 0;
|
||||||
animations_[current_animation_].counter = 0;
|
animations_[current_animation_].counter = 0;
|
||||||
|
animations_[current_animation_].time_accumulator = 0.0f;
|
||||||
animations_[current_animation_].completed = false;
|
animations_[current_animation_].completed = false;
|
||||||
animations_[current_animation_].paused = false;
|
animations_[current_animation_].paused = false;
|
||||||
updateSpriteClip();
|
updateSpriteClip();
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ struct Animation {
|
|||||||
|
|
||||||
std::string name; // Nombre de la animación
|
std::string name; // Nombre de la animación
|
||||||
std::vector<SDL_FRect> frames; // Frames que componen la animación
|
std::vector<SDL_FRect> frames; // Frames que componen la animación
|
||||||
int speed{DEFAULT_SPEED}; // Velocidad de reproducción
|
int speed{DEFAULT_SPEED}; // Velocidad de reproducción (frame-based)
|
||||||
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
|
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
|
||||||
bool completed{false}; // Indica si la animación ha finalizado
|
bool completed{false}; // Indica si la animación ha finalizado
|
||||||
size_t current_frame{0}; // Frame actual en reproducción
|
size_t current_frame{0}; // Frame actual en reproducción
|
||||||
int counter{0}; // Contador para la animación
|
int counter{0}; // Contador para la animación (frame-based)
|
||||||
|
float time_accumulator{0.0f}; // Acumulador de tiempo para animaciones time-based
|
||||||
bool paused{false}; // La animación no avanza
|
bool paused{false}; // La animación no avanza
|
||||||
|
|
||||||
Animation() = default;
|
Animation() = default;
|
||||||
@@ -50,11 +51,13 @@ class AnimatedSprite : public MovingSprite {
|
|||||||
// --- Constructores y destructor ---
|
// --- Constructores y destructor ---
|
||||||
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
|
AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
|
||||||
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
|
AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
|
||||||
explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(std::move(texture)) {}
|
explicit AnimatedSprite(std::shared_ptr<Texture> texture)
|
||||||
|
: MovingSprite(std::move(texture)) {}
|
||||||
~AnimatedSprite() override = default;
|
~AnimatedSprite() override = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void update() override; // Actualiza la animación
|
void update() override; // Actualiza la animación (frame-based)
|
||||||
|
void update(float deltaTime); // Actualiza la animación (time-based)
|
||||||
|
|
||||||
// --- 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
|
||||||
@@ -77,7 +80,8 @@ class AnimatedSprite : public MovingSprite {
|
|||||||
int current_animation_ = 0; // Índice de la animación activa
|
int current_animation_ = 0; // Índice de la animación activa
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void animate(); // Calcula el frame correspondiente a la animación
|
void animate(); // Calcula el frame correspondiente a la animación (frame-based)
|
||||||
|
void animate(float deltaTime); // Calcula el frame correspondiente a la animación (time-based)
|
||||||
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
|
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
|
||||||
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
|
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
|
||||||
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame
|
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
#include <sstream> // Para basic_istringstream
|
#include <sstream> // Para basic_istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
|
|
||||||
#include "utils.h" // Para getFileName
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
#include "utils.h" // Para getFileName
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
Asset *Asset::instance = nullptr;
|
Asset *Asset::instance = nullptr;
|
||||||
@@ -139,6 +140,17 @@ auto Asset::get(const std::string &filename) const -> std::string {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Carga datos del archivo usando ResourceHelper
|
||||||
|
auto Asset::loadData(const std::string &filename) const -> std::vector<uint8_t> {
|
||||||
|
auto it = file_list_.find(filename);
|
||||||
|
if (it != file_list_.end()) {
|
||||||
|
return ResourceHelper::loadFile(it->second.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found for data loading", filename.c_str());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Verifica si un recurso existe
|
// Verifica si un recurso existe
|
||||||
auto Asset::exists(const std::string &filename) const -> bool {
|
auto Asset::exists(const std::string &filename) const -> bool {
|
||||||
return file_list_.find(filename) != file_list_.end();
|
return file_list_.find(filename) != file_list_.end();
|
||||||
@@ -194,9 +206,16 @@ auto Asset::check() const -> bool {
|
|||||||
|
|
||||||
// Comprueba que existe un fichero
|
// Comprueba que existe un fichero
|
||||||
auto Asset::checkFile(const std::string &path) -> bool {
|
auto Asset::checkFile(const std::string &path) -> bool {
|
||||||
std::ifstream file(path);
|
// Intentar primero con ResourceHelper
|
||||||
bool success = file.good();
|
auto data = ResourceHelper::loadFile(path);
|
||||||
file.close();
|
bool success = !data.empty();
|
||||||
|
|
||||||
|
// Si no se encuentra en el pack, intentar con filesystem directo
|
||||||
|
if (!success) {
|
||||||
|
std::ifstream file(path);
|
||||||
|
success = file.good();
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint> // Para uint8_t
|
||||||
#include <string> // Para string
|
#include <string> // Para string
|
||||||
#include <unordered_map> // Para unordered_map
|
#include <unordered_map> // Para unordered_map
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
@@ -33,6 +34,7 @@ class Asset {
|
|||||||
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
|
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
|
||||||
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
|
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
|
||||||
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
|
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
|
||||||
|
[[nodiscard]] auto loadData(const std::string &filename) const -> std::vector<uint8_t>; // Carga datos del archivo
|
||||||
[[nodiscard]] auto check() const -> bool;
|
[[nodiscard]] auto check() const -> bool;
|
||||||
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
||||||
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
|
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
|
||||||
@@ -45,7 +47,9 @@ class Asset {
|
|||||||
bool required; // Indica si el archivo es obligatorio
|
bool required; // Indica si el archivo es obligatorio
|
||||||
|
|
||||||
Item(std::string path, Type asset_type, bool is_required)
|
Item(std::string path, Type asset_type, bool is_required)
|
||||||
: file(std::move(path)), type(asset_type), required(is_required) {}
|
: file(std::move(path)),
|
||||||
|
type(asset_type),
|
||||||
|
required(is_required) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables internas ---
|
// --- Variables internas ---
|
||||||
|
|||||||
106
source/asset_integrated.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include "asset_integrated.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
bool AssetIntegrated::resource_pack_enabled_ = false;
|
||||||
|
|
||||||
|
void AssetIntegrated::initWithResourcePack(const std::string &executable_path,
|
||||||
|
const std::string &resource_pack_path) {
|
||||||
|
// Inicializar Asset normal
|
||||||
|
Asset::init(executable_path);
|
||||||
|
|
||||||
|
// Inicializar ResourceLoader
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
if (loader.initialize(resource_pack_path, true)) {
|
||||||
|
resource_pack_enabled_ = true;
|
||||||
|
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << std::endl;
|
||||||
|
} else {
|
||||||
|
resource_pack_enabled_ = false;
|
||||||
|
std::cout << "Asset system initialized in fallback mode (filesystem)" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> AssetIntegrated::loadFile(const std::string &filename) {
|
||||||
|
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
|
||||||
|
// Intentar cargar del pack de recursos
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
|
||||||
|
// Convertir ruta completa a ruta relativa para el pack
|
||||||
|
std::string relativePath = filename;
|
||||||
|
|
||||||
|
// Si la ruta contiene "data/", extraer la parte relativa
|
||||||
|
size_t dataPos = filename.find("data/");
|
||||||
|
if (dataPos != std::string::npos) {
|
||||||
|
relativePath = filename.substr(dataPos + 5); // +5 para saltar "data/"
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = loader.loadResource(relativePath);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: cargar del filesystem
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char *>(data.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetIntegrated::fileExists(const std::string &filename) const {
|
||||||
|
if (shouldUseResourcePack(filename) && resource_pack_enabled_) {
|
||||||
|
auto &loader = ResourceLoader::getInstance();
|
||||||
|
|
||||||
|
// Convertir ruta completa a ruta relativa para el pack
|
||||||
|
std::string relativePath = filename;
|
||||||
|
size_t dataPos = filename.find("data/");
|
||||||
|
if (dataPos != std::string::npos) {
|
||||||
|
relativePath = filename.substr(dataPos + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader.resourceExists(relativePath)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar en filesystem
|
||||||
|
return std::filesystem::exists(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AssetIntegrated::getSystemPath(const std::string &filename) const {
|
||||||
|
// Los archivos de sistema/config siempre van al filesystem
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetIntegrated::shouldUseResourcePack(const std::string &filepath) const {
|
||||||
|
// Los archivos que NO van al pack:
|
||||||
|
// - Archivos de config/ (ahora están fuera de data/)
|
||||||
|
// - Archivos con absolute=true en assets.txt
|
||||||
|
// - Archivos de sistema (${SYSTEM_FOLDER})
|
||||||
|
|
||||||
|
if (filepath.find("/config/") != std::string::npos ||
|
||||||
|
filepath.find("config/") == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filepath.find("/data/") != std::string::npos ||
|
||||||
|
filepath.find("data/") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
29
source/asset_integrated.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "asset.h"
|
||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
// Extensión de Asset que integra ResourceLoader
|
||||||
|
class AssetIntegrated : public Asset {
|
||||||
|
public:
|
||||||
|
// Inicializa Asset con ResourceLoader
|
||||||
|
static void initWithResourcePack(const std::string &executable_path,
|
||||||
|
const std::string &resource_pack_path = "resources.pack");
|
||||||
|
|
||||||
|
// Carga un archivo usando ResourceLoader como primera opción
|
||||||
|
std::vector<uint8_t> loadFile(const std::string &filename);
|
||||||
|
|
||||||
|
// Verifica si un archivo existe (pack o filesystem)
|
||||||
|
bool fileExists(const std::string &filename) const;
|
||||||
|
|
||||||
|
// Obtiene la ruta completa para archivos del sistema/config
|
||||||
|
std::string getSystemPath(const std::string &filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool resource_pack_enabled_;
|
||||||
|
|
||||||
|
// Determina si un archivo debe cargarse del pack o del filesystem
|
||||||
|
bool shouldUseResourcePack(const std::string &filepath) const;
|
||||||
|
};
|
||||||
@@ -100,7 +100,7 @@ void Audio::stopAllSounds() const {
|
|||||||
|
|
||||||
// Realiza un fundido de salida de la música
|
// Realiza un fundido de salida de la música
|
||||||
void Audio::fadeOutMusic(int milliseconds) const {
|
void Audio::fadeOutMusic(int milliseconds) const {
|
||||||
if (music_enabled_) {
|
if (music_enabled_ && music_.state == MusicState::PLAYING) {
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
JA_FadeOutMusic(milliseconds);
|
JA_FadeOutMusic(milliseconds);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -75,11 +75,15 @@ class Audio {
|
|||||||
bool loop; // Indica si la última pista de música se debe reproducir en bucle
|
bool loop; // Indica si la última pista de música se debe reproducir en bucle
|
||||||
|
|
||||||
// Constructor para inicializar la música con valores predeterminados
|
// Constructor para inicializar la música con valores predeterminados
|
||||||
Music() : state(MusicState::STOPPED), loop(false) {}
|
Music()
|
||||||
|
: state(MusicState::STOPPED),
|
||||||
|
loop(false) {}
|
||||||
|
|
||||||
// Constructor para inicializar con valores específicos
|
// Constructor para inicializar con valores específicos
|
||||||
Music(MusicState init_state, std::string init_name, bool init_loop)
|
Music(MusicState init_state, std::string init_name, bool init_loop)
|
||||||
: state(init_state), name(std::move(init_name)), loop(init_loop) {}
|
: state(init_state),
|
||||||
|
name(std::move(init_name)),
|
||||||
|
loop(init_loop) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables de estado ---
|
// --- Variables de estado ---
|
||||||
|
|||||||
@@ -126,7 +126,14 @@ void Background::initializeTextures() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la lógica del objeto
|
// Actualiza la lógica del objeto
|
||||||
|
// Actualiza la lógica del objeto (compatibilidad)
|
||||||
void Background::update() {
|
void Background::update() {
|
||||||
|
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
|
||||||
|
update(FRAME_TIME_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza la lógica del objeto
|
||||||
|
void Background::update(float delta_time) {
|
||||||
// Actualiza la progresión y calcula transiciones
|
// Actualiza la progresión y calcula transiciones
|
||||||
if (!manual_mode_) {
|
if (!manual_mode_) {
|
||||||
updateProgression();
|
updateProgression();
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class Background {
|
|||||||
~Background(); // Destructor
|
~Background(); // Destructor
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void update(); // Actualiza la lógica del objeto
|
void update(); // Actualiza la lógica del objeto (compatibilidad)
|
||||||
|
void update(float delta_time); // Actualiza la lógica del objeto
|
||||||
void render(); // Dibuja el objeto
|
void render(); // Dibuja el objeto
|
||||||
void reset(); // Reinicia la progresión
|
void reset(); // Reinicia la progresión
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,21 @@
|
|||||||
#include "texture.h" // Para Texture
|
#include "texture.h" // Para Texture
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
|
Balloon::Balloon(const Config& config)
|
||||||
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
|
: sprite_(std::make_unique<AnimatedSprite>(config.texture, config.animation)),
|
||||||
x_(x),
|
x_(config.x),
|
||||||
y_(y),
|
y_(config.y),
|
||||||
vx_(vel_x),
|
vx_(config.vel_x),
|
||||||
being_created_(creation_timer > 0),
|
being_created_(config.creation_counter > 0),
|
||||||
invulnerable_(creation_timer > 0),
|
invulnerable_(config.creation_counter > 0),
|
||||||
stopped_(creation_timer > 0),
|
stopped_(config.creation_counter > 0),
|
||||||
creation_counter_(creation_timer),
|
creation_counter_(config.creation_counter),
|
||||||
creation_counter_ini_(creation_timer),
|
creation_counter_ini_(config.creation_counter),
|
||||||
type_(type),
|
type_(config.type),
|
||||||
size_(size),
|
size_(config.size),
|
||||||
speed_(speed),
|
speed_(config.speed),
|
||||||
play_area_(play_area) {
|
play_area_(config.play_area),
|
||||||
|
sound_(config.sound) {
|
||||||
switch (type_) {
|
switch (type_) {
|
||||||
case Type::BALLOON: {
|
case Type::BALLOON: {
|
||||||
vy_ = 0;
|
vy_ = 0;
|
||||||
@@ -37,9 +38,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
power_ = POWER.at(INDEX);
|
power_ = POWER.at(INDEX);
|
||||||
menace_ = MENACE.at(INDEX);
|
menace_ = MENACE.at(INDEX);
|
||||||
score_ = SCORE.at(INDEX);
|
score_ = SCORE.at(INDEX);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(INDEX);
|
sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
|
||||||
popping_sound_ = POPPING_SOUND.at(INDEX);
|
sound_.popping_file = POPPING_SOUND.at(INDEX);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,17 +52,16 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
power_ = POWER.at(INDEX);
|
power_ = POWER.at(INDEX);
|
||||||
menace_ = MENACE.at(INDEX);
|
menace_ = MENACE.at(INDEX);
|
||||||
score_ = SCORE.at(INDEX);
|
score_ = SCORE.at(INDEX);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(INDEX);
|
sound_.bouncing_file = BOUNCING_SOUND.at(INDEX);
|
||||||
popping_sound_ = POPPING_SOUND.at(INDEX);
|
sound_.popping_file = POPPING_SOUND.at(INDEX);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type::POWERBALL: {
|
case Type::POWERBALL: {
|
||||||
constexpr int INDEX = 3;
|
constexpr int INDEX = 3;
|
||||||
h_ = w_ = WIDTH.at(4);
|
h_ = w_ = WIDTH.at(4);
|
||||||
bouncing_sound_ = BOUNCING_SOUND.at(3);
|
sound_.bouncing_file = BOUNCING_SOUND.at(3);
|
||||||
popping_sound_ = "power_ball_explosion.wav";
|
sound_.popping_file = "power_ball_explosion.wav";
|
||||||
power_ = score_ = menace_ = 0;
|
power_ = score_ = menace_ = 0;
|
||||||
|
|
||||||
vy_ = 0;
|
vy_ = 0;
|
||||||
@@ -70,9 +69,8 @@ Balloon::Balloon(float x, float y, Type type, Size size, float vel_x, float spee
|
|||||||
gravity_ = param.balloon.settings.at(INDEX).grav;
|
gravity_ = param.balloon.settings.at(INDEX).grav;
|
||||||
default_vy_ = param.balloon.settings.at(INDEX).vel;
|
default_vy_ = param.balloon.settings.at(INDEX).vel;
|
||||||
|
|
||||||
sprite_->setRotate(creation_timer <= 0);
|
sprite_->setRotate(config.creation_counter <= 0);
|
||||||
sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0);
|
sprite_->setRotateAmount(vx_ > 0.0F ? 2.0 : -2.0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,8 +231,14 @@ void Balloon::applyGravity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Balloon::playBouncingSound() {
|
void Balloon::playBouncingSound() {
|
||||||
if (bouncing_sound_enabled_) {
|
if (sound_.enabled && sound_.bouncing_enabled) {
|
||||||
playSound(bouncing_sound_);
|
Audio::get()->playSound(sound_.bouncing_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Balloon::playPoppingSound() {
|
||||||
|
if (sound_.enabled && sound_.poping_enabled) {
|
||||||
|
Audio::get()->playSound(sound_.popping_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,23 +372,8 @@ void Balloon::useNormalColor() {
|
|||||||
setAnimation();
|
setAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reproduce sonido
|
|
||||||
void Balloon::playSound(const std::string &name) const {
|
|
||||||
if (!sound_enabled_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto *audio_ = Audio::get();
|
|
||||||
audio_->playSound(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explota el globo
|
// Explota el globo
|
||||||
void Balloon::pop(bool should_sound) {
|
void Balloon::pop(bool should_sound) {
|
||||||
if (should_sound) {
|
if (should_sound) { playPoppingSound(); }
|
||||||
if (poping_sound_enabled_) {
|
|
||||||
playSound(popping_sound_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled_ = false;
|
enabled_ = false;
|
||||||
}
|
}
|
||||||
140
source/balloon.h
@@ -25,10 +25,16 @@ class Balloon {
|
|||||||
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
|
static constexpr std::array<int, 5> WIDTH = {10, 16, 26, 48, 49};
|
||||||
|
|
||||||
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
|
static constexpr std::array<std::string_view, 4> BOUNCING_SOUND = {
|
||||||
"balloon_bounce0.wav", "balloon_bounce1.wav", "balloon_bounce2.wav", "balloon_bounce3.wav"};
|
"balloon_bounce0.wav",
|
||||||
|
"balloon_bounce1.wav",
|
||||||
|
"balloon_bounce2.wav",
|
||||||
|
"balloon_bounce3.wav"};
|
||||||
|
|
||||||
static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
|
static constexpr std::array<std::string_view, 4> POPPING_SOUND = {
|
||||||
"balloon_pop0.wav", "balloon_pop1.wav", "balloon_pop2.wav", "balloon_pop3.wav"};
|
"balloon_pop0.wav",
|
||||||
|
"balloon_pop1.wav",
|
||||||
|
"balloon_pop2.wav",
|
||||||
|
"balloon_pop3.wav"};
|
||||||
|
|
||||||
static constexpr float VELX_POSITIVE = 0.7F;
|
static constexpr float VELX_POSITIVE = 0.7F;
|
||||||
static constexpr float VELX_NEGATIVE = -0.7F;
|
static constexpr float VELX_NEGATIVE = -0.7F;
|
||||||
@@ -52,18 +58,32 @@ class Balloon {
|
|||||||
POWERBALL = 2, // Globo de poder
|
POWERBALL = 2, // Globo de poder
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Estructura para manejo de sonido ---
|
||||||
|
struct Sound {
|
||||||
|
std::string bouncing_file; // Archivo de sonido al rebotar
|
||||||
|
std::string popping_file; // Archivo de sonido al explotar
|
||||||
|
bool bouncing_enabled = false; // Si debe sonar el globo al rebotar
|
||||||
|
bool poping_enabled = true; // Si debe sonar el globo al explotar
|
||||||
|
bool enabled = true; // Indica si los globos deben hacer algun sonido
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Estructura de configuración para inicialización ---
|
||||||
|
struct Config {
|
||||||
|
float x = 0.0F;
|
||||||
|
float y = 0.0F;
|
||||||
|
Type type = Type::BALLOON;
|
||||||
|
Size size = Size::EXTRALARGE;
|
||||||
|
float vel_x = VELX_POSITIVE;
|
||||||
|
float speed = SPEED.at(0);
|
||||||
|
Uint16 creation_counter = 0;
|
||||||
|
SDL_FRect play_area = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||||
|
std::shared_ptr<Texture> texture = nullptr;
|
||||||
|
std::vector<std::string> animation;
|
||||||
|
Sound sound;
|
||||||
|
};
|
||||||
|
|
||||||
// --- Constructores y destructor ---
|
// --- Constructores y destructor ---
|
||||||
Balloon(
|
Balloon(const Config& config);
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
Type type,
|
|
||||||
Size size,
|
|
||||||
float vel_x,
|
|
||||||
float speed,
|
|
||||||
Uint16 creation_timer,
|
|
||||||
SDL_FRect play_area,
|
|
||||||
const std::shared_ptr<Texture>& texture,
|
|
||||||
const std::vector<std::string>& animation);
|
|
||||||
~Balloon() = default;
|
~Balloon() = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
@@ -102,9 +122,9 @@ class Balloon {
|
|||||||
void setVelY(float vel_y) { vy_ = vel_y; }
|
void setVelY(float vel_y) { vy_ = vel_y; }
|
||||||
void setSpeed(float speed) { speed_ = speed; }
|
void setSpeed(float speed) { speed_ = speed; }
|
||||||
void setInvulnerable(bool value) { invulnerable_ = value; }
|
void setInvulnerable(bool value) { invulnerable_ = value; }
|
||||||
void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; }
|
void setBouncingSound(bool value) { sound_.bouncing_enabled = value; }
|
||||||
void setPoppingSound(bool value) { poping_sound_enabled_ = value; }
|
void setPoppingSound(bool value) { sound_.poping_enabled = value; }
|
||||||
void setSound(bool value) { sound_enabled_ = value; }
|
void setSound(bool value) { sound_.enabled = value; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Estructura para el efecto de rebote ---
|
// --- Estructura para el efecto de rebote ---
|
||||||
@@ -114,10 +134,28 @@ class Balloon {
|
|||||||
|
|
||||||
// Tablas de valores predefinidos para el efecto de rebote
|
// Tablas de valores predefinidos para el efecto de rebote
|
||||||
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
|
static constexpr std::array<float, BOUNCE_FRAMES> HORIZONTAL_ZOOM_VALUES = {
|
||||||
1.10F, 1.05F, 1.00F, 0.95F, 0.90F, 0.95F, 1.00F, 1.02F, 1.05F, 1.02F};
|
1.10F,
|
||||||
|
1.05F,
|
||||||
|
1.00F,
|
||||||
|
0.95F,
|
||||||
|
0.90F,
|
||||||
|
0.95F,
|
||||||
|
1.00F,
|
||||||
|
1.02F,
|
||||||
|
1.05F,
|
||||||
|
1.02F};
|
||||||
|
|
||||||
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
|
static constexpr std::array<float, BOUNCE_FRAMES> VERTICAL_ZOOM_VALUES = {
|
||||||
0.90F, 0.95F, 1.00F, 1.05F, 1.10F, 1.05F, 1.00F, 0.98F, 0.95F, 0.98F};
|
0.90F,
|
||||||
|
0.95F,
|
||||||
|
1.00F,
|
||||||
|
1.05F,
|
||||||
|
1.10F,
|
||||||
|
1.05F,
|
||||||
|
1.00F,
|
||||||
|
0.98F,
|
||||||
|
0.95F,
|
||||||
|
0.98F};
|
||||||
|
|
||||||
// Estado del efecto
|
// Estado del efecto
|
||||||
bool enabled_ = false; // Si el efecto está activo
|
bool enabled_ = false; // Si el efecto está activo
|
||||||
@@ -203,47 +241,43 @@ class Balloon {
|
|||||||
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
|
std::unique_ptr<AnimatedSprite> sprite_; // Sprite del objeto globo
|
||||||
|
|
||||||
// --- Variables de estado y físicas ---
|
// --- Variables de estado y físicas ---
|
||||||
float x_; // Posición X
|
float x_; // Posición X
|
||||||
float y_; // Posición Y
|
float y_; // Posición Y
|
||||||
float w_; // Ancho
|
float w_; // Ancho
|
||||||
float h_; // Alto
|
float h_; // Alto
|
||||||
float vx_; // Velocidad X
|
float vx_; // Velocidad X
|
||||||
float vy_; // Velocidad Y
|
float vy_; // Velocidad Y
|
||||||
float gravity_; // Aceleración en Y
|
float gravity_; // Aceleración en Y
|
||||||
float default_vy_; // Velocidad inicial al rebotar
|
float default_vy_; // Velocidad inicial al rebotar
|
||||||
float max_vy_; // Máxima velocidad en Y
|
float max_vy_; // Máxima velocidad en Y
|
||||||
bool being_created_; // Si el globo se está creando
|
bool being_created_; // Si el globo se está creando
|
||||||
bool enabled_ = true; // Si el globo está activo
|
bool enabled_ = true; // Si el globo está activo
|
||||||
bool invulnerable_; // Si el globo es invulnerable
|
bool invulnerable_; // Si el globo es invulnerable
|
||||||
bool stopped_; // Si el globo está parado
|
bool stopped_; // Si el globo está parado
|
||||||
bool use_reversed_colors_ = false; // Si se usa el color alternativo
|
bool use_reversed_colors_ = false; // Si se usa el color alternativo
|
||||||
Circle collider_; // Círculo de colisión
|
Circle collider_; // Círculo de colisión
|
||||||
Uint16 creation_counter_; // Temporizador de creación
|
Uint16 creation_counter_; // Temporizador de creación
|
||||||
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
|
Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación
|
||||||
Uint16 score_; // Puntos al destruir el globo
|
Uint16 score_; // Puntos al destruir el globo
|
||||||
Type type_; // Tipo de globo
|
Type type_; // Tipo de globo
|
||||||
Size size_; // Tamaño de globo
|
Size size_; // Tamaño de globo
|
||||||
Uint8 menace_; // Amenaza que genera el globo
|
Uint8 menace_; // Amenaza que genera el globo
|
||||||
Uint32 counter_ = 0; // Contador interno
|
Uint32 counter_ = 0; // Contador interno
|
||||||
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad
|
float travel_y_ = 1.0F; // Distancia a recorrer en Y antes de aplicar gravedad
|
||||||
float speed_; // Velocidad del globo
|
float speed_; // Velocidad del globo
|
||||||
Uint8 power_; // Poder que alberga el globo
|
Uint8 power_; // Poder que alberga el globo
|
||||||
SDL_FRect play_area_; // Zona de movimiento del globo
|
SDL_FRect play_area_; // Zona de movimiento del globo
|
||||||
std::string bouncing_sound_; // Archivo de sonido al rebotar
|
Sound sound_; // Configuración de sonido del globo
|
||||||
std::string popping_sound_; // Archivo de sonido al explotar
|
BounceEffect bounce_effect_; // Efecto de rebote
|
||||||
bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar
|
|
||||||
bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar
|
|
||||||
bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido
|
|
||||||
BounceEffect bounce_effect_; // Efecto de rebote
|
|
||||||
|
|
||||||
// --- Posicionamiento y transformación ---
|
// --- Posicionamiento y transformación ---
|
||||||
void shiftColliders(); // Alinea el círculo de colisión con el sprite
|
void shiftColliders(); // Alinea el círculo de colisión con el sprite
|
||||||
void shiftSprite(); // Alinea el sprite en pantalla
|
void shiftSprite(); // Alinea el sprite en pantalla
|
||||||
|
|
||||||
// --- Animación y sonido ---
|
// --- Animación y sonido ---
|
||||||
void setAnimation(); // Establece la animación correspondiente
|
void setAnimation(); // Establece la animación correspondiente
|
||||||
void playSound(const std::string& name) const; // Reproduce un sonido por nombre
|
void playBouncingSound(); // Reproduce el sonido de rebote
|
||||||
void playBouncingSound(); // Reproduce el sonido de rebote
|
void playPoppingSound(); // Reproduce el sonido de reventar
|
||||||
|
|
||||||
// --- Movimiento y física ---
|
// --- Movimiento y física ---
|
||||||
void handleHorizontalMovement(); // Maneja el movimiento horizontal
|
void handleHorizontalMovement(); // Maneja el movimiento horizontal
|
||||||
|
|||||||
@@ -15,19 +15,24 @@ class BalloonFormations {
|
|||||||
public:
|
public:
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct SpawnParams {
|
struct SpawnParams {
|
||||||
int x = 0; // Posición en el eje X donde crear el globo
|
float x = 0; // Posición en el eje X donde crear el globo
|
||||||
int y = 0; // Posición en el eje Y donde crear el globo
|
float y = 0; // Posición en el eje Y donde crear el globo
|
||||||
float vel_x = 0.0F; // Velocidad inicial en el eje X
|
float vel_x = 0.0F; // Velocidad inicial en el eje X
|
||||||
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
|
Balloon::Type type = Balloon::Type::BALLOON; // Tipo de globo
|
||||||
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
|
Balloon::Size size = Balloon::Size::SMALL; // Tamaño de globo
|
||||||
int creation_counter = 0; // Temporizador para la creación del globo
|
Uint16 creation_counter = 0; // Temporizador para la creación del globo
|
||||||
|
|
||||||
// Constructor por defecto
|
// Constructor por defecto
|
||||||
SpawnParams() = default;
|
SpawnParams() = default;
|
||||||
|
|
||||||
// Constructor con parámetros
|
// Constructor con parámetros
|
||||||
SpawnParams(int x, int y, float vel_x, Balloon::Type type, Balloon::Size size, int creation_counter)
|
SpawnParams(float x, float y, float vel_x, Balloon::Type type, Balloon::Size size, Uint16 creation_counter)
|
||||||
: x(x), y(y), vel_x(vel_x), type(type), size(size), creation_counter(creation_counter) {}
|
: x(x),
|
||||||
|
y(y),
|
||||||
|
vel_x(vel_x),
|
||||||
|
type(type),
|
||||||
|
size(size),
|
||||||
|
creation_counter(creation_counter) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Formation {
|
struct Formation {
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
BalloonManager::BalloonManager(IStageInfo *stage_info)
|
BalloonManager::BalloonManager(IStageInfo *stage_info)
|
||||||
: explosions_(std::make_unique<Explosions>()), balloon_formations_(std::make_unique<BalloonFormations>()), stage_info_(stage_info) { init(); }
|
: explosions_(std::make_unique<Explosions>()),
|
||||||
|
balloon_formations_(std::make_unique<BalloonFormations>()),
|
||||||
|
stage_info_(stage_info) { init(); }
|
||||||
|
|
||||||
// Inicializa
|
// Inicializa
|
||||||
void BalloonManager::init() {
|
void BalloonManager::init() {
|
||||||
@@ -105,7 +107,15 @@ void BalloonManager::deployRandomFormation(int stage) {
|
|||||||
// Crea los globos de la formación
|
// Crea los globos de la formación
|
||||||
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormationFromPool(stage, formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, (creation_time_enabled_) ? balloon.creation_counter : 0);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = balloon.y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = static_cast<Uint16>(creation_time_enabled_ ? balloon.creation_counter : 0)};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reinicia el contador para el próximo despliegue
|
// Reinicia el contador para el próximo despliegue
|
||||||
@@ -118,15 +128,31 @@ void BalloonManager::deployRandomFormation(int stage) {
|
|||||||
void BalloonManager::deployFormation(int formation_id) {
|
void BalloonManager::deployFormation(int formation_id) {
|
||||||
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, balloon.y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = balloon.y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = balloon.creation_counter};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea una formación de globos específica a una altura determinada
|
// Crea una formación de globos específica a una altura determinada
|
||||||
void BalloonManager::deployFormation(int formation_id, int y) {
|
void BalloonManager::deployFormation(int formation_id, float y) {
|
||||||
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
const auto BALLOONS = balloon_formations_->getFormation(formation_id).balloons;
|
||||||
for (auto balloon : BALLOONS) {
|
for (auto balloon : BALLOONS) {
|
||||||
createBalloon(balloon.x, y, balloon.type, balloon.size, balloon.vel_x, balloon_speed_, balloon.creation_counter);
|
Balloon::Config config = {
|
||||||
|
.x = balloon.x,
|
||||||
|
.y = y,
|
||||||
|
.type = balloon.type,
|
||||||
|
.size = balloon.size,
|
||||||
|
.vel_x = balloon.vel_x,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = balloon.creation_counter};
|
||||||
|
createBalloon(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,13 +178,16 @@ auto BalloonManager::calculateScreenPower() -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crea un globo nuevo en el vector de globos
|
// Crea un globo nuevo en el vector de globos
|
||||||
auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon> {
|
auto BalloonManager::createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon> {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
const int INDEX = static_cast<int>(size);
|
const int INDEX = static_cast<int>(config.size);
|
||||||
balloons_.emplace_back(std::make_shared<Balloon>(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(INDEX), balloon_animations_.at(INDEX)));
|
config.play_area = play_area_;
|
||||||
balloons_.back()->setSound(sound_enabled_);
|
config.texture = balloon_textures_.at(INDEX);
|
||||||
balloons_.back()->setBouncingSound(bouncing_sound_enabled_);
|
config.animation = balloon_animations_.at(INDEX);
|
||||||
balloons_.back()->setPoppingSound(poping_sound_enabled_);
|
config.sound.enabled = sound_enabled_;
|
||||||
|
config.sound.bouncing_enabled = bouncing_sound_enabled_;
|
||||||
|
config.sound.poping_enabled = poping_sound_enabled_;
|
||||||
|
balloons_.emplace_back(std::make_shared<Balloon>(config));
|
||||||
return balloons_.back();
|
return balloons_.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,19 +198,24 @@ auto BalloonManager::createBalloon(float x, int y, Balloon::Type type, Balloon::
|
|||||||
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
|
void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction) {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
// Calcula parametros
|
// Calcula parametros
|
||||||
const float VX = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE;
|
|
||||||
const auto SIZE = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1);
|
|
||||||
const int PARENT_HEIGHT = balloon->getHeight();
|
const int PARENT_HEIGHT = balloon->getHeight();
|
||||||
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
|
const int CHILD_HEIGHT = Balloon::WIDTH.at(static_cast<int>(balloon->getSize()) - 1);
|
||||||
const int CHILD_WIDTH = CHILD_HEIGHT;
|
const int CHILD_WIDTH = CHILD_HEIGHT;
|
||||||
const float Y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2);
|
|
||||||
float x = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
|
const float X = direction == "LEFT" ? balloon->getPosX() + (balloon->getWidth() / 3) : balloon->getPosX() + (2 * (balloon->getWidth() / 3));
|
||||||
const float MIN_X = play_area_.x;
|
const float MIN_X = play_area_.x;
|
||||||
const float MAX_X = play_area_.w - CHILD_WIDTH;
|
const float MAX_X = play_area_.w - CHILD_WIDTH;
|
||||||
x = std::clamp(x - (CHILD_WIDTH / 2), MIN_X, MAX_X);
|
|
||||||
|
Balloon::Config config = {
|
||||||
|
.x = std::clamp(X - (CHILD_WIDTH / 2), MIN_X, MAX_X),
|
||||||
|
.y = balloon->getPosY() + ((PARENT_HEIGHT - CHILD_HEIGHT) / 2),
|
||||||
|
.size = static_cast<Balloon::Size>(static_cast<int>(balloon->getSize()) - 1),
|
||||||
|
.vel_x = direction == "LEFT" ? Balloon::VELX_NEGATIVE : Balloon::VELX_POSITIVE,
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = 0};
|
||||||
|
|
||||||
// Crea el globo
|
// Crea el globo
|
||||||
auto b = createBalloon(x, Y, balloon->getType(), SIZE, VX, balloon_speed_, 0);
|
auto b = createBalloon(config);
|
||||||
|
|
||||||
// Establece parametros
|
// Establece parametros
|
||||||
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
|
b->setVelY(b->getType() == Balloon::Type::BALLOON ? -2.50F : Balloon::VELX_NEGATIVE * 2.0F);
|
||||||
@@ -196,18 +230,32 @@ void BalloonManager::createChildBalloon(const std::shared_ptr<Balloon> &balloon,
|
|||||||
void BalloonManager::createPowerBall() {
|
void BalloonManager::createPowerBall() {
|
||||||
if (can_deploy_balloons_) {
|
if (can_deploy_balloons_) {
|
||||||
constexpr int VALUES = 6;
|
constexpr int VALUES = 6;
|
||||||
constexpr float POS_Y = -Balloon::WIDTH.at(4);
|
const int LUCK = rand() % VALUES;
|
||||||
constexpr int CREATION_TIME = 0;
|
|
||||||
|
|
||||||
const float LEFT = param.game.play_area.rect.x;
|
const float LEFT = param.game.play_area.rect.x;
|
||||||
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
|
const float CENTER = param.game.play_area.center_x - (Balloon::WIDTH.at(4) / 2);
|
||||||
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
|
const float RIGHT = param.game.play_area.rect.w - Balloon::WIDTH.at(4);
|
||||||
|
|
||||||
const int LUCK = rand() % VALUES;
|
|
||||||
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
|
const std::array<float, VALUES> POS_X = {LEFT, LEFT, CENTER, CENTER, RIGHT, RIGHT};
|
||||||
const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE};
|
const std::array<float, VALUES> VEL_X = {Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_POSITIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE, Balloon::VELX_NEGATIVE};
|
||||||
|
|
||||||
balloons_.emplace_back(std::make_unique<Balloon>(POS_X[LUCK], POS_Y, Balloon::Type::POWERBALL, Balloon::Size::EXTRALARGE, VEL_X[LUCK], balloon_speed_, CREATION_TIME, play_area_, balloon_textures_[4], balloon_animations_[4]));
|
Balloon::Config config = {
|
||||||
|
.x = POS_X.at(LUCK),
|
||||||
|
.y = -Balloon::WIDTH.at(4),
|
||||||
|
.type = Balloon::Type::POWERBALL,
|
||||||
|
.size = Balloon::Size::EXTRALARGE,
|
||||||
|
.vel_x = VEL_X.at(LUCK),
|
||||||
|
.speed = balloon_speed_,
|
||||||
|
.creation_counter = 0,
|
||||||
|
.play_area = play_area_,
|
||||||
|
.texture = balloon_textures_.at(4),
|
||||||
|
.animation = balloon_animations_.at(4),
|
||||||
|
.sound = {
|
||||||
|
.bouncing_enabled = bouncing_sound_enabled_,
|
||||||
|
.poping_enabled = poping_sound_enabled_,
|
||||||
|
.enabled = sound_enabled_}};
|
||||||
|
|
||||||
|
balloons_.emplace_back(std::make_unique<Balloon>(config));
|
||||||
balloons_.back()->setInvulnerable(true);
|
balloons_.back()->setInvulnerable(true);
|
||||||
|
|
||||||
power_ball_enabled_ = true;
|
power_ball_enabled_ = true;
|
||||||
@@ -332,19 +380,6 @@ void BalloonManager::createTwoBigBalloons() {
|
|||||||
deployFormation(1);
|
deployFormation(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea una disposición de globos aleatoria
|
|
||||||
void BalloonManager::createRandomBalloons() {
|
|
||||||
const int NUM_BALLOONS = 2 + (rand() % 4);
|
|
||||||
for (int i = 0; i < NUM_BALLOONS; ++i) {
|
|
||||||
const float X = param.game.game_area.rect.x + (rand() % static_cast<int>(param.game.game_area.rect.w)) - Balloon::WIDTH.at(3);
|
|
||||||
const int Y = param.game.game_area.rect.y + (rand() % 50);
|
|
||||||
const auto SIZE = static_cast<Balloon::Size>(rand() % 4);
|
|
||||||
const float VEL_X = (rand() % 2 == 0) ? Balloon::VELX_POSITIVE : Balloon::VELX_NEGATIVE;
|
|
||||||
const int CREATION_COUNTER = 0;
|
|
||||||
createBalloon(X, Y, Balloon::Type::BALLOON, SIZE, VEL_X, balloon_speed_, CREATION_COUNTER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtiene el nivel de ameza actual generado por los globos
|
// Obtiene el nivel de ameza actual generado por los globos
|
||||||
auto BalloonManager::getMenace() -> int {
|
auto BalloonManager::getMenace() -> int {
|
||||||
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
|
return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); });
|
||||||
|
|||||||
@@ -37,14 +37,13 @@ class BalloonManager {
|
|||||||
// --- Creación de formaciones enemigas ---
|
// --- Creación de formaciones enemigas ---
|
||||||
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
|
void deployRandomFormation(int stage); // Crea una formación de globos aleatoria
|
||||||
void deployFormation(int formation_id); // Crea una formación específica
|
void deployFormation(int formation_id); // Crea una formación específica
|
||||||
void deployFormation(int formation_id, int y); // Crea una formación específica con coordenadas
|
void deployFormation(int formation_id, float y); // Crea una formación específica con coordenadas
|
||||||
|
|
||||||
// --- Creación de globos ---
|
// --- Creación de globos ---
|
||||||
auto createBalloon(float x, int y, Balloon::Type type, Balloon::Size size, float velx, float speed, int creation_timer) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
|
auto createBalloon(Balloon::Config config) -> std::shared_ptr<Balloon>; // Crea un nuevo globo
|
||||||
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
|
void createChildBalloon(const std::shared_ptr<Balloon> &balloon, const std::string &direction); // Crea un globo a partir de otro
|
||||||
void createPowerBall(); // Crea una PowerBall
|
void createPowerBall(); // Crea una PowerBall
|
||||||
void createTwoBigBalloons(); // Crea dos globos grandes
|
void createTwoBigBalloons(); // Crea dos globos grandes
|
||||||
void createRandomBalloons(); // Crea una disposición aleatoria de globos
|
|
||||||
|
|
||||||
// --- Control de velocidad y despliegue ---
|
// --- Control de velocidad y despliegue ---
|
||||||
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
|
void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos
|
||||||
|
|||||||
110
source/color.cpp
@@ -116,71 +116,71 @@ constexpr auto Color::hsvToRgb(HSV hsv) -> Color {
|
|||||||
|
|
||||||
// Implementaciones del namespace Colors
|
// Implementaciones del namespace Colors
|
||||||
namespace Colors {
|
namespace Colors {
|
||||||
// Obtiene un color del vector de colores imitando al Coche Fantástico
|
// Obtiene un color del vector de colores imitando al Coche Fantástico
|
||||||
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
|
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color {
|
||||||
int cycle_length = (colors.size() * 2) - 2;
|
int cycle_length = (colors.size() * 2) - 2;
|
||||||
size_t n = counter % cycle_length;
|
size_t n = counter % cycle_length;
|
||||||
|
|
||||||
size_t index;
|
size_t index;
|
||||||
if (n < colors.size()) {
|
if (n < colors.size()) {
|
||||||
index = n; // Avanza: 0,1,2,3
|
index = n; // Avanza: 0,1,2,3
|
||||||
} else {
|
} else {
|
||||||
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
|
index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1
|
||||||
}
|
|
||||||
|
|
||||||
return colors[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
|
return colors[index];
|
||||||
Cycle result{};
|
}
|
||||||
HSV base_hsv = Color::rgbToHsv(base);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
|
auto generateMirroredCycle(Color base, ColorCycleStyle style) -> Cycle {
|
||||||
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
|
Cycle result{};
|
||||||
float hue_shift = 0.0F;
|
HSV base_hsv = Color::rgbToHsv(base);
|
||||||
float sat_shift = 0.0F;
|
|
||||||
float val_shift = 0.0F;
|
|
||||||
|
|
||||||
switch (style) {
|
for (size_t i = 0; i < CYCLE_SIZE; ++i) {
|
||||||
case ColorCycleStyle::SUBTLE_PULSE:
|
float t = static_cast<float>(i) / (CYCLE_SIZE - 1); // 0 → 1
|
||||||
// Solo brillo suave
|
float hue_shift = 0.0F;
|
||||||
val_shift = 0.07F * sinf(t * M_PI);
|
float sat_shift = 0.0F;
|
||||||
break;
|
float val_shift = 0.0F;
|
||||||
|
|
||||||
case ColorCycleStyle::HUE_WAVE:
|
switch (style) {
|
||||||
// Oscilación leve de tono
|
case ColorCycleStyle::SUBTLE_PULSE:
|
||||||
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
|
// Solo brillo suave
|
||||||
val_shift = 0.05F * sinf(t * M_PI);
|
val_shift = 0.07F * sinf(t * M_PI);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ColorCycleStyle::VIBRANT:
|
case ColorCycleStyle::HUE_WAVE:
|
||||||
// Cambios fuertes en tono y brillo
|
// Oscilación leve de tono
|
||||||
hue_shift = 35.0F * sinf(t * M_PI);
|
hue_shift = 15.0F * (t - 0.5F) * 2.0F;
|
||||||
val_shift = 0.2F * sinf(t * M_PI);
|
val_shift = 0.05F * sinf(t * M_PI);
|
||||||
sat_shift = -0.2F * sinf(t * M_PI);
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case ColorCycleStyle::DARKEN_GLOW:
|
case ColorCycleStyle::VIBRANT:
|
||||||
// Se oscurece al centro
|
// Cambios fuertes en tono y brillo
|
||||||
val_shift = -0.15F * sinf(t * M_PI);
|
hue_shift = 35.0F * sinf(t * M_PI);
|
||||||
break;
|
val_shift = 0.2F * sinf(t * M_PI);
|
||||||
|
sat_shift = -0.2F * sinf(t * M_PI);
|
||||||
|
break;
|
||||||
|
|
||||||
case ColorCycleStyle::LIGHT_FLASH:
|
case ColorCycleStyle::DARKEN_GLOW:
|
||||||
// Se ilumina al centro
|
// Se oscurece al centro
|
||||||
val_shift = 0.25F * sinf(t * M_PI);
|
val_shift = -0.15F * sinf(t * M_PI);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
HSV adjusted = {
|
case ColorCycleStyle::LIGHT_FLASH:
|
||||||
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
|
// Se ilumina al centro
|
||||||
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
|
val_shift = 0.25F * sinf(t * M_PI);
|
||||||
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
|
break;
|
||||||
|
|
||||||
Color c = Color::hsvToRgb(adjusted);
|
|
||||||
result[i] = c;
|
|
||||||
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
HSV adjusted = {
|
||||||
|
.h = fmodf(base_hsv.h + hue_shift + 360.0F, 360.0F),
|
||||||
|
.s = fminf(1.0F, fmaxf(0.0F, base_hsv.s + sat_shift)),
|
||||||
|
.v = fminf(1.0F, fmaxf(0.0F, base_hsv.v + val_shift))};
|
||||||
|
|
||||||
|
Color c = Color::hsvToRgb(adjusted);
|
||||||
|
result[i] = c;
|
||||||
|
result[(2 * CYCLE_SIZE) - 1 - i] = c; // espejo
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace Colors
|
||||||
@@ -36,10 +36,17 @@ struct Color {
|
|||||||
|
|
||||||
Uint8 r, g, b, a;
|
Uint8 r, g, b, a;
|
||||||
|
|
||||||
constexpr Color() : r(MIN_COLOR_VALUE), g(MIN_COLOR_VALUE), b(MIN_COLOR_VALUE), a(DEFAULT_ALPHA) {}
|
constexpr Color()
|
||||||
|
: r(MIN_COLOR_VALUE),
|
||||||
|
g(MIN_COLOR_VALUE),
|
||||||
|
b(MIN_COLOR_VALUE),
|
||||||
|
a(DEFAULT_ALPHA) {}
|
||||||
|
|
||||||
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
|
explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
|
||||||
: r(red), g(green), b(blue), a(alpha) {}
|
: r(red),
|
||||||
|
g(green),
|
||||||
|
b(blue),
|
||||||
|
a(alpha) {}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto INVERSE() const -> Color {
|
[[nodiscard]] constexpr auto INVERSE() const -> Color {
|
||||||
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
|
return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
|
||||||
@@ -108,25 +115,25 @@ enum class ColorCycleStyle {
|
|||||||
|
|
||||||
// --- Namespace Colors: constantes y utilidades de color ---
|
// --- Namespace Colors: constantes y utilidades de color ---
|
||||||
namespace Colors {
|
namespace Colors {
|
||||||
// --- Constantes ---
|
// --- Constantes ---
|
||||||
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
|
constexpr size_t CYCLE_SIZE = 6; // Mitad del ciclo espejado
|
||||||
|
|
||||||
// --- Alias ---
|
// --- Alias ---
|
||||||
using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
|
using Cycle = std::array<Color, 2 * CYCLE_SIZE>;
|
||||||
|
|
||||||
// --- Colores predefinidos ---
|
// --- Colores predefinidos ---
|
||||||
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
|
constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
|
||||||
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
|
constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
|
||||||
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
|
constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
|
||||||
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
|
constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);
|
||||||
|
|
||||||
constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
|
constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);
|
||||||
|
|
||||||
constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
|
constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
|
||||||
constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
|
constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
|
||||||
constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
|
constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
|
auto getColorLikeKnightRider(const std::vector<Color> &colors, int counter) -> Color;
|
||||||
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
|
auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
|
||||||
}
|
} // namespace Colors
|
||||||
@@ -76,7 +76,8 @@ struct BalloonSettings {
|
|||||||
float vel;
|
float vel;
|
||||||
float grav;
|
float grav;
|
||||||
constexpr BalloonSettings(float v, float g)
|
constexpr BalloonSettings(float v, float g)
|
||||||
: vel(v), grav(g) {}
|
: vel(v),
|
||||||
|
grav(g) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
constexpr std::array<BalloonSettings, 4> SETTINGS = {{
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ class DefineButtons {
|
|||||||
int button;
|
int button;
|
||||||
|
|
||||||
Button(std::string label, Input::Action action, int button)
|
Button(std::string label, Input::Action action, int button)
|
||||||
: label(std::move(label)), action(action), button(button) {}
|
: label(std::move(label)),
|
||||||
|
action(action),
|
||||||
|
button(button) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Constructor y destructor ---
|
// --- Constructor y destructor ---
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "param.h" // Para loadParamsFromFile
|
#include "param.h" // Para loadParamsFromFile
|
||||||
#include "player.h" // Para Player
|
#include "player.h" // Para Player
|
||||||
#include "resource.h" // Para Resource
|
#include "resource.h" // Para Resource
|
||||||
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "screen.h" // Para Screen
|
#include "screen.h" // Para Screen
|
||||||
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
#include "section.hpp" // Para Name, Options, name, options, AttractMode, attract_mode
|
||||||
#include "sections/credits.h" // Para Credits
|
#include "sections/credits.h" // Para Credits
|
||||||
@@ -41,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
|
|||||||
Section::name = Section::Name::GAME;
|
Section::name = Section::Name::GAME;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#elif _DEBUG
|
#elif _DEBUG
|
||||||
Section::name = Section::Name::GAME;
|
Section::name = Section::Name::INSTRUCTIONS;
|
||||||
Section::options = Section::Options::GAME_PLAY_1P;
|
Section::options = Section::Options::GAME_PLAY_1P;
|
||||||
#else // NORMAL GAME
|
#else // NORMAL GAME
|
||||||
Section::name = Section::Name::LOGO;
|
Section::name = Section::Name::LOGO;
|
||||||
@@ -76,7 +77,13 @@ Director::~Director() {
|
|||||||
// Inicializa todo
|
// Inicializa todo
|
||||||
void Director::init() {
|
void Director::init() {
|
||||||
// Configuración inicial de parametros
|
// Configuración inicial de parametros
|
||||||
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
|
||||||
|
|
||||||
|
#ifdef MACOS_BUNDLE
|
||||||
|
ResourceHelper::initializeResourceSystem(executable_path_ + "/../Resources/resources.pack");
|
||||||
|
#else
|
||||||
|
ResourceHelper::initializeResourceSystem("resources.pack");
|
||||||
|
#endif
|
||||||
loadAssets(); // Crea el índice de archivos
|
loadAssets(); // Crea el índice de archivos
|
||||||
Input::init(Asset::get()->get("gamecontrollerdb.txt"), Asset::get()->get("controllers.json")); // Carga configuración de controles
|
Input::init(Asset::get()->get("gamecontrollerdb.txt"), Asset::get()->get("controllers.json")); // Carga configuración de controles
|
||||||
Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
|
Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
|
||||||
@@ -154,7 +161,7 @@ void Director::loadAssets() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
// Cargar la configuración de assets (también aplicar el prefijo al archivo de configuración)
|
||||||
std::string config_path = executable_path_ + PREFIX + "/data/config/assets.txt";
|
std::string config_path = executable_path_ + PREFIX + "/config/assets.txt";
|
||||||
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
Asset::get()->loadFromFile(config_path, PREFIX, system_folder_);
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Assets configuration loaded successfully");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Assets configuration loaded successfully");
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ void EnterName::incPosition() {
|
|||||||
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
|
} else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGTH
|
||||||
{
|
{
|
||||||
// Copiamos el índice del carácter anterior si es posible.
|
// Copiamos el índice del carácter anterior si es posible.
|
||||||
character_index_[position_] = character_index_[position_ - 1];
|
// character_index_[position_] = character_index_[position_ - 1];
|
||||||
|
|
||||||
|
// Ponemos el caracter "espacio"
|
||||||
|
character_index_[position_] = 0;
|
||||||
} else {
|
} else {
|
||||||
// Si position_ es 0, inicializamos el carácter actual.
|
// Si position_ es 0, inicializamos el carácter actual.
|
||||||
character_index_[position_] = 0;
|
character_index_[position_] = 0;
|
||||||
@@ -144,12 +147,19 @@ auto EnterName::findIndex(char character) const -> int {
|
|||||||
// Devuelve un nombre al azar
|
// Devuelve un nombre al azar
|
||||||
auto EnterName::getRandomName() -> std::string {
|
auto EnterName::getRandomName() -> std::string {
|
||||||
static constexpr std::array<std::string_view, 8> NAMES = {
|
static constexpr std::array<std::string_view, 8> NAMES = {
|
||||||
"BAL1", "TABE", "DOC", "MON", "SAM1", "JORDI", "JDES", "PEPE"};
|
"BAL1",
|
||||||
|
"TABE",
|
||||||
|
"DOC",
|
||||||
|
"MON",
|
||||||
|
"SAM1",
|
||||||
|
"JORDI",
|
||||||
|
"JDES",
|
||||||
|
"PEPE"};
|
||||||
return std::string(NAMES[rand() % NAMES.size()]);
|
return std::string(NAMES[rand() % NAMES.size()]);
|
||||||
}
|
}
|
||||||
// Obtiene el nombre final introducido
|
// Obtiene el nombre final introducido
|
||||||
auto EnterName::getFinalName() -> std::string {
|
auto EnterName::getFinalName() -> std::string {
|
||||||
auto name = trim(name_.substr(0, position_));
|
auto name = trim(name_.substr(0, position_ + 1)); // Devuelve el texto intruducido incluyendo el del selector
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
name = getRandomName();
|
name = getRandomName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ struct ExplosionTexture {
|
|||||||
std::vector<std::string> animation; // Animación para la textura
|
std::vector<std::string> animation; // Animación para la textura
|
||||||
|
|
||||||
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
|
ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim)
|
||||||
: size(sz), texture(std::move(tex)), animation(anim) {}
|
: size(sz),
|
||||||
|
texture(std::move(tex)),
|
||||||
|
animation(anim) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Clase Explosions: gestor de explosiones ---
|
// --- Clase Explosions: gestor de explosiones ---
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ void Fade::init() {
|
|||||||
num_squares_width_ = param.fade.num_squares_width;
|
num_squares_width_ = param.fade.num_squares_width;
|
||||||
num_squares_height_ = param.fade.num_squares_height;
|
num_squares_height_ = param.fade.num_squares_height;
|
||||||
random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms
|
random_squares_duration_ = param.fade.random_squares_duration_ms; // Usar como duración en ms
|
||||||
square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual
|
square_transition_duration_ = random_squares_duration_ / 4; // 25% del tiempo total para la transición individual
|
||||||
random_squares_start_time_ = 0;
|
random_squares_start_time_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ void Fade::render() {
|
|||||||
if (state_ == State::FINISHED && mode_ == Mode::IN) {
|
if (state_ == State::FINISHED && mode_ == Mode::IN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,10 +82,15 @@ void Fade::update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compatibilidad delta-time (ignora el parámetro ya que usa SDL_GetTicks)
|
||||||
|
void Fade::update(float delta_time) {
|
||||||
|
update(); // Llama al método original
|
||||||
|
}
|
||||||
|
|
||||||
void Fade::updatePreState() {
|
void Fade::updatePreState() {
|
||||||
// Sistema basado en tiempo únicamente
|
// Sistema basado en tiempo únicamente
|
||||||
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;
|
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;
|
||||||
|
|
||||||
if (elapsed_time >= static_cast<Uint32>(pre_duration_)) {
|
if (elapsed_time >= static_cast<Uint32>(pre_duration_)) {
|
||||||
state_ = State::FADING;
|
state_ = State::FADING;
|
||||||
// CRÍTICO: Reinicializar tiempo de inicio para tipos que usan random_squares_start_time_
|
// CRÍTICO: Reinicializar tiempo de inicio para tipos que usan random_squares_start_time_
|
||||||
@@ -129,17 +134,17 @@ void Fade::changeToPostState() {
|
|||||||
void Fade::updatePostState() {
|
void Fade::updatePostState() {
|
||||||
// Sistema basado en tiempo únicamente
|
// Sistema basado en tiempo únicamente
|
||||||
Uint32 elapsed_time = SDL_GetTicks() - post_start_time_;
|
Uint32 elapsed_time = SDL_GetTicks() - post_start_time_;
|
||||||
|
|
||||||
if (elapsed_time >= static_cast<Uint32>(post_duration_)) {
|
if (elapsed_time >= static_cast<Uint32>(post_duration_)) {
|
||||||
state_ = State::FINISHED;
|
state_ = State::FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mantener el alpha final correcto para cada tipo de fade
|
// Mantener el alpha final correcto para cada tipo de fade
|
||||||
Uint8 post_alpha = a_;
|
Uint8 post_alpha = a_;
|
||||||
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
|
if (type_ == Type::RANDOM_SQUARE2 || type_ == Type::DIAGONAL) {
|
||||||
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
post_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanBackbuffer(r_, g_, b_, post_alpha);
|
cleanBackbuffer(r_, g_, b_, post_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,16 +209,16 @@ void Fade::updateRandomSquareFade() {
|
|||||||
|
|
||||||
void Fade::updateRandomSquare2Fade() {
|
void Fade::updateRandomSquare2Fade() {
|
||||||
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
int total_squares = num_squares_width_ * num_squares_height_;
|
int total_squares = num_squares_width_ * num_squares_height_;
|
||||||
|
|
||||||
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
||||||
int activation_time = random_squares_duration_ - square_transition_duration_;
|
int activation_time = random_squares_duration_ - square_transition_duration_;
|
||||||
activation_time = std::max(activation_time, square_transition_duration_); // Mínimo igual a la duración de transición
|
activation_time = std::max(activation_time, square_transition_duration_); // Mínimo igual a la duración de transición
|
||||||
|
|
||||||
// Lógica diferente según el modo
|
// Lógica diferente según el modo
|
||||||
int squares_to_activate = 0;
|
int squares_to_activate = 0;
|
||||||
|
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
// OUT: Activa cuadrados gradualmente
|
// OUT: Activa cuadrados gradualmente
|
||||||
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
@@ -222,7 +227,7 @@ void Fade::updateRandomSquare2Fade() {
|
|||||||
} else {
|
} else {
|
||||||
squares_to_activate = total_squares; // Activar todos
|
squares_to_activate = total_squares; // Activar todos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activa nuevos cuadrados y guarda su tiempo de activación
|
// Activa nuevos cuadrados y guarda su tiempo de activación
|
||||||
for (int i = 0; i < squares_to_activate && i < total_squares; ++i) {
|
for (int i = 0; i < squares_to_activate && i < total_squares; ++i) {
|
||||||
if (square_age_[i] == -1) {
|
if (square_age_[i] == -1) {
|
||||||
@@ -232,22 +237,22 @@ void Fade::updateRandomSquare2Fade() {
|
|||||||
} else {
|
} else {
|
||||||
// IN: Todos los cuadrados empiezan activos desde el inicio
|
// IN: Todos los cuadrados empiezan activos desde el inicio
|
||||||
squares_to_activate = total_squares;
|
squares_to_activate = total_squares;
|
||||||
|
|
||||||
// Activa cuadrados gradualmente con tiempo de inicio escalonado
|
// Activa cuadrados gradualmente con tiempo de inicio escalonado
|
||||||
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
int squares_starting_transition = static_cast<int>(activation_progress * total_squares);
|
int squares_starting_transition = static_cast<int>(activation_progress * total_squares);
|
||||||
|
|
||||||
// Asegurar que al menos 1 cuadrado se active desde el primer frame
|
// Asegurar que al menos 1 cuadrado se active desde el primer frame
|
||||||
squares_starting_transition = std::max(squares_starting_transition, 1);
|
squares_starting_transition = std::max(squares_starting_transition, 1);
|
||||||
squares_starting_transition = std::min(squares_starting_transition, total_squares);
|
squares_starting_transition = std::min(squares_starting_transition, total_squares);
|
||||||
|
|
||||||
for (int i = 0; i < squares_starting_transition; ++i) {
|
for (int i = 0; i < squares_starting_transition; ++i) {
|
||||||
if (square_age_[i] == -1) {
|
if (square_age_[i] == -1) {
|
||||||
square_age_[i] = elapsed_time; // Empieza la transición a transparente
|
square_age_[i] = elapsed_time; // Empieza la transición a transparente
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawRandomSquares2();
|
drawRandomSquares2();
|
||||||
|
|
||||||
value_ = calculateValue(0, total_squares, squares_to_activate);
|
value_ = calculateValue(0, total_squares, squares_to_activate);
|
||||||
@@ -265,7 +270,7 @@ void Fade::updateRandomSquare2Fade() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_completed) {
|
if (all_completed) {
|
||||||
// Pintar textura final: OUT opaca, IN transparente
|
// Pintar textura final: OUT opaca, IN transparente
|
||||||
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
@@ -277,17 +282,17 @@ void Fade::updateRandomSquare2Fade() {
|
|||||||
|
|
||||||
void Fade::updateDiagonalFade() {
|
void Fade::updateDiagonalFade() {
|
||||||
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
Uint32 elapsed_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
int total_squares = num_squares_width_ * num_squares_height_;
|
int total_squares = num_squares_width_ * num_squares_height_;
|
||||||
|
|
||||||
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
// Calcula el tiempo de activación: total - tiempo que necesitan los últimos cuadrados
|
||||||
int activation_time = random_squares_duration_ - square_transition_duration_;
|
int activation_time = random_squares_duration_ - square_transition_duration_;
|
||||||
activation_time = std::max(activation_time, square_transition_duration_);
|
activation_time = std::max(activation_time, square_transition_duration_);
|
||||||
|
|
||||||
// Calcula cuántas diagonales deberían estar activas
|
// Calcula cuántas diagonales deberían estar activas
|
||||||
int max_diagonal = num_squares_width_ + num_squares_height_ - 1; // Número total de diagonales
|
int max_diagonal = num_squares_width_ + num_squares_height_ - 1; // Número total de diagonales
|
||||||
int active_diagonals = 0;
|
int active_diagonals = 0;
|
||||||
|
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
// OUT: Activa diagonales gradualmente desde esquina superior izquierda
|
// OUT: Activa diagonales gradualmente desde esquina superior izquierda
|
||||||
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
@@ -296,7 +301,7 @@ void Fade::updateDiagonalFade() {
|
|||||||
} else {
|
} else {
|
||||||
active_diagonals = max_diagonal; // Activar todas
|
active_diagonals = max_diagonal; // Activar todas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activa cuadrados por diagonales
|
// Activa cuadrados por diagonales
|
||||||
for (int diagonal = 0; diagonal < active_diagonals; ++diagonal) {
|
for (int diagonal = 0; diagonal < active_diagonals; ++diagonal) {
|
||||||
activateDiagonal(diagonal, elapsed_time);
|
activateDiagonal(diagonal, elapsed_time);
|
||||||
@@ -304,12 +309,12 @@ void Fade::updateDiagonalFade() {
|
|||||||
} else {
|
} else {
|
||||||
// IN: Todas las diagonales empiezan activas, van desapareciendo
|
// IN: Todas las diagonales empiezan activas, van desapareciendo
|
||||||
active_diagonals = max_diagonal;
|
active_diagonals = max_diagonal;
|
||||||
|
|
||||||
// Activa diagonales gradualmente para transición
|
// Activa diagonales gradualmente para transición
|
||||||
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
if (elapsed_time < static_cast<Uint32>(activation_time)) {
|
||||||
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
float activation_progress = static_cast<float>(elapsed_time) / activation_time;
|
||||||
int diagonals_starting_transition = static_cast<int>(activation_progress * max_diagonal);
|
int diagonals_starting_transition = static_cast<int>(activation_progress * max_diagonal);
|
||||||
|
|
||||||
for (int diagonal = 0; diagonal < diagonals_starting_transition; ++diagonal) {
|
for (int diagonal = 0; diagonal < diagonals_starting_transition; ++diagonal) {
|
||||||
activateDiagonal(diagonal, elapsed_time);
|
activateDiagonal(diagonal, elapsed_time);
|
||||||
}
|
}
|
||||||
@@ -320,7 +325,7 @@ void Fade::updateDiagonalFade() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawDiagonal();
|
drawDiagonal();
|
||||||
|
|
||||||
value_ = calculateValue(0, total_squares, active_diagonals * (total_squares / max_diagonal));
|
value_ = calculateValue(0, total_squares, active_diagonals * (total_squares / max_diagonal));
|
||||||
@@ -338,7 +343,7 @@ void Fade::updateDiagonalFade() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_completed) {
|
if (all_completed) {
|
||||||
// Pintar textura final: OUT opaca, IN transparente
|
// Pintar textura final: OUT opaca, IN transparente
|
||||||
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
Uint8 final_alpha = (mode_ == Mode::OUT) ? 255 : 0;
|
||||||
@@ -354,15 +359,15 @@ void Fade::activateDiagonal(int diagonal_index, Uint32 current_time) {
|
|||||||
// Diagonal 1: (1,0), (0,1)
|
// Diagonal 1: (1,0), (0,1)
|
||||||
// Diagonal 2: (2,0), (1,1), (0,2)
|
// Diagonal 2: (2,0), (1,1), (0,2)
|
||||||
// etc.
|
// etc.
|
||||||
|
|
||||||
for (int x = 0; x < num_squares_width_; ++x) {
|
for (int x = 0; x < num_squares_width_; ++x) {
|
||||||
int y = diagonal_index - x;
|
int y = diagonal_index - x;
|
||||||
|
|
||||||
// Verificar que y está dentro de los límites
|
// Verificar que y está dentro de los límites
|
||||||
if (y >= 0 && y < num_squares_height_) {
|
if (y >= 0 && y < num_squares_height_) {
|
||||||
// Convertir coordenadas (x,y) a índice en el vector
|
// Convertir coordenadas (x,y) a índice en el vector
|
||||||
int index = y * num_squares_width_ + x;
|
int index = y * num_squares_width_ + x;
|
||||||
|
|
||||||
if (index >= 0 && index < static_cast<int>(square_age_.size())) {
|
if (index >= 0 && index < static_cast<int>(square_age_.size())) {
|
||||||
if (square_age_[index] == -1) {
|
if (square_age_[index] == -1) {
|
||||||
square_age_[index] = current_time; // Guarda el tiempo de activación
|
square_age_[index] = current_time; // Guarda el tiempo de activación
|
||||||
@@ -385,30 +390,30 @@ void Fade::drawDiagonal() {
|
|||||||
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
||||||
|
|
||||||
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
||||||
for (size_t i = 0; i < square_.size(); ++i) {
|
for (size_t i = 0; i < square_.size(); ++i) {
|
||||||
Uint8 current_alpha = 0;
|
Uint8 current_alpha = 0;
|
||||||
|
|
||||||
if (square_age_[i] == -1) {
|
if (square_age_[i] == -1) {
|
||||||
// Cuadrado no activado
|
// Cuadrado no activado
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
current_alpha = 0; // OUT: transparente si no activado
|
current_alpha = 0; // OUT: transparente si no activado
|
||||||
} else {
|
} else {
|
||||||
current_alpha = a_; // IN: opaco si no activado
|
current_alpha = a_; // IN: opaco si no activado
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Cuadrado activado - calculamos progreso
|
// Cuadrado activado - calculamos progreso
|
||||||
Uint32 square_elapsed = current_time - square_age_[i];
|
Uint32 square_elapsed = current_time - square_age_[i];
|
||||||
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
||||||
|
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
||||||
} else {
|
} else {
|
||||||
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_alpha > 0) {
|
if (current_alpha > 0) {
|
||||||
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
||||||
SDL_RenderFillRect(renderer_, &square_[i]);
|
SDL_RenderFillRect(renderer_, &square_[i]);
|
||||||
@@ -450,30 +455,30 @@ void Fade::drawRandomSquares2() {
|
|||||||
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND); // Usar BLEND para alpha
|
||||||
|
|
||||||
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
Uint32 current_time = SDL_GetTicks() - random_squares_start_time_;
|
||||||
|
|
||||||
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
// Lógica unificada: sobre textura transparente, pintar cuadrados según su estado
|
||||||
for (size_t i = 0; i < square_.size(); ++i) {
|
for (size_t i = 0; i < square_.size(); ++i) {
|
||||||
Uint8 current_alpha = 0;
|
Uint8 current_alpha = 0;
|
||||||
|
|
||||||
if (square_age_[i] == -1) {
|
if (square_age_[i] == -1) {
|
||||||
// Cuadrado no activado
|
// Cuadrado no activado
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
current_alpha = 0; // OUT: transparente si no activado
|
current_alpha = 0; // OUT: transparente si no activado
|
||||||
} else {
|
} else {
|
||||||
current_alpha = a_; // IN: opaco si no activado
|
current_alpha = a_; // IN: opaco si no activado
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Cuadrado activado - calculamos progreso
|
// Cuadrado activado - calculamos progreso
|
||||||
Uint32 square_elapsed = current_time - square_age_[i];
|
Uint32 square_elapsed = current_time - square_age_[i];
|
||||||
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
float progress = std::min(static_cast<float>(square_elapsed) / square_transition_duration_, 1.0f);
|
||||||
|
|
||||||
if (mode_ == Mode::OUT) {
|
if (mode_ == Mode::OUT) {
|
||||||
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
current_alpha = static_cast<Uint8>(progress * a_); // 0 → 255
|
||||||
} else {
|
} else {
|
||||||
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
current_alpha = static_cast<Uint8>((1.0f - progress) * a_); // 255 → 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_alpha > 0) {
|
if (current_alpha > 0) {
|
||||||
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
SDL_SetRenderDrawColor(renderer_, r_, g_, b_, current_alpha);
|
||||||
SDL_RenderFillRect(renderer_, &square_[i]);
|
SDL_RenderFillRect(renderer_, &square_[i]);
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ class Fade {
|
|||||||
public:
|
public:
|
||||||
// --- Enums ---
|
// --- Enums ---
|
||||||
enum class Type : Uint8 {
|
enum class Type : Uint8 {
|
||||||
FULLSCREEN = 0, // Fundido de pantalla completa
|
FULLSCREEN = 0, // Fundido de pantalla completa
|
||||||
CENTER = 1, // Fundido desde el centro
|
CENTER = 1, // Fundido desde el centro
|
||||||
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
|
RANDOM_SQUARE = 2, // Fundido con cuadrados aleatorios
|
||||||
RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2)
|
RANDOM_SQUARE2 = 3, // Fundido con cuadrados aleatorios (variante 2)
|
||||||
DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda
|
DIAGONAL = 4, // Fundido diagonal desde esquina superior izquierda
|
||||||
VENETIAN = 5, // Fundido tipo persiana veneciana
|
VENETIAN = 5, // Fundido tipo persiana veneciana
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Mode : Uint8 {
|
enum class Mode : Uint8 {
|
||||||
@@ -37,10 +37,11 @@ class Fade {
|
|||||||
~Fade();
|
~Fade();
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void reset(); // Resetea variables para reutilizar el fade
|
void reset(); // Resetea variables para reutilizar el fade
|
||||||
void render(); // Dibuja la transición en pantalla
|
void render(); // Dibuja la transición en pantalla
|
||||||
void update(); // Actualiza el estado interno
|
void update(); // Actualiza el estado interno (ya usa tiempo real)
|
||||||
void activate(); // Activa el fade
|
void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro)
|
||||||
|
void activate(); // Activa el fade
|
||||||
|
|
||||||
// --- Configuración ---
|
// --- Configuración ---
|
||||||
void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade
|
void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade
|
||||||
@@ -104,10 +105,10 @@ class Fade {
|
|||||||
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
|
void calculateVenetianProgress(); // Calcula el progreso del efecto veneciano
|
||||||
|
|
||||||
// --- Dibujo de efectos visuales ---
|
// --- Dibujo de efectos visuales ---
|
||||||
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
|
void drawCenterFadeRectangles(); // Dibuja los rectángulos del fundido central
|
||||||
void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido
|
void drawRandomSquares(int active_count = -1); // Dibuja los cuadrados aleatorios del fundido
|
||||||
void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2)
|
void drawRandomSquares2(); // Dibuja los cuadrados con transición de color (RANDOM_SQUARE2)
|
||||||
void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal
|
void drawDiagonal(); // Dibuja los cuadrados con patrón diagonal
|
||||||
void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica
|
void activateDiagonal(int diagonal_index, Uint32 current_time); // Activa una diagonal específica
|
||||||
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
|
void drawVenetianBlinds(); // Dibuja las persianas venecianas del fundido
|
||||||
};
|
};
|
||||||
@@ -43,7 +43,12 @@ class GameLogo {
|
|||||||
|
|
||||||
Shake() = default;
|
Shake() = default;
|
||||||
Shake(int d, int de, int l, int o)
|
Shake(int d, int de, int l, int o)
|
||||||
: desp(d), delay(de), length(l), remaining(l), counter(de), origin(o) {}
|
: desp(d),
|
||||||
|
delay(de),
|
||||||
|
length(l),
|
||||||
|
remaining(l),
|
||||||
|
counter(de),
|
||||||
|
origin(o) {}
|
||||||
|
|
||||||
void init(int d, int de, int l, int o) {
|
void init(int d, int de, int l, int o) {
|
||||||
desp = d;
|
desp = d;
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class Input {
|
|||||||
bool just_pressed; // Se acaba de pulsar en este fotograma
|
bool just_pressed; // Se acaba de pulsar en este fotograma
|
||||||
|
|
||||||
KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
|
KeyState(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false)
|
||||||
: scancode(scancode), is_held(is_held), just_pressed(just_pressed) {}
|
: scancode(scancode),
|
||||||
|
is_held(is_held),
|
||||||
|
just_pressed(just_pressed) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ButtonState {
|
struct ButtonState {
|
||||||
@@ -43,7 +45,10 @@ class Input {
|
|||||||
bool trigger_active{false}; // Estado del trigger como botón digital
|
bool trigger_active{false}; // Estado del trigger como botón digital
|
||||||
|
|
||||||
ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
|
ButtonState(int btn = static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID), bool is_held = false, bool just_pressed = false, bool axis_act = false)
|
||||||
: button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {}
|
: button(btn),
|
||||||
|
is_held(is_held),
|
||||||
|
just_pressed(just_pressed),
|
||||||
|
axis_active(axis_act) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Keyboard {
|
struct Keyboard {
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
class Texture; // lines 6-6
|
class Texture; // lines 6-6
|
||||||
|
|
||||||
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
|
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
|
||||||
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)), play_area_(play_area), type_(type) {
|
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
|
||||||
|
play_area_(play_area),
|
||||||
|
type_(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ItemType::COFFEE_MACHINE: {
|
case ItemType::COFFEE_MACHINE: {
|
||||||
width_ = COFFEE_MACHINE_WIDTH;
|
width_ = COFFEE_MACHINE_WIDTH;
|
||||||
@@ -90,7 +92,7 @@ void Item::move() {
|
|||||||
|
|
||||||
// Si toca el borde lateral
|
// Si toca el borde lateral
|
||||||
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
|
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
|
||||||
vel_x_ = -vel_x_; // Invierte la velocidad horizontal
|
vel_x_ = -vel_x_; // Invierte la velocidad horizontal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si colisiona por arriba, rebota (excepto la máquina de café)
|
// Si colisiona por arriba, rebota (excepto la máquina de café)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "difficulty.h" // Para Difficulty
|
#include "difficulty.h" // Para Difficulty
|
||||||
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper...
|
||||||
#include "options.h" // Para SettingsOpt...
|
#include "options.h" // Para SettingsOpt...
|
||||||
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -27,14 +28,24 @@ std::vector<Language> languages = {
|
|||||||
auto loadFromFile(const std::string &file_path) -> bool {
|
auto loadFromFile(const std::string &file_path) -> bool {
|
||||||
texts.clear();
|
texts.clear();
|
||||||
|
|
||||||
std::ifstream rfile(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!rfile.is_open()) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json j;
|
json j;
|
||||||
rfile >> j;
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
// Cargar desde datos del pack
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
j = json::parse(content);
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream rfile(file_path);
|
||||||
|
if (!rfile.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rfile >> j;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &el : j.items()) {
|
for (const auto &el : j.items()) {
|
||||||
texts[el.key()] = el.value();
|
texts[el.key()] = el.value();
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ struct Language {
|
|||||||
std::string file_name; // Nombre del fichero con los textos
|
std::string file_name; // Nombre del fichero con los textos
|
||||||
|
|
||||||
Language(Code c, std::string n, std::string fn)
|
Language(Code c, std::string n, std::string fn)
|
||||||
: code(c), name(std::move(n)), file_name(std::move(fn)) {}
|
: code(c),
|
||||||
|
name(std::move(n)),
|
||||||
|
file_name(std::move(fn)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ struct HiScoreEntry {
|
|||||||
bool one_credit_complete; // Indica si se ha conseguido 1CC
|
bool one_credit_complete; // Indica si se ha conseguido 1CC
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
explicit HiScoreEntry(const std::string &n = "", int s = 0, bool occ = false)
|
explicit HiScoreEntry(const std::string &name = "", int score = 0, bool one_credit_complete = false)
|
||||||
: name(n.substr(0, 6)), score(s), one_credit_complete(occ) {}
|
: name(name.substr(0, 6)),
|
||||||
|
score(score),
|
||||||
|
one_credit_complete(one_credit_complete) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Tipos ---
|
// --- Tipos ---
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ void MovingSprite::stop() {
|
|||||||
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
|
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mueve el sprite
|
// Mueve el sprite (frame-based)
|
||||||
void MovingSprite::move() {
|
void MovingSprite::move() {
|
||||||
x_ += vx_;
|
x_ += vx_;
|
||||||
y_ += vy_;
|
y_ += vy_;
|
||||||
@@ -65,16 +65,34 @@ void MovingSprite::move() {
|
|||||||
pos_.y = static_cast<int>(y_);
|
pos_.y = static_cast<int>(y_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables internas del objeto
|
// Mueve el sprite (time-based)
|
||||||
|
void MovingSprite::move(float deltaTime) {
|
||||||
|
x_ += vx_ * deltaTime;
|
||||||
|
y_ += vy_ * deltaTime;
|
||||||
|
|
||||||
|
vx_ += ax_ * deltaTime;
|
||||||
|
vy_ += ay_ * deltaTime;
|
||||||
|
|
||||||
|
pos_.x = static_cast<int>(x_);
|
||||||
|
pos_.y = static_cast<int>(y_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza las variables internas del objeto (frame-based)
|
||||||
void MovingSprite::update() {
|
void MovingSprite::update() {
|
||||||
move();
|
move();
|
||||||
rotate();
|
rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actualiza las variables internas del objeto (time-based)
|
||||||
|
void MovingSprite::update(float deltaTime) {
|
||||||
|
move(deltaTime);
|
||||||
|
rotate(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
// Muestra el sprite por pantalla
|
// Muestra el sprite por pantalla
|
||||||
void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); }
|
void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); }
|
||||||
|
|
||||||
// Establece la rotacion
|
// Establece la rotacion (frame-based)
|
||||||
void MovingSprite::rotate() {
|
void MovingSprite::rotate() {
|
||||||
if (rotate_.enabled) {
|
if (rotate_.enabled) {
|
||||||
++rotate_.counter;
|
++rotate_.counter;
|
||||||
@@ -85,6 +103,15 @@ void MovingSprite::rotate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Establece la rotacion (time-based)
|
||||||
|
void MovingSprite::rotate(float deltaTime) {
|
||||||
|
if (rotate_.enabled) {
|
||||||
|
// Convertir speed (frames) a tiempo: speed frames = speed/60 segundos a 60fps
|
||||||
|
float rotationSpeed = static_cast<float>(rotate_.speed) / 60.0f;
|
||||||
|
rotate_.angle += rotate_.amount * (deltaTime / rotationSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activa o desactiva el efecto de rotación
|
// Activa o desactiva el efecto de rotación
|
||||||
void MovingSprite::setRotate(bool enable) {
|
void MovingSprite::setRotate(bool enable) {
|
||||||
rotate_.enabled = enable;
|
rotate_.enabled = enable;
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ class MovingSprite : public Sprite {
|
|||||||
~MovingSprite() override = default;
|
~MovingSprite() override = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
virtual void update(); // Actualiza las variables internas del objeto
|
virtual void update(); // Actualiza las variables internas del objeto (frame-based)
|
||||||
void clear() override; // Reinicia todas las variables a cero
|
virtual void update(float deltaTime); // Actualiza las variables internas del objeto (time-based)
|
||||||
void stop(); // Elimina el movimiento del sprite
|
void clear() override; // Reinicia todas las variables a cero
|
||||||
void render() override; // Muestra el sprite por pantalla
|
void stop(); // Elimina el movimiento del sprite
|
||||||
|
void render() override; // Muestra el sprite por pantalla
|
||||||
|
|
||||||
// --- Configuración ---
|
// --- Configuración ---
|
||||||
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
|
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
|
||||||
@@ -79,6 +80,8 @@ class MovingSprite : public Sprite {
|
|||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
|
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
|
||||||
void move(); // Mueve el sprite según velocidad y aceleración
|
void move(); // Mueve el sprite según velocidad y aceleración (frame-based)
|
||||||
void rotate(); // Rota el sprite según los parámetros de rotación
|
void move(float deltaTime); // Mueve el sprite según velocidad y aceleración (time-based)
|
||||||
|
void rotate(); // Rota el sprite según los parámetros de rotación (frame-based)
|
||||||
|
void rotate(float deltaTime); // Rota el sprite según los parámetros de rotación (time-based)
|
||||||
};
|
};
|
||||||
@@ -196,46 +196,48 @@ auto setParams(const std::string& var, const std::string& value) -> bool {
|
|||||||
|
|
||||||
// Colores válidos para globos
|
// Colores válidos para globos
|
||||||
static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
|
static const std::unordered_map<std::string, bool> VALID_BALLOON_COLORS = {
|
||||||
{"blue", true}, {"orange", true}, {"red", true}, {"green", true}
|
{"blue", true},
|
||||||
};
|
{"orange", true},
|
||||||
|
{"red", true},
|
||||||
|
{"green", true}};
|
||||||
|
|
||||||
auto validateBalloonColor = [](const std::string& color) -> bool {
|
auto validateBalloonColor = [](const std::string& color) -> bool {
|
||||||
return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end();
|
return VALID_BALLOON_COLORS.find(color) != VALID_BALLOON_COLORS.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
|
static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
|
||||||
{"balloon.color[0]", [validateBalloonColor](const std::string& v) {
|
{"balloon.color[0]", [validateBalloonColor](const std::string& v) {
|
||||||
if (!validateBalloonColor(v)) {
|
if (!validateBalloonColor(v)) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str());
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'blue' por defecto.", v.c_str());
|
||||||
param.balloon.color.at(0) = "blue";
|
param.balloon.color.at(0) = "blue";
|
||||||
} else {
|
} else {
|
||||||
param.balloon.color.at(0) = v;
|
param.balloon.color.at(0) = v;
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
{"balloon.color[1]", [validateBalloonColor](const std::string& v) {
|
{"balloon.color[1]", [validateBalloonColor](const std::string& v) {
|
||||||
if (!validateBalloonColor(v)) {
|
if (!validateBalloonColor(v)) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str());
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'orange' por defecto.", v.c_str());
|
||||||
param.balloon.color.at(1) = "orange";
|
param.balloon.color.at(1) = "orange";
|
||||||
} else {
|
} else {
|
||||||
param.balloon.color.at(1) = v;
|
param.balloon.color.at(1) = v;
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
{"balloon.color[2]", [validateBalloonColor](const std::string& v) {
|
{"balloon.color[2]", [validateBalloonColor](const std::string& v) {
|
||||||
if (!validateBalloonColor(v)) {
|
if (!validateBalloonColor(v)) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str());
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'red' por defecto.", v.c_str());
|
||||||
param.balloon.color.at(2) = "red";
|
param.balloon.color.at(2) = "red";
|
||||||
} else {
|
} else {
|
||||||
param.balloon.color.at(2) = v;
|
param.balloon.color.at(2) = v;
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
{"balloon.color[3]", [validateBalloonColor](const std::string& v) {
|
{"balloon.color[3]", [validateBalloonColor](const std::string& v) {
|
||||||
if (!validateBalloonColor(v)) {
|
if (!validateBalloonColor(v)) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str());
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Color de globo inválido '%s'. Usando 'green' por defecto.", v.c_str());
|
||||||
param.balloon.color.at(3) = "green";
|
param.balloon.color.at(3) = "green";
|
||||||
} else {
|
} else {
|
||||||
param.balloon.color.at(3) = v;
|
param.balloon.color.at(3) = v;
|
||||||
}
|
}
|
||||||
}}};
|
}}};
|
||||||
|
|
||||||
// Lambda para intentar cada mapa de parámetros
|
// Lambda para intentar cada mapa de parámetros
|
||||||
auto try_map = [&](const auto& param_map) -> bool {
|
auto try_map = [&](const auto& param_map) -> bool {
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ struct ParamBalloon {
|
|||||||
|
|
||||||
// Constructor por defecto
|
// Constructor por defecto
|
||||||
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
|
constexpr Settings(float grav_val = 0.0F, float vel_val = 0.0F)
|
||||||
: grav(grav_val), vel(vel_val) {}
|
: grav(grav_val),
|
||||||
|
vel(vel_val) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inicialización con los valores por defecto desde GameDefaults
|
// Inicialización con los valores por defecto desde GameDefaults
|
||||||
@@ -164,7 +165,10 @@ struct ParamPlayer {
|
|||||||
|
|
||||||
// Constructor con tonalidades específicas
|
// Constructor con tonalidades específicas
|
||||||
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
|
Shirt(const Color& darkest_tone, const Color& dark_tone, const Color& base_tone, const Color& light_tone)
|
||||||
: darkest(darkest_tone), dark(dark_tone), base(base_tone), light(light_tone) {}
|
: darkest(darkest_tone),
|
||||||
|
dark(dark_tone),
|
||||||
|
base(base_tone),
|
||||||
|
light(light_tone) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inicialización con valores por defecto
|
// Inicialización con valores por defecto
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
#include <functional> // Para function
|
#include <functional> // Para function
|
||||||
#include <utility> // Para move
|
#include <utility> // Para move
|
||||||
|
|
||||||
|
// Constructor para paths por puntos (compatibilidad)
|
||||||
|
Path::Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
|
||||||
|
: spots(spots_init), is_point_path(true) {
|
||||||
|
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
|
||||||
|
waiting_time_ms = static_cast<float>(waiting_counter_init) * FRAME_TIME_MS;
|
||||||
|
}
|
||||||
|
|
||||||
// Devuelve un vector con los puntos que conforman la ruta
|
// Devuelve un vector con los puntos que conforman la ruta
|
||||||
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> {
|
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> {
|
||||||
std::vector<SDL_FPoint> v;
|
std::vector<SDL_FPoint> v;
|
||||||
@@ -32,10 +39,16 @@ auto createPath(float start, float end, PathType type, float fixed_pos, int step
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la posición y comprueba si ha llegado a su destino
|
// Actualiza la posición y comprueba si ha llegado a su destino (compatibilidad)
|
||||||
void PathSprite::update() {
|
void PathSprite::update() {
|
||||||
|
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
|
||||||
|
update(FRAME_TIME_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza la posición y comprueba si ha llegado a su destino
|
||||||
|
void PathSprite::update(float delta_time) {
|
||||||
if (enabled_ && !has_finished_) {
|
if (enabled_ && !has_finished_) {
|
||||||
moveThroughCurrentPath();
|
moveThroughCurrentPath(delta_time);
|
||||||
goToNextPathOrDie();
|
goToNextPathOrDie();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +92,13 @@ void PathSprite::addPath(Path path, bool centered) {
|
|||||||
|
|
||||||
// Añade un recorrido
|
// Añade un recorrido
|
||||||
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) {
|
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) {
|
||||||
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easing_function), waiting_counter);
|
// Convertir frames a milisegundos
|
||||||
|
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
|
||||||
|
float duration_ms = static_cast<float>(steps) * FRAME_TIME_MS;
|
||||||
|
float waiting_ms = static_cast<float>(waiting_counter) * FRAME_TIME_MS;
|
||||||
|
|
||||||
|
paths_.emplace_back(static_cast<float>(start), static_cast<float>(end), type, static_cast<float>(fixed_pos),
|
||||||
|
duration_ms, waiting_ms, easing_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Añade un recorrido
|
// Añade un recorrido
|
||||||
@@ -95,35 +114,78 @@ void PathSprite::enable() {
|
|||||||
|
|
||||||
enabled_ = true;
|
enabled_ = true;
|
||||||
|
|
||||||
// Establece la posición
|
// Establece la posición inicial
|
||||||
auto &path = paths_.at(current_path_);
|
auto &path = paths_.at(current_path_);
|
||||||
const auto &p = path.spots.at(path.counter);
|
if (path.is_point_path) {
|
||||||
setPosition(p);
|
const auto &p = path.spots.at(path.counter);
|
||||||
|
setPosition(p);
|
||||||
|
} else {
|
||||||
|
// Para paths generados, establecer posición inicial
|
||||||
|
SDL_FPoint initial_pos;
|
||||||
|
if (path.type == PathType::HORIZONTAL) {
|
||||||
|
initial_pos = {path.start_pos, path.fixed_pos};
|
||||||
|
} else {
|
||||||
|
initial_pos = {path.fixed_pos, path.start_pos};
|
||||||
|
}
|
||||||
|
setPosition(initial_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coloca el sprite en los diferentes puntos del recorrido
|
// Coloca el sprite en los diferentes puntos del recorrido
|
||||||
void PathSprite::moveThroughCurrentPath() {
|
void PathSprite::moveThroughCurrentPath(float delta_time) {
|
||||||
auto &path = paths_.at(current_path_);
|
auto &path = paths_.at(current_path_);
|
||||||
|
|
||||||
// Establece la posición
|
if (path.is_point_path) {
|
||||||
const auto &p = path.spots.at(path.counter);
|
// Lógica para paths por puntos (compatibilidad)
|
||||||
setPosition(p);
|
const auto &p = path.spots.at(path.counter);
|
||||||
|
setPosition(p);
|
||||||
|
|
||||||
// Comprobar si ha terminado el recorrido
|
if (!path.on_destination) {
|
||||||
if (!path.on_destination) {
|
++path.counter;
|
||||||
++path.counter;
|
if (path.counter >= static_cast<int>(path.spots.size())) {
|
||||||
if (path.counter >= static_cast<int>(path.spots.size())) {
|
path.on_destination = true;
|
||||||
path.on_destination = true;
|
path.counter = static_cast<int>(path.spots.size()) - 1;
|
||||||
path.counter = static_cast<int>(path.spots.size()) - 1;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Comprobar si ha terminado la espera
|
if (path.on_destination) {
|
||||||
if (path.on_destination) {
|
path.waiting_elapsed += delta_time;
|
||||||
if (path.waiting_counter == 0) {
|
if (path.waiting_elapsed >= path.waiting_time_ms) {
|
||||||
path.finished = true;
|
path.finished = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Lógica para paths generados en tiempo real
|
||||||
|
if (!path.on_destination) {
|
||||||
|
path.elapsed_time += delta_time;
|
||||||
|
|
||||||
|
// Calcular progreso (0.0 a 1.0)
|
||||||
|
float progress = path.elapsed_time / path.duration_ms;
|
||||||
|
if (progress >= 1.0f) {
|
||||||
|
progress = 1.0f;
|
||||||
|
path.on_destination = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplicar función de easing
|
||||||
|
double eased_progress = path.easing_function(progress);
|
||||||
|
|
||||||
|
// Calcular posición actual
|
||||||
|
float current_pos = path.start_pos + (path.end_pos - path.start_pos) * static_cast<float>(eased_progress);
|
||||||
|
|
||||||
|
// Establecer posición según el tipo
|
||||||
|
SDL_FPoint position;
|
||||||
|
if (path.type == PathType::HORIZONTAL) {
|
||||||
|
position = {current_pos, path.fixed_pos};
|
||||||
|
} else {
|
||||||
|
position = {path.fixed_pos, current_pos};
|
||||||
|
}
|
||||||
|
setPosition(position);
|
||||||
} else {
|
} else {
|
||||||
--path.waiting_counter;
|
// Esperar en destino
|
||||||
|
path.waiting_elapsed += delta_time;
|
||||||
|
if (path.waiting_elapsed >= path.waiting_time_ms) {
|
||||||
|
path.finished = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,16 +24,31 @@ enum class PathCentered { // Centrado del recorrido
|
|||||||
};
|
};
|
||||||
|
|
||||||
// --- Estructuras ---
|
// --- Estructuras ---
|
||||||
struct Path { // Define un recorrido para el sprite
|
struct Path { // Define un recorrido para el sprite
|
||||||
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite
|
float start_pos; // Posición inicial
|
||||||
int waiting_counter; // Tiempo de espera una vez en el destino
|
float end_pos; // Posición final
|
||||||
bool on_destination = false; // Indica si ha llegado al destino
|
PathType type; // Tipo de movimiento (horizontal/vertical)
|
||||||
bool finished = false; // Indica si ha terminado de esperarse
|
float fixed_pos; // Posición fija en el eje contrario
|
||||||
int counter = 0; // Contador interno
|
float duration_ms; // Duración de la animación en milisegundos
|
||||||
|
float waiting_time_ms; // Tiempo de espera una vez en el destino
|
||||||
|
std::function<double(double)> easing_function; // Función de easing
|
||||||
|
float elapsed_time = 0.0f; // Tiempo transcurrido
|
||||||
|
float waiting_elapsed = 0.0f; // Tiempo de espera transcurrido
|
||||||
|
bool on_destination = false; // Indica si ha llegado al destino
|
||||||
|
bool finished = false; // Indica si ha terminado de esperarse
|
||||||
|
|
||||||
// Constructor
|
// Constructor para paths generados
|
||||||
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
|
Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
|
||||||
: spots(spots_init), waiting_counter(waiting_counter_init) {}
|
: start_pos(start), end_pos(end), type(path_type), fixed_pos(fixed),
|
||||||
|
duration_ms(duration), waiting_time_ms(waiting), easing_function(std::move(easing)) {}
|
||||||
|
|
||||||
|
// Constructor para paths por puntos (mantenemos compatibilidad)
|
||||||
|
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init);
|
||||||
|
|
||||||
|
// Variables para paths por puntos
|
||||||
|
std::vector<SDL_FPoint> spots; // Solo para paths por puntos
|
||||||
|
int counter = 0; // Solo para paths por puntos
|
||||||
|
bool is_point_path = false; // Indica si es un path por puntos
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Funciones ---
|
// --- Funciones ---
|
||||||
@@ -48,7 +63,8 @@ class PathSprite : public Sprite {
|
|||||||
~PathSprite() override = default;
|
~PathSprite() override = default;
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void update(); // Actualiza la posición del sprite según el recorrido
|
void update(); // Actualiza la posición del sprite según el recorrido (compatibilidad)
|
||||||
|
void update(float delta_time); // Actualiza la posición del sprite según el recorrido
|
||||||
void render() override; // Muestra el sprite por pantalla
|
void render() override; // Muestra el sprite por pantalla
|
||||||
|
|
||||||
// --- Gestión de recorridos ---
|
// --- Gestión de recorridos ---
|
||||||
@@ -71,6 +87,6 @@ class PathSprite : public Sprite {
|
|||||||
std::vector<Path> paths_; // Caminos a recorrer por el sprite
|
std::vector<Path> paths_; // Caminos a recorrer por el sprite
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido
|
void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
|
||||||
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
|
void goToNextPathOrDie(); // Cambia de recorrido o finaliza
|
||||||
};
|
};
|
||||||
@@ -22,7 +22,17 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Player::Player(const Config &config)
|
Player::Player(const Config &config)
|
||||||
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))), power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))), enter_name_(std::make_unique<EnterName>()), hi_score_table_(config.hi_score_table), glowing_entry_(config.glowing_entry), stage_info_(config.stage_info), play_area_(*config.play_area), id_(config.id), default_pos_x_(config.x), default_pos_y_(config.y), demo_(config.demo) {
|
: player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))),
|
||||||
|
power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(4), config.animations.at(1))),
|
||||||
|
enter_name_(std::make_unique<EnterName>()),
|
||||||
|
hi_score_table_(config.hi_score_table),
|
||||||
|
glowing_entry_(config.glowing_entry),
|
||||||
|
stage_info_(config.stage_info),
|
||||||
|
play_area_(*config.play_area),
|
||||||
|
id_(config.id),
|
||||||
|
default_pos_x_(config.x),
|
||||||
|
default_pos_y_(config.y),
|
||||||
|
demo_(config.demo) {
|
||||||
// Configura objetos
|
// Configura objetos
|
||||||
player_sprite_->addTexture(config.texture.at(1));
|
player_sprite_->addTexture(config.texture.at(1));
|
||||||
player_sprite_->addTexture(config.texture.at(2));
|
player_sprite_->addTexture(config.texture.at(2));
|
||||||
@@ -585,6 +595,14 @@ void Player::update() {
|
|||||||
updateShowingName();
|
updateShowingName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::passShowingName() {
|
||||||
|
if (game_completed_) {
|
||||||
|
setPlayingState(State::LEAVING_SCREEN);
|
||||||
|
} else {
|
||||||
|
setPlayingState(State::CONTINUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Incrementa la puntuación del jugador
|
// Incrementa la puntuación del jugador
|
||||||
void Player::addScore(int score, int lowest_hi_score_entry) {
|
void Player::addScore(int score, int lowest_hi_score_entry) {
|
||||||
if (isPlaying()) {
|
if (isPlaying()) {
|
||||||
@@ -624,6 +642,9 @@ void Player::setPlayingState(State state) {
|
|||||||
|
|
||||||
switch (playing_state_) {
|
switch (playing_state_) {
|
||||||
case State::RECOVER: {
|
case State::RECOVER: {
|
||||||
|
score_ = 0; // Pon los puntos a cero para que no se vea en el marcador
|
||||||
|
score_multiplier_ = 1.0F;
|
||||||
|
setScoreboardMode(Scoreboard::Mode::SCORE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::RESPAWNING: {
|
case State::RESPAWNING: {
|
||||||
@@ -781,19 +802,19 @@ void Player::updateInvulnerable() {
|
|||||||
if (playing_state_ == State::PLAYING && invulnerable_) {
|
if (playing_state_ == State::PLAYING && invulnerable_) {
|
||||||
if (invulnerable_counter_ > 0) {
|
if (invulnerable_counter_ > 0) {
|
||||||
--invulnerable_counter_;
|
--invulnerable_counter_;
|
||||||
|
|
||||||
// Frecuencia fija de parpadeo (como el original)
|
// Frecuencia fija de parpadeo (como el original)
|
||||||
constexpr int blink_speed = 8;
|
constexpr int blink_speed = 8;
|
||||||
|
|
||||||
// Calcula proporción decreciente: menos textura blanca hacia el final
|
// Calcula proporción decreciente: menos textura blanca hacia el final
|
||||||
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco)
|
// Al inicio: 50-50, hacia el final: 70-30 (menos blanco)
|
||||||
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER);
|
float progress = 1.0f - (static_cast<float>(invulnerable_counter_) / INVULNERABLE_COUNTER);
|
||||||
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed);
|
int white_frames = static_cast<int>((0.5f - progress * 0.2f) * blink_speed);
|
||||||
|
|
||||||
// Alterna entre texturas con proporción variable
|
// Alterna entre texturas con proporción variable
|
||||||
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames;
|
bool should_show_invulnerable = (invulnerable_counter_ % blink_speed) < white_frames;
|
||||||
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
|
size_t target_texture = should_show_invulnerable ? INVULNERABLE_TEXTURE : coffees_;
|
||||||
|
|
||||||
// Solo cambia textura si es diferente (optimización)
|
// Solo cambia textura si es diferente (optimización)
|
||||||
if (player_sprite_->getActiveTexture() != target_texture) {
|
if (player_sprite_->getActiveTexture() != target_texture) {
|
||||||
player_sprite_->setActiveTexture(target_texture);
|
player_sprite_->setActiveTexture(target_texture);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class Player {
|
|||||||
[[nodiscard]] static auto getWidth() -> int { return WIDTH; }
|
[[nodiscard]] static auto getWidth() -> int { return WIDTH; }
|
||||||
[[nodiscard]] auto getPlayingState() const -> State { return playing_state_; }
|
[[nodiscard]] auto getPlayingState() const -> State { return playing_state_; }
|
||||||
[[nodiscard]] auto getName() const -> const std::string & { return name_; }
|
[[nodiscard]] auto getName() const -> const std::string & { return name_; }
|
||||||
[[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ == 1; }
|
[[nodiscard]] auto get1CC() const -> bool { return game_completed_ && credits_used_ <= 1; }
|
||||||
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
|
[[nodiscard]] auto getEnterNamePositionOverflow() const -> bool { return enter_name_ ? enter_name_->getPositionOverflow() : false; }
|
||||||
|
|
||||||
// Setters inline
|
// Setters inline
|
||||||
@@ -186,6 +186,7 @@ class Player {
|
|||||||
void setWalkingState(State state) { walking_state_ = state; }
|
void setWalkingState(State state) { walking_state_ = state; }
|
||||||
|
|
||||||
void addCredit();
|
void addCredit();
|
||||||
|
void passShowingName();
|
||||||
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
|
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = std::move(gamepad); }
|
||||||
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
|
[[nodiscard]] auto getGamepad() const -> std::shared_ptr<Input::Gamepad> { return gamepad_; }
|
||||||
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
|
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
|
||||||
|
|||||||
@@ -2,25 +2,58 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
|
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
|
||||||
|
|
||||||
#include <algorithm> // Para find_if, max, find
|
#include <algorithm> // Para find_if, max, find
|
||||||
#include <array> // Para array
|
#include <array> // Para array
|
||||||
#include <cstdlib> // Para exit
|
#include <cstdlib> // Para exit, getenv
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <filesystem> // Para filesystem::remove, filesystem::exists
|
||||||
#include <utility> // Para move
|
#include <fstream> // Para ofstream
|
||||||
|
#include <stdexcept> // Para runtime_error
|
||||||
|
#include <utility> // Para move
|
||||||
|
|
||||||
#include "asset.h" // Para Asset
|
#include "asset.h" // Para Asset
|
||||||
#include "color.h" // Para Color
|
#include "color.h" // Para Color
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
#include "external/jail_audio.h" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
||||||
#endif
|
#endif
|
||||||
#include "lang.h" // Para getText
|
#include "lang.h" // Para getText
|
||||||
#include "param.h" // Para Param, param, ParamResource, ParamGame
|
#include "param.h" // Para Param, param, ParamResource, ParamGame
|
||||||
#include "screen.h" // Para Screen
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "text.h" // Para Text
|
#include "screen.h" // Para Screen
|
||||||
|
#include "text.h" // Para Text
|
||||||
|
|
||||||
struct JA_Music_t; // lines 11-11
|
struct JA_Music_t; // lines 11-11
|
||||||
struct JA_Sound_t; // lines 12-12
|
struct JA_Sound_t; // lines 12-12
|
||||||
|
|
||||||
|
// Helper para cargar archivos de audio desde pack o filesystem
|
||||||
|
namespace {
|
||||||
|
std::string createTempAudioFile(const std::string &file_path, std::vector<std::string> &temp_files_tracker) {
|
||||||
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
// Crear archivo temporal
|
||||||
|
std::string temp_dir;
|
||||||
|
#ifdef _WIN32
|
||||||
|
temp_dir = std::getenv("TEMP") ? std::getenv("TEMP") : "C:\\temp";
|
||||||
|
#else
|
||||||
|
temp_dir = "/tmp";
|
||||||
|
#endif
|
||||||
|
std::string temp_path = temp_dir + "/ccae_audio_" + std::to_string(std::hash<std::string>{}(file_path));
|
||||||
|
std::ofstream temp_file(temp_path, std::ios::binary);
|
||||||
|
if (!temp_file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Cannot create temp file %s", temp_path.c_str());
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
temp_file.write(reinterpret_cast<const char *>(resource_data.data()), resource_data.size());
|
||||||
|
temp_file.close();
|
||||||
|
|
||||||
|
// Agregar a la lista de archivos temporales para limpieza posterior
|
||||||
|
temp_files_tracker.push_back(temp_path);
|
||||||
|
|
||||||
|
return temp_path;
|
||||||
|
}
|
||||||
|
return file_path; // Usar ruta original si no está en pack
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// Declaraciones de funciones que necesitas implementar en otros archivos
|
// Declaraciones de funciones que necesitas implementar en otros archivos
|
||||||
|
|
||||||
// Singleton
|
// Singleton
|
||||||
@@ -42,7 +75,8 @@ auto Resource::get() -> Resource * { return Resource::instance; }
|
|||||||
|
|
||||||
// Constructor con modo de carga
|
// Constructor con modo de carga
|
||||||
Resource::Resource(LoadingMode mode)
|
Resource::Resource(LoadingMode mode)
|
||||||
: loading_mode_(mode), loading_text_(nullptr) {
|
: loading_mode_(mode),
|
||||||
|
loading_text_(nullptr) {
|
||||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||||
loading_text_ = Screen::get()->getText();
|
loading_text_ = Screen::get()->getText();
|
||||||
load();
|
load();
|
||||||
@@ -55,6 +89,7 @@ Resource::Resource(LoadingMode mode)
|
|||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
Resource::~Resource() {
|
Resource::~Resource() {
|
||||||
|
cleanupTempAudioFiles();
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +328,8 @@ auto Resource::loadSoundLazy(const std::string &name) -> JA_Sound_t * {
|
|||||||
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||||
for (const auto &file : sound_list) {
|
for (const auto &file : sound_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
return JA_LoadSound(file.c_str());
|
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
|
||||||
|
return JA_LoadSound(audio_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -306,7 +342,8 @@ auto Resource::loadMusicLazy(const std::string &name) -> JA_Music_t * {
|
|||||||
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||||
for (const auto &file : music_list) {
|
for (const auto &file : music_list) {
|
||||||
if (getFileName(file) == name) {
|
if (getFileName(file) == name) {
|
||||||
return JA_LoadMusic(file.c_str());
|
std::string audio_path = createTempAudioFile(file, Resource::get()->temp_audio_files_);
|
||||||
|
return JA_LoadMusic(audio_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -448,7 +485,8 @@ void Resource::loadSounds() {
|
|||||||
auto name = getFileName(l);
|
auto name = getFileName(l);
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
sounds_.emplace_back(name, JA_LoadSound(l.c_str()));
|
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
|
||||||
|
sounds_.emplace_back(name, JA_LoadSound(audio_path.c_str()));
|
||||||
#else
|
#else
|
||||||
sounds_.emplace_back(name, nullptr);
|
sounds_.emplace_back(name, nullptr);
|
||||||
#endif
|
#endif
|
||||||
@@ -466,7 +504,8 @@ void Resource::loadMusics() {
|
|||||||
auto name = getFileName(l);
|
auto name = getFileName(l);
|
||||||
updateLoadingProgress(name);
|
updateLoadingProgress(name);
|
||||||
#ifndef NO_AUDIO
|
#ifndef NO_AUDIO
|
||||||
musics_.emplace_back(name, JA_LoadMusic(l.c_str()));
|
std::string audio_path = createTempAudioFile(l, temp_audio_files_);
|
||||||
|
musics_.emplace_back(name, JA_LoadMusic(audio_path.c_str()));
|
||||||
#else
|
#else
|
||||||
musics_.emplace_back(name, nullptr);
|
musics_.emplace_back(name, nullptr);
|
||||||
#endif
|
#endif
|
||||||
@@ -567,7 +606,7 @@ void Resource::createPlayerTextures() {
|
|||||||
texture->setPaletteColor(0, 19, param.player.default_shirt[player_idx].light.TO_UINT32());
|
texture->setPaletteColor(0, 19, param.player.default_shirt[player_idx].light.TO_UINT32());
|
||||||
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
|
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||||
} else {
|
} else {
|
||||||
// Crear textura nueva desde archivo
|
// Crear textura nueva desde archivo usando ResourceHelper
|
||||||
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
|
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
|
||||||
|
|
||||||
// Añadir todas las paletas
|
// Añadir todas las paletas
|
||||||
@@ -611,7 +650,8 @@ void Resource::createTextTextures() {
|
|||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
NameAndText(std::string name_init, std::string text_init)
|
NameAndText(std::string name_init, std::string text_init)
|
||||||
: name(std::move(name_init)), text(std::move(text_init)) {}
|
: name(std::move(name_init)),
|
||||||
|
text(std::move(text_init)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXTURES");
|
||||||
@@ -623,7 +663,8 @@ void Resource::createTextTextures() {
|
|||||||
{"game_text_5000_points", "5.000"},
|
{"game_text_5000_points", "5.000"},
|
||||||
{"game_text_powerup", Lang::getText("[GAME_TEXT] 4")},
|
{"game_text_powerup", Lang::getText("[GAME_TEXT] 4")},
|
||||||
{"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")},
|
{"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")},
|
||||||
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")}};
|
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")},
|
||||||
|
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
|
||||||
|
|
||||||
auto text1 = getText("04b_25_enhanced");
|
auto text1 = getText("04b_25_enhanced");
|
||||||
for (const auto &s : strings1) {
|
for (const auto &s : strings1) {
|
||||||
@@ -631,27 +672,17 @@ void Resource::createTextTextures() {
|
|||||||
printWithDots("Texture : ", s.name, "[ DONE ]");
|
printWithDots("Texture : ", s.name, "[ DONE ]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texturas de tamaño normal
|
|
||||||
std::vector<NameAndText> strings2 = {
|
|
||||||
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
|
|
||||||
|
|
||||||
auto text2 = getText("04b_25");
|
|
||||||
for (const auto &s : strings2) {
|
|
||||||
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
|
||||||
printWithDots("Texture : ", s.name, "[ DONE ]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texturas de tamaño doble
|
// Texturas de tamaño doble
|
||||||
std::vector<NameAndText> strings3 = {
|
std::vector<NameAndText> strings2 = {
|
||||||
{"game_text_100000_points", "100.000"},
|
{"game_text_100000_points", "100.000"},
|
||||||
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
|
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
|
||||||
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
|
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
|
||||||
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
|
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
|
||||||
{"game_text_game_over", "Game Over"}};
|
{"game_text_game_over", "Game Over"}};
|
||||||
|
|
||||||
auto text3 = getText("04b_25_2x");
|
auto text2 = getText("04b_25_2x_enhanced");
|
||||||
for (const auto &s : strings3) {
|
for (const auto &s : strings2) {
|
||||||
textures_.emplace_back(s.name, text3->writeToTexture(s.text, 1, -4));
|
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
printWithDots("Texture : ", s.name, "[ DONE ]");
|
printWithDots("Texture : ", s.name, "[ DONE ]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -665,7 +696,10 @@ void Resource::createText() {
|
|||||||
std::string white_texture_file; // Textura blanca opcional
|
std::string white_texture_file; // Textura blanca opcional
|
||||||
|
|
||||||
ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
|
ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
|
||||||
: key(std::move(k)), texture_file(std::move(t_file)), text_file(std::move(txt_file)), white_texture_file(std::move(w_file)) {}
|
: key(std::move(k)),
|
||||||
|
texture_file(std::move(t_file)),
|
||||||
|
text_file(std::move(txt_file)),
|
||||||
|
white_texture_file(std::move(w_file)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> CREATING TEXT OBJECTS");
|
||||||
@@ -675,6 +709,7 @@ void Resource::createText() {
|
|||||||
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
|
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
|
||||||
{"04b_25_white", "04b_25_white.png", "04b_25.txt"},
|
{"04b_25_white", "04b_25_white.png", "04b_25.txt"},
|
||||||
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
|
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
|
||||||
|
{"04b_25_2x_enhanced", "04b_25_2x.png", "04b_25_2x.txt", "04b_25_2x_white.png"}, // Nueva fuente con textura blanca
|
||||||
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
|
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
|
||||||
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
|
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
|
||||||
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
|
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
|
||||||
@@ -833,3 +868,18 @@ void Resource::updateLoadingProgress(std::string name) {
|
|||||||
void Resource::updateProgressBar() {
|
void Resource::updateProgressBar() {
|
||||||
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
|
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Limpia archivos temporales de audio
|
||||||
|
void Resource::cleanupTempAudioFiles() {
|
||||||
|
for (const auto &temp_path : temp_audio_files_) {
|
||||||
|
try {
|
||||||
|
if (std::filesystem::exists(temp_path)) {
|
||||||
|
std::filesystem::remove(temp_path);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Removed temp audio file: %s", temp_path.c_str());
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to remove temp audio file %s: %s", temp_path.c_str(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp_audio_files_.clear();
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class Resource {
|
|||||||
JA_Sound_t *sound; // Objeto con el sonido
|
JA_Sound_t *sound; // Objeto con el sonido
|
||||||
|
|
||||||
ResourceSound(std::string name, JA_Sound_t *sound = nullptr)
|
ResourceSound(std::string name, JA_Sound_t *sound = nullptr)
|
||||||
: name(std::move(name)), sound(sound) {}
|
: name(std::move(name)),
|
||||||
|
sound(sound) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceMusic {
|
struct ResourceMusic {
|
||||||
@@ -60,7 +61,8 @@ class Resource {
|
|||||||
JA_Music_t *music; // Objeto con la música
|
JA_Music_t *music; // Objeto con la música
|
||||||
|
|
||||||
ResourceMusic(std::string name, JA_Music_t *music = nullptr)
|
ResourceMusic(std::string name, JA_Music_t *music = nullptr)
|
||||||
: name(std::move(name)), music(music) {}
|
: name(std::move(name)),
|
||||||
|
music(music) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceTexture {
|
struct ResourceTexture {
|
||||||
@@ -68,7 +70,8 @@ class Resource {
|
|||||||
std::shared_ptr<Texture> texture; // Objeto con la textura
|
std::shared_ptr<Texture> texture; // Objeto con la textura
|
||||||
|
|
||||||
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
|
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
|
||||||
: name(std::move(name)), texture(std::move(texture)) {}
|
: name(std::move(name)),
|
||||||
|
texture(std::move(texture)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceTextFile {
|
struct ResourceTextFile {
|
||||||
@@ -76,7 +79,8 @@ class Resource {
|
|||||||
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
|
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
|
||||||
|
|
||||||
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
|
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
|
||||||
: name(std::move(name)), text_file(std::move(text_file)) {}
|
: name(std::move(name)),
|
||||||
|
text_file(std::move(text_file)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceText {
|
struct ResourceText {
|
||||||
@@ -84,7 +88,8 @@ class Resource {
|
|||||||
std::shared_ptr<Text> text; // Objeto de texto
|
std::shared_ptr<Text> text; // Objeto de texto
|
||||||
|
|
||||||
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
|
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
|
||||||
: name(std::move(name)), text(std::move(text)) {}
|
: name(std::move(name)),
|
||||||
|
text(std::move(text)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResourceAnimation {
|
struct ResourceAnimation {
|
||||||
@@ -92,7 +97,8 @@ class Resource {
|
|||||||
AnimationsFileBuffer animation; // Objeto con las animaciones
|
AnimationsFileBuffer animation; // Objeto con las animaciones
|
||||||
|
|
||||||
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
|
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
|
||||||
: name(std::move(name)), animation(std::move(animation)) {}
|
: name(std::move(name)),
|
||||||
|
animation(std::move(animation)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Estructura para el progreso de carga ---
|
// --- Estructura para el progreso de carga ---
|
||||||
@@ -100,8 +106,12 @@ class Resource {
|
|||||||
size_t total; // Número total de recursos
|
size_t total; // Número total de recursos
|
||||||
size_t loaded; // Número de recursos cargados
|
size_t loaded; // Número de recursos cargados
|
||||||
|
|
||||||
ResourceCount() : total(0), loaded(0) {}
|
ResourceCount()
|
||||||
ResourceCount(size_t total) : total(total), loaded(0) {}
|
: total(0),
|
||||||
|
loaded(0) {}
|
||||||
|
ResourceCount(size_t total)
|
||||||
|
: total(total),
|
||||||
|
loaded(0) {}
|
||||||
|
|
||||||
void add(size_t amount) { loaded += amount; }
|
void add(size_t amount) { loaded += amount; }
|
||||||
void increase() { loaded++; }
|
void increase() { loaded++; }
|
||||||
@@ -129,6 +139,9 @@ class Resource {
|
|||||||
SDL_FRect loading_wired_rect_;
|
SDL_FRect loading_wired_rect_;
|
||||||
SDL_FRect loading_full_rect_;
|
SDL_FRect loading_full_rect_;
|
||||||
|
|
||||||
|
// --- Archivos temporales ---
|
||||||
|
std::vector<std::string> temp_audio_files_; // Rutas de archivos temporales de audio para limpieza
|
||||||
|
|
||||||
// --- Métodos internos de carga y gestión ---
|
// --- Métodos internos de carga y gestión ---
|
||||||
void loadSounds(); // Carga los sonidos
|
void loadSounds(); // Carga los sonidos
|
||||||
void loadMusics(); // Carga las músicas
|
void loadMusics(); // Carga las músicas
|
||||||
@@ -147,6 +160,7 @@ class Resource {
|
|||||||
void load(); // Carga todos los recursos
|
void load(); // Carga todos los recursos
|
||||||
void clearSounds(); // Vacía el vector de sonidos
|
void clearSounds(); // Vacía el vector de sonidos
|
||||||
void clearMusics(); // Vacía el vector de músicas
|
void clearMusics(); // Vacía el vector de músicas
|
||||||
|
void cleanupTempAudioFiles(); // Limpia archivos temporales de audio
|
||||||
|
|
||||||
// --- Métodos para carga perezosa ---
|
// --- Métodos para carga perezosa ---
|
||||||
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido
|
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido
|
||||||
|
|||||||
96
source/resource_helper.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "resource_helper.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ResourceHelper {
|
||||||
|
static bool resource_system_initialized = false;
|
||||||
|
|
||||||
|
bool initializeResourceSystem(const std::string& pack_file) {
|
||||||
|
auto& loader = ResourceLoader::getInstance();
|
||||||
|
resource_system_initialized = loader.initialize(pack_file, true);
|
||||||
|
|
||||||
|
if (resource_system_initialized) {
|
||||||
|
std::cout << "Resource system initialized with pack: " << pack_file << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Resource system using fallback mode (filesystem only)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Always return true as fallback is acceptable
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownResourceSystem() {
|
||||||
|
if (resource_system_initialized) {
|
||||||
|
ResourceLoader::getInstance().shutdown();
|
||||||
|
resource_system_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> loadFile(const std::string& filepath) {
|
||||||
|
if (resource_system_initialized && shouldUseResourcePack(filepath)) {
|
||||||
|
auto& loader = ResourceLoader::getInstance();
|
||||||
|
std::string pack_path = getPackPath(filepath);
|
||||||
|
|
||||||
|
auto data = loader.loadResource(pack_path);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a filesystem
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldUseResourcePack(const std::string& filepath) {
|
||||||
|
// Archivos que NO van al pack:
|
||||||
|
// - config/ (ahora está fuera de data/)
|
||||||
|
// - archivos absolutos del sistema
|
||||||
|
|
||||||
|
if (filepath.find("config/") != std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si contiene "data/" es candidato para el pack
|
||||||
|
if (filepath.find("data/") != std::string::npos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getPackPath(const std::string& asset_path) {
|
||||||
|
std::string pack_path = asset_path;
|
||||||
|
|
||||||
|
// Normalizar separadores de path a '/'
|
||||||
|
std::replace(pack_path.begin(), pack_path.end(), '\\', '/');
|
||||||
|
|
||||||
|
// Remover prefijo "data/" si existe
|
||||||
|
size_t data_pos = pack_path.find("data/");
|
||||||
|
if (data_pos != std::string::npos) {
|
||||||
|
pack_path = pack_path.substr(data_pos + 5); // +5 para saltar "data/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remover cualquier prefijo de path absoluto
|
||||||
|
size_t last_data = pack_path.rfind("data/");
|
||||||
|
if (last_data != std::string::npos) {
|
||||||
|
pack_path = pack_path.substr(last_data + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pack_path;
|
||||||
|
}
|
||||||
|
} // namespace ResourceHelper
|
||||||
47
source/resource_helper.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
// Helper functions para integrar ResourceLoader con el sistema existente
|
||||||
|
namespace ResourceHelper {
|
||||||
|
// Inicializa ResourceLoader (llamar al inicio del programa)
|
||||||
|
bool initializeResourceSystem(const std::string& pack_file = "resources.pack");
|
||||||
|
|
||||||
|
// Cierra ResourceLoader
|
||||||
|
void shutdownResourceSystem();
|
||||||
|
|
||||||
|
// Carga un archivo usando ResourceLoader o fallback a filesystem
|
||||||
|
std::vector<uint8_t> loadFile(const std::string& filepath);
|
||||||
|
|
||||||
|
// Verifica si un archivo debería cargarse del pack vs filesystem
|
||||||
|
bool shouldUseResourcePack(const std::string& filepath);
|
||||||
|
|
||||||
|
// Convierte ruta Asset a ruta relativa para ResourceLoader
|
||||||
|
std::string getPackPath(const std::string& asset_path);
|
||||||
|
|
||||||
|
// Wrappea la carga de archivos para mantener compatibilidad
|
||||||
|
template <typename T>
|
||||||
|
T* loadResourceFile(const std::string& asset_path, T* (*loader_func)(const char*)) {
|
||||||
|
auto data = loadFile(asset_path);
|
||||||
|
if (data.empty()) {
|
||||||
|
return loader_func(asset_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear archivo temporal para funciones que esperan path
|
||||||
|
std::string temp_path = "/tmp/ccae_" + std::to_string(std::hash<std::string>{}(asset_path));
|
||||||
|
std::ofstream temp_file(temp_path, std::ios::binary);
|
||||||
|
temp_file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
|
temp_file.close();
|
||||||
|
|
||||||
|
T* result = loader_func(temp_path.c_str());
|
||||||
|
std::filesystem::remove(temp_path);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace ResourceHelper
|
||||||
135
source/resource_loader.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "resource_loader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||||
|
|
||||||
|
ResourceLoader::ResourceLoader()
|
||||||
|
: resourcePack(nullptr),
|
||||||
|
fallbackToFiles(true) {}
|
||||||
|
|
||||||
|
ResourceLoader& ResourceLoader::getInstance() {
|
||||||
|
if (!instance) {
|
||||||
|
instance = std::unique_ptr<ResourceLoader>(new ResourceLoader());
|
||||||
|
}
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLoader::~ResourceLoader() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceLoader::initialize(const std::string& packFile, bool enableFallback) {
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
fallbackToFiles = enableFallback;
|
||||||
|
packPath = packFile;
|
||||||
|
|
||||||
|
if (std::filesystem::exists(packFile)) {
|
||||||
|
resourcePack = new ResourcePack();
|
||||||
|
if (resourcePack->loadPack(packFile)) {
|
||||||
|
std::cout << "Resource pack loaded successfully: " << packFile << std::endl;
|
||||||
|
std::cout << "Resources available: " << resourcePack->getResourceCount() << std::endl;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
delete resourcePack;
|
||||||
|
resourcePack = nullptr;
|
||||||
|
std::cerr << "Failed to load resource pack: " << packFile << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
std::cout << "Using fallback mode: loading resources from data/ directory" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Resource pack not found and fallback disabled: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceLoader::shutdown() {
|
||||||
|
if (resourcePack) {
|
||||||
|
delete resourcePack;
|
||||||
|
resourcePack = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourceLoader::loadResource(const std::string& filename) {
|
||||||
|
if (resourcePack && resourcePack->hasResource(filename)) {
|
||||||
|
return resourcePack->getResource(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
return loadFromFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Resource not found: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceLoader::resourceExists(const std::string& filename) {
|
||||||
|
if (resourcePack && resourcePack->hasResource(filename)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackToFiles) {
|
||||||
|
std::string fullPath = getDataPath(filename);
|
||||||
|
return std::filesystem::exists(fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourceLoader::loadFromFile(const std::string& filename) {
|
||||||
|
std::string fullPath = getDataPath(filename);
|
||||||
|
|
||||||
|
std::ifstream file(fullPath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << fullPath << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(data.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << fullPath << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResourceLoader::getDataPath(const std::string& filename) {
|
||||||
|
return "data/" + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ResourceLoader::getLoadedResourceCount() const {
|
||||||
|
if (resourcePack) {
|
||||||
|
return resourcePack->getResourceCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ResourceLoader::getAvailableResources() const {
|
||||||
|
if (resourcePack) {
|
||||||
|
return resourcePack->getResourceList();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (fallbackToFiles && std::filesystem::exists("data")) {
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
std::string filename = std::filesystem::relative(entry.path(), "data").string();
|
||||||
|
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||||
|
result.push_back(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
38
source/resource_loader.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef RESOURCE_LOADER_H
|
||||||
|
#define RESOURCE_LOADER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "resource_pack.h"
|
||||||
|
|
||||||
|
class ResourceLoader {
|
||||||
|
private:
|
||||||
|
static std::unique_ptr<ResourceLoader> instance;
|
||||||
|
ResourcePack* resourcePack;
|
||||||
|
std::string packPath;
|
||||||
|
bool fallbackToFiles;
|
||||||
|
|
||||||
|
ResourceLoader();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ResourceLoader& getInstance();
|
||||||
|
~ResourceLoader();
|
||||||
|
|
||||||
|
bool initialize(const std::string& packFile, bool enableFallback = true);
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
std::vector<uint8_t> loadResource(const std::string& filename);
|
||||||
|
bool resourceExists(const std::string& filename);
|
||||||
|
|
||||||
|
void setFallbackToFiles(bool enable) { fallbackToFiles = enable; }
|
||||||
|
bool getFallbackToFiles() const { return fallbackToFiles; }
|
||||||
|
|
||||||
|
size_t getLoadedResourceCount() const;
|
||||||
|
std::vector<std::string> getAvailableResources() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> loadFromFile(const std::string& filename);
|
||||||
|
std::string getDataPath(const std::string& filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
224
source/resource_pack.cpp
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
#include "resource_pack.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCAE_RESOURCES_2024";
|
||||||
|
|
||||||
|
ResourcePack::ResourcePack()
|
||||||
|
: loaded(false) {}
|
||||||
|
|
||||||
|
ResourcePack::~ResourcePack() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) {
|
||||||
|
uint32_t checksum = 0x12345678;
|
||||||
|
for (size_t i = 0; i < data.size(); ++i) {
|
||||||
|
checksum = ((checksum << 5) + checksum) + data[i];
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||||
|
if (key.empty()) return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data.size(); ++i) {
|
||||||
|
data[i] ^= key[i % key.length()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::decryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||||
|
encryptData(data, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::loadPack(const std::string& packFile) {
|
||||||
|
std::ifstream file(packFile, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open pack file: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char header[4];
|
||||||
|
file.read(header, 4);
|
||||||
|
if (std::string(header, 4) != "CCAE") {
|
||||||
|
std::cerr << "Error: Invalid pack file format" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||||
|
if (version != 1) {
|
||||||
|
std::cerr << "Error: Unsupported pack version: " << version << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t resourceCount;
|
||||||
|
file.read(reinterpret_cast<char*>(&resourceCount), sizeof(resourceCount));
|
||||||
|
|
||||||
|
resources.clear();
|
||||||
|
resources.reserve(resourceCount);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < resourceCount; ++i) {
|
||||||
|
uint32_t filenameLength;
|
||||||
|
file.read(reinterpret_cast<char*>(&filenameLength), sizeof(filenameLength));
|
||||||
|
|
||||||
|
std::string filename(filenameLength, '\0');
|
||||||
|
file.read(&filename[0], filenameLength);
|
||||||
|
|
||||||
|
ResourceEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
|
||||||
|
resources[filename] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t dataSize;
|
||||||
|
file.read(reinterpret_cast<char*>(&dataSize), sizeof(dataSize));
|
||||||
|
|
||||||
|
data.resize(dataSize);
|
||||||
|
file.read(reinterpret_cast<char*>(data.data()), dataSize);
|
||||||
|
|
||||||
|
decryptData(data, DEFAULT_ENCRYPT_KEY);
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::savePack(const std::string& packFile) {
|
||||||
|
std::ofstream file(packFile, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not create pack file: " << packFile << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write("CCAE", 4);
|
||||||
|
|
||||||
|
uint32_t version = 1;
|
||||||
|
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||||
|
|
||||||
|
uint32_t resourceCount = static_cast<uint32_t>(resources.size());
|
||||||
|
file.write(reinterpret_cast<const char*>(&resourceCount), sizeof(resourceCount));
|
||||||
|
|
||||||
|
for (const auto& [filename, entry] : resources) {
|
||||||
|
uint32_t filenameLength = static_cast<uint32_t>(filename.length());
|
||||||
|
file.write(reinterpret_cast<const char*>(&filenameLength), sizeof(filenameLength));
|
||||||
|
file.write(filename.c_str(), filenameLength);
|
||||||
|
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> encryptedData = data;
|
||||||
|
encryptData(encryptedData, DEFAULT_ENCRYPT_KEY);
|
||||||
|
|
||||||
|
uint64_t dataSize = encryptedData.size();
|
||||||
|
file.write(reinterpret_cast<const char*>(&dataSize), sizeof(dataSize));
|
||||||
|
file.write(reinterpret_cast<const char*>(encryptedData.data()), dataSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::addFile(const std::string& filename, const std::string& filepath) {
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Could not open file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize fileSize = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<uint8_t> fileData(fileSize);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(fileData.data()), fileSize)) {
|
||||||
|
std::cerr << "Error: Could not read file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
entry.offset = data.size();
|
||||||
|
entry.size = fileData.size();
|
||||||
|
entry.checksum = calculateChecksum(fileData);
|
||||||
|
|
||||||
|
data.insert(data.end(), fileData.begin(), fileData.end());
|
||||||
|
resources[filename] = entry;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::addDirectory(const std::string& directory) {
|
||||||
|
if (!std::filesystem::exists(directory)) {
|
||||||
|
std::cerr << "Error: Directory does not exist: " << directory << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
std::string filepath = entry.path().string();
|
||||||
|
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||||
|
|
||||||
|
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||||
|
|
||||||
|
if (!addFile(filename, filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ResourcePack::getResource(const std::string& filename) {
|
||||||
|
auto it = resources.find(filename);
|
||||||
|
if (it == resources.end()) {
|
||||||
|
std::cerr << "Error: Resource not found: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResourceEntry& entry = it->second;
|
||||||
|
if (entry.offset + entry.size > data.size()) {
|
||||||
|
std::cerr << "Error: Invalid resource data: " << filename << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> result(data.begin() + entry.offset,
|
||||||
|
data.begin() + entry.offset + entry.size);
|
||||||
|
|
||||||
|
uint32_t checksum = calculateChecksum(result);
|
||||||
|
if (checksum != entry.checksum) {
|
||||||
|
std::cerr << "Warning: Checksum mismatch for resource: " << filename << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourcePack::hasResource(const std::string& filename) const {
|
||||||
|
return resources.find(filename) != resources.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourcePack::clear() {
|
||||||
|
resources.clear();
|
||||||
|
data.clear();
|
||||||
|
loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ResourcePack::getResourceCount() const {
|
||||||
|
return resources.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ResourcePack::getResourceList() const {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(resources.size());
|
||||||
|
|
||||||
|
for (const auto& [filename, entry] : resources) {
|
||||||
|
result.push_back(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
46
source/resource_pack.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef RESOURCE_PACK_H
|
||||||
|
#define RESOURCE_PACK_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct ResourceEntry {
|
||||||
|
std::string filename;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourcePack {
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, ResourceEntry> resources;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
bool loaded;
|
||||||
|
|
||||||
|
uint32_t calculateChecksum(const std::vector<uint8_t>& data);
|
||||||
|
void encryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||||
|
void decryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ResourcePack();
|
||||||
|
~ResourcePack();
|
||||||
|
|
||||||
|
bool loadPack(const std::string& packFile);
|
||||||
|
bool savePack(const std::string& packFile);
|
||||||
|
|
||||||
|
bool addFile(const std::string& filename, const std::string& filepath);
|
||||||
|
bool addDirectory(const std::string& directory);
|
||||||
|
|
||||||
|
std::vector<uint8_t> getResource(const std::string& filename);
|
||||||
|
bool hasResource(const std::string& filename) const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
size_t getResourceCount() const;
|
||||||
|
std::vector<std::string> getResourceList() const;
|
||||||
|
|
||||||
|
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -40,7 +40,8 @@ Scoreboard::Scoreboard()
|
|||||||
: renderer_(Screen::get()->getRenderer()),
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
|
||||||
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
|
||||||
text_scoreboard_(Resource::get()->getText("8bithud")) {
|
text_(Resource::get()->getText("8bithud")),
|
||||||
|
enter_name_text_(Resource::get()->getText("smb2")) {
|
||||||
// Inicializa variables
|
// Inicializa variables
|
||||||
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(Id::SIZE); ++i) {
|
||||||
name_.at(i).clear();
|
name_.at(i).clear();
|
||||||
@@ -198,50 +199,50 @@ void Scoreboard::renderPanelContent(size_t panel_index) {
|
|||||||
|
|
||||||
void Scoreboard::renderScoreMode(size_t panel_index) {
|
void Scoreboard::renderScoreMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// MULT
|
// MULT
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
|
text_->writeDX(Text::COLOR | Text::CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_.at(panel_index)).substr(0, 3), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderDemoMode() {
|
void Scoreboard::renderDemoMode() {
|
||||||
// DEMO MODE
|
// DEMO MODE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_);
|
||||||
|
|
||||||
// PRESS START TO PLAY
|
// PRESS START TO PLAY
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderWaitingMode() {
|
void Scoreboard::renderWaitingMode() {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// PRESS START TO PLAY
|
// PRESS START TO PLAY
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderGameOverMode() {
|
void Scoreboard::renderGameOverMode() {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// PLEASE WAIT
|
// PLEASE WAIT
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderStageInfoMode() {
|
void Scoreboard::renderStageInfoMode() {
|
||||||
// STAGE
|
// STAGE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + " " + std::to_string(stage_), 1, text_color1_);
|
||||||
|
|
||||||
// POWERMETER
|
// POWERMETER
|
||||||
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
|
power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
|
||||||
@@ -250,76 +251,75 @@ void Scoreboard::renderStageInfoMode() {
|
|||||||
power_meter_sprite_->render();
|
power_meter_sprite_->render();
|
||||||
|
|
||||||
// HI-SCORE
|
// HI-SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_);
|
||||||
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
|
const std::string NAME = hi_score_name_.empty() ? "" : hi_score_name_ + " - ";
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, NAME + updateScoreText(hi_score_), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderContinueMode(size_t panel_index) {
|
void Scoreboard::renderContinueMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// CONTINUE
|
// CONTINUE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_.at(panel_index)), 1, text_color2_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
void Scoreboard::renderEnterNameMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// ENTER NAME
|
// ENTER NAME
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
renderNameInputField(panel_index);
|
renderNameInputField(panel_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderNameInputField(size_t panel_index) {
|
void Scoreboard::renderNameInputField(size_t panel_index) {
|
||||||
SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0F, 7.0F};
|
SDL_FRect rect = {
|
||||||
|
.x = enter_name_pos_.x,
|
||||||
|
.y = enter_name_pos_.y,
|
||||||
|
.w = static_cast<float>(enter_name_text_->getCharacterSize() - 2),
|
||||||
|
.h = static_cast<float>(enter_name_text_->getCharacterSize())};
|
||||||
|
|
||||||
// Recorre todos los slots de letras del nombre
|
// Recorre todos los slots de letras del nombre
|
||||||
for (size_t j = 0; j < NAME_SIZE; ++j) {
|
for (size_t j = 0; j < NAME_SIZE; ++j) {
|
||||||
// Selecciona el color
|
// Dibuja la linea. Si coincide con el selector solo se dibuja 2 de cada 4 veces
|
||||||
const Color COLOR = j < selector_pos_.at(panel_index) ? text_color2_ : text_color1_;
|
if (j != selector_pos_.at(panel_index) || time_counter_ % 4 >= 2) {
|
||||||
|
SDL_SetRenderDrawColor(renderer_, text_color1_.r, text_color1_.g, text_color1_.b, 255);
|
||||||
if (j != selector_pos_.at(panel_index) || time_counter_ % 3 == 0) {
|
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
|
||||||
// Dibuja la linea
|
|
||||||
if (j >= selector_pos_.at(panel_index)) {
|
|
||||||
SDL_SetRenderDrawColor(renderer_, COLOR.r, COLOR.g, COLOR.b, 255);
|
|
||||||
SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dibuja la letra
|
|
||||||
if (j < record_name_.at(panel_index).size()) {
|
|
||||||
text_scoreboard_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), COLOR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rect.x += 7;
|
|
||||||
|
// Dibuja la letra
|
||||||
|
if (j < record_name_.at(panel_index).size()) {
|
||||||
|
enter_name_text_->writeColored(rect.x, rect.y, record_name_.at(panel_index).substr(j, 1), text_color2_);
|
||||||
|
}
|
||||||
|
rect.x += enter_name_text_->getCharacterSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
void Scoreboard::renderShowNameMode(size_t panel_index) {
|
||||||
// SCORE
|
// SCORE
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y, name_.at(panel_index), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
|
|
||||||
// NAME
|
// NAME
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_);
|
||||||
|
|
||||||
/* TEXTO CENTRADO */
|
// NOMBRE INTRODUCIDO
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
|
enter_name_text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y, record_name_.at(panel_index), 1, Colors::getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
void Scoreboard::renderGameCompletedMode(size_t panel_index) {
|
||||||
// GAME OVER
|
// GAME OVER
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_);
|
||||||
|
|
||||||
// SCORE
|
// SCORE
|
||||||
if (time_counter_ % 10 < 8) {
|
if (time_counter_ % 10 < 8) {
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_);
|
||||||
text_scoreboard_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
text_->writeDX(Text::CENTER | Text::COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_.at(panel_index)), 1, text_color2_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ void Scoreboard::recalculateAnchors() {
|
|||||||
slot4_4_ = {.x = COL, .y = ROW4};
|
slot4_4_ = {.x = COL, .y = ROW4};
|
||||||
|
|
||||||
// Primer cuadrado para poner el nombre de record
|
// Primer cuadrado para poner el nombre de record
|
||||||
const int ENTER_NAME_LENGTH = text_scoreboard_->length(std::string(NAME_SIZE, 'A'));
|
const int ENTER_NAME_LENGTH = enter_name_text_->length(std::string(NAME_SIZE, 'A'));
|
||||||
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
enter_name_pos_.x = COL - (ENTER_NAME_LENGTH / 2);
|
||||||
enter_name_pos_.y = ROW4;
|
enter_name_pos_.y = ROW4;
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ class Scoreboard {
|
|||||||
SDL_Renderer *renderer_; // El renderizador de la ventana
|
SDL_Renderer *renderer_; // El renderizador de la ventana
|
||||||
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase
|
||||||
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase
|
||||||
std::shared_ptr<Text> text_scoreboard_; // Fuente para el marcador del juego
|
std::shared_ptr<Text> text_; // Fuente para el marcador del juego
|
||||||
|
std::shared_ptr<Text> enter_name_text_; // Fuente para la introducción de nombre del jugador
|
||||||
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
|
SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador
|
||||||
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
|
std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel
|
||||||
|
|
||||||
|
|||||||
@@ -224,8 +224,10 @@ void Screen::renderInfo() {
|
|||||||
void Screen::loadShaders() {
|
void Screen::loadShaders() {
|
||||||
if (shader_source_.empty()) {
|
if (shader_source_.empty()) {
|
||||||
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
|
const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl";
|
||||||
std::ifstream f(Asset::get()->get(GLSL_FILE).c_str());
|
auto data = Asset::get()->loadData(GLSL_FILE);
|
||||||
shader_source_ = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
if (!data.empty()) {
|
||||||
|
shader_source_ = std::string(data.begin(), data.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,11 @@ class Screen {
|
|||||||
Color color; // Color del flash
|
Color color; // Color del flash
|
||||||
|
|
||||||
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
|
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF))
|
||||||
: enabled(enabled), length(length), delay(delay), counter(length), color(color) {}
|
: enabled(enabled),
|
||||||
|
length(length),
|
||||||
|
delay(delay),
|
||||||
|
counter(length),
|
||||||
|
color(color) {}
|
||||||
|
|
||||||
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
|
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); }
|
||||||
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
|
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; }
|
||||||
@@ -109,7 +113,14 @@ class Screen {
|
|||||||
bool enabled; // Indica si el efecto está activo
|
bool enabled; // Indica si el efecto está activo
|
||||||
|
|
||||||
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
|
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800)
|
||||||
: desp(dp), delay(dl), counter(cnt), length(len), remaining(rem), original_pos(orig_pos), original_width(orig_width), enabled(en) {}
|
: desp(dp),
|
||||||
|
delay(dl),
|
||||||
|
counter(cnt),
|
||||||
|
length(len),
|
||||||
|
remaining(rem),
|
||||||
|
original_pos(orig_pos),
|
||||||
|
original_width(orig_width),
|
||||||
|
enabled(en) {}
|
||||||
|
|
||||||
// Activa el efecto de sacudida y guarda la posición y tamaño originales
|
// Activa el efecto de sacudida y guarda la posición y tamaño originales
|
||||||
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {
|
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) {
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ constexpr std::string_view TEXT_COPYRIGHT = "@2020,2025 JailDesigner";
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Credits::Credits()
|
Credits::Credits()
|
||||||
: balloon_manager_(std::make_unique<BalloonManager>(nullptr)), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))), canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))) {
|
: balloon_manager_(std::make_unique<BalloonManager>(nullptr)),
|
||||||
|
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)),
|
||||||
|
fade_in_(std::make_unique<Fade>()),
|
||||||
|
fade_out_(std::make_unique<Fade>()),
|
||||||
|
text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))),
|
||||||
|
canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, static_cast<int>(param.game.width), static_cast<int>(param.game.height))) {
|
||||||
if (text_texture_ == nullptr) {
|
if (text_texture_ == nullptr) {
|
||||||
throw std::runtime_error("Failed to create SDL texture for text.");
|
throw std::runtime_error("Failed to create SDL texture for text.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,18 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Game::Game(Player::Id player_id, int current_stage, bool demo)
|
Game::Game(Player::Id player_id, int current_stage, bool demo)
|
||||||
: renderer_(Screen::get()->getRenderer()), screen_(Screen::get()), input_(Input::get()), canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })), stage_manager_(std::make_unique<StageManager>()), balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())), background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))), fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), tabe_(std::make_unique<Tabe>()), hit_(Hit(Resource::get()->getTexture("hit.png"))) {
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
|
screen_(Screen::get()),
|
||||||
|
input_(Input::get()),
|
||||||
|
canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)),
|
||||||
|
pause_manager_(std::make_unique<PauseManager>([this](bool is_paused) { onPauseStateChanged(is_paused); })),
|
||||||
|
stage_manager_(std::make_unique<StageManager>()),
|
||||||
|
balloon_manager_(std::make_unique<BalloonManager>(stage_manager_.get())),
|
||||||
|
background_(std::make_unique<Background>(stage_manager_->getPowerNeededToReachStage(stage_manager_->getTotalStages() - 1))),
|
||||||
|
fade_in_(std::make_unique<Fade>()),
|
||||||
|
fade_out_(std::make_unique<Fade>()),
|
||||||
|
tabe_(std::make_unique<Tabe>()),
|
||||||
|
hit_(Hit(Resource::get()->getTexture("hit.png"))) {
|
||||||
// Pasa variables
|
// Pasa variables
|
||||||
demo_.enabled = demo;
|
demo_.enabled = demo;
|
||||||
|
|
||||||
@@ -285,11 +296,11 @@ void Game::updateStage() {
|
|||||||
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
|
if (current_stage_index == total_stages - 1) { // Penúltima fase (será la última)
|
||||||
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
|
createMessage(paths, Resource::get()->getTexture("game_text_last_stage"));
|
||||||
} else {
|
} else {
|
||||||
auto text = Resource::get()->getText("04b_25_2x");
|
auto text = Resource::get()->getText("04b_25_2x_enhanced");
|
||||||
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
|
const std::string CAPTION = Lang::getText("[GAME_TEXT] 2") +
|
||||||
std::to_string(total_stages - current_stage_index) +
|
std::to_string(total_stages - current_stage_index) +
|
||||||
Lang::getText("[GAME_TEXT] 2A");
|
Lang::getText("[GAME_TEXT] 2A");
|
||||||
createMessage(paths, text->writeToTexture(CAPTION, 1, -4));
|
createMessage(paths, text->writeDXToTexture(Text::STROKE, CAPTION, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,15 +378,6 @@ void Game::updateGameStateCompleted() {
|
|||||||
updatePathSprites();
|
updatePathSprites();
|
||||||
cleanVectors();
|
cleanVectors();
|
||||||
|
|
||||||
// Para la música y elimina todos los globos e items
|
|
||||||
if (game_completed_counter_ == 0) {
|
|
||||||
stopMusic(); // Detiene la música
|
|
||||||
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
|
|
||||||
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
|
|
||||||
destroyAllItems(); // Destruye todos los items
|
|
||||||
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comienza las celebraciones
|
// Comienza las celebraciones
|
||||||
// Muestra el mensaje de felicitación y da los puntos a los jugadores
|
// Muestra el mensaje de felicitación y da los puntos a los jugadores
|
||||||
if (game_completed_counter_ == START_CELEBRATIONS) {
|
if (game_completed_counter_ == START_CELEBRATIONS) {
|
||||||
@@ -606,6 +608,7 @@ void Game::handleItemDrop(const std::shared_ptr<Balloon> &balloon, const std::sh
|
|||||||
|
|
||||||
if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) {
|
if (DROPPED_ITEM != ItemType::COFFEE_MACHINE) {
|
||||||
createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
|
createItem(DROPPED_ITEM, balloon->getPosX(), balloon->getPosY());
|
||||||
|
playSound("item_drop.wav");
|
||||||
} else {
|
} else {
|
||||||
createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT);
|
createItem(DROPPED_ITEM, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT);
|
||||||
coffee_machine_enabled_ = true;
|
coffee_machine_enabled_ = true;
|
||||||
@@ -733,7 +736,6 @@ auto Game::dropItem() -> ItemType {
|
|||||||
// Crea un objeto item
|
// Crea un objeto item
|
||||||
void Game::createItem(ItemType type, float x, float y) {
|
void Game::createItem(ItemType type, float x, float y) {
|
||||||
items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1]));
|
items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1]));
|
||||||
playSound("item_drop.wav");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vacia el vector de items
|
// Vacia el vector de items
|
||||||
@@ -1371,17 +1373,27 @@ void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) {
|
|||||||
|
|
||||||
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
||||||
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
|
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
|
||||||
|
if (!player) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::UP);
|
handleFireInput(player, BulletType::UP);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire = 1;
|
demo_.keys.fire = 1;
|
||||||
#endif
|
#endif
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::LEFT);
|
handleFireInput(player, BulletType::LEFT);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire_left = 1;
|
demo_.keys.fire_left = 1;
|
||||||
#endif
|
#endif
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
handleFireInput(player, BulletType::RIGHT);
|
handleFireInput(player, BulletType::RIGHT);
|
||||||
#ifdef RECORDING
|
#ifdef RECORDING
|
||||||
demo_.keys.fire_right = 1;
|
demo_.keys.fire_right = 1;
|
||||||
@@ -1395,6 +1407,7 @@ void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
|
|||||||
player->setPlayingState(Player::State::RECOVER);
|
player->setPlayingState(Player::State::RECOVER);
|
||||||
player->addCredit();
|
player->addCredit();
|
||||||
sendPlayerToTheFront(player);
|
sendPlayerToTheFront(player);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
||||||
@@ -1420,33 +1433,53 @@ void Game::handlePlayerWaitingInput(const std::shared_ptr<Player> &player) {
|
|||||||
void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
||||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else if (player->getEnterNamePositionOverflow()) {
|
return;
|
||||||
|
}
|
||||||
|
if (player->getEnterNamePositionOverflow()) {
|
||||||
player->setInput(Input::Action::START);
|
player->setInput(Input::Action::START);
|
||||||
player->setPlayingState(Player::State::SHOWING_NAME);
|
player->setPlayingState(Player::State::SHOWING_NAME);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
updateHiScoreName();
|
updateHiScoreName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::RIGHT);
|
|
||||||
}
|
}
|
||||||
} else if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) ||
|
player->setInput(Input::Action::RIGHT);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad()) ||
|
||||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::LEFT);
|
|
||||||
}
|
}
|
||||||
} else if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
player->setInput(Input::Action::LEFT);
|
||||||
|
playSound("service_menu_back.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::UP);
|
player->setInput(Input::Action::UP);
|
||||||
} else if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
playSound("service_menu_move.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
player->setInput(Input::Action::DOWN);
|
player->setInput(Input::Action::DOWN);
|
||||||
} else if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
playSound("service_menu_move.wav");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, player->getUsesKeyboard(), player->getGamepad())) {
|
||||||
if (player->isShowingName()) {
|
if (player->isShowingName()) {
|
||||||
player->setPlayingState(Player::State::CONTINUE);
|
player->passShowingName();
|
||||||
} else {
|
return;
|
||||||
player->setInput(Input::Action::START);
|
|
||||||
player->setPlayingState(Player::State::SHOWING_NAME);
|
|
||||||
updateHiScoreName();
|
|
||||||
}
|
}
|
||||||
|
player->setInput(Input::Action::START);
|
||||||
|
player->setPlayingState(Player::State::SHOWING_NAME);
|
||||||
|
playSound("service_menu_select.wav");
|
||||||
|
updateHiScoreName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1457,20 +1490,16 @@ void Game::initDemo(Player::Id player_id) {
|
|||||||
setState(State::PLAYING);
|
setState(State::PLAYING);
|
||||||
|
|
||||||
// Aleatoriza la asignación del fichero con los datos del modo demostracion
|
// Aleatoriza la asignación del fichero con los datos del modo demostracion
|
||||||
{
|
const auto DEMO1 = rand() % 2;
|
||||||
const auto DEMO1 = rand() % 2;
|
const auto DEMO2 = (DEMO1 == 0) ? 1 : 0;
|
||||||
const auto DEMO2 = (DEMO1 == 0) ? 1 : 0;
|
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1));
|
||||||
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1));
|
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2));
|
||||||
demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selecciona una pantalla al azar
|
// Selecciona una pantalla al azar
|
||||||
{
|
constexpr auto NUM_DEMOS = 3;
|
||||||
constexpr auto NUM_DEMOS = 3;
|
const auto DEMO = rand() % NUM_DEMOS;
|
||||||
const auto DEMO = rand() % NUM_DEMOS;
|
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
|
||||||
constexpr std::array<int, NUM_DEMOS> STAGES = {0, 3, 5};
|
stage_manager_->jumpToStage(STAGES.at(DEMO));
|
||||||
stage_manager_->jumpToStage(STAGES.at(DEMO));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activa o no al otro jugador
|
// Activa o no al otro jugador
|
||||||
if (rand() % 3 != 0) {
|
if (rand() % 3 != 0) {
|
||||||
@@ -1554,60 +1583,51 @@ void Game::initDifficultyVars() {
|
|||||||
// Inicializa los jugadores
|
// Inicializa los jugadores
|
||||||
void Game::initPlayers(Player::Id player_id) {
|
void Game::initPlayers(Player::Id player_id) {
|
||||||
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
|
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
|
||||||
|
const Player::State STATE = demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN;
|
||||||
|
|
||||||
// Crea al jugador uno y lo pone en modo espera
|
// Crea al jugador uno y lo pone en modo espera
|
||||||
Player::Config config_player1;
|
Player::Config config_player1{
|
||||||
config_player1.id = Player::Id::PLAYER1;
|
.id = Player::Id::PLAYER1,
|
||||||
config_player1.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2);
|
.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2),
|
||||||
config_player1.y = Y;
|
.y = Y,
|
||||||
config_player1.demo = demo_.enabled;
|
.demo = demo_.enabled,
|
||||||
config_player1.play_area = ¶m.game.play_area.rect;
|
.play_area = ¶m.game.play_area.rect,
|
||||||
config_player1.texture = player_textures_.at(0);
|
.texture = player_textures_.at(0),
|
||||||
config_player1.animations = player_animations_;
|
.animations = player_animations_,
|
||||||
config_player1.hi_score_table = &Options::settings.hi_score_table;
|
.hi_score_table = &Options::settings.hi_score_table,
|
||||||
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
|
.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1),
|
||||||
config_player1.stage_info = stage_manager_.get();
|
.stage_info = stage_manager_.get()};
|
||||||
|
|
||||||
auto player1 = std::make_unique<Player>(config_player1);
|
auto player1 = std::make_unique<Player>(config_player1);
|
||||||
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
|
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
|
||||||
player1->setName(Lang::getText("[SCOREBOARD] 1"));
|
player1->setName(Lang::getText("[SCOREBOARD] 1"));
|
||||||
player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance);
|
player1->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER1).instance);
|
||||||
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
|
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
|
||||||
player1->setPlayingState(Player::State::WAITING);
|
player1->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER1) ? STATE : Player::State::WAITING);
|
||||||
players_.push_back(std::move(player1));
|
|
||||||
|
|
||||||
// Crea al jugador dos y lo pone en modo espera
|
// Crea al jugador dos y lo pone en modo espera
|
||||||
Player::Config config_player2;
|
Player::Config config_player2{
|
||||||
config_player2.id = Player::Id::PLAYER2;
|
.id = Player::Id::PLAYER2,
|
||||||
config_player2.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2);
|
.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2),
|
||||||
config_player2.y = Y;
|
.y = Y,
|
||||||
config_player2.demo = demo_.enabled;
|
.demo = demo_.enabled,
|
||||||
config_player2.play_area = ¶m.game.play_area.rect;
|
.play_area = ¶m.game.play_area.rect,
|
||||||
config_player2.texture = player_textures_.at(1);
|
.texture = player_textures_.at(1),
|
||||||
config_player2.animations = player_animations_;
|
.animations = player_animations_,
|
||||||
config_player2.hi_score_table = &Options::settings.hi_score_table;
|
.hi_score_table = &Options::settings.hi_score_table,
|
||||||
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
|
.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1),
|
||||||
config_player2.stage_info = stage_manager_.get();
|
.stage_info = stage_manager_.get()};
|
||||||
|
|
||||||
auto player2 = std::make_unique<Player>(config_player2);
|
auto player2 = std::make_unique<Player>(config_player2);
|
||||||
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
|
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
|
||||||
player2->setName(Lang::getText("[SCOREBOARD] 2"));
|
player2->setName(Lang::getText("[SCOREBOARD] 2"));
|
||||||
player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance);
|
player2->setGamepad(Options::gamepad_manager.getGamepad(Player::Id::PLAYER2).instance);
|
||||||
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
|
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
|
||||||
player2->setPlayingState(Player::State::WAITING);
|
player2->setPlayingState((player_id == Player::Id::BOTH_PLAYERS || player_id == Player::Id::PLAYER2) ? STATE : Player::State::WAITING);
|
||||||
players_.push_back(std::move(player2));
|
|
||||||
|
|
||||||
// Activa el jugador que coincide con el "player_id" o ambos si es "0"
|
// Añade los jugadores al vector de forma que el jugador 1 se pinte por delante del jugador 2
|
||||||
if (player_id == Player::Id::BOTH_PLAYERS) {
|
players_.push_back(std::move(player2));
|
||||||
// Activa ambos jugadores
|
players_.push_back(std::move(player1));
|
||||||
getPlayer(Player::Id::PLAYER1)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
getPlayer(Player::Id::PLAYER2)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
} else {
|
|
||||||
// Activa el jugador elegido
|
|
||||||
auto player = getPlayer(player_id);
|
|
||||||
player->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
|
|
||||||
sendPlayerToTheFront(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registra los jugadores en Options
|
// Registra los jugadores en Options
|
||||||
for (const auto &player : players_) {
|
for (const auto &player : players_) {
|
||||||
@@ -1812,6 +1832,19 @@ void Game::checkAndUpdateBalloonSpeed() {
|
|||||||
void Game::setState(State state) {
|
void Game::setState(State state) {
|
||||||
state_ = state;
|
state_ = state;
|
||||||
counter_ = 0;
|
counter_ = 0;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case State::COMPLETED: // Para la música y elimina todos los globos e items
|
||||||
|
stopMusic(); // Detiene la música
|
||||||
|
balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos
|
||||||
|
playSound("power_ball_explosion.wav"); // Sonido de destruir todos los globos
|
||||||
|
destroyAllItems(); // Destruye todos los items
|
||||||
|
background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas
|
||||||
|
tabe_->disableSpawning(); // Deshabilita la creacion de Tabes
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::playSound(const std::string &name) const {
|
void Game::playSound(const std::string &name) const {
|
||||||
@@ -1873,20 +1906,17 @@ void Game::handleDebugEvents(const SDL_Event &event) {
|
|||||||
static int formation_id_ = 0;
|
static int formation_id_ = 0;
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
if (event.type == SDL_EVENT_KEY_DOWN && static_cast<int>(event.key.repeat) == 0) {
|
||||||
switch (event.key.key) {
|
switch (event.key.key) {
|
||||||
case SDLK_1: // Crea una powerball
|
case SDLK_1: { // Crea una powerball
|
||||||
{
|
|
||||||
balloon_manager_->createPowerBall();
|
balloon_manager_->createPowerBall();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_2: // Activa o desactiva la aparición de globos
|
case SDLK_2: { // Activa o desactiva la aparición de globos
|
||||||
{
|
|
||||||
static bool deploy_balloons_ = true;
|
static bool deploy_balloons_ = true;
|
||||||
deploy_balloons_ = !deploy_balloons_;
|
deploy_balloons_ = !deploy_balloons_;
|
||||||
balloon_manager_->enableBalloonDeployment(deploy_balloons_);
|
balloon_manager_->enableBalloonDeployment(deploy_balloons_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_3: // Activa el modo para pasar el juego automaticamente
|
case SDLK_3: { // Activa el modo para pasar el juego automaticamente
|
||||||
{
|
|
||||||
auto_pop_balloons_ = !auto_pop_balloons_;
|
auto_pop_balloons_ = !auto_pop_balloons_;
|
||||||
Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)});
|
Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)});
|
||||||
if (auto_pop_balloons_) {
|
if (auto_pop_balloons_) {
|
||||||
@@ -1896,24 +1926,20 @@ void Game::handleDebugEvents(const SDL_Event &event) {
|
|||||||
balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_);
|
balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_4: // Suelta un item
|
case SDLK_4: { // Suelta un item
|
||||||
{
|
|
||||||
createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40);
|
createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_5: // 5.000
|
case SDLK_5: { // 5.000
|
||||||
{
|
|
||||||
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
||||||
createItemText(X, game_text_textures_.at(2));
|
createItemText(X, game_text_textures_.at(2));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_6: // Crea un mensaje
|
case SDLK_6: { // Crea un mensaje
|
||||||
{
|
|
||||||
createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready"));
|
createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDLK_7: // 100.000
|
case SDLK_7: { // 100.000
|
||||||
{
|
|
||||||
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
const int X = players_.at(0)->getPosX() + ((Player::WIDTH - game_text_textures_[3]->getWidth()) / 2);
|
||||||
createItemText(X, game_text_textures_.at(6));
|
createItemText(X, game_text_textures_.at(6));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -254,8 +254,8 @@ class Game {
|
|||||||
|
|
||||||
// --- Sistema de globos y enemigos ---
|
// --- Sistema de globos y enemigos ---
|
||||||
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
|
void handleBalloonDestruction(std::shared_ptr<Balloon> balloon, const std::shared_ptr<Player> &player); // Procesa destrucción de globo
|
||||||
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
|
void handleTabeHitEffects(); // Gestiona efectos al golpear a Tabe
|
||||||
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
|
void checkAndUpdateBalloonSpeed(); // Ajusta velocidad de globos según progreso
|
||||||
|
|
||||||
// --- Gestión de fases y progresión ---
|
// --- Gestión de fases y progresión ---
|
||||||
void updateStage(); // Verifica y actualiza cambio de fase
|
void updateStage(); // Verifica y actualiza cambio de fase
|
||||||
|
|||||||
@@ -29,7 +29,14 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
HiScoreTable::HiScoreTable()
|
HiScoreTable::HiScoreTable()
|
||||||
: renderer_(Screen::get()->getRenderer()), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), fade_(std::make_unique<Fade>()), background_(std::make_unique<Background>()), ticks_(0), view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}), fade_mode_(Fade::Mode::IN), background_fade_color_(Color(0, 0, 0)) {
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
|
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
|
||||||
|
fade_(std::make_unique<Fade>()),
|
||||||
|
background_(std::make_unique<Background>()),
|
||||||
|
last_time_(0),
|
||||||
|
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
|
||||||
|
fade_mode_(Fade::Mode::IN),
|
||||||
|
background_fade_color_(Color(0, 0, 0)) {
|
||||||
// Inicializa el resto
|
// Inicializa el resto
|
||||||
Section::name = Section::Name::HI_SCORE_TABLE;
|
Section::name = Section::Name::HI_SCORE_TABLE;
|
||||||
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
|
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
|
||||||
@@ -46,17 +53,14 @@ HiScoreTable::~HiScoreTable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables
|
// Actualiza las variables
|
||||||
void HiScoreTable::update() {
|
void HiScoreTable::update(float delta_time) {
|
||||||
if (SDL_GetTicks() - ticks_ > param.game.speed) {
|
Screen::get()->update(); // Actualiza el objeto screen
|
||||||
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
|
|
||||||
Screen::get()->update(); // Actualiza el objeto screen
|
|
||||||
|
|
||||||
updateSprites(); // Actualiza las posiciones de los sprites de texto
|
updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
|
||||||
background_->update(); // Actualiza el fondo
|
background_->update(delta_time); // Actualiza el fondo
|
||||||
updateFade(); // Gestiona el fade
|
updateFade(delta_time); // Gestiona el fade
|
||||||
updateCounter(); // Gestiona el contador y sus eventos
|
updateCounter(); // Gestiona el contador y sus eventos
|
||||||
fillTexture(); // Dibuja los sprites en la textura
|
fillTexture(); // Dibuja los sprites en la textura
|
||||||
}
|
|
||||||
|
|
||||||
Audio::update();
|
Audio::update();
|
||||||
}
|
}
|
||||||
@@ -110,20 +114,32 @@ void HiScoreTable::checkInput() {
|
|||||||
GlobalInputs::check();
|
GlobalInputs::check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula el tiempo transcurrido desde el último frame
|
||||||
|
float HiScoreTable::calculateDeltaTime() {
|
||||||
|
const Uint64 current_time = SDL_GetTicks();
|
||||||
|
const float delta_time = static_cast<float>(current_time - last_time_);
|
||||||
|
last_time_ = current_time;
|
||||||
|
return delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
// Bucle para la pantalla de instrucciones
|
// Bucle para la pantalla de instrucciones
|
||||||
void HiScoreTable::run() {
|
void HiScoreTable::run() {
|
||||||
|
last_time_ = SDL_GetTicks();
|
||||||
Audio::get()->playMusic("title.ogg");
|
Audio::get()->playMusic("title.ogg");
|
||||||
|
|
||||||
while (Section::name == Section::Name::HI_SCORE_TABLE) {
|
while (Section::name == Section::Name::HI_SCORE_TABLE) {
|
||||||
|
const float delta_time = calculateDeltaTime();
|
||||||
|
|
||||||
checkInput();
|
checkInput();
|
||||||
update();
|
update(delta_time);
|
||||||
checkEvents(); // Tiene que ir antes del render
|
checkEvents(); // Tiene que ir antes del render
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestiona el fade
|
// Gestiona el fade
|
||||||
void HiScoreTable::updateFade() {
|
void HiScoreTable::updateFade(float delta_time) {
|
||||||
fade_->update();
|
fade_->update(delta_time);
|
||||||
|
|
||||||
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
|
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
|
||||||
fade_->reset();
|
fade_->reset();
|
||||||
@@ -242,7 +258,7 @@ void HiScoreTable::createSprites() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las posiciones de los sprites de texto
|
// Actualiza las posiciones de los sprites de texto
|
||||||
void HiScoreTable::updateSprites() {
|
void HiScoreTable::updateSprites(float delta_time) {
|
||||||
constexpr int INIT_COUNTER = 190;
|
constexpr int INIT_COUNTER = 190;
|
||||||
const int COUNTER_BETWEEN_ENTRIES = 16;
|
const int COUNTER_BETWEEN_ENTRIES = 16;
|
||||||
if (counter_ >= INIT_COUNTER) {
|
if (counter_ >= INIT_COUNTER) {
|
||||||
@@ -255,7 +271,7 @@ void HiScoreTable::updateSprites() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto const &entry : entry_names_) {
|
for (auto const &entry : entry_names_) {
|
||||||
entry->update();
|
entry->update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
glowEntryNames();
|
glowEntryNames();
|
||||||
|
|||||||
@@ -45,26 +45,27 @@ class HiScoreTable {
|
|||||||
|
|
||||||
// --- Variables ---
|
// --- Variables ---
|
||||||
Uint16 counter_ = 0; // Contador
|
Uint16 counter_ = 0; // Contador
|
||||||
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||||
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
|
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
|
||||||
Fade::Mode fade_mode_; // Modo de fade a utilizar
|
Fade::Mode fade_mode_; // Modo de fade a utilizar
|
||||||
Color background_fade_color_; // Color de atenuación del fondo
|
Color background_fade_color_; // Color de atenuación del fondo
|
||||||
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
|
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void update(); // Actualiza las variables
|
void update(float delta_time); // Actualiza las variables
|
||||||
void render(); // Pinta en pantalla
|
void render(); // Pinta en pantalla
|
||||||
static void checkEvents(); // Comprueba los eventos
|
static void checkEvents(); // Comprueba los eventos
|
||||||
static void checkInput(); // Comprueba las entradas
|
static void checkInput(); // Comprueba las entradas
|
||||||
static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles
|
static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles
|
||||||
void fillTexture(); // Dibuja los sprites en la textura
|
void fillTexture(); // Dibuja los sprites en la textura
|
||||||
void updateFade(); // Gestiona el fade
|
void updateFade(float delta_time); // Gestiona el fade
|
||||||
void createSprites(); // Crea los sprites con los textos
|
void createSprites(); // Crea los sprites con los textos
|
||||||
void updateSprites(); // Actualiza las posiciones de los sprites de texto
|
void updateSprites(float delta_time); // Actualiza las posiciones de los sprites de texto
|
||||||
void initFade(); // Inicializa el fade
|
void initFade(); // Inicializa el fade
|
||||||
void initBackground(); // Inicializa el fondo
|
void initBackground(); // Inicializa el fondo
|
||||||
auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas
|
auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas
|
||||||
void iniEntryColors(); // Inicializa los colores de las entradas
|
void iniEntryColors(); // Inicializa los colores de las entradas
|
||||||
void glowEntryNames(); // Hace brillar los nombres de la tabla de records
|
void glowEntryNames(); // Hace brillar los nombres de la tabla de records
|
||||||
void updateCounter(); // Gestiona el contador
|
void updateCounter(); // Gestiona el contador
|
||||||
|
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
|
||||||
};
|
};
|
||||||
@@ -26,7 +26,12 @@
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Instructions::Instructions()
|
Instructions::Instructions()
|
||||||
: renderer_(Screen::get()->getRenderer()), texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), text_(Resource::get()->getText("smb2")), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)), fade_(std::make_unique<Fade>()) {
|
: renderer_(Screen::get()->getRenderer()),
|
||||||
|
texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
|
||||||
|
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
|
||||||
|
text_(Resource::get()->getText("smb2")),
|
||||||
|
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)),
|
||||||
|
fade_(std::make_unique<Fade>()) {
|
||||||
// Configura las texturas
|
// Configura las texturas
|
||||||
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
|
SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
|
||||||
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
|
SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
|
||||||
@@ -199,18 +204,15 @@ void Instructions::fillBackbuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables
|
// Actualiza las variables
|
||||||
void Instructions::update() {
|
void Instructions::update(float delta_time) {
|
||||||
if (SDL_GetTicks() - ticks_ > param.game.speed) {
|
Screen::get()->update(); // Actualiza el objeto screen
|
||||||
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
|
|
||||||
Screen::get()->update(); // Actualiza el objeto screen
|
|
||||||
|
|
||||||
counter_++; // Incrementa el contador
|
counter_++; // Incrementa el contador
|
||||||
updateSprites(); // Actualiza los sprites
|
updateSprites(); // Actualiza los sprites
|
||||||
updateBackbuffer(); // Gestiona la textura con los graficos
|
updateBackbuffer(); // Gestiona la textura con los graficos
|
||||||
tiled_bg_->update(); // Actualiza el mosaico de fondo
|
tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
|
||||||
fade_->update(); // Actualiza el objeto "fade"
|
fade_->update(delta_time); // Actualiza el objeto "fade"
|
||||||
fillBackbuffer(); // Rellena el backbuffer
|
fillBackbuffer(); // Rellena el backbuffer
|
||||||
}
|
|
||||||
|
|
||||||
Audio::update();
|
Audio::update();
|
||||||
}
|
}
|
||||||
@@ -250,12 +252,24 @@ void Instructions::checkInput() {
|
|||||||
GlobalInputs::check();
|
GlobalInputs::check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula el tiempo transcurrido desde el último frame
|
||||||
|
float Instructions::calculateDeltaTime() {
|
||||||
|
const Uint64 current_time = SDL_GetTicks();
|
||||||
|
const float delta_time = static_cast<float>(current_time - last_time_);
|
||||||
|
last_time_ = current_time;
|
||||||
|
return delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
// Bucle para la pantalla de instrucciones
|
// Bucle para la pantalla de instrucciones
|
||||||
void Instructions::run() {
|
void Instructions::run() {
|
||||||
|
last_time_ = SDL_GetTicks();
|
||||||
Audio::get()->playMusic("title.ogg");
|
Audio::get()->playMusic("title.ogg");
|
||||||
|
|
||||||
while (Section::name == Section::Name::INSTRUCTIONS) {
|
while (Section::name == Section::Name::INSTRUCTIONS) {
|
||||||
|
const float delta_time = calculateDeltaTime();
|
||||||
|
|
||||||
checkInput();
|
checkInput();
|
||||||
update();
|
update(delta_time);
|
||||||
checkEvents(); // Tiene que ir antes del render
|
checkEvents(); // Tiene que ir antes del render
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ struct Line { // Almacena información de línea animada
|
|||||||
|
|
||||||
// Constructor de Line
|
// Constructor de Line
|
||||||
Line(int y, float x, int direction)
|
Line(int y, float x, int direction)
|
||||||
: y(y), x(x), direction(direction) {}
|
: y(y),
|
||||||
|
x(x),
|
||||||
|
direction(direction) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clase Instructions
|
// Clase Instructions
|
||||||
@@ -61,7 +63,7 @@ class Instructions {
|
|||||||
|
|
||||||
// --- Variables ---
|
// --- Variables ---
|
||||||
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
|
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
|
||||||
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
|
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||||
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
|
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
|
||||||
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
|
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
|
||||||
float item_space_ = 2.0; // Espacio entre los items en pantalla
|
float item_space_ = 2.0; // Espacio entre los items en pantalla
|
||||||
@@ -71,7 +73,7 @@ class Instructions {
|
|||||||
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
|
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void update(); // Actualiza las variables
|
void update(float delta_time); // Actualiza las variables
|
||||||
void render(); // Pinta en pantalla
|
void render(); // Pinta en pantalla
|
||||||
static void checkEvents(); // Comprueba los eventos
|
static void checkEvents(); // Comprueba los eventos
|
||||||
static void checkInput(); // Comprueba las entradas
|
static void checkInput(); // Comprueba las entradas
|
||||||
@@ -80,7 +82,8 @@ class Instructions {
|
|||||||
void iniSprites(); // Inicializa los sprites de los items
|
void iniSprites(); // Inicializa los sprites de los items
|
||||||
void updateSprites(); // Actualiza los sprites
|
void updateSprites(); // Actualiza los sprites
|
||||||
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
|
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
|
||||||
static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas
|
static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas (ya usa tiempo real)
|
||||||
static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
|
static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
|
||||||
void updateBackbuffer(); // Gestiona la textura con los gráficos
|
void updateBackbuffer(); // Gestiona la textura con los gráficos
|
||||||
|
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
|
||||||
};
|
};
|
||||||
@@ -207,24 +207,21 @@ void Intro::switchText(int from_index, int to_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables del objeto
|
// Actualiza las variables del objeto
|
||||||
void Intro::update() {
|
void Intro::update(float delta_time) {
|
||||||
if (SDL_GetTicks() - ticks_ > param.game.speed) {
|
Screen::get()->update(); // Actualiza el objeto screen
|
||||||
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
|
|
||||||
Screen::get()->update(); // Actualiza el objeto screen
|
|
||||||
|
|
||||||
tiled_bg_->update(); // Actualiza el fondo
|
tiled_bg_->update(delta_time); // Actualiza el fondo
|
||||||
|
|
||||||
switch (state_) {
|
switch (state_) {
|
||||||
case State::SCENES:
|
case State::SCENES:
|
||||||
updateSprites();
|
updateSprites(delta_time);
|
||||||
updateTexts();
|
updateTexts(delta_time);
|
||||||
updateScenes();
|
updateScenes();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State::POST:
|
case State::POST:
|
||||||
updatePostState();
|
updatePostState();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio::update();
|
Audio::update();
|
||||||
@@ -253,12 +250,24 @@ void Intro::render() {
|
|||||||
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
|
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula el tiempo transcurrido desde el último frame
|
||||||
|
float Intro::calculateDeltaTime() {
|
||||||
|
const Uint64 current_time = SDL_GetTicks();
|
||||||
|
const float delta_time = static_cast<float>(current_time - last_time_);
|
||||||
|
last_time_ = current_time;
|
||||||
|
return delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
// Bucle principal
|
// Bucle principal
|
||||||
void Intro::run() {
|
void Intro::run() {
|
||||||
|
last_time_ = SDL_GetTicks();
|
||||||
Audio::get()->playMusic("intro.ogg", 0);
|
Audio::get()->playMusic("intro.ogg", 0);
|
||||||
|
|
||||||
while (Section::name == Section::Name::INTRO) {
|
while (Section::name == Section::Name::INTRO) {
|
||||||
|
const float delta_time = calculateDeltaTime();
|
||||||
|
|
||||||
checkInput();
|
checkInput();
|
||||||
update();
|
update(delta_time);
|
||||||
checkEvents(); // Tiene que ir antes del render
|
checkEvents(); // Tiene que ir antes del render
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
@@ -444,20 +453,20 @@ void Intro::initTexts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los sprites
|
// Actualiza los sprites
|
||||||
void Intro::updateSprites() {
|
void Intro::updateSprites(float delta_time) {
|
||||||
for (auto &sprite : card_sprites_) {
|
for (auto &sprite : card_sprites_) {
|
||||||
sprite->update();
|
sprite->update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &sprite : shadow_sprites_) {
|
for (auto &sprite : shadow_sprites_) {
|
||||||
sprite->update();
|
sprite->update(delta_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los textos
|
// Actualiza los textos
|
||||||
void Intro::updateTexts() {
|
void Intro::updateTexts(float delta_time) {
|
||||||
for (auto &text : texts_) {
|
for (auto &text : texts_) {
|
||||||
text->update();
|
text->update(delta_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Intro {
|
|||||||
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
|
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
|
||||||
|
|
||||||
// --- Variables ---
|
// --- Variables ---
|
||||||
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
|
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||||
int scene_ = 0; // Indica qué escena está activa
|
int scene_ = 0; // Indica qué escena está activa
|
||||||
State state_ = State::SCENES; // Estado principal de la intro
|
State state_ = State::SCENES; // Estado principal de la intro
|
||||||
PostState post_state_ = PostState::STOP_BG; // Estado POST
|
PostState post_state_ = PostState::STOP_BG; // Estado POST
|
||||||
@@ -50,19 +50,20 @@ class Intro {
|
|||||||
Color bg_color_ = param.intro.bg_color; // Color de fondo
|
Color bg_color_ = param.intro.bg_color; // Color de fondo
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void update(); // Actualiza las variables del objeto
|
void update(float delta_time); // Actualiza las variables del objeto
|
||||||
void render(); // Dibuja el objeto en pantalla
|
void render(); // Dibuja el objeto en pantalla
|
||||||
static void checkInput(); // Comprueba las entradas
|
static void checkInput(); // Comprueba las entradas
|
||||||
static void checkEvents(); // Comprueba los eventos
|
static void checkEvents(); // Comprueba los eventos
|
||||||
void updateScenes(); // Actualiza las escenas de la intro
|
void updateScenes(); // Actualiza las escenas de la intro
|
||||||
void initSprites(); // Inicializa las imágenes
|
void initSprites(); // Inicializa las imágenes
|
||||||
void initTexts(); // Inicializa los textos
|
void initTexts(); // Inicializa los textos
|
||||||
void updateSprites(); // Actualiza los sprites
|
void updateSprites(float delta_time); // Actualiza los sprites
|
||||||
void updateTexts(); // Actualiza los textos
|
void updateTexts(float delta_time); // Actualiza los textos
|
||||||
void renderSprites(); // Dibuja los sprites
|
void renderSprites(); // Dibuja los sprites
|
||||||
void renderTexts(); // Dibuja los textos
|
void renderTexts(); // Dibuja los textos
|
||||||
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
|
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
|
||||||
void updatePostState(); // Actualiza el estado POST
|
void updatePostState(); // Actualiza el estado POST
|
||||||
|
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
|
||||||
|
|
||||||
// --- Métodos para manejar cada escena individualmente ---
|
// --- Métodos para manejar cada escena individualmente ---
|
||||||
void updateScene0();
|
void updateScene0();
|
||||||
|
|||||||
@@ -79,21 +79,23 @@ void Logo::checkInput() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gestiona el logo de JAILGAMES
|
// Gestiona el logo de JAILGAMES
|
||||||
void Logo::updateJAILGAMES() {
|
void Logo::updateJAILGAMES(float delta_time) {
|
||||||
if (counter_ == 30) {
|
if (counter_ == 30) {
|
||||||
Audio::get()->playSound("logo.wav");
|
Audio::get()->playSound("logo.wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (counter_ > 30) {
|
if (counter_ > 30) {
|
||||||
|
const float pixels_to_move = SPEED * delta_time;
|
||||||
|
|
||||||
for (int i = 0; i < (int)jail_sprite_.size(); ++i) {
|
for (int i = 0; i < (int)jail_sprite_.size(); ++i) {
|
||||||
if (jail_sprite_[i]->getX() != dest_.x) {
|
if (jail_sprite_[i]->getX() != dest_.x) {
|
||||||
if (i % 2 == 0) {
|
if (i % 2 == 0) {
|
||||||
jail_sprite_[i]->incX(-SPEED);
|
jail_sprite_[i]->incX(-pixels_to_move);
|
||||||
if (jail_sprite_[i]->getX() < dest_.x) {
|
if (jail_sprite_[i]->getX() < dest_.x) {
|
||||||
jail_sprite_[i]->setX(dest_.x);
|
jail_sprite_[i]->setX(dest_.x);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
jail_sprite_[i]->incX(SPEED);
|
jail_sprite_[i]->incX(pixels_to_move);
|
||||||
if (jail_sprite_[i]->getX() > dest_.x) {
|
if (jail_sprite_[i]->getX() > dest_.x) {
|
||||||
jail_sprite_[i]->setX(dest_.x);
|
jail_sprite_[i]->setX(dest_.x);
|
||||||
}
|
}
|
||||||
@@ -129,16 +131,21 @@ void Logo::updateTextureColors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza las variables
|
// Actualiza las variables
|
||||||
void Logo::update() {
|
void Logo::update(float delta_time) {
|
||||||
if (SDL_GetTicks() - ticks_ > param.game.speed) {
|
static float logic_accumulator = 0.0f;
|
||||||
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
|
logic_accumulator += delta_time;
|
||||||
Screen::get()->update(); // Actualiza el objeto screen
|
|
||||||
|
|
||||||
updateJAILGAMES(); // Actualiza el logo de JAILGAMES
|
// Ejecutar lógica a 60 FPS (cada 16.67ms) para mantener consistencia en counter_ y colores
|
||||||
|
constexpr float LOGIC_FRAME_TIME = 1000.0f / 60.0f;
|
||||||
|
|
||||||
|
if (logic_accumulator >= LOGIC_FRAME_TIME) {
|
||||||
|
Screen::get()->update(); // Actualiza el objeto screen
|
||||||
updateTextureColors(); // Actualiza los colores de las texturas
|
updateTextureColors(); // Actualiza los colores de las texturas
|
||||||
++counter_; // Gestiona el contador
|
++counter_; // Gestiona el contador
|
||||||
|
logic_accumulator -= LOGIC_FRAME_TIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateJAILGAMES(delta_time); // Actualiza el logo de JAILGAMES con delta-time real
|
||||||
Audio::update();
|
Audio::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,11 +161,23 @@ void Logo::render() {
|
|||||||
SCREEN->render();
|
SCREEN->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula el tiempo transcurrido desde el último frame
|
||||||
|
float Logo::calculateDeltaTime() {
|
||||||
|
const Uint64 current_time = SDL_GetTicks();
|
||||||
|
const float delta_time = static_cast<float>(current_time - last_time_);
|
||||||
|
last_time_ = current_time;
|
||||||
|
return delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
// Bucle para el logo del juego
|
// Bucle para el logo del juego
|
||||||
void Logo::run() {
|
void Logo::run() {
|
||||||
|
last_time_ = SDL_GetTicks();
|
||||||
|
|
||||||
while (Section::name == Section::Name::LOGO) {
|
while (Section::name == Section::Name::LOGO) {
|
||||||
|
const float delta_time = calculateDeltaTime();
|
||||||
|
|
||||||
checkInput();
|
checkInput();
|
||||||
update();
|
update(delta_time);
|
||||||
checkEvents(); // Tiene que ir antes del render
|
checkEvents(); // Tiene que ir antes del render
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Logo {
|
|||||||
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro
|
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro
|
||||||
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo
|
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo
|
||||||
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo
|
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo
|
||||||
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea
|
static constexpr float SPEED = 8.0f / 15.0f; // Velocidad de desplazamiento de cada línea (píxeles por ms)
|
||||||
|
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
|
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
|
||||||
@@ -42,15 +42,16 @@ class Logo {
|
|||||||
// --- Variables ---
|
// --- Variables ---
|
||||||
std::vector<Color> color_; // Vector con los colores para el fade
|
std::vector<Color> color_; // Vector con los colores para el fade
|
||||||
int counter_ = 0; // Contador
|
int counter_ = 0; // Contador
|
||||||
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
|
Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
|
||||||
SDL_FPoint dest_; // Posición donde dibujar el logo
|
SDL_FPoint dest_; // Posición donde dibujar el logo
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void update(); // Actualiza las variables
|
void update(float delta_time); // Actualiza las variables
|
||||||
void render(); // Dibuja en pantalla
|
void render(); // Dibuja en pantalla
|
||||||
static void checkEvents(); // Comprueba el manejador de eventos
|
static void checkEvents(); // Comprueba el manejador de eventos
|
||||||
static void checkInput(); // Comprueba las entradas
|
static void checkInput(); // Comprueba las entradas
|
||||||
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES
|
void updateJAILGAMES(float delta_time); // Gestiona el logo de JAILGAMES
|
||||||
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
|
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
|
||||||
void updateTextureColors(); // Gestiona el color de las texturas
|
void updateTextureColors(); // Gestiona el color de las texturas
|
||||||
|
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,7 +37,13 @@ class Texture;
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Title::Title()
|
Title::Title()
|
||||||
: text_(Resource::get()->getText("smb2_grad")), fade_(std::make_unique<Fade>()), tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)), game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)), mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), state_(TitleState::LOGO_ANIMATING), num_controllers_(Input::get()->getNumGamepads()) {
|
: text_(Resource::get()->getText("smb2_grad")),
|
||||||
|
fade_(std::make_unique<Fade>()),
|
||||||
|
tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)),
|
||||||
|
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
|
||||||
|
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
|
||||||
|
state_(TitleState::LOGO_ANIMATING),
|
||||||
|
num_controllers_(Input::get()->getNumGamepads()) {
|
||||||
// Configura objetos
|
// Configura objetos
|
||||||
tiled_bg_->setColor(param.title.bg_color);
|
tiled_bg_->setColor(param.title.bg_color);
|
||||||
game_logo_->enable();
|
game_logo_->enable();
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class Sprite {
|
|||||||
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
|
[[nodiscard]] auto getTexture() const -> std::shared_ptr<Texture> { return textures_.at(texture_index_); }
|
||||||
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
|
void setTexture(std::shared_ptr<Texture> texture) { textures_.at(texture_index_) = std::move(texture); }
|
||||||
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
|
void addTexture(const std::shared_ptr<Texture>& texture) { textures_.push_back(texture); }
|
||||||
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
|
auto setActiveTexture(size_t index) -> bool; // Cambia la textura activa por índice
|
||||||
[[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
|
[[nodiscard]] auto getActiveTexture() const -> size_t { return texture_index_; } // Alias para getActiveTextureIndex
|
||||||
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
|
[[nodiscard]] auto getTextureCount() const -> size_t { return textures_.size(); } // Obtiene el número total de texturas
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
|
|||||||
@@ -210,4 +210,14 @@ void Tabe::disable() {
|
|||||||
// Detiene/activa el timer
|
// Detiene/activa el timer
|
||||||
void Tabe::pauseTimer(bool value) {
|
void Tabe::pauseTimer(bool value) {
|
||||||
timer_.setPaused(value);
|
timer_.setPaused(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deshabilita el spawning permanentemente
|
||||||
|
void Tabe::disableSpawning() {
|
||||||
|
timer_.setSpawnDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Habilita el spawning nuevamente
|
||||||
|
void Tabe::enableSpawning() {
|
||||||
|
timer_.setSpawnDisabled(false);
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ class Tabe {
|
|||||||
void setState(State state); // Establece el estado
|
void setState(State state); // Establece el estado
|
||||||
auto tryToGetBonus() -> bool; // Intenta obtener el bonus
|
auto tryToGetBonus() -> bool; // Intenta obtener el bonus
|
||||||
void pauseTimer(bool value); // Detiene/activa el timer
|
void pauseTimer(bool value); // Detiene/activa el timer
|
||||||
|
void disableSpawning(); // Deshabilita el spawning permanentemente
|
||||||
|
void enableSpawning(); // Habilita el spawning nuevamente
|
||||||
|
|
||||||
// --- Getters ---
|
// --- Getters ---
|
||||||
auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión
|
auto getCollider() -> SDL_FRect& { return sprite_->getRect(); } // Obtiene el área de colisión
|
||||||
@@ -54,7 +56,8 @@ class Tabe {
|
|||||||
Uint32 current_time; // Tiempo actual
|
Uint32 current_time; // Tiempo actual
|
||||||
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
|
Uint32 delta_time; // Diferencia de tiempo desde la última actualización
|
||||||
Uint32 last_time; // Tiempo de la última actualización
|
Uint32 last_time; // Tiempo de la última actualización
|
||||||
bool is_paused{false}; // Indica si el temporizador está pausado
|
bool is_paused{false}; // Indica si el temporizador está pausado (por pausa de juego)
|
||||||
|
bool spawn_disabled{false}; // Indica si el spawning está deshabilitado permanentemente
|
||||||
|
|
||||||
// Constructor - los parámetros min_time y max_time están en mintos
|
// Constructor - los parámetros min_time y max_time están en mintos
|
||||||
Timer(float min_time, float max_time)
|
Timer(float min_time, float max_time)
|
||||||
@@ -75,8 +78,8 @@ class Tabe {
|
|||||||
void update() {
|
void update() {
|
||||||
current_time = SDL_GetTicks();
|
current_time = SDL_GetTicks();
|
||||||
|
|
||||||
// Solo actualizar si no está pausado
|
// Solo actualizar si no está pausado (ni por juego ni por spawn deshabilitado)
|
||||||
if (!is_paused) {
|
if (!is_paused && !spawn_disabled) {
|
||||||
delta_time = current_time - last_time;
|
delta_time = current_time - last_time;
|
||||||
|
|
||||||
if (time_until_next_spawn > delta_time) {
|
if (time_until_next_spawn > delta_time) {
|
||||||
@@ -101,9 +104,20 @@ class Tabe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pausa o reanuda el spawning
|
||||||
|
void setSpawnDisabled(bool disabled) {
|
||||||
|
if (spawn_disabled != disabled) {
|
||||||
|
spawn_disabled = disabled;
|
||||||
|
// Al reactivar, actualizar last_time para evitar saltos
|
||||||
|
if (!disabled) {
|
||||||
|
last_time = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Indica si el temporizador ha finalizado
|
// Indica si el temporizador ha finalizado
|
||||||
[[nodiscard]] auto shouldSpawn() const -> bool {
|
[[nodiscard]] auto shouldSpawn() const -> bool {
|
||||||
return time_until_next_spawn == 0 && !is_paused;
|
return time_until_next_spawn == 0 && !is_paused && !spawn_disabled;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,16 @@
|
|||||||
|
|
||||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, endl, ifstream
|
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, endl, ifstream
|
||||||
#include <iostream> // Para cerr
|
#include <iostream> // Para cerr
|
||||||
|
#include <sstream> // Para istringstream
|
||||||
#include <stdexcept> // Para runtime_error
|
#include <stdexcept> // Para runtime_error
|
||||||
#include <string_view> // Para string_view
|
#include <string_view> // Para string_view
|
||||||
|
|
||||||
#include "color.h" // Para Color
|
#include "color.h" // Para Color
|
||||||
#include "screen.h" // Para Screen
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
#include "sprite.h" // Para Sprite
|
#include "screen.h" // Para Screen
|
||||||
#include "texture.h" // Para Texture
|
#include "sprite.h" // Para Sprite
|
||||||
#include "utils.h" // Para getFileName, printWithDots
|
#include "texture.h" // Para Texture
|
||||||
|
#include "utils.h" // Para getFileName, printWithDots
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Text::Text(const std::shared_ptr<Texture> &texture, const std::string &text_file) {
|
Text::Text(const std::shared_ptr<Texture> &texture, const std::string &text_file) {
|
||||||
@@ -155,7 +157,7 @@ auto Text::writeToTexture(const std::string &text, int zoom, int kerning, int le
|
|||||||
auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color text_color, Uint8 shadow_distance, Color shadow_color, int length) -> std::shared_ptr<Texture> {
|
auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color text_color, Uint8 shadow_distance, Color shadow_color, int length) -> std::shared_ptr<Texture> {
|
||||||
auto *renderer = Screen::get()->getRenderer();
|
auto *renderer = Screen::get()->getRenderer();
|
||||||
auto texture = std::make_shared<Texture>(renderer);
|
auto texture = std::make_shared<Texture>(renderer);
|
||||||
|
|
||||||
// Calcula las dimensiones considerando los efectos
|
// Calcula las dimensiones considerando los efectos
|
||||||
auto base_width = Text::length(text, kerning);
|
auto base_width = Text::length(text, kerning);
|
||||||
auto base_height = box_height_;
|
auto base_height = box_height_;
|
||||||
@@ -163,10 +165,10 @@ auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, C
|
|||||||
auto height = base_height;
|
auto height = base_height;
|
||||||
auto offset_x = 0;
|
auto offset_x = 0;
|
||||||
auto offset_y = 0;
|
auto offset_y = 0;
|
||||||
|
|
||||||
const auto STROKED = ((flags & Text::STROKE) == Text::STROKE);
|
const auto STROKED = ((flags & Text::STROKE) == Text::STROKE);
|
||||||
const auto SHADOWED = ((flags & Text::SHADOW) == Text::SHADOW);
|
const auto SHADOWED = ((flags & Text::SHADOW) == Text::SHADOW);
|
||||||
|
|
||||||
if (STROKED) {
|
if (STROKED) {
|
||||||
// Para stroke, el texto se expande en todas las direcciones por shadow_distance
|
// Para stroke, el texto se expande en todas las direcciones por shadow_distance
|
||||||
width = base_width + (shadow_distance * 2);
|
width = base_width + (shadow_distance * 2);
|
||||||
@@ -178,7 +180,7 @@ auto Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, C
|
|||||||
width = base_width + shadow_distance;
|
width = base_width + shadow_distance;
|
||||||
height = base_height + shadow_distance;
|
height = base_height + shadow_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *temp = SDL_GetRenderTarget(renderer);
|
auto *temp = SDL_GetRenderTarget(renderer);
|
||||||
|
|
||||||
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
@@ -198,18 +200,18 @@ void Text::writeColored(int x, int y, const std::string &text, Color color, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escribe el texto con colores usando un sprite específico
|
// Escribe el texto con colores usando un sprite específico
|
||||||
void Text::writeColoredWithSprite(Sprite* sprite, int x, int y, const std::string &text, Color color, int kerning, int length) {
|
void Text::writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning, int length) {
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
const std::string_view VISIBLE_TEXT = (length == -1) ? std::string_view(text) : std::string_view(text).substr(0, length);
|
const std::string_view VISIBLE_TEXT = (length == -1) ? std::string_view(text) : std::string_view(text).substr(0, length);
|
||||||
|
|
||||||
auto *texture = sprite->getTexture().get();
|
auto *texture = sprite->getTexture().get();
|
||||||
|
|
||||||
// Guarda el alpha original y aplica el nuevo
|
// Guarda el alpha original y aplica el nuevo
|
||||||
Uint8 original_alpha;
|
Uint8 original_alpha;
|
||||||
SDL_GetTextureAlphaMod(texture->getSDLTexture(), &original_alpha);
|
SDL_GetTextureAlphaMod(texture->getSDLTexture(), &original_alpha);
|
||||||
texture->setAlpha(color.a);
|
texture->setAlpha(color.a);
|
||||||
texture->setColor(color.r, color.g, color.b);
|
texture->setColor(color.r, color.g, color.b);
|
||||||
|
|
||||||
sprite->setY(y);
|
sprite->setY(y);
|
||||||
for (const auto CH : VISIBLE_TEXT) {
|
for (const auto CH : VISIBLE_TEXT) {
|
||||||
const auto INDEX = static_cast<unsigned char>(CH);
|
const auto INDEX = static_cast<unsigned char>(CH);
|
||||||
@@ -221,7 +223,7 @@ void Text::writeColoredWithSprite(Sprite* sprite, int x, int y, const std::strin
|
|||||||
shift += offset_[INDEX].w + kerning;
|
shift += offset_[INDEX].w + kerning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restaura los valores originales
|
// Restaura los valores originales
|
||||||
texture->setColor(255, 255, 255);
|
texture->setColor(255, 255, 255);
|
||||||
texture->setAlpha(255);
|
texture->setAlpha(255);
|
||||||
@@ -231,26 +233,26 @@ void Text::writeColoredWithSprite(Sprite* sprite, int x, int y, const std::strin
|
|||||||
void Text::writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length) {
|
void Text::writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length) {
|
||||||
auto *renderer = Screen::get()->getRenderer();
|
auto *renderer = Screen::get()->getRenderer();
|
||||||
auto *original_target = SDL_GetRenderTarget(renderer);
|
auto *original_target = SDL_GetRenderTarget(renderer);
|
||||||
|
|
||||||
// Calcula dimensiones de la textura temporal
|
// Calcula dimensiones de la textura temporal
|
||||||
auto text_width = Text::length(text, kerning);
|
auto text_width = Text::length(text, kerning);
|
||||||
auto text_height = box_height_;
|
auto text_height = box_height_;
|
||||||
auto temp_width = text_width + (shadow_distance * 2);
|
auto temp_width = text_width + (shadow_distance * 2);
|
||||||
auto temp_height = text_height + (shadow_distance * 2);
|
auto temp_height = text_height + (shadow_distance * 2);
|
||||||
|
|
||||||
// Crea textura temporal
|
// Crea textura temporal
|
||||||
auto temp_texture = std::make_shared<Texture>(renderer);
|
auto temp_texture = std::make_shared<Texture>(renderer);
|
||||||
temp_texture->createBlank(temp_width, temp_height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
temp_texture->createBlank(temp_width, temp_height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET);
|
||||||
temp_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
temp_texture->setBlendMode(SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
// Renderiza el stroke en la textura temporal
|
// Renderiza el stroke en la textura temporal
|
||||||
temp_texture->setAsRenderTarget(renderer);
|
temp_texture->setAsRenderTarget(renderer);
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
|
||||||
// Selecciona el sprite apropiado para el stroke
|
// Selecciona el sprite apropiado para el stroke
|
||||||
auto *stroke_sprite = white_sprite_ ? white_sprite_.get() : sprite_.get();
|
auto *stroke_sprite = white_sprite_ ? white_sprite_.get() : sprite_.get();
|
||||||
|
|
||||||
// Renderiza stroke sin alpha (sólido) en textura temporal
|
// Renderiza stroke sin alpha (sólido) en textura temporal
|
||||||
Color solid_color = Color(stroke_color.r, stroke_color.g, stroke_color.b, 255);
|
Color solid_color = Color(stroke_color.r, stroke_color.g, stroke_color.b, 255);
|
||||||
for (int dist = 1; dist <= shadow_distance; ++dist) {
|
for (int dist = 1; dist <= shadow_distance; ++dist) {
|
||||||
@@ -260,10 +262,10 @@ void Text::writeStrokeWithAlpha(int x, int y, const std::string &text, int kerni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restaura render target original
|
// Restaura render target original
|
||||||
SDL_SetRenderTarget(renderer, original_target);
|
SDL_SetRenderTarget(renderer, original_target);
|
||||||
|
|
||||||
// Renderiza la textura temporal con el alpha deseado
|
// Renderiza la textura temporal con el alpha deseado
|
||||||
temp_texture->setAlpha(stroke_color.a);
|
temp_texture->setAlpha(stroke_color.a);
|
||||||
temp_texture->render(x - shadow_distance, y - shadow_distance);
|
temp_texture->render(x - shadow_distance, y - shadow_distance);
|
||||||
@@ -372,25 +374,41 @@ auto Text::loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>
|
|||||||
tf->box_height = 0;
|
tf->box_height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abre el fichero para leer los valores
|
// Intenta cargar desde ResourceHelper primero
|
||||||
std::ifstream file(file_path);
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
std::istringstream stream;
|
||||||
|
bool using_resource_data = false;
|
||||||
|
|
||||||
if (file.is_open() && file.good()) {
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
|
||||||
|
|
||||||
|
if ((using_resource_data && stream.good()) || (!using_resource_data && file.is_open() && file.good())) {
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
|
|
||||||
// Lee los dos primeros valores del fichero
|
// Lee los dos primeros valores del fichero
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
tf->box_width = std::stoi(buffer);
|
tf->box_width = std::stoi(buffer);
|
||||||
|
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
std::getline(file, buffer);
|
std::getline(input_stream, buffer);
|
||||||
tf->box_height = std::stoi(buffer);
|
tf->box_height = std::stoi(buffer);
|
||||||
|
|
||||||
// lee el resto de datos del fichero
|
// lee el resto de datos del fichero
|
||||||
auto index = 32;
|
auto index = 32;
|
||||||
auto line_read = 0;
|
auto line_read = 0;
|
||||||
while (std::getline(file, buffer)) {
|
while (std::getline(input_stream, buffer)) {
|
||||||
// Almacena solo las lineas impares
|
// Almacena solo las lineas impares
|
||||||
if (line_read % 2 == 1) {
|
if (line_read % 2 == 1) {
|
||||||
tf->offset[index++].w = std::stoi(buffer);
|
tf->offset[index++].w = std::stoi(buffer);
|
||||||
@@ -401,9 +419,11 @@ auto Text::loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>
|
|||||||
line_read++;
|
line_read++;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cierra el fichero
|
// Cierra el fichero si se usó
|
||||||
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]");
|
||||||
file.close();
|
if (!using_resource_data && file.is_open()) {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// El fichero no se puede abrir
|
// El fichero no se puede abrir
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class Text {
|
|||||||
static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero
|
static auto loadFile(const std::string &file_path) -> std::shared_ptr<Text::File>; // Llena una estructura Text::File desde un fichero
|
||||||
|
|
||||||
// --- Métodos privados ---
|
// --- Métodos privados ---
|
||||||
void writeColoredWithSprite(Sprite* sprite, int x, int y, const std::string &text, Color color, int kerning = 1, int length = -1); // Escribe con un sprite específico
|
void writeColoredWithSprite(Sprite *sprite, int x, int y, const std::string &text, Color color, int kerning = 1, int length = -1); // Escribe con un sprite específico
|
||||||
void writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1); // Escribe stroke con alpha correcto
|
void writeStrokeWithAlpha(int x, int y, const std::string &text, int kerning, Color stroke_color, Uint8 shadow_distance, int length = -1); // Escribe stroke con alpha correcto
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -12,9 +12,10 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "color.h" // Para getFileName, Color, printWithDots
|
#include "color.h" // Para getFileName, Color, printWithDots
|
||||||
#include "external/gif.h" // Para Gif
|
#include "external/gif.h" // Para Gif
|
||||||
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
#include "resource_helper.h" // Para ResourceHelper
|
||||||
|
#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
@@ -64,7 +65,19 @@ auto Texture::loadFromFile(const std::string &file_path) -> bool {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int orig_format;
|
int orig_format;
|
||||||
unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
unsigned char *data = nullptr;
|
||||||
|
|
||||||
|
// Intentar cargar desde ResourceHelper primero
|
||||||
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
data = stbi_load_from_memory(resource_data.data(), resource_data.size(), &width, &height, &orig_format, req_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
if (data == nullptr) {
|
||||||
|
data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
|
||||||
|
}
|
||||||
|
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str());
|
||||||
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
|
||||||
@@ -212,22 +225,30 @@ auto Texture::loadSurface(const std::string &file_path) -> std::shared_ptr<Surfa
|
|||||||
// Libera la superficie actual
|
// Libera la superficie actual
|
||||||
unloadSurface();
|
unloadSurface();
|
||||||
|
|
||||||
// Abrir el archivo usando std::ifstream para manejo automático del recurso
|
std::vector<Uint8> buffer;
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
|
||||||
if (!file) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtener el tamaño del archivo
|
// Intentar cargar desde ResourceHelper primero
|
||||||
std::streamsize size = file.tellg();
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
file.seekg(0, std::ios::beg);
|
if (!resource_data.empty()) {
|
||||||
|
buffer = resource_data;
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
|
||||||
// Leer el contenido del archivo en un buffer
|
// Obtener el tamaño del archivo
|
||||||
std::vector<Uint8> buffer(size);
|
std::streamsize size = file.tellg();
|
||||||
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
file.seekg(0, std::ios::beg);
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
|
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
// Leer el contenido del archivo en un buffer
|
||||||
|
buffer.resize(size);
|
||||||
|
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear un objeto Gif y llamar a la función loadGif
|
// Crear un objeto Gif y llamar a la función loadGif
|
||||||
@@ -283,24 +304,33 @@ void Texture::setPaletteColor(int palette, int index, Uint32 color) {
|
|||||||
auto Texture::loadPaletteFromFile(const std::string &file_path) -> Palette {
|
auto Texture::loadPaletteFromFile(const std::string &file_path) -> Palette {
|
||||||
Palette palette;
|
Palette palette;
|
||||||
|
|
||||||
// Abrir el archivo GIF
|
std::vector<Uint8> buffer;
|
||||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
|
||||||
if (!file) {
|
// Intentar cargar desde ResourceHelper primero
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
if (!resource_data.empty()) {
|
||||||
|
buffer = resource_data;
|
||||||
|
} else {
|
||||||
|
// Fallback a filesystem directo
|
||||||
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Fichero no encontrado: " + file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener el tamaño del archivo y leerlo en un buffer
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
buffer.resize(size);
|
||||||
|
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str());
|
||||||
|
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
|
printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]");
|
||||||
|
|
||||||
// Obtener el tamaño del archivo y leerlo en un buffer
|
|
||||||
std::streamsize size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
std::vector<Uint8> buffer(size);
|
|
||||||
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str());
|
|
||||||
throw std::runtime_error("Error al leer el fichero: " + file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
|
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
|
||||||
GIF::Gif gif;
|
GIF::Gif gif;
|
||||||
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
|
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
|
||||||
@@ -346,16 +376,33 @@ auto Texture::readPalFile(const std::string &file_path) -> Palette {
|
|||||||
Palette palette{};
|
Palette palette{};
|
||||||
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
|
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
|
||||||
|
|
||||||
std::ifstream file(file_path);
|
// Intentar cargar desde ResourceHelper primero
|
||||||
if (!file.is_open()) {
|
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||||
throw std::runtime_error("No se pudo abrir el archivo .pal");
|
std::istringstream stream;
|
||||||
|
bool using_resource_data = false;
|
||||||
|
|
||||||
|
if (!resource_data.empty()) {
|
||||||
|
std::string content(resource_data.begin(), resource_data.end());
|
||||||
|
stream.str(content);
|
||||||
|
using_resource_data = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback a archivo directo
|
||||||
|
std::ifstream file;
|
||||||
|
if (!using_resource_data) {
|
||||||
|
file.open(file_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("No se pudo abrir el archivo .pal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream &input_stream = using_resource_data ? stream : static_cast<std::istream &>(file);
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
int line_number = 0;
|
int line_number = 0;
|
||||||
int color_index = 0;
|
int color_index = 0;
|
||||||
|
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(input_stream, line)) {
|
||||||
++line_number;
|
++line_number;
|
||||||
|
|
||||||
// Ignorar las tres primeras líneas del archivo
|
// Ignorar las tres primeras líneas del archivo
|
||||||
@@ -380,6 +427,8 @@ auto Texture::readPalFile(const std::string &file_path) -> Palette {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
if (!using_resource_data && file.is_open()) {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,9 @@ struct Surface {
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays)
|
Surface(Uint16 width, Uint16 height, std::shared_ptr<Uint8[]> pixels) // NOLINT(modernize-avoid-c-arrays)
|
||||||
: data(std::move(pixels)), w(width), h(height) {}
|
: data(std::move(pixels)),
|
||||||
|
w(width),
|
||||||
|
h(height) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clase Texture: gestiona texturas, paletas y renderizado
|
// Clase Texture: gestiona texturas, paletas y renderizado
|
||||||
|
|||||||
@@ -81,9 +81,15 @@ void TiledBG::render() {
|
|||||||
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
|
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza la lógica de la clase
|
// Actualiza la lógica de la clase (compatibilidad)
|
||||||
void TiledBG::update() {
|
void TiledBG::update() {
|
||||||
updateDesp();
|
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
|
||||||
|
update(FRAME_TIME_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualiza la lógica de la clase
|
||||||
|
void TiledBG::update(float delta_time) {
|
||||||
|
updateDesp(delta_time);
|
||||||
updateStop();
|
updateStop();
|
||||||
|
|
||||||
switch (mode_) {
|
switch (mode_) {
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ class TiledBG {
|
|||||||
~TiledBG();
|
~TiledBG();
|
||||||
|
|
||||||
// --- Métodos principales ---
|
// --- Métodos principales ---
|
||||||
void render(); // Pinta la clase en pantalla
|
void render(); // Pinta la clase en pantalla
|
||||||
void update(); // Actualiza la lógica de la clase
|
void update(); // Actualiza la lógica de la clase (compatibilidad)
|
||||||
|
void update(float delta_time); // Actualiza la lógica de la clase
|
||||||
|
|
||||||
// --- Configuración ---
|
// --- Configuración ---
|
||||||
void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad
|
void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad
|
||||||
@@ -54,7 +55,8 @@ class TiledBG {
|
|||||||
bool stopping_ = false; // Indica si se está deteniendo
|
bool stopping_ = false; // Indica si se está deteniendo
|
||||||
|
|
||||||
// --- Métodos internos ---
|
// --- Métodos internos ---
|
||||||
void fillTexture(); // Rellena la textura con el contenido
|
void fillTexture(); // Rellena la textura con el contenido
|
||||||
void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento
|
void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento (compatibilidad)
|
||||||
void updateStop(); // Detiene el desplazamiento de forma ordenada
|
void updateDesp(float delta_time) { desp_ += speed_ * delta_time / (1000.0f / 60.0f); } // Actualiza el desplazamiento
|
||||||
|
void updateStop(); // Detiene el desplazamiento de forma ordenada
|
||||||
};
|
};
|
||||||
@@ -23,7 +23,9 @@ class MenuOption {
|
|||||||
|
|
||||||
// --- Constructor y destructor ---
|
// --- Constructor y destructor ---
|
||||||
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
|
MenuOption(std::string caption, ServiceMenu::SettingsGroup group, bool hidden = false)
|
||||||
: caption_(std::move(caption)), group_(group), hidden_(hidden) {}
|
: caption_(std::move(caption)),
|
||||||
|
group_(group),
|
||||||
|
hidden_(hidden) {}
|
||||||
virtual ~MenuOption() = default;
|
virtual ~MenuOption() = default;
|
||||||
|
|
||||||
// --- Getters ---
|
// --- Getters ---
|
||||||
@@ -52,7 +54,8 @@ class MenuOption {
|
|||||||
class BoolOption : public MenuOption {
|
class BoolOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var)
|
BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var)
|
||||||
: MenuOption(cap, grp), linked_variable_(var) {}
|
: MenuOption(cap, grp),
|
||||||
|
linked_variable_(var) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
||||||
[[nodiscard]] auto getValueAsString() const -> std::string override {
|
[[nodiscard]] auto getValueAsString() const -> std::string override {
|
||||||
@@ -74,7 +77,11 @@ class BoolOption : public MenuOption {
|
|||||||
class IntOption : public MenuOption {
|
class IntOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step)
|
IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step)
|
||||||
: MenuOption(cap, grp), linked_variable_(var), min_value_(min), max_value_(max), step_value_(step) {}
|
: MenuOption(cap, grp),
|
||||||
|
linked_variable_(var),
|
||||||
|
min_value_(min),
|
||||||
|
max_value_(max),
|
||||||
|
step_value_(step) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::ADJUST; }
|
||||||
[[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); }
|
[[nodiscard]] auto getValueAsString() const -> std::string override { return std::to_string(*linked_variable_); }
|
||||||
@@ -150,7 +157,8 @@ class ListOption : public MenuOption {
|
|||||||
class FolderOption : public MenuOption {
|
class FolderOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target)
|
FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target)
|
||||||
: MenuOption(cap, grp), target_group_(target) {}
|
: MenuOption(cap, grp),
|
||||||
|
target_group_(target) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
||||||
[[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; }
|
[[nodiscard]] auto getTargetGroup() const -> ServiceMenu::SettingsGroup override { return target_group_; }
|
||||||
@@ -162,7 +170,8 @@ class FolderOption : public MenuOption {
|
|||||||
class ActionOption : public MenuOption {
|
class ActionOption : public MenuOption {
|
||||||
public:
|
public:
|
||||||
ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false)
|
ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false)
|
||||||
: MenuOption(cap, grp, hidden), action_(std::move(action)) {}
|
: MenuOption(cap, grp, hidden),
|
||||||
|
action_(std::move(action)) {}
|
||||||
|
|
||||||
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
[[nodiscard]] auto getBehavior() const -> Behavior override { return Behavior::SELECT; }
|
||||||
void executeAction() override {
|
void executeAction() override {
|
||||||
@@ -183,7 +192,11 @@ class ActionListOption : public MenuOption {
|
|||||||
using ActionExecutor = std::function<void()>;
|
using ActionExecutor = std::function<void()>;
|
||||||
|
|
||||||
ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false)
|
ActionListOption(const std::string &caption, ServiceMenu::SettingsGroup group, std::vector<std::string> options, ValueGetter getter, ValueSetter setter, ActionExecutor action_executor, bool hidden = false)
|
||||||
: MenuOption(caption, group, hidden), options_(std::move(options)), value_getter_(std::move(getter)), value_setter_(std::move(setter)), action_executor_(std::move(action_executor)) {
|
: MenuOption(caption, group, hidden),
|
||||||
|
options_(std::move(options)),
|
||||||
|
value_getter_(std::move(getter)),
|
||||||
|
value_setter_(std::move(setter)),
|
||||||
|
action_executor_(std::move(action_executor)) {
|
||||||
updateCurrentIndex();
|
updateCurrentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ void MenuRenderer::ShowHideAnimation::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
|
MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text)
|
||||||
: element_text_(std::move(element_text)), title_text_(std::move(title_text)) {
|
: element_text_(std::move(element_text)),
|
||||||
|
title_text_(std::move(title_text)) {
|
||||||
initializeMaxSizes();
|
initializeMaxSizes();
|
||||||
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
|
setPosition(param.game.game_area.center_x, param.game.game_area.center_y, PositionMode::CENTERED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ class Notifier {
|
|||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
explicit Notification()
|
explicit Notification()
|
||||||
: texture(nullptr), sprite(nullptr), rect{0, 0, 0, 0} {}
|
: texture(nullptr),
|
||||||
|
sprite(nullptr),
|
||||||
|
rect{0, 0, 0, 0} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Objetos y punteros ---
|
// --- Objetos y punteros ---
|
||||||
|
|||||||