continue amb els ambits

This commit is contained in:
2026-04-02 23:01:39 +02:00
parent 9ffd29bea8
commit 6faa80eef4
11 changed files with 199 additions and 109 deletions

View File

@@ -182,7 +182,8 @@ categories:
- keyword: HELP - keyword: HELP
handler: cmd_help handler: cmd_help
description: "Show this help" description: "Show this help"
usage: "HELP / ?" usage: "HELP [<command>]"
dynamic_completions: true
- keyword: "?" - keyword: "?"
handler: cmd_help handler: cmd_help
@@ -271,6 +272,7 @@ categories:
description: "Game cheats (GAME only)" description: "Game cheats (GAME only)"
usage: "CHEAT [INFINITE LIVES|INVINCIBILITY|OPEN THE JAIL|CLOSE THE JAIL]" usage: "CHEAT [INFINITE LIVES|INVINCIBILITY|OPEN THE JAIL|CLOSE THE JAIL]"
hidden: true hidden: true
help_hidden: true
completions: completions:
CHEAT: [INFINITE, INVINCIBILITY, OPEN, CLOSE] CHEAT: [INFINITE, INVINCIBILITY, OPEN, CLOSE]
CHEAT INFINITE: [LIVES] CHEAT INFINITE: [LIVES]
@@ -282,3 +284,4 @@ categories:
CHEAT CLOSE THE: [JAIL] CHEAT CLOSE THE: [JAIL]
debug_extras: debug_extras:
hidden: false hidden: false
help_hidden: false

View File

@@ -18,9 +18,9 @@
#include "core/resources/resource_list.hpp" // Para Resource::List #include "core/resources/resource_list.hpp" // Para Resource::List
#include "core/resources/resource_types.hpp" // Para RoomResource #include "core/resources/resource_types.hpp" // Para RoomResource
#include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar #include "game/editor/editor_statusbar.hpp" // Para EditorStatusBar
#include "game/game_control.hpp" // Para GameControl
#include "game/editor/room_saver.hpp" // Para RoomSaver #include "game/editor/room_saver.hpp" // Para RoomSaver
#include "game/entities/player.hpp" // Para Player #include "game/entities/player.hpp" // Para Player
#include "game/game_control.hpp" // Para GameControl
#include "game/gameplay/enemy_manager.hpp" // Para EnemyManager #include "game/gameplay/enemy_manager.hpp" // Para EnemyManager
#include "game/gameplay/item_manager.hpp" // Para ItemManager #include "game/gameplay/item_manager.hpp" // Para ItemManager
#include "game/gameplay/room.hpp" // Para Room #include "game/gameplay/room.hpp" // Para Room
@@ -69,12 +69,18 @@ void MapEditor::loadSettings() {
if (yaml.contains("grid")) { settings_.grid = yaml["grid"].get_value<bool>(); } if (yaml.contains("grid")) { settings_.grid = yaml["grid"].get_value<bool>(); }
if (yaml.contains("show_render_info")) { settings_.show_render_info = yaml["show_render_info"].get_value<bool>(); } if (yaml.contains("show_render_info")) { settings_.show_render_info = yaml["show_render_info"].get_value<bool>(); }
if (yaml.contains("minimap_bg")) { if (yaml.contains("minimap_bg")) {
if (yaml["minimap_bg"].is_string()) { settings_.minimap_bg = yaml["minimap_bg"].get_value<std::string>(); } if (yaml["minimap_bg"].is_string()) {
else if (yaml["minimap_bg"].is_integer()) { settings_.minimap_bg = std::to_string(yaml["minimap_bg"].get_value<int>()); } settings_.minimap_bg = yaml["minimap_bg"].get_value<std::string>();
} else if (yaml["minimap_bg"].is_integer()) {
settings_.minimap_bg = std::to_string(yaml["minimap_bg"].get_value<int>());
}
} }
if (yaml.contains("minimap_conn")) { if (yaml.contains("minimap_conn")) {
if (yaml["minimap_conn"].is_string()) { settings_.minimap_conn = yaml["minimap_conn"].get_value<std::string>(); } if (yaml["minimap_conn"].is_string()) {
else if (yaml["minimap_conn"].is_integer()) { settings_.minimap_conn = std::to_string(yaml["minimap_conn"].get_value<int>()); } settings_.minimap_conn = yaml["minimap_conn"].get_value<std::string>();
} else if (yaml["minimap_conn"].is_integer()) {
settings_.minimap_conn = std::to_string(yaml["minimap_conn"].get_value<int>());
}
} }
} catch (...) { } catch (...) {
// Fichero corrupto o vacío, usar defaults // Fichero corrupto o vacío, usar defaults
@@ -116,8 +122,9 @@ auto MapEditor::showGrid(bool show) -> std::string {
// Parsea un color por nombre o índice numérico // Parsea un color por nombre o índice numérico
static auto parseColor(const std::string& value) -> Uint8 { static auto parseColor(const std::string& value) -> Uint8 {
try { return static_cast<Uint8>(std::stoi(value)); } try {
catch (...) { return stringToColor(value); } return static_cast<Uint8>(std::stoi(value));
} catch (...) { return stringToColor(value); }
} }
void MapEditor::toggleMiniMap() { void MapEditor::toggleMiniMap() {
@@ -243,7 +250,7 @@ void MapEditor::exit() {
// Restaurar prompt y scope de la consola // Restaurar prompt y scope de la consola
selected_enemy_ = -1; selected_enemy_ = -1;
Console::get()->setPrompt("> "); Console::get()->setPrompt("> ");
Console::get()->setScope(""); Console::get()->setScope("debug");
drag_ = {}; drag_ = {};
statusbar_.reset(); statusbar_.reset();
room_.reset(); room_.reset();
@@ -421,14 +428,13 @@ void MapEditor::handleEvent(const SDL_Event& event) {
// Tile bajo el cursor como tile actual del picker // Tile bajo el cursor como tile actual del picker
int tile_index = mouse_tile_y_ * 32 + mouse_tile_x_; int tile_index = mouse_tile_y_ * 32 + mouse_tile_x_;
int current = (tile_index >= 0 && tile_index < static_cast<int>(room_data_.tile_map.size())) int current = (tile_index >= 0 && tile_index < static_cast<int>(room_data_.tile_map.size()))
? room_data_.tile_map[tile_index] ? room_data_.tile_map[tile_index]
: -1; : -1;
tile_picker_.on_select = [this](int tile) { tile_picker_.on_select = [this](int tile) {
brush_tile_ = tile; brush_tile_ = tile;
}; };
tile_picker_.open(room_->getTileSetFile(), current, tile_picker_.open(room_->getTileSetFile(), current, stringToColor(room_data_.bg_color));
stringToColor(room_data_.bg_color));
return; return;
} }
@@ -1178,19 +1184,36 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
// Dirección opuesta para la conexión recíproca // Dirección opuesta para la conexión recíproca
std::string opposite; std::string opposite;
std::string* our_field = nullptr; std::string* our_field = nullptr;
if (property == "UP") { opposite = "lower_room"; our_field = &room_data_.upper_room; } if (property == "UP") {
if (property == "DOWN") { opposite = "upper_room"; our_field = &room_data_.lower_room; } opposite = "lower_room";
if (property == "LEFT") { opposite = "right_room"; our_field = &room_data_.left_room; } our_field = &room_data_.upper_room;
if (property == "RIGHT") { opposite = "left_room"; our_field = &room_data_.right_room; } }
if (property == "DOWN") {
opposite = "upper_room";
our_field = &room_data_.lower_room;
}
if (property == "LEFT") {
opposite = "right_room";
our_field = &room_data_.left_room;
}
if (property == "RIGHT") {
opposite = "left_room";
our_field = &room_data_.right_room;
}
// Si había una conexión anterior, romper la recíproca de la otra room // Si había una conexión anterior, romper la recíproca de la otra room
if (our_field != nullptr && *our_field != "0" && !our_field->empty()) { if (our_field != nullptr && *our_field != "0" && !our_field->empty()) {
auto old_other = Resource::Cache::get()->getRoom(*our_field); auto old_other = Resource::Cache::get()->getRoom(*our_field);
if (old_other) { if (old_other) {
if (opposite == "upper_room") { old_other->upper_room = "0"; } if (opposite == "upper_room") {
else if (opposite == "lower_room") { old_other->lower_room = "0"; } old_other->upper_room = "0";
else if (opposite == "left_room") { old_other->left_room = "0"; } } else if (opposite == "lower_room") {
else if (opposite == "right_room") { old_other->right_room = "0"; } old_other->lower_room = "0";
} else if (opposite == "left_room") {
old_other->left_room = "0";
} else if (opposite == "right_room") {
old_other->right_room = "0";
}
// Guardar la otra room // Guardar la otra room
std::string other_path = Resource::List::get()->get(*our_field); std::string other_path = Resource::List::get()->get(*our_field);
if (!other_path.empty()) { if (!other_path.empty()) {
@@ -1207,10 +1230,15 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
if (connection != "0") { if (connection != "0") {
auto other = Resource::Cache::get()->getRoom(connection); auto other = Resource::Cache::get()->getRoom(connection);
if (other) { if (other) {
if (opposite == "upper_room") { other->upper_room = room_path_; } if (opposite == "upper_room") {
else if (opposite == "lower_room") { other->lower_room = room_path_; } other->upper_room = room_path_;
else if (opposite == "left_room") { other->left_room = room_path_; } } else if (opposite == "lower_room") {
else if (opposite == "right_room") { other->right_room = room_path_; } other->lower_room = room_path_;
} else if (opposite == "left_room") {
other->left_room = room_path_;
} else if (opposite == "right_room") {
other->right_room = room_path_;
}
std::string other_path = Resource::List::get()->get(connection); std::string other_path = Resource::List::get()->get(connection);
if (!other_path.empty()) { if (!other_path.empty()) {
auto other_yaml = RoomSaver::loadYAML(other_path); auto other_yaml = RoomSaver::loadYAML(other_path);
@@ -1306,10 +1334,15 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
// Comprobar que no hay ya una room en esa dirección // Comprobar que no hay ya una room en esa dirección
if (!direction.empty()) { if (!direction.empty()) {
std::string* existing = nullptr; std::string* existing = nullptr;
if (direction == "UP") { existing = &room_data_.upper_room; } if (direction == "UP") {
else if (direction == "DOWN") { existing = &room_data_.lower_room; } existing = &room_data_.upper_room;
else if (direction == "LEFT") { existing = &room_data_.left_room; } } else if (direction == "DOWN") {
else if (direction == "RIGHT") { existing = &room_data_.right_room; } existing = &room_data_.lower_room;
} else if (direction == "LEFT") {
existing = &room_data_.left_room;
} else if (direction == "RIGHT") {
existing = &room_data_.right_room;
}
if (existing != nullptr && *existing != "0" && !existing->empty()) { if (existing != nullptr && *existing != "0" && !existing->empty()) {
return "Already connected " + toLower(direction) + ": " + *existing; return "Already connected " + toLower(direction) + ": " + *existing;
} }
@@ -1319,8 +1352,9 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
auto& rooms = Resource::Cache::get()->getRooms(); auto& rooms = Resource::Cache::get()->getRooms();
std::set<int> used; std::set<int> used;
for (const auto& r : rooms) { for (const auto& r : rooms) {
try { used.insert(std::stoi(r.name.substr(0, r.name.find('.')))); } try {
catch (...) {} used.insert(std::stoi(r.name.substr(0, r.name.find('.'))));
} catch (...) {}
} }
int new_num = 1; int new_num = 1;
while (used.contains(new_num)) { ++new_num; } while (used.contains(new_num)) { ++new_num; }
@@ -1351,10 +1385,15 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
new_room.tile_map.resize(32 * 16, -1); new_room.tile_map.resize(32 * 16, -1);
// Conexión recíproca: la nueva room conecta de vuelta a la actual // Conexión recíproca: la nueva room conecta de vuelta a la actual
if (direction == "UP") { new_room.lower_room = room_path_; } if (direction == "UP") {
else if (direction == "DOWN") { new_room.upper_room = room_path_; } new_room.lower_room = room_path_;
else if (direction == "LEFT") { new_room.right_room = room_path_; } } else if (direction == "DOWN") {
else if (direction == "RIGHT") { new_room.left_room = room_path_; } new_room.upper_room = room_path_;
} else if (direction == "LEFT") {
new_room.right_room = room_path_;
} else if (direction == "RIGHT") {
new_room.left_room = room_path_;
}
// Conexiones del YAML // Conexiones del YAML
auto connStr = [](const std::string& c) -> std::string { return (c == "0") ? "null" : c; }; auto connStr = [](const std::string& c) -> std::string { return (c == "0") ? "null" : c; };
@@ -1400,10 +1439,15 @@ auto MapEditor::createNewRoom(const std::string& direction) -> std::string {
addRoomToAssetsYaml(new_name); addRoomToAssetsYaml(new_name);
// Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva) // Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva)
if (direction == "UP") { room_data_.upper_room = new_name; } if (direction == "UP") {
else if (direction == "DOWN") { room_data_.lower_room = new_name; } room_data_.upper_room = new_name;
else if (direction == "LEFT") { room_data_.left_room = new_name; } } else if (direction == "DOWN") {
else if (direction == "RIGHT") { room_data_.right_room = new_name; } room_data_.lower_room = new_name;
} else if (direction == "LEFT") {
room_data_.left_room = new_name;
} else if (direction == "RIGHT") {
room_data_.right_room = new_name;
}
if (!direction.empty()) { autosave(); } if (!direction.empty()) { autosave(); }
@@ -1425,15 +1469,23 @@ auto MapEditor::deleteRoom() -> std::string {
// Buscar una room vecina a la que navegar después de borrar // Buscar una room vecina a la que navegar después de borrar
std::string target = "0"; std::string target = "0";
if (room_data_.upper_room != "0" && !room_data_.upper_room.empty()) { target = room_data_.upper_room; } if (room_data_.upper_room != "0" && !room_data_.upper_room.empty()) {
else if (room_data_.lower_room != "0" && !room_data_.lower_room.empty()) { target = room_data_.lower_room; } target = room_data_.upper_room;
else if (room_data_.left_room != "0" && !room_data_.left_room.empty()) { target = room_data_.left_room; } } else if (room_data_.lower_room != "0" && !room_data_.lower_room.empty()) {
else if (room_data_.right_room != "0" && !room_data_.right_room.empty()) { target = room_data_.right_room; } target = room_data_.lower_room;
} else if (room_data_.left_room != "0" && !room_data_.left_room.empty()) {
target = room_data_.left_room;
} else if (room_data_.right_room != "0" && !room_data_.right_room.empty()) {
target = room_data_.right_room;
}
if (target == "0") { if (target == "0") {
// Buscar la primera room que no sea esta // Buscar la primera room que no sea esta
for (const auto& r : Resource::Cache::get()->getRooms()) { for (const auto& r : Resource::Cache::get()->getRooms()) {
if (r.name != deleted_name) { target = r.name; break; } if (r.name != deleted_name) {
target = r.name;
break;
}
} }
} }
if (target == "0") { return "Cannot delete: no other room to navigate to"; } if (target == "0") { return "Cannot delete: no other room to navigate to"; }
@@ -1443,10 +1495,15 @@ auto MapEditor::deleteRoom() -> std::string {
if (neighbor == "0" || neighbor.empty()) { return; } if (neighbor == "0" || neighbor.empty()) { return; }
auto other = Resource::Cache::get()->getRoom(neighbor); auto other = Resource::Cache::get()->getRoom(neighbor);
if (!other) { return; } if (!other) { return; }
if (field_name == "upper_room") { other->upper_room = "0"; } if (field_name == "upper_room") {
else if (field_name == "lower_room") { other->lower_room = "0"; } other->upper_room = "0";
else if (field_name == "left_room") { other->left_room = "0"; } } else if (field_name == "lower_room") {
else if (field_name == "right_room") { other->right_room = "0"; } other->lower_room = "0";
} else if (field_name == "left_room") {
other->left_room = "0";
} else if (field_name == "right_room") {
other->right_room = "0";
}
// Guardar la otra room // Guardar la otra room
std::string other_path = Resource::List::get()->get(neighbor); std::string other_path = Resource::List::get()->get(neighbor);
if (!other_path.empty()) { if (!other_path.empty()) {
@@ -1474,8 +1531,7 @@ auto MapEditor::deleteRoom() -> std::string {
// Quitar del cache // Quitar del cache
auto& cache_rooms = Resource::Cache::get()->getRooms(); auto& cache_rooms = Resource::Cache::get()->getRooms();
cache_rooms.erase( cache_rooms.erase(
std::remove_if(cache_rooms.begin(), cache_rooms.end(), std::remove_if(cache_rooms.begin(), cache_rooms.end(), [&](const RoomResource& r) { return r.name == deleted_name; }),
[&](const RoomResource& r) { return r.name == deleted_name; }),
cache_rooms.end()); cache_rooms.end());
// Quitar de assets.yaml // Quitar de assets.yaml

View File

@@ -118,12 +118,12 @@ class MapEditor {
// Estado del editor // Estado del editor
bool active_{false}; bool active_{false};
DragState drag_; DragState drag_;
int selected_enemy_{-1}; // Índice del enemigo seleccionado (-1 = ninguno) int selected_enemy_{-1}; // Índice del enemigo seleccionado (-1 = ninguno)
int selected_item_{-1}; // Índice del item seleccionado (-1 = ninguno) int selected_item_{-1}; // Índice del item seleccionado (-1 = ninguno)
static constexpr int NO_BRUSH = -2; // Sin brush activo static constexpr int NO_BRUSH = -2; // Sin brush activo
static constexpr int ERASER_BRUSH = -1; // Brush borrador (pinta tile vacío = -1) static constexpr int ERASER_BRUSH = -1; // Brush borrador (pinta tile vacío = -1)
int brush_tile_{NO_BRUSH}; // Tile activo para pintar int brush_tile_{NO_BRUSH}; // Tile activo para pintar
bool painting_{false}; // true mientras se está pintando con click izquierdo mantenido bool painting_{false}; // true mientras se está pintando con click izquierdo mantenido
// Datos de la habitación // Datos de la habitación
Room::Data room_data_; Room::Data room_data_;

View File

@@ -3,8 +3,8 @@
#include "game/editor/mini_map.hpp" #include "game/editor/mini_map.hpp"
#include <algorithm> // Para std::max, std::min #include <algorithm> // Para std::max, std::min
#include <cmath> // Para std::floor
#include <array> // Para std::array #include <array> // Para std::array
#include <cmath> // Para std::floor
#include <iostream> // Para cout #include <iostream> // Para cout
#include <map> // Para std::map #include <map> // Para std::map
#include <queue> // Para queue (BFS) #include <queue> // Para queue (BFS)
@@ -18,7 +18,9 @@
#include "utils/utils.hpp" // Para stringToColor #include "utils/utils.hpp" // Para stringToColor
// Constructor: construye todo el minimapa // Constructor: construye todo el minimapa
MiniMap::MiniMap(Uint8 bg_color, Uint8 conn_color) : bg_color_(bg_color), conn_color_(conn_color) { MiniMap::MiniMap(Uint8 bg_color, Uint8 conn_color)
: bg_color_(bg_color),
conn_color_(conn_color) {
buildTileColorTable("standard.gif"); buildTileColorTable("standard.gif");
layoutRooms(); layoutRooms();
buildRoomSurfaces(); buildRoomSurfaces();
@@ -217,8 +219,7 @@ void MiniMap::composeFinalSurface() {
if (!mini.surface) { continue; } if (!mini.surface) { continue; }
int px = cellPixelX(mini.pos.x) + SHADOW_OFFSET; int px = cellPixelX(mini.pos.x) + SHADOW_OFFSET;
int py = cellPixelY(mini.pos.y) + SHADOW_OFFSET; int py = cellPixelY(mini.pos.y) + SHADOW_OFFSET;
SDL_FRect shadow = {.x = static_cast<float>(px), .y = static_cast<float>(py), SDL_FRect shadow = {.x = static_cast<float>(px), .y = static_cast<float>(py), .w = static_cast<float>(CELL_W), .h = static_cast<float>(CELL_H)};
.w = static_cast<float>(CELL_W), .h = static_cast<float>(CELL_H)};
map_surface_->fillRect(&shadow, COLOR_SHADOW); map_surface_->fillRect(&shadow, COLOR_SHADOW);
} }
@@ -229,13 +230,11 @@ void MiniMap::composeFinalSurface() {
int py = cellPixelY(mini.pos.y); int py = cellPixelY(mini.pos.y);
// Borde negro (la celda entera) // Borde negro (la celda entera)
SDL_FRect cell = {.x = static_cast<float>(px), .y = static_cast<float>(py), SDL_FRect cell = {.x = static_cast<float>(px), .y = static_cast<float>(py), .w = static_cast<float>(CELL_W), .h = static_cast<float>(CELL_H)};
.w = static_cast<float>(CELL_W), .h = static_cast<float>(CELL_H)};
map_surface_->fillRect(&cell, COLOR_ROOM_BORDER); map_surface_->fillRect(&cell, COLOR_ROOM_BORDER);
// Miniroom dentro del borde // Miniroom dentro del borde
SDL_FRect dst = {.x = static_cast<float>(px + BORDER), .y = static_cast<float>(py + BORDER), SDL_FRect dst = {.x = static_cast<float>(px + BORDER), .y = static_cast<float>(py + BORDER), .w = static_cast<float>(ROOM_W), .h = static_cast<float>(ROOM_H)};
.w = static_cast<float>(ROOM_W), .h = static_cast<float>(ROOM_H)};
mini.surface->render(nullptr, &dst); mini.surface->render(nullptr, &dst);
} }
@@ -255,8 +254,7 @@ void MiniMap::drawConnections() {
if (room_data->right_room != "0" && !room_data->right_room.empty() && room_positions_.contains(room_data->right_room)) { if (room_data->right_room != "0" && !room_data->right_room.empty() && room_positions_.contains(room_data->right_room)) {
int x1 = px + CELL_W; int x1 = px + CELL_W;
int y_mid = py + CELL_H / 2 - 1; int y_mid = py + CELL_H / 2 - 1;
SDL_FRect line = {.x = static_cast<float>(x1), .y = static_cast<float>(y_mid), SDL_FRect line = {.x = static_cast<float>(x1), .y = static_cast<float>(y_mid), .w = static_cast<float>(GAP), .h = 3.0F};
.w = static_cast<float>(GAP), .h = 3.0F};
map_surface_->fillRect(&line, conn_color_); map_surface_->fillRect(&line, conn_color_);
} }
@@ -264,8 +262,7 @@ void MiniMap::drawConnections() {
if (room_data->lower_room != "0" && !room_data->lower_room.empty() && room_positions_.contains(room_data->lower_room)) { if (room_data->lower_room != "0" && !room_data->lower_room.empty() && room_positions_.contains(room_data->lower_room)) {
int x_mid = px + CELL_W / 2 - 1; int x_mid = px + CELL_W / 2 - 1;
int y1 = py + CELL_H; int y1 = py + CELL_H;
SDL_FRect line = {.x = static_cast<float>(x_mid), .y = static_cast<float>(y1), SDL_FRect line = {.x = static_cast<float>(x_mid), .y = static_cast<float>(y1), .w = 3.0F, .h = static_cast<float>(GAP)};
.w = 3.0F, .h = static_cast<float>(GAP)};
map_surface_->fillRect(&line, conn_color_); map_surface_->fillRect(&line, conn_color_);
} }
} }
@@ -309,8 +306,7 @@ void MiniMap::render(const std::string& current_room) {
// Renderizar la surface del minimapa con el viewport actual (alineado a pixel) // Renderizar la surface del minimapa con el viewport actual (alineado a pixel)
float vx = std::floor(view_x_); float vx = std::floor(view_x_);
float vy = std::floor(view_y_); float vy = std::floor(view_y_);
SDL_FRect dst = {.x = vx, .y = vy, SDL_FRect dst = {.x = vx, .y = vy, .w = static_cast<float>(map_width_ + SHADOW_OFFSET), .h = static_cast<float>(map_height_ + SHADOW_OFFSET)};
.w = static_cast<float>(map_width_ + SHADOW_OFFSET), .h = static_cast<float>(map_height_ + SHADOW_OFFSET)};
map_surface_->render(nullptr, &dst); map_surface_->render(nullptr, &dst);
// Highlight de la room actual (solo si está completamente visible en el play area) // Highlight de la room actual (solo si está completamente visible en el play area)

View File

@@ -4,11 +4,11 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <functional> // Para function #include <functional> // Para function
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
#include <vector> // Para vector #include <vector> // Para vector
class Surface; class Surface;
@@ -58,9 +58,9 @@ class MiniMap {
auto cellPixelY(int grid_y) const -> int { return PADDING + (grid_y - min_grid_y_) * (CELL_H + GAP); } auto cellPixelY(int grid_y) const -> int { return PADDING + (grid_y - min_grid_y_) * (CELL_H + GAP); }
// Tabla de color predominante por tile index // Tabla de color predominante por tile index
std::vector<Uint8> tile_colors_; // tile_index → palette color index std::vector<Uint8> tile_colors_; // tile_index → palette color index
int tileset_width_{0}; // Ancho del tileset en tiles int tileset_width_{0}; // Ancho del tileset en tiles
Uint8 tileset_transparent_{16}; // Color transparente del tileset Uint8 tileset_transparent_{16}; // Color transparente del tileset
// Rooms renderizadas y posicionadas // Rooms renderizadas y posicionadas
std::unordered_map<std::string, RoomMini> room_positions_; std::unordered_map<std::string, RoomMini> room_positions_;
@@ -84,20 +84,20 @@ class MiniMap {
float view_start_y_{0.0F}; float view_start_y_{0.0F};
// Constantes // Constantes
static constexpr int ROOM_W = 32; // Ancho de una room en pixels del minimapa static constexpr int ROOM_W = 32; // Ancho de una room en pixels del minimapa
static constexpr int ROOM_H = 16; // Alto de una room en pixels del minimapa static constexpr int ROOM_H = 16; // Alto de una room en pixels del minimapa
static constexpr int BORDER = 1; // Borde alrededor de cada room static constexpr int BORDER = 1; // Borde alrededor de cada room
static constexpr int CELL_W = ROOM_W + BORDER * 2; // Room + borde static constexpr int CELL_W = ROOM_W + BORDER * 2; // Room + borde
static constexpr int CELL_H = ROOM_H + BORDER * 2; static constexpr int CELL_H = ROOM_H + BORDER * 2;
static constexpr int GAP = 4; // Separación entre celdas static constexpr int GAP = 4; // Separación entre celdas
static constexpr int SHADOW_OFFSET = 1; // Desplazamiento de la sombra static constexpr int SHADOW_OFFSET = 1; // Desplazamiento de la sombra
static constexpr int PADDING = 4; // Padding alrededor del minimapa static constexpr int PADDING = 4; // Padding alrededor del minimapa
// Colores del minimapa (índices de paleta) // Colores del minimapa (índices de paleta)
Uint8 bg_color_{2}; // Fondo general (configurable) Uint8 bg_color_{2}; // Fondo general (configurable)
Uint8 conn_color_{14}; // Líneas de conexión (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_ROOM_BORDER = 0; // Borde de cada miniroom
static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom static constexpr Uint8 COLOR_SHADOW = 1; // Sombra de cada miniroom
}; };
#endif // _DEBUG #endif // _DEBUG

View File

@@ -14,9 +14,7 @@
static constexpr int BORDER_PAD = 3; static constexpr int BORDER_PAD = 3;
// Abre el picker con un tileset // Abre el picker con un tileset
void TilePicker::open(const std::string& tileset_name, int current_tile, void TilePicker::open(const std::string& tileset_name, int current_tile, int bg_color, int source_color, int target_color, int tile_spacing_in, int tile_spacing_out) {
int bg_color, int source_color, int target_color,
int tile_spacing_in, int tile_spacing_out) {
tileset_ = Resource::Cache::get()->getSurface(tileset_name); tileset_ = Resource::Cache::get()->getSurface(tileset_name);
if (!tileset_) { if (!tileset_) {
open_ = false; open_ = false;
@@ -70,16 +68,15 @@ void TilePicker::open(const std::string& tileset_name, int current_tile,
SDL_FRect src = { SDL_FRect src = {
.x = static_cast<float>(col * src_cell), .x = static_cast<float>(col * src_cell),
.y = static_cast<float>(row * src_cell), .y = static_cast<float>(row * src_cell),
.w = TS, .h = TS}; .w = TS,
.h = TS};
// Destino: posición en el frame con spacing de salida // Destino: posición en el frame con spacing de salida
int dst_x = BORDER_PAD + col * out_cell; int dst_x = BORDER_PAD + col * out_cell;
int dst_y = BORDER_PAD + row * out_cell; int dst_y = BORDER_PAD + row * out_cell;
if (source_color >= 0 && target_color >= 0) { if (source_color >= 0 && target_color >= 0) {
tileset_->renderWithColorReplace(dst_x, dst_y, tileset_->renderWithColorReplace(dst_x, dst_y, static_cast<Uint8>(source_color), static_cast<Uint8>(target_color), &src);
static_cast<Uint8>(source_color),
static_cast<Uint8>(target_color), &src);
} else { } else {
SDL_FRect dst = {.x = static_cast<float>(dst_x), .y = static_cast<float>(dst_y), .w = TS, .h = TS}; SDL_FRect dst = {.x = static_cast<float>(dst_x), .y = static_cast<float>(dst_y), .w = TS, .h = TS};
tileset_->render(&src, &dst); tileset_->render(&src, &dst);
@@ -97,8 +94,7 @@ void TilePicker::open(const std::string& tileset_name, int current_tile,
visible_height_ = PlayArea::HEIGHT; visible_height_ = PlayArea::HEIGHT;
frame_dst_ = {.x = static_cast<float>(offset_x_), .y = static_cast<float>(offset_y), frame_dst_ = {.x = static_cast<float>(offset_x_), .y = static_cast<float>(offset_y), .w = static_cast<float>(frame_w), .h = static_cast<float>(frame_h)};
.w = static_cast<float>(frame_w), .h = static_cast<float>(frame_h)};
// Si el frame es más alto que el play area, necesitará scroll // Si el frame es más alto que el play area, necesitará scroll
if (frame_h > visible_height_) { if (frame_h > visible_height_) {
@@ -138,8 +134,7 @@ void TilePicker::render() {
int max_scroll = frame_h - visible_height_; int max_scroll = frame_h - visible_height_;
scroll_y_ = std::clamp(scroll_y_, 0, max_scroll); scroll_y_ = std::clamp(scroll_y_, 0, max_scroll);
SDL_FRect src = {.x = 0, .y = static_cast<float>(scroll_y_), SDL_FRect src = {.x = 0, .y = static_cast<float>(scroll_y_), .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
.w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
SDL_FRect dst = {.x = frame_dst_.x, .y = 0, .w = frame_dst_.w, .h = static_cast<float>(visible_height_)}; SDL_FRect dst = {.x = frame_dst_.x, .y = 0, .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
frame_surface_->render(&src, &dst); frame_surface_->render(&src, &dst);
} }

View File

@@ -29,9 +29,7 @@ class TilePicker {
// source_color/target_color: sustitución de color (-1 = sin sustitución) // source_color/target_color: sustitución de color (-1 = sin sustitución)
// tile_spacing_in: pixels de separación entre tiles en el fichero fuente // tile_spacing_in: pixels de separación entre tiles en el fichero fuente
// tile_spacing_out: pixels de separación visual entre tiles al mostrar // tile_spacing_out: pixels de separación visual entre tiles al mostrar
void open(const std::string& tileset_name, int current_tile = -1, void open(const std::string& tileset_name, int current_tile = -1, int bg_color = -1, int source_color = -1, int target_color = -1, int tile_spacing_in = 0, int tile_spacing_out = 1);
int bg_color = -1, int source_color = -1, int target_color = -1,
int tile_spacing_in = 0, int tile_spacing_out = 1);
void close(); void close();
[[nodiscard]] auto isOpen() const -> bool { return open_; } [[nodiscard]] auto isOpen() const -> bool { return open_; }
@@ -54,8 +52,8 @@ class TilePicker {
int hover_tile_{-1}; // Tile bajo el cursor int hover_tile_{-1}; // Tile bajo el cursor
// Spacing // Spacing
int spacing_in_{0}; // Spacing en el fichero fuente int spacing_in_{0}; // Spacing en el fichero fuente
int spacing_out_{1}; // Spacing visual al mostrar int spacing_out_{1}; // Spacing visual al mostrar
// Scroll y posicionamiento // Scroll y posicionamiento
int scroll_y_{0}; // Scroll vertical en pixels int scroll_y_{0}; // Scroll vertical en pixels

View File

@@ -114,8 +114,7 @@ void TilemapRenderer::setTile(int index, int tile_value) {
Screen::get()->setRendererSurface(map_surface_); Screen::get()->setRendererSurface(map_surface_);
// Borrar la celda con el color de fondo // Borrar la celda con el color de fondo
SDL_FRect cell = {.x = static_cast<float>(col * TILE_SIZE), .y = static_cast<float>(row * TILE_SIZE), SDL_FRect cell = {.x = static_cast<float>(col * TILE_SIZE), .y = static_cast<float>(row * TILE_SIZE), .w = static_cast<float>(TILE_SIZE), .h = static_cast<float>(TILE_SIZE)};
.w = static_cast<float>(TILE_SIZE), .h = static_cast<float>(TILE_SIZE)};
map_surface_->fillRect(&cell, stringToColor(bg_color_)); map_surface_->fillRect(&cell, stringToColor(bg_color_));
// Dibujar el nuevo tile (si no es vacío ni animado) // Dibujar el nuevo tile (si no es vacío ni animado)
@@ -123,8 +122,9 @@ void TilemapRenderer::setTile(int index, int tile_value) {
const bool IS_ANIMATED = (tile_value >= 18 * tile_set_width_) && (tile_value < 19 * tile_set_width_); const bool IS_ANIMATED = (tile_value >= 18 * tile_set_width_) && (tile_value < 19 * tile_set_width_);
if (!IS_ANIMATED) { if (!IS_ANIMATED) {
SDL_FRect clip = {.x = static_cast<float>((tile_value % tile_set_width_) * TILE_SIZE), SDL_FRect clip = {.x = static_cast<float>((tile_value % tile_set_width_) * TILE_SIZE),
.y = static_cast<float>((tile_value / tile_set_width_) * TILE_SIZE), .y = static_cast<float>((tile_value / tile_set_width_) * TILE_SIZE),
.w = static_cast<float>(TILE_SIZE), .h = static_cast<float>(TILE_SIZE)}; .w = static_cast<float>(TILE_SIZE),
.h = static_cast<float>(TILE_SIZE)};
tileset_surface_->render(col * TILE_SIZE, row * TILE_SIZE, &clip); tileset_surface_->render(col * TILE_SIZE, row * TILE_SIZE, &clip);
} }
} }

View File

@@ -79,6 +79,11 @@ Game::Game(Mode mode)
Cheevos::get()->enable(!Options::cheats.enabled()); // Deshabilita los logros si hay trucos activados Cheevos::get()->enable(!Options::cheats.enabled()); // Deshabilita los logros si hay trucos activados
Cheevos::get()->clearUnobtainableState(); Cheevos::get()->clearUnobtainableState();
#ifdef _DEBUG
Console::get()->setScope("debug");
#else
Console::get()->setScope("game");
#endif
Console::get()->on_toggle = [this](bool open) { player_->setIgnoreInput(open); }; Console::get()->on_toggle = [this](bool open) { player_->setIgnoreInput(open); };
if (Console::get()->isActive()) { player_->setIgnoreInput(true); } if (Console::get()->isActive()) { player_->setIgnoreInput(true); }
GameControl::change_player_skin = [this](const std::string& skin_name) -> void { GameControl::change_player_skin = [this](const std::string& skin_name) -> void {

View File

@@ -58,7 +58,7 @@ class Console {
// Constantes de consola // Constantes de consola
static constexpr std::string_view CONSOLE_NAME = "JDD Console"; static constexpr std::string_view CONSOLE_NAME = "JDD Console";
static constexpr std::string_view CONSOLE_VERSION = "v2.1"; static constexpr std::string_view CONSOLE_VERSION = "v2.2";
static constexpr int MAX_LINE_CHARS = 32; static constexpr int MAX_LINE_CHARS = 32;
static constexpr int MAX_HISTORY_SIZE = 20; static constexpr int MAX_HISTORY_SIZE = 20;
static constexpr float CURSOR_ON_TIME = 0.5F; static constexpr float CURSOR_ON_TIME = 0.5F;

View File

@@ -1032,6 +1032,11 @@ void CommandRegistry::registerHandlers() {
dynamic_providers_["SET BGCOLOR"] = color_provider; dynamic_providers_["SET BGCOLOR"] = color_provider;
dynamic_providers_["EDIT MAPBG"] = color_provider; dynamic_providers_["EDIT MAPBG"] = color_provider;
dynamic_providers_["EDIT MAPCONN"] = color_provider; dynamic_providers_["EDIT MAPCONN"] = color_provider;
// HELP: lista de comandos visibles en el scope activo
dynamic_providers_["HELP"] = [this]() -> std::vector<std::string> {
return getVisibleKeywords();
};
dynamic_providers_["SET BORDER"] = color_provider; dynamic_providers_["SET BORDER"] = color_provider;
dynamic_providers_["SET ITEMCOLOR1"] = color_provider; dynamic_providers_["SET ITEMCOLOR1"] = color_provider;
dynamic_providers_["SET ITEMCOLOR2"] = color_provider; dynamic_providers_["SET ITEMCOLOR2"] = color_provider;
@@ -1151,6 +1156,7 @@ void CommandRegistry::load(const std::string& yaml_path) {
if (extras.contains("description")) { def.description = extras["description"].get_value<std::string>(); } if (extras.contains("description")) { def.description = extras["description"].get_value<std::string>(); }
if (extras.contains("usage")) { def.usage = extras["usage"].get_value<std::string>(); } if (extras.contains("usage")) { def.usage = extras["usage"].get_value<std::string>(); }
if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value<bool>(); } if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value<bool>(); }
if (extras.contains("help_hidden")) { def.help_hidden = extras["help_hidden"].get_value<bool>(); }
if (extras.contains("completions")) { if (extras.contains("completions")) {
def.completions.clear(); def.completions.clear();
auto extras_completions = extras["completions"]; auto extras_completions = extras["completions"];
@@ -1176,7 +1182,29 @@ void CommandRegistry::load(const std::string& yaml_path) {
} }
// Registrar el handler de HELP (captura this) // Registrar el handler de HELP (captura this)
handlers_["cmd_help"] = [this](const std::vector<std::string>&) -> std::string { handlers_["cmd_help"] = [this](const std::vector<std::string>& args) -> std::string {
if (!args.empty()) {
// HELP <command>: mostrar ayuda detallada de un comando
const auto* cmd = findCommand(args[0]);
if (cmd != nullptr) {
std::string kw_lower = cmd->keyword;
std::ranges::transform(kw_lower, kw_lower.begin(), ::tolower);
std::string result = kw_lower + ": " + cmd->description + "\n" + cmd->usage;
// Listar subcomandos/opciones si hay completions
auto opts = getCompletions(cmd->keyword);
if (!opts.empty()) {
result += "\noptions:";
for (const auto& opt : opts) {
std::string opt_lower = opt;
std::ranges::transform(opt_lower, opt_lower.begin(), ::tolower);
result += " " + opt_lower;
}
}
return result;
}
return "Unknown command: " + args[0];
}
std::cout << generateTerminalHelp(); std::cout << generateTerminalHelp();
return generateConsoleHelp(); return generateConsoleHelp();
}; };
@@ -1288,6 +1316,15 @@ auto CommandRegistry::generateConsoleHelp() const -> std::string {
} }
auto CommandRegistry::getCompletions(const std::string& path) const -> std::vector<std::string> { auto CommandRegistry::getCompletions(const std::string& path) const -> std::vector<std::string> {
// Verificar que el comando raíz es visible en el scope activo
if (!active_scope_.empty()) {
std::string root = path;
auto space = root.find(' ');
if (space != std::string::npos) { root = root.substr(0, space); }
const auto* cmd = findCommand(root);
if (cmd != nullptr && !isCommandVisible(*cmd)) { return {}; }
}
// Primero: buscar proveedor dinámico (tiene prioridad si existe) // Primero: buscar proveedor dinámico (tiene prioridad si existe)
const auto dyn_it = dynamic_providers_.find(path); const auto dyn_it = dynamic_providers_.find(path);
if (dyn_it != dynamic_providers_.end()) { if (dyn_it != dynamic_providers_.end()) {