7#include "absl/strings/str_format.h"
15#include "imgui/imgui.h"
25 const int type_val =
static_cast<int>(type);
26 if (type_val <= 0x12) {
29 if (type_val <= 0x1E) {
185 constexpr const char* categories[] = {
"All doors",
"Room links",
"Exits",
188 std::vector<std::pair<int, std::string>> entries;
203 const int id =
static_cast<int>(type);
206 entries.emplace_back(
id, name);
209 std::stable_sort(entries.begin(), entries.end(),
210 [](
const auto& a,
const auto& b) {
211 return workbench::DungeonBrowserFold(a.second) <
212 workbench::DungeonBrowserFold(b.second);
214 std::array<SDL_Color, 256> colors{};
217 current_room_id_ < static_cast<int>(
rooms_->
size())
222 std::copy_n(palette.begin(), std::min(palette.size(), colors.size()),
226 if (ImGui::BeginChild(
"##DoorLibrary", ImVec2(0, 0),
false)) {
227 ImGui::BeginDisabled(!can_place);
228 for (
const auto& [
id, name] : entries) {
237 ImGui::GetWindowDrawList(), preview.pixels,
238 SDL_Rect{0, 0, preview.width, preview.height}, pos,
239 ImVec2(pos.x + size.x, pos.y + size.y), colors,
243 absl::StrFormat(
"place_door_%02X",
id));
244 if (picked && can_place) {
250 ImGui::EndDisabled();
252 ImGui::TextDisabled(
"No matches");
283 const int type_key =
static_cast<int>(type);
292 if (rom && rom->
is_loaded() && !gfx_buffer.empty()) {
300 door_def.
type = type;
304 drawer.
DrawDoor(door_def, 0, bg1, bg2,
nullptr,
nullptr,
nullptr);
308 const int start_x = tile_x * 8;
309 const int start_y = tile_y * 8;
310 const int w = std::max(1, dims.width_tiles * 8);
311 const int h = std::max(1, dims.height_tiles * 8);
315 cached.
pixels.assign(
static_cast<size_t>(w) *
static_cast<size_t>(h), 255);
317 const auto& bg1_bmp = bg1.
bitmap();
318 const auto& bg2_bmp = bg2.
bitmap();
319 const auto& bg1_data = bg1_bmp.vector();
320 const auto& bg2_data = bg2_bmp.vector();
322 for (
int y = 0; y < h; ++y) {
323 for (
int x = 0; x < w; ++x) {
324 const int bmp_x = start_x + x;
325 const int bmp_y = start_y + y;
326 if (bmp_x >= 0 && bmp_x < 512 && bmp_y >= 0 && bmp_y < 512) {
327 const int idx = bmp_y * 512 + bmp_x;
329 if (idx <
static_cast<int>(bg1_data.size()) && bg1_data[idx] != 255) {
331 }
else if (idx <
static_cast<int>(bg2_data.size()) &&
332 bg2_data[idx] != 255) {
335 cached.
pixels[y * w + x] = px;
341 auto [inserted_it, _] =
343 return inserted_it->second;
365 current_room_id_ < static_cast<int>(
rooms_->
size()))
369 std::array<SDL_Color, 256> color_table{};
372 for (
size_t p_idx = 0; p_idx < std::min<size_t>(pal.size(), 256); ++p_idx) {
373 color_table[p_idx] = pal[p_idx];
390 ImGui::TextWrapped(
"Select a door style, then click a wall to place it.");
402 constexpr float kDoorCardHeight = 48.0f;
403 constexpr float kDoorCardSpacing = 6.0f;
404 constexpr float kMinDoorCardWidth = 94.0f;
406 const float grid_height =
407 show_room_doors ? 150.0f
408 : std::max(1.0f, ImGui::GetContentRegionAvail().y);
409 ImGui::BeginChild(
"##DoorTypeGrid", ImVec2(0, grid_height),
true,
410 ImGuiWindowFlags_AlwaysVerticalScrollbar);
411 ImGui::BeginDisabled(!can_edit);
412 const float panel_width = ImGui::GetContentRegionAvail().x;
413 const int items_per_row =
414 std::max(1,
static_cast<int>((panel_width + kDoorCardSpacing) /
415 (kMinDoorCardWidth + kDoorCardSpacing)));
416 const float card_width = std::max(
418 (panel_width - (items_per_row - 1) * kDoorCardSpacing) / items_per_row);
420 ImDrawList* draw_list = ImGui::GetWindowDrawList();
423 for (
size_t i = 0; i < kDoorTypes.size(); ++i) {
424 const auto door_type = kDoorTypes[i];
426 const int type_val =
static_cast<int>(door_type);
427 ImGui::PushID(
static_cast<int>(i));
429 const ImVec2 card_pos = ImGui::GetCursorScreenPos();
433 is_selected ? theme.selection_pulsing : theme.panel_bg_darker},
437 ImGui::Button(
"##door_card", ImVec2(card_width, kDoorCardHeight));
439 absl::StrFormat(
"place_door_%02X", type_val));
440 if (picked && can_edit) {
449 const ImVec2 preview_box_min(card_pos.x + 4.0f, card_pos.y + 4.0f);
450 const ImVec2 preview_box_max(card_pos.x + 36.0f,
451 card_pos.y + kDoorCardHeight - 4.0f);
455 SDL_Rect bounds{0, 0, preview.width, preview.height};
456 if (bounds.w > 0 && bounds.h > 0 && !preview.pixels.empty()) {
458 draw_list, preview.pixels, bounds, preview_box_min, preview_box_max,
464 draw_list->AddRect(preview_box_min, preview_box_max,
465 ImGui::GetColorU32(theme.status_inactive), 2.0f);
467 ImVec2(preview_box_min.x + 7.0f, preview_box_min.y + 8.0f),
468 ImGui::GetColorU32(theme.status_inactive), DoorTypeIcon(door_type));
471 const std::string id_text = absl::StrFormat(
"%02X", type_val);
472 const std::string name_text = ShortDoorTypeLabel(door_type);
473 draw_list->AddText(ImVec2(card_pos.x + 40.0f, card_pos.y + 6.0f),
474 ImGui::GetColorU32(theme.text_info), id_text.c_str());
476 ImVec2(card_pos.x + 40.0f, card_pos.y + 24.0f),
477 ImGui::GetColorU32(is_selected ? theme.text_primary
478 : theme.text_secondary_gray),
484 ImVec2(card_pos.x + card_width, card_pos.y + kDoorCardHeight),
485 ImGui::GetColorU32(theme.selection_primary), 4.0f, 0, 2.0f);
488 if (ImGui::IsItemHovered()) {
489 ImGui::SetTooltip(tr(
"%s (0x%02X)\nClick to select for placement"),
496 if (col < items_per_row && i < kDoorTypes.size() - 1) {
497 ImGui::SameLine(0, kDoorCardSpacing);
503 ImGui::EndDisabled();
512 const auto& doors = room.GetDoors();
517 ImGui::TextColored(theme.text_secondary_gray,
522 int selected_door_index = -1;
525 const auto selected_entity =
527 if (selected_entity.type == EntityType::Door) {
528 selected_door_index =
static_cast<int>(selected_entity.index);
532 if (selected_door_index >= 0 &&
533 selected_door_index <
static_cast<int>(doors.size())) {
534 ImGui::BeginDisabled(!can_edit);
535 uint8_t raw_type =
static_cast<uint8_t
>(doors[selected_door_index].type);
536 ImGui::AlignTextToFramePadding();
537 ImGui::TextUnformatted(tr(
"Selected raw type"));
539 ImGui::SetNextItemWidth(72.0f);
541 "##SelectedDoorRawType", ImGuiDataType_U8, &raw_type,
"%02X",
542 ImGuiInputTextFlags_CharsHexadecimal,
543 {
reinterpret_cast<uintptr_t
>(
rooms_),
545 static_cast<uint64_t
>(selected_door_index)});
547 "Selected door raw type byte");
548 ImGui::EndDisabled();
549 if (type_changed && can_edit) {
558 ImGui::BeginChild(
"##DoorList", ImVec2(0, 0),
true);
559 for (
size_t i = 0; i < doors.size(); ++i) {
560 const auto& door = doors[i];
561 const auto [tile_x, tile_y] = door.GetTileCoords();
565 ImGui::PushID(
static_cast<int>(i));
566 const std::string row_label = absl::StrFormat(
"[%zu] %s", i, type_name);
567 const bool row_selected = ImGui::Selectable(
568 row_label.c_str(), selected_door_index ==
static_cast<int>(i));
570 "Select a door in the current dungeon room");
577 ImGui::TextColored(theme.text_secondary_gray,
"(%s @ %d,%d)",
578 dir_name.c_str(), tile_x, tile_y);
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
InteractionContext * context() const
Get the interaction context.
DungeonCanvasViewer * canvas_viewer_
bool HasMatchingViewer() const
workbench::DungeonBrowserState browser_state_
uint64_t cached_door_graphics_revision_
const CachedDoorPreview & GetOrCreateDoorPreview(zelda3::DoorType type, const zelda3::Room &room)
zelda3::DoorType selected_door_type_
DungeonRoomStore * rooms_
DungeonCanvasViewer * ResolveCanvasViewer()
std::function< DungeonCanvasViewer *()> canvas_viewer_provider_
void OnClose() override
Called when panel is hidden.
void Draw(bool *p_open) override
Draw the panel content.
std::unordered_map< int, CachedDoorPreview > door_preview_cache_
std::function< void()> open_selection_inspector_callback_
bool door_placement_mode_
void DrawContent(bool show_room_doors)
zelda3::DoorType GetDoorType() const
Get current door type for placement.
bool MutateDoorType(size_t index, zelda3::DoorType new_type)
Change the type of a door in place, re-encoding ROM bytes.
DungeonRoomStore * rooms() const
int current_room_id() const
DungeonObjectInteraction & object_interaction()
bool IsObjectInteractionEnabled() const
bool header_read_only() const
SelectedEntity GetSelectedEntity() const
bool IsDoorPlacementActive() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
bool HasEntitySelection() const
void SelectEntity(EntityType type, size_t index)
void SetDoorPlacementMode(bool enabled, zelda3::DoorType type=zelda3::DoorType::NormalDoor)
static constexpr uint8_t kBackgroundBufferTransparentIndex
static bool DrawSpritePreview(ImDrawList *draw_list, const std::vector< uint8_t > &preview, const SDL_Rect &bounds, const ImVec2 &box_min, const ImVec2 &box_max, const std::array< SDL_Color, 256 > &color_table, uint8_t transparent_index=kSpritePreviewTransparentIndex)
TileObjectHandler & tile_handler()
DoorInteractionHandler & door_handler()
void EnsureBitmapInitialized()
RAII guard for ImGui style colors.
Draws dungeon objects to background buffers using game patterns.
void DrawDoor(const DoorDef &door, int door_index, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr, gfx::BackgroundBuffer *layout_bg2=nullptr)
Draw a door to background buffers.
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
const std::vector< SDL_Color > & rendered_palette() const
uint64_t graphics_revision() const
#define ICON_MD_EXIT_TO_APP
#define ICON_MD_DOOR_SLIDING
#define ICON_MD_SWAP_HORIZ
#define ICON_MD_VISIBILITY
#define ICON_MD_DOOR_FRONT
#define ICON_MD_SELECT_ALL
#define ICON_MD_OPEN_IN_NEW
#define ICON_MD_WATER_DROP
const AgentUITheme & GetTheme()
std::string ShortDoorTypeLabel(zelda3::DoorType type)
ImVec4 DoorTypeAccent(const AgentUITheme &theme, zelda3::DoorType type)
const char * DoorTypeIcon(zelda3::DoorType type)
bool DungeonBrowserMatches(std::string_view query, int id, std::string_view name)
void DrawDungeonBrowserControls(DungeonBrowserState &state, std::span< const char *const > categories)
bool DrawDungeonBrowserRow(int id, const std::string &name, bool selected, DrawPreview preview, std::string_view badge={})
Editors are the view controllers for the application.
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool InputScalarDeferred(const char *label, ImGuiDataType data_type, void *data, const char *format, ImGuiInputTextFlags flags, InputScalarTargetIdentity target_identity)
DoorType
Door types from ALTTP.
@ NormalDoorOneSidedShutter
Normal door (lower layer; with one-sided shutters)
@ TopShutterLower
Top-sided shutter door (lower layer)
@ FancyDungeonExitLower
Fancy dungeon exit (lower layer)
@ FancyDungeonExit
Fancy dungeon exit.
@ SmallKeyDoor
Small key door.
@ ExitLower
Exit (lower layer)
@ SmallKeyStairsDown
Small key stairs (downwards)
@ BombableCaveExit
Bombable cave exit.
@ SmallKeyStairsUp
Small key stairs (upwards)
@ DungeonSwapMarker
Dungeon swap marker.
@ NormalDoor
Normal door (upper layer)
@ BombableDoor
Bombable door.
@ LayerSwapMarker
Layer swap marker.
@ ExplicitRoomDoor
Explicit room door.
@ BottomShutterLower
Bottom-sided shutter door (lower layer)
@ ExplodingWall
Exploding wall.
@ TopSidedShutter
Top-sided shutter door.
@ LitCaveExitLower
Lit cave exit (lower layer)
@ DoubleSidedShutterLower
Double-sided shutter (lower layer)
@ UnopenableBigKeyDoor
Unopenable, double-sided big key door.
@ NormalDoorLower
Normal door (lower layer)
@ BottomSidedShutter
Bottom-sided shutter door.
@ SmallKeyStairsDownLower
Small key stairs (lower layer; downwards)
@ CurtainDoor
Curtain door.
@ WaterfallDoor
Waterfall door.
@ BigKeyDoor
Big key door.
@ EyeWatchDoor
Eye watch door.
@ SmallKeyStairsUpLower
Small key stairs (lower layer; upwards)
@ DoubleSidedShutter
Double sided shutter door.
constexpr std::array< DoorType, 32 > GetPlaceableDoorTypes()
Get supported door types suitable for editor placement controls.
constexpr bool IsRoomConnectionDoorType(DoorType type)
Return true when a door can represent an adjacent-room connection.
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
constexpr bool IsExitDoorType(DoorType type)
Return true for terminal exits that do not pair with another room.
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
@ North
Top wall (horizontal door, 4x3 tiles)
Centralized theme colors for Agent UI components.
ImVec4 dungeon_object_door
std::vector< uint8_t > pixels
std::pair< int, int > GetTileCoords() const
DoorDimensions GetDimensions() const