From bb38600aac24a267ad8d75400e6629d897cebaa4 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 11 Apr 2026 16:25:56 +0200 Subject: [PATCH] linters --- .clang-tidy | 15 +- CLAUDE.md | 85 +++------- CMakeLists.txt | 30 ++++ source/core/audio/audio.hpp | 5 +- source/core/input/input.cpp | 3 +- source/core/input/input_types.hpp | 3 +- source/core/rendering/gif.cpp | 4 +- source/core/rendering/palette_manager.cpp | 2 +- source/core/rendering/palette_manager.hpp | 3 +- source/core/rendering/pixel_reveal.hpp | 3 +- source/core/rendering/render_info.hpp | 4 +- source/core/rendering/screen.cpp | 24 +-- source/core/rendering/screen.hpp | 3 +- .../core/rendering/sdl3gpu/sdl3gpu_shader.cpp | 2 +- source/core/rendering/shader_backend.hpp | 3 +- .../core/rendering/sprite/animated_sprite.cpp | 2 +- .../core/rendering/sprite/dissolve_sprite.hpp | 5 +- source/core/rendering/surface.cpp | 30 ++-- source/core/rendering/surface.hpp | 4 +- source/core/rendering/text.cpp | 13 +- source/core/resources/resource_cache.cpp | 5 +- source/core/resources/resource_cache.hpp | 3 +- source/core/resources/resource_list.cpp | 2 +- source/core/resources/resource_list.hpp | 2 +- source/core/system/debug.cpp | 8 +- source/game/editor/editor_statusbar.cpp | 10 +- source/game/editor/editor_statusbar.hpp | 9 +- source/game/editor/map_editor.cpp | 148 +++++++++--------- source/game/editor/map_editor.hpp | 28 ++-- source/game/editor/mini_map.cpp | 2 +- source/game/editor/tile_picker.cpp | 43 ++--- source/game/editor/tile_picker.hpp | 1 + source/game/entities/door.hpp | 3 +- source/game/entities/moving_platform.cpp | 6 +- source/game/entities/moving_platform.hpp | 3 +- source/game/entities/player.cpp | 12 +- source/game/entities/player.hpp | 7 +- source/game/entities/solid_actor.hpp | 1 + source/game/gameplay/collision_map.cpp | 2 +- source/game/gameplay/room.hpp | 3 +- source/game/gameplay/room_format.cpp | 6 +- source/game/gameplay/scoreboard.cpp | 16 +- source/game/gameplay/scoreboard.hpp | 1 + source/game/gameplay/tile_collider.hpp | 3 +- source/game/gameplay/tilemap_renderer.cpp | 5 +- source/game/options.cpp | 60 ++++--- source/game/scene_manager.hpp | 6 +- source/game/scenes/game.cpp | 3 + source/game/scenes/game.hpp | 5 +- source/game/scenes/logo.hpp | 3 +- source/game/scenes/title.cpp | 20 +-- source/game/scenes/title.hpp | 6 +- source/game/ui/console.cpp | 18 ++- source/game/ui/console.hpp | 3 +- source/game/ui/console_commands.cpp | 13 +- source/game/ui/notifier.cpp | 2 - source/game/ui/notifier.hpp | 7 +- 57 files changed, 371 insertions(+), 347 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 897695a..115d2f3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,20 +2,12 @@ Checks: - readability-* - modernize-* - performance-* - - bugprone-unchecked-optional-access - - bugprone-sizeof-expression - - bugprone-suspicious-missing-comma - - bugprone-suspicious-index - - bugprone-undefined-memory-manipulation - - bugprone-use-after-move - - bugprone-out-of-bound-access + - bugprone-* - -readability-identifier-length - -readability-magic-numbers - - -bugprone-narrowing-conversions - - -performance-enum-size - - -performance-inefficient-string-concatenation - -bugprone-integer-division - -bugprone-easily-swappable-parameters + - -bugprone-narrowing-conversions - -modernize-avoid-c-arrays,-warnings-as-errors WarningsAsErrors: '*' @@ -25,6 +17,9 @@ HeaderFilterRegex: 'source/(?!core/audio/jail_audio\.hpp|core/rendering/sdl3gpu/ FormatStyle: file CheckOptions: + # bugprone-empty-catch: aceptar catches vacíos marcados con @INTENTIONAL en un comentario + - { key: bugprone-empty-catch.IgnoreCatchWithKeywords, value: '@INTENTIONAL' } + # Variables locales en snake_case - { key: readability-identifier-naming.VariableCase, value: lower_case } diff --git a/CLAUDE.md b/CLAUDE.md index 39a8922..681a083 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,82 +117,37 @@ xvfb-run -a -s "-screen 0 1280x720x24" ./projecte_2026 --- -## Static Analysis Tools (Linters) +## Static Analysis & Formatting (CMake targets) -This project uses two complementary static analysis tools for code quality: - -### clang-tidy (Modernization & Best Practices) - -**Purpose:** Detects style issues, modernization opportunities, and potential bugs. Best for refactoring and applying C++20 best practices. - -**IMPORTANT:** Always run on specific files, NOT on the entire project, to avoid long execution times and errors in unrelated files. +Los linters y el formateador están integrados como targets de CMake. Todos excluyen `source/external/` y los headers SPIR-V generados (`*_spv.h`). ```bash -# Analyze a specific file (RECOMMENDED) -tools/linter/run_clang-tidy.sh source/game/entities/player.cpp - -# Analyze multiple specific files -tools/linter/run_clang-tidy.sh source/game/entities/player.cpp source/game/entities/enemy.cpp - -# Apply fixes automatically to a specific file -tools/linter/run_clang-tidy.sh --fix source/game/entities/player.cpp - -# Analyze entire project (SLOW, may have errors in defaults.hpp) -tools/linter/run_clang-tidy.sh +# Desde el directorio build/ +cmake --build . --target tidy # clang-tidy sobre todo el código (sin fixes) +cmake --build . --target tidy-fix # clang-tidy aplicando fixes automáticos +cmake --build . --target cppcheck # cppcheck (warning/style/performance/portability, --std=c++20) +cmake --build . --target format # clang-format -i sobre todo el código +cmake --build . --target format-check # clang-format en modo dry-run (CI) ``` -**Known False Positives:** -- `defaults.hpp`: May report errors in constant definitions -- `readability-magic-numbers` / `cppcoreguidelines-avoid-magic-numbers`: Acceptable for game constants (block sizes, speeds, etc.) +Los targets se definen en [CMakeLists.txt](CMakeLists.txt) (sección *STATIC ANALYSIS TARGETS*) y se generan únicamente si las herramientas correspondientes (`clang-tidy`, `clang-format`, `cppcheck`) están instaladas en el sistema. -### cppcheck (Bug Detection & Memory Safety) +**Falsos positivos conocidos de clang-tidy:** +- `defaults.hpp`: puede reportar errores en definiciones de constantes. +- `readability-magic-numbers` / `cppcoreguidelines-avoid-magic-numbers`: aceptables para constantes de juego (tamaños de bloque, velocidades, etc). -**Purpose:** Detects bugs, memory leaks, undefined behavior, and style issues. Complementary to clang-tidy. +**Notas sobre cppcheck:** +- Avisos `unusedStructMember` son habituales en estructuras de datos accedidas vía serialización/yaml o por código `#ifdef _DEBUG`; revisar antes de borrar nada. +- El target usa la lista explícita de fuentes (excluyendo `source/external/` y `*_spv.h`), no `--project=compile_commands.json`. Si añades flags nuevos, actualiza el target en [CMakeLists.txt](CMakeLists.txt). +- Por defecto cppcheck solo evalúa 12 combinaciones de `#ifdef`. Para analizar todas, añadir `--force` (mucho más lento). + +**Para análisis puntual de un solo fichero**, llamar la herramienta directamente: ```bash -# Warning, style, and performance analysis (RECOMMENDED for daily use) -tools/linter/run_cppcheck.sh -w --path source/game/entities/player.cpp - -# Exhaustive analysis (slower, more thorough) -tools/linter/run_cppcheck.sh -a --path source/game/entities/ - -# Detect unused functions (whole project analysis) -tools/linter/run_cppcheck.sh -u - -# Analyze entire project with warning level -tools/linter/run_cppcheck.sh -w +clang-tidy -p build source/game/entities/player.cpp +cppcheck --enable=warning,style --std=c++20 -I source source/game/entities/player.cpp ``` -**Output:** Results are saved in `tools/linter/cppcheck-result-*.txt` - -**Known False Positives:** -- `unusedFunction`: May report false positives for callback functions or public API methods -- `passedByValue`: Acceptable for small types like SDL_FRect in game code -- `constParameter`: Not always applicable for API consistency - -### When to Use Each Tool - -| Scenario | Tool | Reason | -|----------|------|--------| -| **Daily refactoring** | clang-tidy | Fast, auto-fixable issues | -| **Before committing** | Both | Comprehensive quality check | -| **After major changes** | cppcheck -a | Deep bug analysis | -| **Hunting memory leaks** | cppcheck | Specialized detection | -| **Code modernization** | clang-tidy --fix | Automatic C++20 upgrades | - -### Integration with Development Workflow - -When refactoring code (especially with `/refactor-class` command): -1. Make structural changes -2. **Compile** to verify syntax -3. **Run clang-tidy** (without --fix) to identify issues -4. **Run cppcheck -w** to catch bugs -5. Review and fix legitimate issues -6. Apply automatic fixes if appropriate -7. Recompile and test - -**Note:** The `/refactor-class` command automatically runs both linters after compilation and provides intelligent analysis of results. - --- ## Important Code Consistency Rules diff --git a/CMakeLists.txt b/CMakeLists.txt index f649371..57118c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,7 @@ set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAK # Buscar herramientas de análisis estático find_program(CLANG_TIDY_EXE NAMES clang-tidy) find_program(CLANG_FORMAT_EXE NAMES clang-format) +find_program(CPPCHECK_EXE NAMES cppcheck) # Recopilar todos los archivos fuente para formateo file(GLOB_RECURSE ALL_SOURCE_FILES @@ -278,6 +279,13 @@ set(CLANG_TIDY_SOURCES ${ALL_SOURCE_FILES}) list(FILTER CLANG_TIDY_SOURCES EXCLUDE REGEX ".*jail_audio\\.hpp$") list(FILTER CLANG_TIDY_SOURCES EXCLUDE REGEX ".*_spv\\.h$") +# Para cppcheck, pasar solo .cpp (los headers se procesan transitivamente). +# Si pasamos .hpp como TUs independientes, cppcheck reporta falsos positivos de +# 'unusedStructMember' porque no hace análisis cross-TU y ve miembros de clase +# cuyo uso vive en un .cpp distinto. +set(CPPCHECK_SOURCES ${ALL_SOURCE_FILES}) +list(FILTER CPPCHECK_SOURCES INCLUDE REGEX ".*\\.cpp$") + # Targets de clang-tidy if(CLANG_TIDY_EXE) add_custom_target(tidy @@ -322,6 +330,28 @@ else() message(STATUS "clang-format no encontrado - targets 'format' y 'format-check' no disponibles") endif() +# Targets de cppcheck +if(CPPCHECK_EXE) + add_custom_target(cppcheck + COMMAND ${CPPCHECK_EXE} + --enable=warning,style,performance,portability + --std=c++20 + --language=c++ + --inline-suppr + --suppress=missingIncludeSystem + --suppress=toomanyconfigs + -D_DEBUG + -DLINUX_BUILD + --quiet + -I ${CMAKE_SOURCE_DIR}/source + ${CPPCHECK_SOURCES} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running cppcheck..." + ) +else() + message(STATUS "cppcheck no encontrado - target 'cppcheck' no disponible") +endif() + # --- 6. PACK RESOURCES TARGETS --- set(PACK_TOOL_SOURCES ${CMAKE_SOURCE_DIR}/tools/pack_resources/pack_resources.cpp diff --git a/source/core/audio/audio.hpp b/source/core/audio/audio.hpp index e29463f..bfc32c4 100644 --- a/source/core/audio/audio.hpp +++ b/source/core/audio/audio.hpp @@ -1,5 +1,6 @@ #pragma once +#include // Para int8_t, uint8_t #include // Para string #include // Para move @@ -7,13 +8,13 @@ class Audio { public: // --- Enums --- - enum class Group : int { + enum class Group : std::int8_t { ALL = -1, // Todos los grupos GAME = 0, // Sonidos del juego INTERFACE = 1 // Sonidos de la interfaz }; - enum class MusicState { + enum class MusicState : std::uint8_t { PLAYING, // Reproduciendo música PAUSED, // Música pausada STOPPED, // Música detenida diff --git a/source/core/input/input.cpp b/source/core/input/input.cpp index 49f46b5..4cb2b10 100644 --- a/source/core/input/input.cpp +++ b/source/core/input/input.cpp @@ -407,8 +407,9 @@ auto Input::handleEvent(const SDL_Event& event) -> std::string { // NOLINT(read return addGamepad(event.gdevice.which); case SDL_EVENT_GAMEPAD_REMOVED: return removeGamepad(event.gdevice.which); + default: + return {}; } - return {}; } auto Input::addGamepad(int device_index) -> std::string { // NOLINT(readability-convert-member-functions-to-static) diff --git a/source/core/input/input_types.hpp b/source/core/input/input_types.hpp index d1a315a..e056a17 100644 --- a/source/core/input/input_types.hpp +++ b/source/core/input/input_types.hpp @@ -2,11 +2,12 @@ #include +#include #include #include // --- Enums --- -enum class InputAction : int { // Acciones de entrada posibles en el juego +enum class InputAction : std::uint8_t { // Acciones de entrada posibles en el juego // Inputs de movimiento LEFT, RIGHT, diff --git a/source/core/rendering/gif.cpp b/source/core/rendering/gif.cpp index 654090e..5fb3bc1 100644 --- a/source/core/rendering/gif.cpp +++ b/source/core/rendering/gif.cpp @@ -224,8 +224,8 @@ namespace GIF { if ((screen_descriptor.fields & 0x80) != 0) { int global_color_table_size = 1 << ((screen_descriptor.fields & 0x07) + 1); global_color_table.resize(global_color_table_size); - std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); - buffer += 3 * global_color_table_size; + std::memcpy(global_color_table.data(), buffer, static_cast(3) * static_cast(global_color_table_size)); + buffer += static_cast(3) * global_color_table_size; } // Supongamos que 'buffer' es el puntero actual y TRAILER es 0x3B diff --git a/source/core/rendering/palette_manager.cpp b/source/core/rendering/palette_manager.cpp index 0778c42..64cd553 100644 --- a/source/core/rendering/palette_manager.cpp +++ b/source/core/rendering/palette_manager.cpp @@ -69,7 +69,7 @@ namespace { if (SZ == 0) { return palette; } // Matriz de coste NxM (ampliada a SZxSZ con ceros para hacerla cuadrada) - std::vector cost(SZ * SZ, 0); + std::vector cost(static_cast(SZ) * static_cast(SZ), 0); for (int i = 0; i < N; ++i) { for (int j = 0; j < M; ++j) { cost[(i * SZ) + j] = rgbDistanceSq(palette[i], reference[j]); diff --git a/source/core/rendering/palette_manager.hpp b/source/core/rendering/palette_manager.hpp index 1000324..74a23c4 100644 --- a/source/core/rendering/palette_manager.hpp +++ b/source/core/rendering/palette_manager.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,7 +15,7 @@ using Palette = std::array; class Surface; // Modo de ordenación de paletas -enum class PaletteSortMode : int { +enum class PaletteSortMode : std::uint8_t { ORIGINAL = 0, // Paleta tal cual viene del fichero OPTIMAL = 1, // Asignación óptima a la paleta por defecto (Hungarian algorithm) REFERENCE = 2, // Asignación greedy a la paleta por defecto diff --git a/source/core/rendering/pixel_reveal.hpp b/source/core/rendering/pixel_reveal.hpp index 557abc7..59eb16e 100644 --- a/source/core/rendering/pixel_reveal.hpp +++ b/source/core/rendering/pixel_reveal.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include // Para shared_ptr #include // Para vector @@ -10,7 +11,7 @@ class Surface; class PixelReveal { public: // Modo de revelado: aleatorio por fila o en orden de bisección (dithering ordenado 1D) - enum class RevealMode { RANDOM, + enum class RevealMode : std::uint8_t { RANDOM, ORDERED }; // Constructor diff --git a/source/core/rendering/render_info.hpp b/source/core/rendering/render_info.hpp index 569e4e9..2197d4b 100644 --- a/source/core/rendering/render_info.hpp +++ b/source/core/rendering/render_info.hpp @@ -1,5 +1,7 @@ #pragma once +#include + class RenderInfo { public: // Singleton @@ -20,7 +22,7 @@ class RenderInfo { static constexpr float SLIDE_SPEED = 120.0F; private: - enum class Status { HIDDEN, + enum class Status : std::uint8_t { HIDDEN, RISING, ACTIVE, VANISHING }; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index fd33f13..08ea88e 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -292,12 +292,12 @@ void Screen::adjustWindowSize() { // Reservamos memoria una sola vez. // Si el buffer es más pequeño que la superficie, crash asegurado. - border_pixel_buffer_.resize(static_cast(window_width_ * window_height_)); - game_pixel_buffer_.resize(static_cast(Options::game.width * Options::game.height)); + border_pixel_buffer_.resize(static_cast(window_width_) * static_cast(window_height_)); + game_pixel_buffer_.resize(static_cast(Options::game.width) * static_cast(Options::game.height)); // border_pixel_buffer_ es el buffer que se sube a la GPU (tamaño total ventana). if (Options::video.border.enabled) { - border_pixel_buffer_.resize(static_cast(window_width_ * window_height_)); + border_pixel_buffer_.resize(static_cast(window_width_) * static_cast(window_height_)); } // Lógica de centrado y redimensionado de ventana SDL @@ -397,15 +397,17 @@ void Screen::textureToRenderer() { // Rellena solo el marco con el color cacheado — sin lookups de paleta. // El área central (juego) se deja sin tocar; el overlay la sobreescribe igualmente. + const auto BORDER_W_SZ = static_cast(BORDER_W); + const auto GAME_W_SZ = static_cast(GAME_W); // Franjas superior e inferior (ancho completo) - std::fill_n(border_pixel_buffer_.data(), OFF_Y * BORDER_W, border_argb_color_); - std::fill_n(&border_pixel_buffer_[(OFF_Y + GAME_H) * BORDER_W], - (BORDER_H - OFF_Y - GAME_H) * BORDER_W, + std::fill_n(border_pixel_buffer_.data(), static_cast(OFF_Y) * BORDER_W_SZ, border_argb_color_); + std::fill_n(&border_pixel_buffer_[(static_cast(OFF_Y) + GAME_H) * BORDER_W_SZ], + (static_cast(BORDER_H - OFF_Y - GAME_H)) * BORDER_W_SZ, border_argb_color_); // Columnas laterales en las filas del área de juego for (int y = OFF_Y; y < OFF_Y + GAME_H; ++y) { - std::fill_n(&border_pixel_buffer_[y * BORDER_W], OFF_X, border_argb_color_); - std::fill_n(&border_pixel_buffer_[(y * BORDER_W) + OFF_X + GAME_W], + std::fill_n(&border_pixel_buffer_[static_cast(y) * BORDER_W_SZ], OFF_X, border_argb_color_); + std::fill_n(&border_pixel_buffer_[(static_cast(y) * BORDER_W_SZ) + OFF_X + GAME_W], BORDER_W - OFF_X - GAME_W, border_argb_color_); } @@ -413,9 +415,9 @@ void Screen::textureToRenderer() { // Overlay del juego sobre el centro del buffer (ambos paths) game_surface_->toARGBBuffer(game_pixel_buffer_.data()); for (int y = 0; y < GAME_H; ++y) { - const Uint32* src = &game_pixel_buffer_[y * GAME_W]; - Uint32* dst = &border_pixel_buffer_[((OFF_Y + y) * BORDER_W) + OFF_X]; - std::memcpy(dst, src, GAME_W * sizeof(Uint32)); + const Uint32* src = &game_pixel_buffer_[static_cast(y) * GAME_W_SZ]; + Uint32* dst = &border_pixel_buffer_[((static_cast(OFF_Y) + y) * BORDER_W_SZ) + OFF_X]; + std::memcpy(dst, src, GAME_W_SZ * sizeof(Uint32)); } shader_backend_->uploadPixels(border_pixel_buffer_.data(), BORDER_W, BORDER_H); diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index 61ff3b1..59e8c11 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -4,6 +4,7 @@ #include // Para Uint32 #include // Para size_t +#include // Para uint8_t #include // Para shared_ptr, __shared_ptr_access #include // Para string #include // Para std::pair @@ -18,7 +19,7 @@ class Text; class Screen { public: // Tipos de filtro - enum class Filter : Uint32 { + enum class Filter : std::uint8_t { NEAREST = 0, LINEAR = 1, }; diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp index 1131035..ab401ba 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp @@ -762,7 +762,7 @@ namespace Rendering { } // Copia directa — el upscale lo hace la GPU en el primer render pass - std::memcpy(mapped, pixels, static_cast(width * height * 4)); + std::memcpy(mapped, pixels, static_cast(width) * static_cast(height) * 4U); SDL_UnmapGPUTransferBuffer(device_, upload_buffer_); } diff --git a/source/core/rendering/shader_backend.hpp b/source/core/rendering/shader_backend.hpp index df5427b..def2c60 100644 --- a/source/core/rendering/shader_backend.hpp +++ b/source/core/rendering/shader_backend.hpp @@ -2,13 +2,14 @@ #include +#include #include #include namespace Rendering { /** @brief Identificador del shader de post-procesado activo */ - enum class ShaderType { POSTFX, + enum class ShaderType : std::uint8_t { POSTFX, CRTPI }; /** diff --git a/source/core/rendering/sprite/animated_sprite.cpp b/source/core/rendering/sprite/animated_sprite.cpp index 8269189..f27b702 100644 --- a/source/core/rendering/sprite/animated_sprite.cpp +++ b/source/core/rendering/sprite/animated_sprite.cpp @@ -61,7 +61,7 @@ static auto parseAnimations(const fkyaml::node& yaml, float frame_width, float f animation.speeds.push_back(s.get_value()); } } else { - float spd = speed_node.get_value(); + auto spd = speed_node.get_value(); if (spd > 0.0F) { animation.speeds.assign(animation.frames.size(), spd); } diff --git a/source/core/rendering/sprite/dissolve_sprite.hpp b/source/core/rendering/sprite/dissolve_sprite.hpp index 7292852..67c6320 100644 --- a/source/core/rendering/sprite/dissolve_sprite.hpp +++ b/source/core/rendering/sprite/dissolve_sprite.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include "core/rendering/sprite/animated_sprite.hpp" // Para SurfaceAnimatedSprite @@ -9,7 +10,7 @@ class Surface; // Direcció de la dissolució -enum class DissolveDirection { NONE, +enum class DissolveDirection : std::uint8_t { NONE, DOWN, UP }; @@ -41,7 +42,7 @@ class DissolveSprite : public AnimatedSprite { void setColorReplace(Uint8 source, Uint8 target); private: - enum class TransitionMode { NONE, + enum class TransitionMode : std::uint8_t { NONE, DISSOLVING, GENERATING }; diff --git a/source/core/rendering/surface.cpp b/source/core/rendering/surface.cpp index 8d8a7af..a20066e 100644 --- a/source/core/rendering/surface.cpp +++ b/source/core/rendering/surface.cpp @@ -177,10 +177,10 @@ void Surface::fillRect(const SDL_FRect* rect, Uint8 color) { // NOLINT(readabil // Rellenar fila a fila con memset (memoria contigua por fila) Uint8* data_ptr = surface_data_->data.get(); - const int SURF_WIDTH = static_cast(surface_data_->width); - const int ROW_WIDTH = static_cast(x_end) - static_cast(x_start); + const auto SURF_WIDTH = static_cast(surface_data_->width); + const auto ROW_WIDTH = static_cast(static_cast(x_end) - static_cast(x_start)); for (int y = static_cast(y_start); y < static_cast(y_end); ++y) { - std::memset(data_ptr + (y * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); + std::memset(data_ptr + (static_cast(y) * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); } } @@ -194,10 +194,10 @@ void Surface::drawRectBorder(const SDL_FRect* rect, Uint8 color) { // NOLINT(re // Dibujar bordes horizontales con memset (líneas contiguas en memoria) Uint8* data_ptr = surface_data_->data.get(); - const int SURF_WIDTH = static_cast(surface_data_->width); - const int ROW_WIDTH = static_cast(x_end) - static_cast(x_start); - std::memset(data_ptr + (static_cast(y_start) * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); - std::memset(data_ptr + ((static_cast(y_end) - 1) * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); + const auto SURF_WIDTH = static_cast(surface_data_->width); + const auto ROW_WIDTH = static_cast(static_cast(x_end) - static_cast(x_start)); + std::memset(data_ptr + (static_cast(y_start) * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); + std::memset(data_ptr + ((static_cast(y_end) - 1) * SURF_WIDTH) + static_cast(x_start), color, ROW_WIDTH); // Dibujar bordes verticales for (int y = y_start; y < y_end; ++y) { @@ -295,8 +295,8 @@ void Surface::render(int x, int y, SDL_FRect* src_rect, SDL_FlipMode flip) { // float h = (src_rect != nullptr) ? src_rect->h : surface_data_->height; // Guardar dimensiones originales antes del clipping (necesarias para flip) - float orig_w = (src_rect != nullptr) ? src_rect->w : static_cast(surface_data_->width); - float orig_h = (src_rect != nullptr) ? src_rect->h : static_cast(surface_data_->height); + float orig_w = (src_rect != nullptr) ? src_rect->w : surface_data_->width; + float orig_h = (src_rect != nullptr) ? src_rect->h : surface_data_->height; // Limitar la región para evitar accesos fuera de rango en origen w = std::min(w, surface_data_->width - sx); @@ -467,7 +467,7 @@ static auto computeFadeDensity(int screen_y, int fade_h, int canvas_height) -> f } // Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig) -void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect) const { +void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, const SDL_FRect* src_rect) const { // Aplicar render offset x += Screen::get()->getRenderOffsetX(); y += Screen::get()->getRenderOffsetY(); @@ -508,7 +508,7 @@ void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height } // Idem però reemplaçant un color índex -void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect) const { +void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, const SDL_FRect* src_rect) const { // Aplicar render offset x += Screen::get()->getRenderOffsetX(); y += Screen::get()->getRenderOffsetY(); @@ -599,8 +599,8 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) { // const int WIDTH = surface_data_->width; const int HEIGHT = surface_data_->height; for (int y = 0; y < HEIGHT; ++y) { - const Uint8* src_row = src + (y * WIDTH); - Uint32* dst_row = pixels + (y * row_stride); + const Uint8* src_row = src + (static_cast(y) * static_cast(WIDTH)); + Uint32* dst_row = pixels + (static_cast(y) * static_cast(row_stride)); for (int x = 0; x < WIDTH; ++x) { dst_row[x] = pal[src_row[x]]; } @@ -648,8 +648,8 @@ void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_FR const int WIDTH = surface_data_->width; const int HEIGHT = surface_data_->height; for (int y = 0; y < HEIGHT; ++y) { - const Uint8* src_row = src + (y * WIDTH); - Uint32* dst_row = pixels + (y * row_stride); + const Uint8* src_row = src + (static_cast(y) * static_cast(WIDTH)); + Uint32* dst_row = pixels + (static_cast(y) * static_cast(row_stride)); for (int x = 0; x < WIDTH; ++x) { dst_row[x] = pal[src_row[x]]; } diff --git a/source/core/rendering/surface.hpp b/source/core/rendering/surface.hpp index 02a20e5..ce7665e 100644 --- a/source/core/rendering/surface.hpp +++ b/source/core/rendering/surface.hpp @@ -85,10 +85,10 @@ class Surface { void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE) const; // Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig) - void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect = nullptr) const; + void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, const SDL_FRect* src_rect = nullptr) const; // Idem però reemplaçant un color índex (per a sprites sobre fons del mateix color) - void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, SDL_FRect* src_rect = nullptr) const; + void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color, const SDL_FRect* src_rect = nullptr) const; // Establece un color en la paleta void setColor(int index, Uint32 color); diff --git a/source/core/rendering/text.cpp b/source/core/rendering/text.cpp index 71e4e79..2ffd572 100644 --- a/source/core/rendering/text.cpp +++ b/source/core/rendering/text.cpp @@ -22,22 +22,19 @@ auto Text::nextCodepoint(const std::string& s, size_t& pos) -> uint32_t { // NO if (c < 0x80) { cp = c; extra = 0; - } else if (c < 0xC0) { + } else if (c < 0xC0 || c >= 0xF8) { + // Byte de continuación suelto o lead byte inválido pos++; return 0xFFFD; - } // byte de continuación suelto - else if (c < 0xE0) { + } else if (c < 0xE0) { cp = c & 0x1F; extra = 1; } else if (c < 0xF0) { cp = c & 0x0F; extra = 2; - } else if (c < 0xF8) { + } else { cp = c & 0x07; extra = 3; - } else { - pos++; - return 0xFFFD; } pos++; @@ -291,7 +288,7 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerni if (COLORED) { writeColored(x, y, text, text_color, kerning, lenght); } else { - writeColored(x, y, text, text_color, kerning, lenght); + write(x, y, text, kerning, lenght); } } diff --git a/source/core/resources/resource_cache.cpp b/source/core/resources/resource_cache.cpp index fc238cd..5685162 100644 --- a/source/core/resources/resource_cache.cpp +++ b/source/core/resources/resource_cache.cpp @@ -235,7 +235,7 @@ namespace Resource { // Obtiene todas las habitaciones auto Cache::getRooms() -> std::vector& { if (loading_mode_ == LoadingMode::LAZY) { - for (auto& r : rooms_) { + for (const auto& r : rooms_) { if (r.room == nullptr) { loadRoomByName(r.name); } } } @@ -248,6 +248,7 @@ namespace Resource { std::cerr << "[ ERROR ] Path: " << file_path << '\n'; std::cerr << "[ ERROR ] Reason: " << e.what() << '\n'; std::cerr << "[ ERROR ] Check config/assets.yaml configuration\n"; + // cppcheck-suppress rethrowNoCurrentException -- helper [[noreturn]] invocado desde dentro de un catch; cppcheck no puede ver que el rethrow es válido a través de la llamada. throw; } @@ -548,6 +549,8 @@ namespace Resource { exit(0); } break; + default: + break; } } } diff --git a/source/core/resources/resource_cache.hpp b/source/core/resources/resource_cache.hpp index 07fa954..56f46d8 100644 --- a/source/core/resources/resource_cache.hpp +++ b/source/core/resources/resource_cache.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include // Para shared_ptr #include // Para string #include @@ -11,7 +12,7 @@ namespace Resource { class Cache { public: - enum class LoadingMode { + enum class LoadingMode : std::uint8_t { EAGER, // Carga todos los recursos en init() (comportamiento por defecto, producción) LAZY // Sólo registra nombres; carga cada recurso la primera vez que se pide (desarrollo) }; diff --git a/source/core/resources/resource_list.cpp b/source/core/resources/resource_list.cpp index fef3b8a..d06e703 100644 --- a/source/core/resources/resource_list.cpp +++ b/source/core/resources/resource_list.cpp @@ -75,11 +75,11 @@ namespace Resource { } // Buscar la última entrada con el mismo prefijo de ruta e insertar después - std::string entry = " - " + var_path + "\n"; auto last_pos = content.rfind(var_path.substr(0, var_path.rfind('/'))); if (last_pos != std::string::npos) { auto end_of_line = content.find('\n', last_pos); if (end_of_line != std::string::npos) { + std::string entry = " - " + var_path + "\n"; content.insert(end_of_line + 1, entry); } } diff --git a/source/core/resources/resource_list.hpp b/source/core/resources/resource_list.hpp index 10ba6ba..731e0de 100644 --- a/source/core/resources/resource_list.hpp +++ b/source/core/resources/resource_list.hpp @@ -12,7 +12,7 @@ namespace Resource { class List { public: // --- Enums --- - enum class Type : int { + enum class Type : std::uint8_t { DATA, // Datos BITMAP, // Imágenes ANIMATION, // Animaciones diff --git a/source/core/system/debug.cpp b/source/core/system/debug.cpp index f2590f5..ee38f0b 100644 --- a/source/core/system/debug.cpp +++ b/source/core/system/debug.cpp @@ -41,9 +41,11 @@ void Debug::render() { // NOLINT(readability-make-member-function-const) // Watch window: valores persistentes (key: value) for (const auto& [key, value] : watches_) { - const std::string LINE = key + ": " + value; - text->write(x_, y, LINE); - w = std::max(w, text->length(LINE)); + std::string line = key; + line += ": "; + line += value; + text->write(x_, y, line); + w = std::max(w, text->length(line)); y += DESP_Y; if (y > 192 - CHAR_SIZE) { y = y_; diff --git a/source/game/editor/editor_statusbar.cpp b/source/game/editor/editor_statusbar.cpp index 771c6af..0594f16 100644 --- a/source/game/editor/editor_statusbar.cpp +++ b/source/game/editor/editor_statusbar.cpp @@ -14,13 +14,9 @@ // Constructor EditorStatusBar::EditorStatusBar(std::string room_number) - : room_number_(std::move(room_number)) { - const float SURFACE_WIDTH = Options::game.width; - constexpr float SURFACE_HEIGHT = 24.0F; // 3 líneas de 8px - - surface_ = std::make_shared(SURFACE_WIDTH, SURFACE_HEIGHT); - surface_dest_ = {.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = SURFACE_WIDTH, .h = SURFACE_HEIGHT}; -} + : surface_(std::make_shared(Options::game.width, SURFACE_HEIGHT)), + surface_dest_{.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = Options::game.width, .h = SURFACE_HEIGHT}, + room_number_(std::move(room_number)) {} // Pinta la barra de estado en pantalla void EditorStatusBar::render() { diff --git a/source/game/editor/editor_statusbar.hpp b/source/game/editor/editor_statusbar.hpp index af36446..645a9da 100644 --- a/source/game/editor/editor_statusbar.hpp +++ b/source/game/editor/editor_statusbar.hpp @@ -27,10 +27,11 @@ class EditorStatusBar { // Constantes de posición (en pixels dentro de la surface de 256x24) // Font 8bithud lowercase = 6px alto → 3 líneas con 8px de separación - static constexpr int LINE1_Y = 1; // Room number + tile coords + extra - static constexpr int LINE2_Y = 9; // Propiedades de room / enemy info - static constexpr int LINE3_Y = 17; // Conexiones+items / enemy detail - static constexpr int LEFT_X = 4; // Margen izquierdo + static constexpr float SURFACE_HEIGHT = 24.0F; // 3 líneas de 8px + static constexpr int LINE1_Y = 1; // Room number + tile coords + extra + static constexpr int LINE2_Y = 9; // Propiedades de room / enemy info + static constexpr int LINE3_Y = 17; // Conexiones+items / enemy detail + static constexpr int LEFT_X = 4; // Margen izquierdo // Objetos std::shared_ptr surface_; // Surface donde dibujar la barra diff --git a/source/game/editor/map_editor.cpp b/source/game/editor/map_editor.cpp index e9b6aab..720f217 100644 --- a/source/game/editor/map_editor.cpp +++ b/source/game/editor/map_editor.cpp @@ -92,7 +92,7 @@ void MapEditor::loadSettings() { } } } catch (...) { - // Fichero corrupto o vacío, usar defaults + // @INTENTIONAL: fichero corrupto o vacío → usar defaults } } @@ -231,8 +231,8 @@ void MapEditor::enter(std::shared_ptr room, std::shared_ptr player painting_ = false; // Siempre dejar de pintar al cambiar de room // Asegurar que collision_tile_map tiene el tamaño correcto - if (room_data_.collision_tile_map.size() != static_cast(Map::WIDTH * Map::HEIGHT)) { - room_data_.collision_tile_map.resize(Map::WIDTH * Map::HEIGHT, 0); + if (room_data_.collision_tile_map.size() != static_cast(Map::WIDTH) * static_cast(Map::HEIGHT)) { + room_data_.collision_tile_map.resize(static_cast(Map::WIDTH) * static_cast(Map::HEIGHT), 0); } active_ = true; @@ -361,30 +361,31 @@ void MapEditor::update(float delta_time) { } // Renderiza el editor +void MapEditor::renderCollisionOverlay() const { + auto collision_surface = Resource::Cache::get()->getSurface("collision.gif"); + if (!collision_surface) { return; } + const int TILE_W = Tile::SIZE; + for (int y = 0; y < Map::HEIGHT; ++y) { + for (int x = 0; x < Map::WIDTH; ++x) { + int index = (y * Map::WIDTH) + x; + if (index >= static_cast(room_data_.collision_tile_map.size())) { continue; } + int tile = room_data_.collision_tile_map[index]; + if (tile <= 0) { continue; } // 0 = vacío, no dibujar + SDL_FRect clip = { + .x = static_cast(tile * TILE_W), + .y = 0, + .w = static_cast(TILE_W), + .h = static_cast(TILE_W)}; + collision_surface->render(x * TILE_W, y * TILE_W, &clip); + } + } +} + void MapEditor::render() { // El tilemap ya ha sido renderizado por Game::renderPlaying() antes de llamar aquí // Si estamos editando colisiones, superponer el mapa de colisiones - if (editing_collision_) { - auto collision_surface = Resource::Cache::get()->getSurface("collision.gif"); - if (collision_surface) { - const int TILE_W = Tile::SIZE; - for (int y = 0; y < Map::HEIGHT; ++y) { - for (int x = 0; x < Map::WIDTH; ++x) { - int index = (y * Map::WIDTH) + x; - if (index >= static_cast(room_data_.collision_tile_map.size())) { continue; } - int tile = room_data_.collision_tile_map[index]; - if (tile <= 0) { continue; } // 0 = vacío, no dibujar - SDL_FRect clip = { - .x = static_cast(tile * TILE_W), - .y = 0, - .w = static_cast(TILE_W), - .h = static_cast(TILE_W)}; - collision_surface->render(x * TILE_W, y * TILE_W, &clip); - } - } - } - } + if (editing_collision_) { renderCollisionOverlay(); } // Grid (debajo de todo) if (settings_.grid) { @@ -526,7 +527,7 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun // Deseleccionar entidades selection_.clear(); - const std::string tileset_name = editing_collision_ ? "collision.gif" : room_->getTileSetFile(); + const std::string TILESET_NAME = editing_collision_ ? "collision.gif" : room_->getTileSetFile(); int tile_index = (mouse_tile_y_ * 32) + mouse_tile_x_; int current = 0; if (editing_collision_) { @@ -539,10 +540,10 @@ void MapEditor::handleEvent(const SDL_Event& event) { // NOLINT(readability-fun : -1; } - tile_picker_.on_select = [this, tileset_name](int col, int row, int width, int height) { - brush_ = buildPatternFromTileset(tileset_name, col, row, width, height); + tile_picker_.on_select = [this, TILESET_NAME](int col, int row, int width, int height) { + brush_ = buildPatternFromTileset(TILESET_NAME, col, row, width, height); }; - tile_picker_.open(tileset_name, current, 0, -1, -1, 0, 1, true); + tile_picker_.open(TILESET_NAME, current, 0, -1, -1, 0, 1, true); return; } @@ -664,7 +665,7 @@ void MapEditor::handleMouseUp() { if (selection_.is(drag_.entity_type) && selection_.index == drag_.index) { selection_.clear(); // deselect } else { - selection_ = {drag_.entity_type, drag_.index}; // select + selection_ = {.type = drag_.entity_type, .index = drag_.index}; // select } } else { selection_.clear(); @@ -696,7 +697,8 @@ void MapEditor::handleMouseUp() { drag_ = {}; } -// Commit de un drag de entidad (initial, bound1, bound2) para cualquier EntityType +// Commit de un drag de entidad (initial, bound1, bound2) para cualquier EntityType. +// NOLINTNEXTLINE(readability-function-cognitive-complexity) -- switch sobre EntityType con una rama por tipo; refactor a visitor requiere cambio de diseño. auto MapEditor::commitEntityDrag() -> bool { const int IDX = drag_.index; const int SNAP_X = static_cast(drag_.snap_x); @@ -710,14 +712,14 @@ auto MapEditor::commitEntityDrag() -> bool { room_data_.enemies[IDX].x = drag_.snap_x; room_data_.enemies[IDX].y = drag_.snap_y; room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]); - selection_ = {EntityType::ENEMY, IDX}; + selection_ = {.type = EntityType::ENEMY, .index = IDX}; return true; } break; case EntityType::ITEM: if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) { room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y); - selection_ = {EntityType::ITEM, IDX}; + selection_ = {.type = EntityType::ITEM, .index = IDX}; return true; } break; @@ -733,7 +735,7 @@ auto MapEditor::commitEntityDrag() -> bool { } } room_->getPlatformManager()->getPlatform(IDX)->resetToInitialPosition(plat); - selection_ = {EntityType::PLATFORM, IDX}; + selection_ = {.type = EntityType::PLATFORM, .index = IDX}; return true; } break; @@ -744,7 +746,7 @@ auto MapEditor::commitEntityDrag() -> bool { // sprite→data igual que con items. room_data_.keys[IDX].x = drag_.snap_x; room_data_.keys[IDX].y = drag_.snap_y; - selection_ = {EntityType::KEY, IDX}; + selection_ = {.type = EntityType::KEY, .index = IDX}; return true; } break; @@ -756,7 +758,7 @@ auto MapEditor::commitEntityDrag() -> bool { room_data_.doors[IDX].x = drag_.snap_x; room_data_.doors[IDX].y = drag_.snap_y; room_->getDoorManager()->moveDoor(IDX, drag_.snap_x, drag_.snap_y); - selection_ = {EntityType::DOOR, IDX}; + selection_ = {.type = EntityType::DOOR, .index = IDX}; return true; } break; @@ -772,7 +774,7 @@ auto MapEditor::commitEntityDrag() -> bool { room_data_.enemies[IDX].x1 = SNAP_X; room_data_.enemies[IDX].y1 = SNAP_Y; room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]); - selection_ = {EntityType::ENEMY, IDX}; + selection_ = {.type = EntityType::ENEMY, .index = IDX}; return true; } break; @@ -788,7 +790,7 @@ auto MapEditor::commitEntityDrag() -> bool { room_data_.enemies[IDX].x2 = SNAP_X; room_data_.enemies[IDX].y2 = SNAP_Y; room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]); - selection_ = {EntityType::ENEMY, IDX}; + selection_ = {.type = EntityType::ENEMY, .index = IDX}; return true; } break; @@ -803,7 +805,8 @@ auto MapEditor::commitEntityDrag() -> bool { return false; } -// Mueve visualmente la entidad arrastrada a la posición snapped +// Mueve visualmente la entidad arrastrada a la posición snapped. +// NOLINTNEXTLINE(readability-function-cognitive-complexity) -- switch sobre EntityType con cases paralelos para cada tipo. void MapEditor::moveEntityVisual() { switch (drag_.target) { case DragTarget::ENTITY_INITIAL: @@ -857,8 +860,6 @@ void MapEditor::moveEntityVisual() { case DragTarget::ENTITY_BOUND1: case DragTarget::ENTITY_BOUND2: // Los boundaries se actualizan visualmente en renderEntityBoundaries() via drag_.snap - break; - default: break; } @@ -946,7 +947,8 @@ void MapEditor::renderSelectionHighlight() { game_surface->drawRectBorder(&border, DRAG_COLOR); } -// Estampa el patrón del brush en la posición indicada (anclaje top-left) +// Estampa el patrón del brush en la posición indicada (anclaje top-left). +// NOLINTNEXTLINE(readability-function-cognitive-complexity) -- nested loops + casos TRANSPARENT/ERASE/tile normal y ramas collision vs normal. void MapEditor::stampBrushAt(int tile_x, int tile_y) { if (brush_.isEmpty()) { return; } for (int dy = 0; dy < brush_.height; ++dy) { @@ -997,7 +999,7 @@ auto MapEditor::sampleBrush(int x1, int y1, int x2, int y2) const -> BrushPatter BrushPattern p; p.width = (x2 - x1) + 1; p.height = (y2 - y1) + 1; - p.tiles.reserve(static_cast(p.width * p.height)); + p.tiles.reserve(static_cast(p.width) * static_cast(p.height)); const auto& src = editing_collision_ ? room_data_.collision_tile_map : room_data_.tile_map; for (int y = y1; y <= y2; ++y) { for (int x = x1; x <= x2; ++x) { @@ -1012,7 +1014,7 @@ auto MapEditor::sampleBrush(int x1, int y1, int x2, int y2) const -> BrushPatter // Construye un BrushPattern leyendo tiles consecutivos de un tileset. // Usado por el TilePicker cuando se hace selección rectangular. -auto MapEditor::buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) const -> BrushPattern { +auto MapEditor::buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) -> BrushPattern { BrushPattern p; auto surface = Resource::Cache::get()->getSurface(tileset_name); if (!surface || width <= 0 || height <= 0) { return p; } @@ -1020,7 +1022,7 @@ auto MapEditor::buildPatternFromTileset(const std::string& tileset_name, int col if (cols <= 0) { return p; } p.width = width; p.height = height; - p.tiles.reserve(static_cast(width * height)); + p.tiles.reserve(static_cast(width) * static_cast(height)); for (int dy = 0; dy < height; ++dy) { for (int dx = 0; dx < width; ++dx) { int tile = ((row + dy) * cols) + (col + dx); @@ -1036,8 +1038,8 @@ void MapEditor::renderBrushPreview() { auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } - const std::string tileset_name = editing_collision_ ? std::string("collision.gif") : room_->getTileSetFile(); - auto tileset = Resource::Cache::get()->getSurface(tileset_name); + const std::string TILESET_NAME = editing_collision_ ? std::string("collision.gif") : room_->getTileSetFile(); + auto tileset = Resource::Cache::get()->getSurface(TILESET_NAME); int cols = (tileset) ? (static_cast(tileset->getWidth()) / Tile::SIZE) : 0; constexpr auto TS = static_cast(Tile::SIZE); @@ -1052,7 +1054,7 @@ void MapEditor::renderBrushPreview() { float dst_y = static_cast(ty) * TS; if (value == BrushPattern::ERASE) { SDL_FRect erase_cell = {.x = dst_x, .y = dst_y, .w = TS, .h = TS}; - game_surface->fillRect(&erase_cell, static_cast(room_data_.bg_color)); + game_surface->fillRect(&erase_cell, room_data_.bg_color); } else if (tileset && cols > 0) { SDL_FRect src = { .x = static_cast(value % cols) * TS, @@ -1077,7 +1079,7 @@ void MapEditor::renderBrushPreview() { } // Renderiza el rectángulo del eyedropper en progreso (cyan brillante) -void MapEditor::renderEyedropperRect() { +void MapEditor::renderEyedropperRect() const { auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } int x1 = std::clamp(eyedropper_.start_tile_x, 0, Map::WIDTH - 1); @@ -1152,7 +1154,7 @@ auto MapEditor::entityBoundaries(EntityType type, int index) const -> BoundaryDa switch (type) { case EntityType::ENEMY: { const auto& e = room_data_.enemies[index]; - return {e.x1, e.y1, e.x2, e.y2}; + return {.x1 = e.x1, .y1 = e.y1, .x2 = e.x2, .y2 = e.y2}; } default: return {}; @@ -1212,7 +1214,8 @@ auto MapEditor::entityLabel(EntityType type) -> const char* { } } -// Dibuja marcadores de boundaries y líneas de ruta para enemigos y plataformas +// Dibuja marcadores de boundaries y líneas de ruta para enemigos y plataformas. +// NOLINTNEXTLINE(readability-function-cognitive-complexity) -- switch sobre EntityType con ramas de enemigo patrullante vs plataforma con waypoints. void MapEditor::renderEntityBoundaries() { auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } @@ -1395,7 +1398,7 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv const auto& e = room_data_.enemies[selection_.index]; std::string anim = e.animation_path; auto dot = anim.rfind('.'); - if (dot != std::string::npos) { anim = anim.substr(0, dot); } + if (dot != std::string::npos) { anim.resize(dot); } line2 = "enemy " + std::to_string(selection_.index) + ": " + anim; line3 = "vx:" + std::to_string(static_cast(e.vx)) + @@ -1420,7 +1423,7 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv if (selection_.index < static_cast(room_data_.platforms.size())) { const auto& p = room_data_.platforms[selection_.index]; std::string anim = p.animation_path; - if (anim.size() > 5 && anim.substr(anim.size() - 5) == ".yaml") { anim = anim.substr(0, anim.size() - 5); } + if (anim.ends_with(".yaml")) { anim.resize(anim.size() - 5); } line2 = "platform " + std::to_string(selection_.index) + ": " + anim; line3 = "speed:" + std::to_string(static_cast(p.speed)) + " " + (p.loop == LoopMode::CIRCULAR ? "circular" : "pingpong"); if (p.easing != "linear") { line3 += " " + p.easing; } @@ -1432,7 +1435,7 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv const auto& k = room_data_.keys[selection_.index]; std::string anim = k.animation_path; auto dot = anim.rfind('.'); - if (dot != std::string::npos) { anim = anim.substr(0, dot); } + if (dot != std::string::npos) { anim.resize(dot); } line2 = "key " + std::to_string(selection_.index) + ": " + anim; line3 = "id: " + k.id; } @@ -1443,7 +1446,7 @@ void MapEditor::updateStatusBarInfo() { // NOLINT(readability-function-cognitiv const auto& d = room_data_.doors[selection_.index]; std::string anim = d.animation_path; auto dot = anim.rfind('.'); - if (dot != std::string::npos) { anim = anim.substr(0, dot); } + if (dot != std::string::npos) { anim.resize(dot); } line2 = "door " + std::to_string(selection_.index) + ": " + anim; line3 = "id: " + d.id; } @@ -1495,7 +1498,6 @@ auto MapEditor::getSetCompletions() const -> std::vector { case EntityType::PLATFORM: return {"ANIMATION", "SPEED", "LOOP", "EASING"}; case EntityType::KEY: - return {"ID", "ANIMATION"}; case EntityType::DOOR: return {"ID", "ANIMATION"}; default: @@ -1534,7 +1536,7 @@ auto MapEditor::getAnimationCompletions() const -> std::vector { if (path.extension() != ".yaml") { continue; } result.push_back(toUpper(path.stem().string())); } - std::sort(result.begin(), result.end()); + std::ranges::sort(result); return result; } @@ -1652,7 +1654,7 @@ auto MapEditor::addEnemy() -> std::string { // Seleccionar el nuevo enemigo int new_index = static_cast(room_data_.enemies.size()) - 1; - selection_ = {EntityType::ENEMY, new_index}; + selection_ = {.type = EntityType::ENEMY, .index = new_index}; autosave(); return "Added enemy " + std::to_string(new_index); @@ -1699,7 +1701,7 @@ auto MapEditor::duplicateEnemy() -> std::string { // Seleccionar el nuevo enemigo int new_index = static_cast(room_data_.enemies.size()) - 1; - selection_ = {EntityType::ENEMY, new_index}; + selection_ = {.type = EntityType::ENEMY, .index = new_index}; autosave(); return "Duplicated as enemy " + std::to_string(new_index); @@ -1907,7 +1909,7 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { // // Comprobar que no hay ya una room en esa dirección if (!direction.empty()) { - std::string* existing = nullptr; + const std::string* existing = nullptr; if (direction == "UP") { existing = &room_data_.upper_room; } else if (direction == "DOWN") { @@ -1923,12 +1925,14 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { // } // Encontrar el primer número libre (reutiliza huecos) - auto& rooms = Resource::Cache::get()->getRooms(); + const auto& rooms = Resource::Cache::get()->getRooms(); std::set used; for (const auto& r : rooms) { try { used.insert(std::stoi(r.name.substr(0, r.name.find('.')))); - } catch (...) {} + } catch (...) { + // @INTENTIONAL: nombre de room no es numérico → saltar + } } int new_num = 1; while (used.contains(new_num)) { ++new_num; } @@ -1970,7 +1974,7 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { // // Persistir vía la autoridad del formato (no más std::ofstream a pelo) auto save_result = RoomFormat::saveYAML(new_path, new_room); - if (save_result.find("Error") == 0) { return save_result; } + if (save_result.starts_with("Error")) { return save_result; } // Registrar en Resource::List (mapa + assets.yaml) y cache Resource::List::get()->addAsset(new_path, Resource::List::Type::ROOM); @@ -2212,7 +2216,7 @@ auto MapEditor::addItem() -> std::string { room_->getItemManager()->addItem(std::make_shared(new_item)); int new_index = static_cast(room_data_.items.size()) - 1; - selection_ = {EntityType::ITEM, new_index}; + selection_ = {.type = EntityType::ITEM, .index = new_index}; autosave(); return "Added item " + std::to_string(new_index); @@ -2250,7 +2254,7 @@ auto MapEditor::duplicateItem() -> std::string { room_->getItemManager()->addItem(std::make_shared(copy)); int new_index = static_cast(room_data_.items.size()) - 1; - selection_ = {EntityType::ITEM, new_index}; + selection_ = {.type = EntityType::ITEM, .index = new_index}; autosave(); return "Duplicated as item " + std::to_string(new_index); @@ -2328,14 +2332,14 @@ auto MapEditor::addPlatform() -> std::string { constexpr float CENTER_Y = PlayArea::CENTER_Y; constexpr float ROUTE_HALF = 2.0F * Tile::SIZE; new_platform.path = { - {CENTER_X - ROUTE_HALF, CENTER_Y, 0.0F}, - {CENTER_X + ROUTE_HALF, CENTER_Y, 0.0F}}; + {.x = CENTER_X - ROUTE_HALF, .y = CENTER_Y, .wait = 0.0F}, + {.x = CENTER_X + ROUTE_HALF, .y = CENTER_Y, .wait = 0.0F}}; room_data_.platforms.push_back(new_platform); room_->getPlatformManager()->addPlatform(std::make_shared(new_platform)); int new_index = static_cast(room_data_.platforms.size()) - 1; - selection_ = {EntityType::PLATFORM, new_index}; + selection_ = {.type = EntityType::PLATFORM, .index = new_index}; autosave(); return "Added platform " + std::to_string(new_index); @@ -2375,7 +2379,7 @@ auto MapEditor::duplicatePlatform() -> std::string { room_->getPlatformManager()->addPlatform(std::make_shared(copy)); int new_index = static_cast(room_data_.platforms.size()) - 1; - selection_ = {EntityType::PLATFORM, new_index}; + selection_ = {.type = EntityType::PLATFORM, .index = new_index}; autosave(); return "Duplicated as platform " + std::to_string(new_index); @@ -2441,7 +2445,7 @@ auto MapEditor::addKey() -> std::string { } int new_index = static_cast(room_data_.keys.size()) - 1; - selection_ = {EntityType::KEY, new_index}; + selection_ = {.type = EntityType::KEY, .index = new_index}; autosave(); return "Added key " + std::to_string(new_index); @@ -2484,7 +2488,7 @@ auto MapEditor::duplicateKey() -> std::string { } int new_index = static_cast(room_data_.keys.size()) - 1; - selection_ = {EntityType::KEY, new_index}; + selection_ = {.type = EntityType::KEY, .index = new_index}; autosave(); return "Duplicated as key " + std::to_string(new_index); @@ -2568,7 +2572,7 @@ auto MapEditor::addDoor() -> std::string { } int new_index = static_cast(room_data_.doors.size()) - 1; - selection_ = {EntityType::DOOR, new_index}; + selection_ = {.type = EntityType::DOOR, .index = new_index}; autosave(); return "Added door " + std::to_string(new_index); @@ -2608,7 +2612,7 @@ auto MapEditor::duplicateDoor() -> std::string { } int new_index = static_cast(room_data_.doors.size()) - 1; - selection_ = {EntityType::DOOR, new_index}; + selection_ = {.type = EntityType::DOOR, .index = new_index}; autosave(); return "Duplicated as door " + std::to_string(new_index); @@ -2629,7 +2633,7 @@ static auto pickGridColor(Uint8 bg, const std::shared_ptr& surface) -> } // Dibuja la cuadrícula de tiles (líneas de puntos, 1 pixel sí / 1 no) -void MapEditor::renderGrid() const { +void MapEditor::renderGrid() { auto game_surface = Screen::get()->getRendererSurface(); if (!game_surface) { return; } diff --git a/source/game/editor/map_editor.hpp b/source/game/editor/map_editor.hpp index 524ccf9..5a1357d 100644 --- a/source/game/editor/map_editor.hpp +++ b/source/game/editor/map_editor.hpp @@ -4,9 +4,10 @@ #include -#include // Para shared_ptr, unique_ptr -#include // Para string -#include // Para vector +#include // Para uint8_t +#include // Para shared_ptr, unique_ptr +#include // Para string +#include // Para vector #include "game/editor/mini_map.hpp" // Para MiniMap #include "game/editor/tile_picker.hpp" // Para TilePicker @@ -23,7 +24,7 @@ class EditorStatusBar; // Tipo de entidad editable en el editor -enum class EntityType { NONE, +enum class EntityType : std::uint8_t { NONE, ENEMY, ITEM, PLATFORM, @@ -106,7 +107,7 @@ class MapEditor { auto deleteRoom() -> std::string; // Opciones del editor (llamados desde console_commands / teclas) - auto showInfo(bool show) -> std::string; + static auto showInfo(bool show) -> std::string; auto showGrid(bool show) -> std::string; auto setEditingCollision(bool collision) -> std::string; [[nodiscard]] auto isGridEnabled() const -> bool { return settings_.grid; } @@ -164,7 +165,7 @@ class MapEditor { void saveSettings() const; // Tipos para drag & drop - enum class DragTarget { NONE, + enum class DragTarget : std::uint8_t { NONE, PLAYER, ENTITY_INITIAL, ENTITY_BOUND1, @@ -183,18 +184,19 @@ class MapEditor { // Métodos internos void updateMousePosition(); + void renderCollisionOverlay() const; void renderEntityBoundaries(); static void renderBoundaryMarker(float x, float y, Uint8 color); void renderSelectionHighlight(); void renderBrushPreview(); - void renderEyedropperRect(); - void renderGrid() const; + void renderEyedropperRect() const; + static void renderGrid(); void handleMouseDown(float game_x, float game_y); void handleMouseUp(); void stampBrushAt(int tile_x, int tile_y); void commitEyedropper(); [[nodiscard]] auto sampleBrush(int x1, int y1, int x2, int y2) const -> BrushPattern; - [[nodiscard]] auto buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) const -> BrushPattern; + [[nodiscard]] static auto buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) -> BrushPattern; // Reconstruye todas las puertas vivas desde room_data_, limpiando primero // los WALLs antiguos del CollisionMap. Lo usa setDoorProperty cuando un @@ -213,12 +215,12 @@ class MapEditor { struct BoundaryData { int x1, y1, x2, y2; }; - auto entityCount(EntityType type) const -> int; + [[nodiscard]] auto entityCount(EntityType type) const -> int; auto entityRect(EntityType type, int index) -> SDL_FRect; static auto entityHasBoundaries(EntityType type) -> bool; - auto entityBoundaries(EntityType type, int index) const -> BoundaryData; - auto entityPosition(EntityType type, int index) const -> std::pair; - auto entityDataCount(EntityType type) const -> int; + [[nodiscard]] auto entityBoundaries(EntityType type, int index) const -> BoundaryData; + [[nodiscard]] auto entityPosition(EntityType type, int index) const -> std::pair; + [[nodiscard]] auto entityDataCount(EntityType type) const -> int; static auto entityLabel(EntityType type) -> const char*; // Estado del editor diff --git a/source/game/editor/mini_map.cpp b/source/game/editor/mini_map.cpp index b72874e..32f0701 100644 --- a/source/game/editor/mini_map.cpp +++ b/source/game/editor/mini_map.cpp @@ -85,7 +85,7 @@ auto MiniMap::getOrBuildTileColorTable(const std::string& tileset_name) -> const // Posiciona las rooms en un grid usando BFS desde las conexiones void MiniMap::layoutRooms() { - auto& rooms = Resource::Cache::get()->getRooms(); + const auto& rooms = Resource::Cache::get()->getRooms(); if (rooms.empty()) { return; } // Mapa de nombre → Room::Data diff --git a/source/game/editor/tile_picker.cpp b/source/game/editor/tile_picker.cpp index fe7707d..53bff5a 100644 --- a/source/game/editor/tile_picker.cpp +++ b/source/game/editor/tile_picker.cpp @@ -166,8 +166,8 @@ void TilePicker::render() { int cells_h = row_max - row_min + 1; float rx = tileset_screen_x + static_cast(col_min * out_cell); float ry = tileset_screen_y + static_cast(row_min * out_cell); - float rw = static_cast((cells_w * out_cell) - spacing_out_); - float rh = static_cast((cells_h * out_cell) - spacing_out_); + auto rw = static_cast((cells_w * out_cell) - spacing_out_); + auto rh = static_cast((cells_h * out_cell) - spacing_out_); if (ry + rh > 0 && ry < static_cast(visible_height_)) { SDL_FRect rect_box = {.x = rx, .y = ry, .w = rw, .h = rh}; game_surface->drawRectBorder(&rect_box, 15); @@ -198,6 +198,27 @@ void TilePicker::render() { } } +// Invoca el callback con el rect formado por rect_start_tile_ y el hover actual, luego cierra. +void TilePicker::commitRectSelection() { + int end_tile = (hover_tile_ >= 0) ? hover_tile_ : last_valid_hover_; + if (end_tile >= 0 && rect_start_tile_ >= 0) { + int c1 = rect_start_tile_ % tileset_width_; + int r1 = rect_start_tile_ / tileset_width_; + int c2 = end_tile % tileset_width_; + int r2 = end_tile / tileset_width_; + int col_min = std::min(c1, c2); + int col_max = std::max(c1, c2); + int row_min = std::min(r1, r2); + int row_max = std::max(r1, r2); + int width = col_max - col_min + 1; + int height = row_max - row_min + 1; + if (on_select) { on_select(col_min, row_min, width, height); } + } + selecting_rect_ = false; + rect_start_tile_ = -1; + close(); +} + // Maneja eventos del picker void TilePicker::handleEvent(const SDL_Event& event) { if (!open_) { return; } @@ -226,23 +247,7 @@ void TilePicker::handleEvent(const SDL_Event& event) { } if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT && selecting_rect_) { - int end_tile = (hover_tile_ >= 0) ? hover_tile_ : last_valid_hover_; - if (end_tile >= 0 && rect_start_tile_ >= 0) { - int c1 = rect_start_tile_ % tileset_width_; - int r1 = rect_start_tile_ / tileset_width_; - int c2 = end_tile % tileset_width_; - int r2 = end_tile / tileset_width_; - int col_min = std::min(c1, c2); - int col_max = std::max(c1, c2); - int row_min = std::min(r1, r2); - int row_max = std::max(r1, r2); - int width = col_max - col_min + 1; - int height = row_max - row_min + 1; - if (on_select) { on_select(col_min, row_min, width, height); } - } - selecting_rect_ = false; - rect_start_tile_ = -1; - close(); + commitRectSelection(); } if (event.type == SDL_EVENT_MOUSE_WHEEL) { diff --git a/source/game/editor/tile_picker.hpp b/source/game/editor/tile_picker.hpp index 8914207..e7c646b 100644 --- a/source/game/editor/tile_picker.hpp +++ b/source/game/editor/tile_picker.hpp @@ -44,6 +44,7 @@ class TilePicker { private: void updateMousePosition(); + void commitRectSelection(); bool open_{false}; std::shared_ptr tileset_; // Surface del tileset original diff --git a/source/game/entities/door.hpp b/source/game/entities/door.hpp index 92ce877..ea777db 100644 --- a/source/game/entities/door.hpp +++ b/source/game/entities/door.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include // Para string @@ -24,7 +25,7 @@ class AnimatedSprite; */ class Door : public SolidActor { public: - enum class State : int { + enum class State : std::uint8_t { CLOSED = 0, OPENING = 1, OPENED = 2 diff --git a/source/game/entities/moving_platform.cpp b/source/game/entities/moving_platform.cpp index 9cbeb04..4395cc4 100644 --- a/source/game/entities/moving_platform.cpp +++ b/source/game/entities/moving_platform.cpp @@ -82,7 +82,7 @@ void MovingPlatform::recalcSegmentLength() { float dx = path_[to].x - path_[from].x; float dy = path_[to].y - path_[from].y; - segment_length_ = std::sqrt(dx * dx + dy * dy); + segment_length_ = std::sqrt((dx * dx) + (dy * dy)); } // Avanza al siguiente segmento @@ -174,8 +174,8 @@ void MovingPlatform::update(float delta_time) { int from = getSegmentFrom(); int to = getSegmentTo(); - float new_x = path_[from].x + (path_[to].x - path_[from].x) * t; - float new_y = path_[from].y + (path_[to].y - path_[from].y) * t; + float new_x = path_[from].x + ((path_[to].x - path_[from].x) * t); + float new_y = path_[from].y + ((path_[to].y - path_[from].y) * t); sprite_->setPosX(new_x); sprite_->setPosY(new_y); } diff --git a/source/game/entities/moving_platform.hpp b/source/game/entities/moving_platform.hpp index b082c42..eba1804 100644 --- a/source/game/entities/moving_platform.hpp +++ b/source/game/entities/moving_platform.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include // Para string #include // Para vector @@ -18,7 +19,7 @@ struct Waypoint { }; // Modo de recorrido de la ruta -enum class LoopMode { PINGPONG, +enum class LoopMode : std::uint8_t { PINGPONG, CIRCULAR }; // Tipo de función de easing diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index f9637ff..9cdce47 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -540,18 +540,10 @@ void Player::transitionToState(State state) { switch (state) { case State::ON_GROUND: - vy_ = 0; - // Clamp vx al aterrizar (el salto puede dar un boost extra) - if (vx_ > HORIZONTAL_VELOCITY) { vx_ = HORIZONTAL_VELOCITY; } - if (vx_ < -HORIZONTAL_VELOCITY) { vx_ = -HORIZONTAL_VELOCITY; } - if (previous_state_ == State::ON_AIR) { - Audio::get()->playSound(land_sound_, Audio::Group::GAME); - } - break; case State::ON_SLOPE: vy_ = 0; - if (vx_ > HORIZONTAL_VELOCITY) { vx_ = HORIZONTAL_VELOCITY; } - if (vx_ < -HORIZONTAL_VELOCITY) { vx_ = -HORIZONTAL_VELOCITY; } + // Clamp vx al aterrizar (el salto puede dar un boost extra) + vx_ = std::clamp(vx_, -HORIZONTAL_VELOCITY, HORIZONTAL_VELOCITY); if (previous_state_ == State::ON_AIR) { Audio::get()->playSound(land_sound_, Audio::Group::GAME); } diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index 1f396d4..f1e5366 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include // Para string #include @@ -17,13 +18,13 @@ class SolidActor; class Player { public: // --- Enums y Structs --- - enum class State { + enum class State : std::uint8_t { ON_GROUND, ON_SLOPE, ON_AIR, }; - enum class Direction { + enum class Direction : std::uint8_t { LEFT, RIGHT, UP, @@ -151,7 +152,7 @@ class Player { void syncSpriteAndCollider(); void placeSprite(); void animate(float delta_time); - auto handleBorders() const -> Room::Border; + [[nodiscard]] auto handleBorders() const -> Room::Border; // --- Inicialización --- void initSprite(const std::string& animations_path); diff --git a/source/game/entities/solid_actor.hpp b/source/game/entities/solid_actor.hpp index cdc212f..01cee35 100644 --- a/source/game/entities/solid_actor.hpp +++ b/source/game/entities/solid_actor.hpp @@ -25,6 +25,7 @@ */ class SolidActor { public: + // NOLINTNEXTLINE(performance-enum-size) -- bitmask con margen para crecer enum Flags : uint32_t { BLOCKS_PLAYER = 1U << 0U, CARRY_ON_TOP = 1U << 1U, diff --git a/source/game/gameplay/collision_map.cpp b/source/game/gameplay/collision_map.cpp index 5e003da..1371592 100644 --- a/source/game/gameplay/collision_map.cpp +++ b/source/game/gameplay/collision_map.cpp @@ -5,7 +5,7 @@ CollisionMap::CollisionMap(std::vector collision_tile_map) : collision_tile_map_(std::move(collision_tile_map)), - extended_tile_map_(EW * EH, 0), + extended_tile_map_(static_cast(EW) * static_cast(EH), 0), tile_collider_(extended_tile_map_, EW, EH, CollisionBorder::PX) { buildExtendedCenter(); } diff --git a/source/game/gameplay/room.hpp b/source/game/gameplay/room.hpp index f9e431b..b4820e3 100644 --- a/source/game/gameplay/room.hpp +++ b/source/game/gameplay/room.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include // Para string #include // Para vector @@ -28,7 +29,7 @@ class TilemapRenderer; class Room { public: // -- Enumeraciones y estructuras --- - enum class Border : int { + enum class Border : std::uint8_t { TOP = 0, RIGHT = 1, BOTTOM = 2, diff --git a/source/game/gameplay/room_format.cpp b/source/game/gameplay/room_format.cpp index 0fe0c8a..4862303 100644 --- a/source/game/gameplay/room_format.cpp +++ b/source/game/gameplay/room_format.cpp @@ -78,7 +78,7 @@ auto RoomFormat::convertAutoSurface(const fkyaml::node& node) -> int { auto RoomFormat::flattenTilemap(const std::vector>& tilemap_2d) -> std::vector { std::vector tilemap_flat; - tilemap_flat.reserve(Map::WIDTH * Map::HEIGHT); + tilemap_flat.reserve(static_cast(Map::WIDTH) * static_cast(Map::HEIGHT)); for (const auto& row : tilemap_2d) { for (int tile : row) { @@ -535,8 +535,8 @@ auto RoomFormat::createDefault() -> Room::Data { data.right_room = "0"; // Tilemaps del tamaño correcto, vacíos - data.tile_map.resize(Map::WIDTH * Map::HEIGHT, -1); - data.collision_tile_map.resize(Map::WIDTH * Map::HEIGHT, 0); + data.tile_map.resize(static_cast(Map::WIDTH) * static_cast(Map::HEIGHT), -1); + data.collision_tile_map.resize(static_cast(Map::WIDTH) * static_cast(Map::HEIGHT), 0); return data; } diff --git a/source/game/gameplay/scoreboard.cpp b/source/game/gameplay/scoreboard.cpp index 23d6b26..fc4364b 100644 --- a/source/game/gameplay/scoreboard.cpp +++ b/source/game/gameplay/scoreboard.cpp @@ -16,13 +16,9 @@ // Constructor Scoreboard::Scoreboard(std::shared_ptr data) - : data_(std::move(data)) { - const float SURFACE_WIDTH = Options::game.width; - constexpr float SURFACE_HEIGHT = 24.0F; // 3 líneas de 8px - - surface_ = std::make_shared(SURFACE_WIDTH, SURFACE_HEIGHT); - surface_dest_ = {.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = SURFACE_WIDTH, .h = SURFACE_HEIGHT}; -} + : data_(std::move(data)), + surface_(std::make_shared(Options::game.width, SURFACE_HEIGHT)), + surface_dest_{.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = Options::game.width, .h = SURFACE_HEIGHT} {} // Pinta el objeto en pantalla void Scoreboard::render() { @@ -91,19 +87,19 @@ void Scoreboard::fillTexture() { const std::string TIME_LABEL = Locale::get()->get("scoreboard.time"); // Ancho total: labels proporcionales + valores monoespaciados - const int LINE1_W = text->length(LIVES_LABEL) + text->lengthMono(LIVES_STR, MONO_W) + text->length(SEP) + text->length(ITEMS_LABEL) + text->lengthMono(ITEMS_STR, MONO_W) + text->length(SEP) + text->length(TIME_LABEL) + text->lengthMono(TIME_STR, MONO_W); + const int LINE1_W = text->length(LIVES_LABEL) + Text::lengthMono(LIVES_STR, MONO_W) + text->length(SEP) + text->length(ITEMS_LABEL) + Text::lengthMono(ITEMS_STR, MONO_W) + text->length(SEP) + text->length(TIME_LABEL) + Text::lengthMono(TIME_STR, MONO_W); int x = (CANVAS_W - LINE1_W) / 2; text->writeColored(x, LINE1_Y, LIVES_LABEL, LABEL_COLOR); x += text->length(LIVES_LABEL); text->writeColoredMono(x, LINE1_Y, LIVES_STR, VALUE_COLOR, MONO_W); - x += text->lengthMono(LIVES_STR, MONO_W); + x += Text::lengthMono(LIVES_STR, MONO_W); text->writeColored(x, LINE1_Y, SEP, LABEL_COLOR); x += text->length(SEP); text->writeColored(x, LINE1_Y, ITEMS_LABEL, LABEL_COLOR); x += text->length(ITEMS_LABEL); text->writeColoredMono(x, LINE1_Y, ITEMS_STR, VALUE_COLOR, MONO_W); - x += text->lengthMono(ITEMS_STR, MONO_W); + x += Text::lengthMono(ITEMS_STR, MONO_W); text->writeColored(x, LINE1_Y, SEP, LABEL_COLOR); x += text->length(SEP); text->writeColored(x, LINE1_Y, TIME_LABEL, LABEL_COLOR); diff --git a/source/game/gameplay/scoreboard.hpp b/source/game/gameplay/scoreboard.hpp index 1a2bdf4..291df30 100644 --- a/source/game/gameplay/scoreboard.hpp +++ b/source/game/gameplay/scoreboard.hpp @@ -39,6 +39,7 @@ class Scoreboard { // Constantes de tiempo // Posición de los elementos (2 líneas centradas verticalmente en surface de 24px) + static constexpr float SURFACE_HEIGHT = 24.0F; // 3 líneas de 8px static constexpr int LINE1_Y = 5; static constexpr int LINE2_Y = 13; diff --git a/source/game/gameplay/tile_collider.hpp b/source/game/gameplay/tile_collider.hpp index 03564fe..454956f 100644 --- a/source/game/gameplay/tile_collider.hpp +++ b/source/game/gameplay/tile_collider.hpp @@ -1,12 +1,13 @@ #pragma once +#include #include #include "utils/defines.hpp" class TileCollider { public: - enum class Tile : int { + enum class Tile : std::uint8_t { EMPTY = 0, WALL = 1, PASSABLE = 2, diff --git a/source/game/gameplay/tilemap_renderer.cpp b/source/game/gameplay/tilemap_renderer.cpp index 66bcdce..c88c665 100644 --- a/source/game/gameplay/tilemap_renderer.cpp +++ b/source/game/gameplay/tilemap_renderer.cpp @@ -11,9 +11,8 @@ TilemapRenderer::TilemapRenderer(std::vector tile_map, int tile_set_width, : tile_map_(std::move(tile_map)), tile_set_width_(tile_set_width), tileset_surface_(std::move(tileset_surface)), - bg_color_(bg_color) { - map_surface_ = std::make_shared(PlayArea::WIDTH, PlayArea::HEIGHT); -} + bg_color_(bg_color), + map_surface_(std::make_shared(PlayArea::WIDTH, PlayArea::HEIGHT)) {} void TilemapRenderer::initialize(const std::vector& collision_tile_map) { fillMapTexture(collision_tile_map); diff --git a/source/game/options.cpp b/source/game/options.cpp index f8c0573..bc9863d 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -368,12 +368,14 @@ namespace Options { if (sh_node.contains("current_postfx_preset")) { try { video.shader.current_postfx_preset_name = sh_node["current_postfx_preset"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (sh_node.contains("current_crtpi_preset")) { try { video.shader.current_crtpi_preset_name = sh_node["current_crtpi_preset"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } } @@ -551,24 +553,28 @@ namespace Options { if (a.contains("enabled")) { try { audio.enabled = a["enabled"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (a.contains("volume")) { try { audio.volume = std::clamp(a["volume"].get_value(), 0.0F, 1.0F); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (a.contains("music")) { const auto& m = a["music"]; if (m.contains("enabled")) { try { audio.music.enabled = m["enabled"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (m.contains("volume")) { try { audio.music.volume = std::clamp(m["volume"].get_value(), 0.0F, 1.0F); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } } if (a.contains("sound")) { @@ -576,12 +582,14 @@ namespace Options { if (s.contains("enabled")) { try { audio.sound.enabled = s["enabled"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (s.contains("volume")) { try { audio.sound.volume = std::clamp(s["volume"].get_value(), 0.0F, 1.0F); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } } } @@ -594,27 +602,32 @@ namespace Options { if (c.contains("transparent")) { try { console.transparent = c["transparent"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (c.contains("bg_color")) { try { console.bg_color = std::clamp(c["bg_color"].get_value(), 0, 255); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (c.contains("msg_color")) { try { console.msg_color = std::clamp(c["msg_color"].get_value(), 0, 255); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (c.contains("prompt_color")) { try { console.prompt_color = std::clamp(c["prompt_color"].get_value(), 0, 255); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (c.contains("command_color")) { try { console.command_color = std::clamp(c["command_color"].get_value(), 0, 255); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } } @@ -876,7 +889,8 @@ namespace Options { if (node.contains(key)) { try { target = node[key].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } } @@ -1166,32 +1180,38 @@ namespace Options { if (p.contains("mask_type")) { try { preset.mask_type = p["mask_type"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (p.contains("enable_scanlines")) { try { preset.enable_scanlines = p["enable_scanlines"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (p.contains("enable_multisample")) { try { preset.enable_multisample = p["enable_multisample"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (p.contains("enable_gamma")) { try { preset.enable_gamma = p["enable_gamma"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (p.contains("enable_curvature")) { try { preset.enable_curvature = p["enable_curvature"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } if (p.contains("enable_sharper")) { try { preset.enable_sharper = p["enable_sharper"].get_value(); - } catch (...) {} + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } } crtpi_presets.push_back(preset); } diff --git a/source/game/scene_manager.hpp b/source/game/scene_manager.hpp index a29025b..77538bd 100644 --- a/source/game/scene_manager.hpp +++ b/source/game/scene_manager.hpp @@ -1,5 +1,7 @@ #pragma once +#include + /* Namespace SceneManager: gestiona el flujo entre las diferentes escenas del juego. @@ -10,7 +12,7 @@ namespace SceneManager { // --- Escenas del programa --- - enum class Scene { + enum class Scene : std::uint8_t { LOGO, // Pantalla del logo TITLE, // Pantalla de título/menú principal GAME, // Juego principal @@ -19,7 +21,7 @@ namespace SceneManager { }; // --- Opciones para transiciones entre escenas --- - enum class Options { + enum class Options : std::uint8_t { NONE, // Sin opciones especiales LOGO_TO_TITLE, // Del logo al título }; diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index f19bbf7..9d1605f 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -818,6 +818,7 @@ auto Game::getOrCreateRoom(const std::string& room_path) -> std::shared_ptr const std::vector* { auto name = room_->getRoom(b); @@ -843,6 +844,7 @@ void Game::buildCollisionBorders() { } return nullptr; }; + // NOLINTEND(readability-identifier-naming) CollisionMap::AdjacentData adj; adj.top = getAdjacentCollision(Room::Border::TOP); @@ -861,6 +863,7 @@ void Game::buildCollisionBorders() { // que los sweeps del Player vean AABBs dinámicos (puertas, plataformas) // de la room vecina cuando está cerca del borde, sin tener que esperar // a una transición completa de room. + // NOLINTNEXTLINE(readability-identifier-naming) -- lambda local: se lee como función, no es constante. auto getAdjacentSolidActors = [&](Room::Border b) -> SolidActorManager* { auto name = room_->getRoom(b); if (name == "0") { return nullptr; } diff --git a/source/game/scenes/game.hpp b/source/game/scenes/game.hpp index 55041fa..b0455cc 100644 --- a/source/game/scenes/game.hpp +++ b/source/game/scenes/game.hpp @@ -2,6 +2,7 @@ #include +#include // Para uint8_t #include // Para initializer_list #include // Para shared_ptr #include // Para string @@ -18,12 +19,12 @@ class Surface; class Game { public: // --- Estructuras --- - enum class Mode { + enum class Mode : std::uint8_t { DEMO, GAME }; - enum class State { + enum class State : std::uint8_t { PLAYING, // Normal gameplay BLACK_SCREEN, // Black screen after death (0.30s) GAME_OVER, // Intermediate state before changing scene diff --git a/source/game/scenes/logo.hpp b/source/game/scenes/logo.hpp index a7cd828..0dc34b2 100644 --- a/source/game/scenes/logo.hpp +++ b/source/game/scenes/logo.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para std::function #include // Para shared_ptr #include // Para vector @@ -16,7 +17,7 @@ class Logo { using EasingFunction = std::function; // Función de easing (permite lambdas) // --- Enumeraciones --- - enum class State { + enum class State : std::uint8_t { INITIAL, // Espera inicial JAILGAMES_SLIDE_IN, // Las líneas de JAILGAMES se deslizan hacia el centro SINCE_1998_FADE_IN, // Aparición gradual del texto "Since 1998" diff --git a/source/game/scenes/title.cpp b/source/game/scenes/title.cpp index 1b1fd34..8c9336f 100644 --- a/source/game/scenes/title.cpp +++ b/source/game/scenes/title.cpp @@ -271,7 +271,7 @@ void Title::renderMainMenu() { const int TOTAL_HEIGHT = 2 * SPACING; // 2 espacios entre 3 items const int START_Y = MENU_CENTER_Y - (TOTAL_HEIGHT / 2); - auto* loc = Locale::get(); + const auto* loc = Locale::get(); menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.menu.play"), 1, COLOR); menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + SPACING, loc->get("title.menu.keyboard"), 1, COLOR); menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (2 * SPACING), loc->get("title.menu.joystick"), 1, COLOR); @@ -360,20 +360,6 @@ auto Title::isKeyDuplicate(SDL_Scancode scancode, int current_step) -> bool { / return false; } -// Retorna el nombre de la accion para el paso actual -auto Title::getActionName(int step) -> std::string { // NOLINT(readability-convert-member-functions-to-static) - switch (step) { - case 0: - return "LEFT"; - case 1: - return "RIGHT"; - case 2: - return "JUMP"; - default: - return "UNKNOWN"; - } -} - // Aplica y guarda las teclas redefinidas void Title::applyKeyboardRemap() { // NOLINT(readability-convert-member-functions-to-static) // Guardar las nuevas teclas en Options::controls @@ -402,7 +388,7 @@ void Title::renderKeyboardRemap() const { const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE); // Centrado aproximado // Mensaje principal: "PRESS KEY FOR [ACTION]" o "KEYS DEFINED" si completado - auto* loc = Locale::get(); + const auto* loc = Locale::get(); if (remap_step_ >= 3) { menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.defined"), 1, COLOR); } else { @@ -446,7 +432,7 @@ void Title::renderJoystickRemap() const { const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE); // Centrado aproximado // Mensaje principal: "PRESS BUTTON FOR [ACTION]" o "BUTTONS DEFINED" si completado - auto* loc = Locale::get(); + const auto* loc = Locale::get(); if (remap_step_ >= 3) { menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.defined"), 1, COLOR); } else { diff --git a/source/game/scenes/title.hpp b/source/game/scenes/title.hpp index 152c418..600a1f2 100644 --- a/source/game/scenes/title.hpp +++ b/source/game/scenes/title.hpp @@ -2,7 +2,8 @@ #include -#include // Para std::array +#include // Para std::array +#include #include // Para shared_ptr #include // Para string @@ -24,7 +25,7 @@ class Title { private: // --- Estructuras y enumeraciones --- - enum class State { + enum class State : std::uint8_t { MAIN_MENU, FADE_MENU, POST_FADE_MENU, @@ -58,7 +59,6 @@ class Title { auto isButtonDuplicate(int button, int current_step) -> bool; // Valida si un boton esta duplicado void applyKeyboardRemap(); // Aplica y guarda las teclas redefinidas void applyJoystickRemap(); // Aplica y guarda los botones del gamepad redefinidos - static auto getActionName(int step) -> std::string; // Retorna el nombre de la accion (LEFT/RIGHT/JUMP) static auto getButtonName(int button) -> std::string; // Retorna el nombre amigable del boton del gamepad void fillTitleSurface(); // Dibuja los elementos en la surface diff --git a/source/game/ui/console.cpp b/source/game/ui/console.cpp index 3d75854..166857f 100644 --- a/source/game/ui/console.cpp +++ b/source/game/ui/console.cpp @@ -64,9 +64,16 @@ auto Console::wrapText(const std::string& text) const -> std::vector> word) { - const std::string TEST = current_line.empty() ? word : (current_line + ' ' + word); - if (text_->length(TEST) <= MAX_PX) { - current_line = TEST; + std::string test; + if (current_line.empty()) { + test = word; + } else { + test = current_line; + test += ' '; + test += word; + } + if (text_->length(test) <= MAX_PX) { + current_line = test; } else { if (!current_line.empty()) { result.push_back(current_line); } current_line = word; @@ -381,7 +388,10 @@ void Console::handleEvent(const SDL_Event& event) { // NOLINT(readability-funct const auto OPTS = registry_.getCompletions(BASE_CMD); for (const auto& arg : OPTS) { if (SUB_PREFIX.empty() || std::string_view{arg}.starts_with(SUB_PREFIX)) { - tab_matches_.emplace_back(BASE_CMD + " " + arg); + std::string match = BASE_CMD; + match += ' '; + match += arg; + tab_matches_.emplace_back(std::move(match)); } } } diff --git a/source/game/ui/console.hpp b/source/game/ui/console.hpp index 7a24471..c6a30eb 100644 --- a/source/game/ui/console.hpp +++ b/source/game/ui/console.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para deque (historial) #include // Para function #include // Para shared_ptr @@ -46,7 +47,7 @@ class Console { std::function on_toggle; private: - enum class Status { + enum class Status : std::uint8_t { HIDDEN, RISING, ACTIVE, diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index c9d1e6b..4dfdc1a 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -32,7 +32,7 @@ // Toggle genérico para comandos booleanos ON/OFF (reemplaza macro BOOL_TOGGLE_CMD) static auto boolToggle( const std::string& label, - bool& option, + const bool& option, const std::function& toggle_fn, const std::vector& args) -> std::string { if (args.empty()) { @@ -259,7 +259,9 @@ static auto cmdZoom(const std::vector& args) -> std::string { if (N == Options::window.zoom) { return "Zoom already " + std::to_string(N); } Screen::get()->setWindowZoom(N); return "Zoom " + std::to_string(Options::window.zoom); - } catch (...) {} + } catch (...) { + // @INTENTIONAL: argumento no numérico → mostrar usage + } return "usage: zoom [up|down|<1-" + std::to_string(Screen::getMaxZoom()) + ">]"; } @@ -704,7 +706,7 @@ static auto cmdEdit(const std::vector& args) -> std::string { // N if ((args[0] == "SHOW" || args[0] == "HIDE") && args.size() >= 2) { if ((MapEditor::get() == nullptr) || !MapEditor::get()->isActive()) { return "Editor not active"; } bool show = (args[0] == "SHOW"); - if (args[1] == "INFO") { return MapEditor::get()->showInfo(show); } + if (args[1] == "INFO") { return MapEditor::showInfo(show); } if (args[1] == "GRID") { return MapEditor::get()->showGrid(show); } } // EDIT DRAW / EDIT COLLISION @@ -878,6 +880,7 @@ static auto cmdCheat(const std::vector& args) -> std::string { // auto& cheat = Options::cheats.infinite_lives; using State = Options::Cheat::State; const std::vector REST(args.begin() + 2, args.end()); + // cppcheck-suppress knownConditionTrueFalse -- cppcheck no infiere que REST puede estar vacío cuando args.size() == 2. if (REST.empty()) { cheat = (cheat == State::ENABLED) ? State::DISABLED : State::ENABLED; } else if (REST[0] == "ON") { @@ -1113,7 +1116,7 @@ void CommandRegistry::registerHandlers() { // NOLINT(readability-function-cogni if (path.find("tilesets") == std::string::npos) { continue; } std::string name = getFileName(path); auto dot = name.rfind('.'); - if (dot != std::string::npos) { name = name.substr(0, dot); } + if (dot != std::string::npos) { name.resize(dot); } result.push_back(toUpper(name)); } return result; @@ -1362,7 +1365,7 @@ auto CommandRegistry::getCompletions(const std::string& path) const -> std::vect if (!active_scope_.empty()) { std::string root = path; auto space = root.find(' '); - if (space != std::string::npos) { root = root.substr(0, space); } + if (space != std::string::npos) { root.resize(space); } const auto* cmd = findCommand(root); if (cmd != nullptr && !isCommandVisible(*cmd)) { return {}; } } diff --git a/source/game/ui/notifier.cpp b/source/game/ui/notifier.cpp index 140d3d9..077eee4 100644 --- a/source/game/ui/notifier.cpp +++ b/source/game/ui/notifier.cpp @@ -116,8 +116,6 @@ void Notifier::update(float delta_time) { } case Status::FINISHED: - break; - default: break; } diff --git a/source/game/ui/notifier.hpp b/source/game/ui/notifier.hpp index 915a45d..76b76b5 100644 --- a/source/game/ui/notifier.hpp +++ b/source/game/ui/notifier.hpp @@ -2,6 +2,7 @@ #include +#include #include // Para shared_ptr #include // Para string, basic_string #include // Para vector @@ -13,13 +14,13 @@ class DeltaTimer; // lines 11-11 class Notifier { public: // Justificado para las notificaciones - enum class TextAlign { + enum class TextAlign : std::uint8_t { LEFT, CENTER, }; // Forma de las notificaciones - enum class Shape { + enum class Shape : std::uint8_t { ROUNDED, SQUARED, }; @@ -65,7 +66,7 @@ class Notifier { private: // Tipos anidados - enum class Status { + enum class Status : std::uint8_t { RISING, STAY, VANISHING,