- afegides les habitacions de tot el joc (buides)
- minimapa mostra els numeros d'habitacio - tecla per a fer captures de pantalla
This commit is contained in:
@@ -7,8 +7,9 @@
|
||||
|
||||
#include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
|
||||
#include "core/locale/locale.hpp" // Para Locale
|
||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/render_info.hpp" // Para RenderInfo
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/screenshot.hpp" // Para Screenshot
|
||||
#ifdef _DEBUG
|
||||
#include "core/system/debug.hpp" // Para Debug (persistencia de render_info en debug.yaml)
|
||||
#endif
|
||||
@@ -124,6 +125,17 @@ namespace GlobalInputs {
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())});
|
||||
}
|
||||
|
||||
void handleScreenshot() {
|
||||
std::vector<Uint32> buffer;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
Screen::get()->captureComposite(buffer, width, height);
|
||||
std::string filename = Screenshot::save(buffer.data(), width, height);
|
||||
if (!filename.empty()) {
|
||||
Notifier::get()->show({filename});
|
||||
}
|
||||
}
|
||||
|
||||
void handleNextPaletteSortMode() {
|
||||
Screen::get()->nextPaletteSortMode();
|
||||
Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())});
|
||||
@@ -194,6 +206,10 @@ namespace GlobalInputs {
|
||||
if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||
return InputAction::TOGGLE_CONSOLE;
|
||||
}
|
||||
if (Input::get()->checkAction(InputAction::SCREENSHOT, Input::DO_NOT_ALLOW_REPEAT) &&
|
||||
((SDL_GetModState() & SDL_KMOD_CTRL) != 0U)) {
|
||||
return InputAction::SCREENSHOT;
|
||||
}
|
||||
return InputAction::NONE;
|
||||
}
|
||||
|
||||
@@ -294,6 +310,10 @@ namespace GlobalInputs {
|
||||
if (Console::get() != nullptr) { Console::get()->toggle(); }
|
||||
break;
|
||||
|
||||
case InputAction::SCREENSHOT:
|
||||
handleScreenshot();
|
||||
break;
|
||||
|
||||
case InputAction::TOGGLE_INFO:
|
||||
if (RenderInfo::get() != nullptr) {
|
||||
// Leemos la intención antes del toggle: isActive() incluye la
|
||||
|
||||
@@ -53,7 +53,8 @@ Input::Input(std::string game_controller_db_path)
|
||||
{Action::TOGGLE_VSYNC, KeyState{.scancode = SDL_SCANCODE_F10}},
|
||||
{Action::PAUSE, KeyState{.scancode = SDL_SCANCODE_F11}},
|
||||
{Action::TOGGLE_INFO, KeyState{.scancode = SDL_SCANCODE_F12}},
|
||||
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_GRAVE}}};
|
||||
{Action::TOGGLE_CONSOLE, KeyState{.scancode = SDL_SCANCODE_GRAVE}},
|
||||
{Action::SCREENSHOT, KeyState{.scancode = SDL_SCANCODE_S}}};
|
||||
|
||||
initSDLGamePad(); // Inicializa el subsistema SDL_INIT_GAMEPAD
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
|
||||
{InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"},
|
||||
{InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"},
|
||||
{InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"},
|
||||
{InputAction::SCREENSHOT, "SCREENSHOT"},
|
||||
{InputAction::NONE, "NONE"}};
|
||||
|
||||
const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
||||
@@ -49,6 +50,7 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
|
||||
{"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER},
|
||||
{"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET},
|
||||
{"TOGGLE_DEBUG", InputAction::TOGGLE_INFO},
|
||||
{"SCREENSHOT", InputAction::SCREENSHOT},
|
||||
{"NONE", InputAction::NONE}};
|
||||
|
||||
const std::unordered_map<SDL_GamepadButton, std::string> BUTTON_TO_STRING = {
|
||||
|
||||
@@ -36,6 +36,7 @@ enum class InputAction : std::uint8_t { // Acciones de entrada posibles en el j
|
||||
NEXT_PALETTE_SORT,
|
||||
TOGGLE_INFO,
|
||||
TOGGLE_CONSOLE,
|
||||
SCREENSHOT,
|
||||
|
||||
// Input obligatorio
|
||||
NONE,
|
||||
|
||||
@@ -221,6 +221,40 @@ void Screen::setBorderColor(Uint8 color) {
|
||||
border_argb_color_ = border_pixel_buffer_[0];
|
||||
}
|
||||
|
||||
// Captura el contenido actual (borde + juego si el borde está activo, solo juego si no)
|
||||
void Screen::captureComposite(std::vector<Uint32>& buffer, int& out_width, int& out_height) const {
|
||||
const int GAME_W = static_cast<int>(game_surface_->getWidth());
|
||||
const int GAME_H = static_cast<int>(game_surface_->getHeight());
|
||||
|
||||
if (Options::video.border.enabled) {
|
||||
const int BORDER_W = static_cast<int>(border_surface_->getWidth());
|
||||
const int BORDER_H = static_cast<int>(border_surface_->getHeight());
|
||||
const int OFF_X = Options::video.border.width;
|
||||
const int OFF_Y = Options::video.border.height;
|
||||
|
||||
buffer.resize(static_cast<size_t>(BORDER_W) * BORDER_H);
|
||||
border_surface_->toARGBBuffer(buffer.data());
|
||||
|
||||
std::vector<Uint32> game_buf(static_cast<size_t>(GAME_W) * GAME_H);
|
||||
game_surface_->toARGBBuffer(game_buf.data());
|
||||
|
||||
for (int y = 0; y < GAME_H; ++y) {
|
||||
std::memcpy(
|
||||
&buffer[(static_cast<size_t>(OFF_Y + y) * BORDER_W) + OFF_X],
|
||||
&game_buf[static_cast<size_t>(y) * GAME_W],
|
||||
static_cast<size_t>(GAME_W) * sizeof(Uint32));
|
||||
}
|
||||
|
||||
out_width = BORDER_W;
|
||||
out_height = BORDER_H;
|
||||
} else {
|
||||
buffer.resize(static_cast<size_t>(GAME_W) * GAME_H);
|
||||
game_surface_->toARGBBuffer(buffer.data());
|
||||
out_width = GAME_W;
|
||||
out_height = GAME_H;
|
||||
}
|
||||
}
|
||||
|
||||
// Cambia entre borde visible y no visible
|
||||
void Screen::toggleBorder() {
|
||||
Options::video.border.enabled = !Options::video.border.enabled;
|
||||
|
||||
@@ -82,6 +82,9 @@ class Screen {
|
||||
void setNotificationsEnabled(bool value); // Establece la visibilidad de las notificaciones
|
||||
void updateZoomFactor(); // Recalcula y almacena el factor de zoom real
|
||||
|
||||
// Captura el contenido actual (borde + juego si el borde está activo, solo juego si no)
|
||||
void captureComposite(std::vector<Uint32>& buffer, int& out_width, int& out_height) const;
|
||||
|
||||
// Getters
|
||||
auto getRenderer() -> SDL_Renderer*;
|
||||
auto getRendererSurface() -> std::shared_ptr<Surface>;
|
||||
|
||||
70
source/core/rendering/screenshot.cpp
Normal file
70
source/core/rendering/screenshot.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "core/rendering/screenshot.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <chrono> // Para system_clock, time_point
|
||||
#include <ctime> // Para localtime, strftime, time_t, tm
|
||||
#include <filesystem> // Para create_directories
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "external/stb_image_write.h" // Para stbi_write_png
|
||||
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
|
||||
namespace Screenshot {
|
||||
|
||||
namespace {
|
||||
// Genera la ruta del fichero y crea la carpeta si no existe
|
||||
auto generateFilePath(std::string& filename) -> std::string {
|
||||
std::filesystem::create_directories("screenshots");
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm* tm = std::localtime(&time);
|
||||
|
||||
char timestamp[20];
|
||||
std::strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", tm);
|
||||
|
||||
filename = std::string("scr_") + timestamp + ".png";
|
||||
return "screenshots/" + filename;
|
||||
}
|
||||
|
||||
// Convierte ARGB8888 a RGBA8888 in-place y guarda como PNG
|
||||
auto writePng(Uint32* buffer, int width, int height, const std::string& filepath) -> bool {
|
||||
for (int i = 0; i < width * height; ++i) {
|
||||
Uint32 p = buffer[i];
|
||||
Uint32 a = (p >> 24) & 0xFF;
|
||||
Uint32 r = (p >> 16) & 0xFF;
|
||||
Uint32 g = (p >> 8) & 0xFF;
|
||||
Uint32 b = p & 0xFF;
|
||||
buffer[i] = r | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
return stbi_write_png(filepath.c_str(), width, height, 4, buffer, width * 4) != 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto save(const Surface& surface) -> std::string {
|
||||
int width = static_cast<int>(surface.getWidth());
|
||||
int height = static_cast<int>(surface.getHeight());
|
||||
std::vector<Uint32> buffer(static_cast<size_t>(width * height));
|
||||
surface.toARGBBuffer(buffer.data());
|
||||
|
||||
std::string filename;
|
||||
std::string filepath = generateFilePath(filename);
|
||||
|
||||
return writePng(buffer.data(), width, height, filepath) ? filename : "";
|
||||
}
|
||||
|
||||
auto save(const Uint32* buffer, int width, int height) -> std::string {
|
||||
// Copia local para la conversión ARGB → RGBA
|
||||
std::vector<Uint32> copy(buffer, buffer + static_cast<size_t>(width * height));
|
||||
|
||||
std::string filename;
|
||||
std::string filepath = generateFilePath(filename);
|
||||
|
||||
return writePng(copy.data(), width, height, filepath) ? filename : "";
|
||||
}
|
||||
|
||||
} // namespace Screenshot
|
||||
15
source/core/rendering/screenshot.hpp
Normal file
15
source/core/rendering/screenshot.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <string> // Para string
|
||||
|
||||
class Surface;
|
||||
|
||||
namespace Screenshot {
|
||||
// Guarda la surface como PNG en screenshots/. Retorna el nombre del fichero o "" si falla.
|
||||
auto save(const Surface& surface) -> std::string;
|
||||
|
||||
// Guarda un buffer ARGB8888 como PNG en screenshots/.
|
||||
auto save(const Uint32* buffer, int width, int height) -> std::string;
|
||||
} // namespace Screenshot
|
||||
@@ -139,6 +139,7 @@ class Surface {
|
||||
|
||||
// Paleta
|
||||
void setPalette(const std::array<Uint32, 256>& palette) { palette_ = palette; }
|
||||
[[nodiscard]] auto getPalette() const -> const Palette& { return palette_; }
|
||||
[[nodiscard]] auto getPaletteColor(Uint8 index) const -> Uint32 { return palette_[index]; }
|
||||
|
||||
// Inicializa la sub paleta
|
||||
|
||||
1724
source/external/stb_image_write.h
vendored
Normal file
1724
source/external/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -119,7 +119,7 @@ namespace Defaults::Localization {
|
||||
} // namespace Defaults::Localization
|
||||
|
||||
namespace Defaults::Game::Room {
|
||||
constexpr const char* INITIAL = "03.yaml"; // Habitación de inicio
|
||||
constexpr const char* INITIAL = "001.yaml"; // Habitación de inicio
|
||||
} // namespace Defaults::Game::Room
|
||||
|
||||
namespace Defaults::Game::Player {
|
||||
|
||||
@@ -1806,7 +1806,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
try {
|
||||
int num = std::stoi(val);
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "%02d.yaml", num);
|
||||
std::snprintf(buf, sizeof(buf), "%03d.yaml", num);
|
||||
connection = buf;
|
||||
} catch (...) {
|
||||
connection = val;
|
||||
@@ -1937,7 +1937,7 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string { //
|
||||
int new_num = 1;
|
||||
while (used.contains(new_num)) { ++new_num; }
|
||||
char name_buf[16];
|
||||
std::snprintf(name_buf, sizeof(name_buf), "%02d.yaml", new_num);
|
||||
std::snprintf(name_buf, sizeof(name_buf), "%03d.yaml", new_num);
|
||||
std::string new_name = name_buf;
|
||||
|
||||
// Derivar la ruta de la carpeta de rooms
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "core/rendering/screen.hpp" // Para Screen
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/rendering/screenshot.hpp" // Para Screenshot::save
|
||||
#include "core/rendering/text.hpp" // Para Text (números de room)
|
||||
#include "core/resources/resource_cache.hpp" // Para Resource::Cache
|
||||
#include "game/gameplay/room.hpp" // Para Room::Data
|
||||
#include "utils/defines.hpp" // Para Tile::SIZE, PlayArea
|
||||
@@ -283,6 +285,25 @@ void MiniMap::drawConnections() {
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja los números de room centrados en cada celda
|
||||
void MiniMap::renderRoomNumbers(float offset_x, float offset_y) {
|
||||
auto text = Resource::Cache::get()->getText("8bithud");
|
||||
if (!text) { return; }
|
||||
|
||||
int font_h = text->getCharacterSize();
|
||||
|
||||
for (const auto& [name, mini] : room_positions_) {
|
||||
// "001.yaml" → "001"
|
||||
std::string number = name.substr(0, name.find_last_of('.'));
|
||||
|
||||
int text_w = text->length(number);
|
||||
int text_x = static_cast<int>(offset_x) + cellPixelX(mini.pos.x) + (CELL_W - text_w) / 2;
|
||||
int text_y = static_cast<int>(offset_y) + cellPixelY(mini.pos.y) + (CELL_H - font_h) / 2;
|
||||
|
||||
text->writeDX(Text::COLOR_FLAG | Text::SHADOW_FLAG, text_x, text_y, number, 1, COLOR_NUMBER_TEXT, 1, COLOR_NUMBER_SHADOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Centra el viewport en una room
|
||||
void MiniMap::centerOnRoom(const std::string& room_name) {
|
||||
auto it = room_positions_.find(room_name);
|
||||
@@ -324,6 +345,9 @@ void MiniMap::render(const std::string& current_room) {
|
||||
SDL_FRect dst = {.x = vx, .y = vy, .w = static_cast<float>(map_width_ + SHADOW_OFFSET), .h = static_cast<float>(map_height_ + SHADOW_OFFSET)};
|
||||
map_surface_->render(nullptr, &dst);
|
||||
|
||||
// Números de room (sobre la surface del mapa, antes del highlight)
|
||||
if (show_numbers_) { renderRoomNumbers(vx, vy); }
|
||||
|
||||
// Highlight de la room actual (solo si está completamente visible en el play area)
|
||||
auto it = room_positions_.find(current_room);
|
||||
if (it != room_positions_.end()) {
|
||||
@@ -340,6 +364,32 @@ void MiniMap::render(const std::string& current_room) {
|
||||
|
||||
// Maneja eventos del minimapa (drag para explorar, click para navegar)
|
||||
void MiniMap::handleEvent(const SDL_Event& event, const std::string& current_room) {
|
||||
// Toggle de números de room
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_N && static_cast<int>(event.key.repeat) == 0) {
|
||||
show_numbers_ = !show_numbers_;
|
||||
return;
|
||||
}
|
||||
|
||||
// Captura del minimapa
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_S && static_cast<int>(event.key.repeat) == 0) {
|
||||
if (map_surface_) {
|
||||
// Renderizar números sobre map_surface_ si están activos
|
||||
if (show_numbers_) {
|
||||
auto prev = Screen::get()->getRendererSurface();
|
||||
Screen::get()->setRendererSurface(map_surface_);
|
||||
renderRoomNumbers(0.0F, 0.0F);
|
||||
Screen::get()->setRendererSurface(prev);
|
||||
}
|
||||
auto game_surface = Screen::get()->getRendererSurface();
|
||||
if (game_surface) { map_surface_->setPalette(game_surface->getPalette()); }
|
||||
std::string file = Screenshot::save(*map_surface_);
|
||||
if (!file.empty()) { std::cout << "MiniMap screenshot: " << file << "\n"; }
|
||||
// Recomponer para limpiar los números de la surface
|
||||
if (show_numbers_) { composeFinalSurface(); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) {
|
||||
// Guardar posición inicial para detectar si es click o drag
|
||||
float mouse_x = 0.0F;
|
||||
|
||||
@@ -62,6 +62,7 @@ class MiniMap {
|
||||
void composeFinalSurface();
|
||||
auto getRoomMiniSurface(const std::string& room_name) -> std::shared_ptr<Surface>;
|
||||
void drawConnections();
|
||||
void renderRoomNumbers(float offset_x, float offset_y);
|
||||
auto roomAtScreen(float screen_x, float screen_y) -> std::string;
|
||||
auto cellPixelX(int grid_x) const -> int { return PADDING + ((grid_x - min_grid_x_) * (CELL_W + GAP)); }
|
||||
auto cellPixelY(int grid_y) const -> int { return PADDING + ((grid_y - min_grid_y_) * (CELL_H + GAP)); }
|
||||
@@ -89,6 +90,7 @@ class MiniMap {
|
||||
float drag_start_y_{0.0F};
|
||||
float view_start_x_{0.0F}; // Viewport al inicio del drag
|
||||
float view_start_y_{0.0F};
|
||||
bool show_numbers_{false}; // Toggle para mostrar números de room
|
||||
|
||||
// Constantes
|
||||
static constexpr int ROOM_W = Map::WIDTH; // Ancho de una room en pixels del minimapa (1 pixel por tile)
|
||||
@@ -103,8 +105,10 @@ class MiniMap {
|
||||
// Colores del minimapa (índices de paleta)
|
||||
Uint8 bg_color_{2}; // Fondo general (configurable)
|
||||
Uint8 conn_color_{14}; // Líneas de conexión (configurable)
|
||||
static constexpr Uint8 COLOR_ROOM_BORDER = 0; // Borde de cada miniroom
|
||||
static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom
|
||||
static constexpr Uint8 COLOR_ROOM_BORDER = 0; // Borde de cada miniroom
|
||||
static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom
|
||||
static constexpr Uint8 COLOR_NUMBER_TEXT = 15; // Texto de números (blanco)
|
||||
static constexpr Uint8 COLOR_NUMBER_SHADOW = 1; // Sombra de números (oscuro)
|
||||
};
|
||||
|
||||
#endif // _DEBUG
|
||||
|
||||
@@ -100,7 +100,7 @@ void RoomFormat::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, con
|
||||
|
||||
const auto& room_node = yaml["room"];
|
||||
|
||||
// Extract room number from filename (e.g., "01.yaml" → "01")
|
||||
// Extract room number from filename (e.g., "001.yaml" → "001")
|
||||
room.number = file_name.substr(0, file_name.find_last_of('.'));
|
||||
|
||||
// --- Resolución de zona + overrides (tileSetFile, music) ---
|
||||
|
||||
@@ -583,7 +583,7 @@ static auto changeRoomWithEditor(const std::string& room_file) -> std::string {
|
||||
|
||||
static auto cmdRoom(const std::vector<std::string>& args) -> std::string { // NOLINT(readability-function-cognitive-complexity)
|
||||
if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; }
|
||||
if (args.empty()) { return "usage: room <1-60>|next|prev|left|right|up|down"; }
|
||||
if (args.empty()) { return "usage: room <num>|next|prev|left|right|up|down"; }
|
||||
|
||||
// DELETE: borrar la habitación actual
|
||||
if (args[0] == "DELETE") {
|
||||
@@ -619,11 +619,11 @@ static auto cmdRoom(const std::vector<std::string>& args) -> std::string { // N
|
||||
} else {
|
||||
try {
|
||||
num = std::stoi(args[0]);
|
||||
} catch (...) { return "usage: room <1-60>|next|prev|left|right|up|down"; }
|
||||
} catch (...) { return "usage: room <num>|next|prev|left|right|up|down"; }
|
||||
}
|
||||
if (num < 1 || num > 60) { return "Room must be between 1 and 60"; }
|
||||
if (num < 0) { return "Room number must be >= 0"; }
|
||||
char buf[16];
|
||||
std::snprintf(buf, sizeof(buf), "%02d.yaml", num);
|
||||
std::snprintf(buf, sizeof(buf), "%03d.yaml", num);
|
||||
return changeRoomWithEditor(buf);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user