10#include "absl/strings/str_format.h"
13#include "imgui/imgui.h"
37 std::shared_ptr<zelda3::DungeonObjectEditor> object_editor)
38 : renderer_(renderer),
40 canvas_viewer_(canvas_viewer),
41 object_selector_(rom),
42 object_editor_(object_editor) {
92 if (tile_handler.was_placement_blocked()) {
94 tile_handler.clear_placement_blocked();
98 "Object limit reached (%d max) - placement blocked",
111 auto& sprite_handler = coordinator.sprite_handler();
112 if (sprite_handler.was_placement_blocked()) {
113 const auto reason = sprite_handler.placement_block_reason();
114 sprite_handler.clear_placement_blocked();
118 "Sprite limit reached (%d max) - placement blocked",
131 auto& door_handler = coordinator.door_handler();
132 if (door_handler.was_placement_blocked()) {
133 const auto reason = door_handler.placement_block_reason();
134 door_handler.clear_placement_blocked();
138 "Door limit reached (%d max) - placement blocked", max_doors));
157 float available_height = ImGui::GetContentRegionAvail().y;
158 float browser_height =
163 theme.text_secondary_gray,
164 "Choose an object to place. Double-click an entry to inspect its draw "
165 "routine or open the tile editor.");
166 ImGui::BeginChild(
"ObjectBrowserRegion", ImVec2(0, browser_height),
true);
206 const size_t selection_count =
207 viewer ? viewer->object_interaction().GetSelectionCount() : 0;
218 ImGui::TextColored(theme.status_warning,
226 }
else if (selection_count == 1) {
227 ImGui::TextColored(theme.status_success,
235 }
else if (selection_count > 1) {
236 ImGui::TextColored(theme.status_success,
246 ImGui::TextColored(theme.text_secondary_gray,
248 " Browse objects below to place them. Click room "
249 "objects to edit them in the Object Editor.");
287 if (!room.IsLoaded()) {
288 room.LoadRoomGraphics(room.blockset());
301 if (preview_obj.
tiles().empty()) {
306 const uint8_t* gfx_data = room.get_gfx_buffer().data();
316 std::vector<SDL_Color> colors(256);
317 size_t color_index = 0;
318 for (
size_t pal_idx = 0;
319 pal_idx < palette_group.
size() && color_index < 256; ++pal_idx) {
320 const auto& pal = palette_group[pal_idx];
321 for (
size_t i = 0; i < pal.size() && color_index < 256; ++i) {
322 ImVec4 rgb = pal[i].rgb();
323 colors[color_index++] = {
static_cast<Uint8
>(rgb.x),
324 static_cast<Uint8
>(rgb.y),
325 static_cast<Uint8
>(rgb.z), 255};
328 colors[255] = {0, 0, 0, 0};
329 bitmap.SetPalette(colors);
330 if (bitmap.surface()) {
331 SDL_SetColorKey(bitmap.surface(), SDL_TRUE, 255);
332 SDL_SetSurfaceBlendMode(bitmap.surface(), SDL_BLENDMODE_BLEND);
344 if (bitmap.modified() && bitmap.surface() &&
345 bitmap.mutable_data().size() > 0) {
346 SDL_LockSurface(bitmap.surface());
347 size_t surface_size = bitmap.surface()->h * bitmap.surface()->pitch;
348 size_t data_size = bitmap.mutable_data().size();
349 if (surface_size >= data_size) {
350 memcpy(bitmap.surface()->pixels, bitmap.mutable_data().data(),
353 SDL_UnlockSurface(bitmap.surface());
378 {ImGuiCol_Header, ImVec4(0.15f, 0.25f, 0.35f, 1.0f)},
379 {ImGuiCol_HeaderHovered, ImVec4(0.20f, 0.30f, 0.40f, 1.0f)},
382 bool header_open = ImGui::CollapsingHeader(
387 ImGuiTreeNodeFlags_DefaultOpen);
394 if (ImGui::BeginTable(
"StaticEditorLayout", 2,
395 ImGuiTableFlags_BordersInnerV)) {
396 ImGui::TableSetupColumn(
"Info", ImGuiTableColumnFlags_WidthFixed, 200);
397 ImGui::TableSetupColumn(
"Preview", ImGuiTableColumnFlags_WidthStretch);
399 ImGui::TableNextRow();
402 ImGui::TableNextColumn();
405 ImGui::TextColored(theme.text_info,
ICON_MD_TAG " Object ID");
413 ImGui::TextColored(theme.text_info,
ICON_MD_BRUSH " Draw Routine");
425 ImGui::Text(
"Orientation: %s",
430 ImGui::TextColored(theme.status_warning,
ICON_MD_LAYERS " Both BG");
440 ImGui::SetClipboardText(
443 if (ImGui::IsItemHovered()) {
444 ImGui::SetTooltip(
"Copy object ID to clipboard");
447 if (ImGui::Button(
ICON_MD_CODE " Export ASM", ImVec2(-1, 0))) {
448 const std::string asm_preview = absl::StrFormat(
449 "; Object 0x%02X (%s)\n"
450 "; Auto-generated preview stub\n"
452 "dw $%02X ; object id\n"
453 "; draw_routine=%d tile_count=%d\n",
458 ImGui::SetClipboardText(asm_preview.c_str());
460 if (ImGui::IsItemHovered()) {
461 ImGui::SetTooltip(
"Copy ASM preview stub to clipboard");
470 if (ImGui::IsItemHovered()) {
471 ImGui::SetTooltip(
"Open tile editor to rearrange 8x8 tiles");
483 ImGui::TableNextColumn();
485 ImGui::TextColored(theme.text_secondary_gray,
"Preview:");
512 ImVec2(24, 56),
"No preview available",
513 ImGui::GetColorU32(theme.text_secondary_gray));
519 ImGui::TextColored(theme.text_secondary_gray,
ICON_MD_INFO
520 " Double-click objects in browser\n"
521 "to view their draw routine info.");
544 size_t object_count = room.GetTileObjects().size();
545 size_t sprite_count = room.GetSprites().size();
546 size_t door_count = room.GetDoors().size();
550 for (
const auto& obj : room.GetTileObjects()) {
551 if (obj.id_ >= 0xF9 && obj.id_ <= 0xFD) {
563 auto usage_color = [&](
size_t count,
int max_val) -> ImVec4 {
564 float ratio =
static_cast<float>(count) /
static_cast<float>(max_val);
566 return theme.status_error;
568 if (ratio >= 0.75f) {
569 return theme.status_warning;
571 return theme.text_secondary_gray;
575 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 2));
577 ImGui::TextColored(usage_color(object_count, kMaxObjects),
579 if (ImGui::IsItemHovered()) {
580 ImGui::SetTooltip(
"Objects: %zu of %d maximum", object_count, kMaxObjects);
584 ImGui::TextColored(usage_color(sprite_count, kMaxSprites),
586 if (ImGui::IsItemHovered()) {
587 ImGui::SetTooltip(
"Sprites: %zu of %d maximum", sprite_count, kMaxSprites);
591 ImGui::TextColored(usage_color(door_count, kMaxDoors),
593 if (ImGui::IsItemHovered()) {
594 ImGui::SetTooltip(
"Doors: %zu of %d maximum", door_count, kMaxDoors);
598 ImGui::TextColored(usage_color(chest_count, kMaxChests),
600 if (ImGui::IsItemHovered()) {
601 ImGui::SetTooltip(
"Chests: %d of %d maximum", chest_count, kMaxChests);
604 ImGui::PopStyleVar();
609 if (!result.errors.empty() || !result.warnings.empty()) {
610 for (
const auto& err : result.errors) {
611 ImGui::TextColored(theme.status_error,
ICON_MD_ERROR " %s", err.c_str());
613 for (
const auto& warn : result.warnings) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
DungeonObjectInteraction & object_interaction()
void ClearPreviewObject()
void SetPreviewObject(const zelda3::RoomObject &object)
void SetObjectInteractionEnabled(bool enabled)
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
bool IsObjectLoaded() const
DungeonRoomStore * get_rooms()
void SetObjectDoubleClickCallback(std::function< void(int)> callback)
zelda3::GameData * game_data() const
void SelectObject(int obj_id, int subtype=-1)
void SetStaticEditorObjectId(int obj_id)
void DrawObjectAssetBrowser()
void SetObjectSelectedCallback(std::function< void(const zelda3::RoomObject &)> callback)
TileObjectHandler & tile_handler()
void SetPlacementError(const std::string &message)
static constexpr double kPlacementErrorDuration
void DrawObjectSelector()
void DrawRoomValidationBar()
zelda3::ObjectDrawInfo static_editor_draw_info_
gfx::IRenderer * renderer_
void DrawStaticObjectEditor()
int static_editor_object_id_
std::function< DungeonCanvasViewer *()> canvas_viewer_provider_
gfx::BackgroundBuffer static_preview_buffer_
TileEditorCallback tile_editor_callback_
zelda3::RoomObject preview_object_
DungeonCanvasViewer * ResolveCanvasViewer()
void DrawInteractionSummary()
std::shared_ptr< zelda3::DungeonObjectEditor > object_editor_
std::function< void()> open_object_editor_callback_
DungeonCanvasViewer * canvas_viewer_
gui::Canvas static_preview_canvas_
DungeonObjectSelector object_selector_
void OpenStaticObjectEditor(int object_id)
std::string last_placement_error_
ObjectSelectorContent(gfx::IRenderer *renderer, Rom *rom, DungeonCanvasViewer *canvas_viewer, std::shared_ptr< zelda3::DungeonObjectEditor > object_editor=nullptr)
void SelectObject(int obj_id)
void CloseStaticObjectEditor()
bool static_preview_rendered_
double placement_error_time_
void Draw(bool *p_open) override
Draw the panel content.
std::unique_ptr< zelda3::ObjectParser > object_parser_
void SetAgentOptimizedLayout(bool enabled)
PlacementBlockReason placement_block_reason() const
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void ProcessTextureQueue(IRenderer *renderer)
void EnsureBitmapInitialized()
Defines an abstract interface for all rendering operations.
void AddTextAt(ImVec2 local_pos, const std::string &text, uint32_t color)
RAII guard for ImGui style colors.
RAII guard for ImGui style vars.
ValidationResult ValidateRoom(const Room &room)
Draws dungeon objects to background buffers using game patterns.
void InitializeDrawRoutines()
Initialize draw routine registry Must be called before drawing objects.
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw a room object to background buffers.
const std::vector< gfx::TileInfo > & tiles() const
#define ICON_MD_GRID_VIEW
#define ICON_MD_CONSTRUCTION
#define ICON_MD_DOOR_FRONT
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_PEST_CONTROL
#define ICON_MD_TOUCH_APP
#define ICON_MD_SELECT_ALL
#define ICON_MD_OPEN_IN_NEW
#define ICON_MD_CONTENT_COPY
#define ICON_MD_INVENTORY_2
#define ICON_MD_ADD_CIRCLE
const AgentUITheme & GetTheme()
void EndCanvas(Canvas &canvas)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
bool DangerButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a danger action button (error color).
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool RenderPreviewPanel(const CanvasRuntime &rt, gfx::Bitmap &bmp, const PreviewPanelOpts &opts)
constexpr size_t kMaxTileObjects
constexpr size_t kMaxDoors
std::string GetObjectName(int object_id)
constexpr int kNumberOfRooms
constexpr size_t kMaxChests
constexpr size_t kMaxTotalSprites
PaletteGroup dungeon_main
Represents a group of palettes.
std::optional< float > grid_step
gfx::PaletteGroupMap palette_groups