8#include "absl/strings/str_format.h"
17#include "imgui/imgui.h"
33 auto& room = (*rooms_)[room_id];
46 ImGui::SetNextWindowPos(
48 ImGuiCond_FirstUseEver);
49 ImGui::SetNextWindowSize(ImVec2(300, 0), ImGuiCond_FirstUseEver);
51 ImGuiWindowFlags_NoCollapse)) {
52 ImGui::Text(tr(
"Room: 0x%03X (%d)"), room_id, room_id);
54 ImGui::Text(tr(
"Graphics"));
55 ImGui::Text(tr(
" Blockset: 0x%02X"), room.
blockset());
56 ImGui::Text(tr(
" Palette: 0x%02X"), room.
palette());
57 ImGui::Text(tr(
" Layout: 0x%02X"), room.
layout_id());
58 ImGui::Text(tr(
" Spriteset: 0x%02X"), room.
spriteset());
60 ImGui::Text(tr(
"Content"));
62 ImGui::Text(tr(
" Sprites: %zu"), room.
GetSprites().size());
64 ImGui::Text(tr(
"Buffers"));
67 ImGui::Text(tr(
" BG1: %dx%d %s"), bg1.width(), bg1.height(),
68 bg1.texture() ?
"(has texture)" :
"(NO TEXTURE)");
69 ImGui::Text(tr(
" BG2: %dx%d %s"), bg2.width(), bg2.height(),
70 bg2.texture() ?
"(has texture)" :
"(NO TEXTURE)");
72 ImGui::Text(tr(
"Layers (4-way)"));
78 if (ImGui::Checkbox(tr(
"BG1 Layout"), &bg1l)) {
81 if (ImGui::Checkbox(tr(
"BG1 Objects"), &bg1o)) {
84 if (ImGui::Checkbox(tr(
"BG2 Layout"), &bg2l)) {
87 if (ImGui::Checkbox(tr(
"BG2 Objects"), &bg2o)) {
90 int blend =
static_cast<int>(
92 if (ImGui::SliderInt(tr(
"BG2 Blend"), &blend, 0, 4)) {
100 ImGui::Text(tr(
"Layout Override"));
101 static bool enable_override =
false;
102 ImGui::Checkbox(tr(
"Enable Override"), &enable_override);
103 if (enable_override) {
110 ImGui::Text(tr(
"Preview State"));
111 if (
auto* editor_state =
113 editor_state !=
nullptr) {
114 bool preview_state_changed =
false;
116 bool water_face_active = editor_state->IsWaterFaceActive(room_id);
117 if (ImGui::Checkbox(tr(
"Water Face Active"), &water_face_active)) {
118 editor_state->SetWaterFaceActive(room_id, water_face_active);
119 preview_state_changed =
true;
122 bool dam_floodgate_open = editor_state->IsDamFloodgateOpen(room_id);
123 if (ImGui::Checkbox(tr(
"Dam Floodgate Open"), &dam_floodgate_open)) {
124 editor_state->SetDamFloodgateOpen(room_id, dam_floodgate_open);
125 preview_state_changed =
true;
128 bool wall_moved = editor_state->IsWallMoved(room_id);
129 if (ImGui::Checkbox(tr(
"Moving Wall Shifted"), &wall_moved)) {
130 editor_state->SetWallMoved(room_id, wall_moved);
131 preview_state_changed =
true;
134 bool floor_bombed = editor_state->IsFloorBombable(room_id);
135 if (ImGui::Checkbox(tr(
"Bombed Floor Open"), &floor_bombed)) {
136 editor_state->SetFloorBombable(room_id, floor_bombed);
137 preview_state_changed =
true;
142 bool blind_light_active =
143 editor_state->IsFloorBombable(kThievesTownEastAtticRoomId);
144 if (ImGui::Checkbox(tr(
"East Attic Open / Blind Light"),
145 &blind_light_active)) {
146 editor_state->SetFloorBombable(kThievesTownEastAtticRoomId,
148 preview_state_changed =
true;
150 if (ImGui::IsItemHovered()) {
151 ImGui::SetTooltip(tr(
152 "Object FF1 uses the persisted bombed-floor flag for room 0x065, "
153 "even when previewed in Blind's room."));
156 bool rupee_floor_cleared = editor_state->IsRupeeFloorCleared(room_id);
157 if (ImGui::Checkbox(tr(
"Rupee Floor Cleared"), &rupee_floor_cleared)) {
158 editor_state->SetRupeeFloorCleared(room_id, rupee_floor_cleared);
159 preview_state_changed =
true;
162 if (ImGui::Button(tr(
"Reset Preview State"))) {
163 editor_state->Reset();
164 preview_state_changed =
true;
167 if (preview_state_changed) {
172 tr(
"Preview state controls require EditorDungeonState."));
177 ImGui::Text(tr(
"Object Outline Filters"));
178 ImGui::Text(tr(
"By Type:"));
179 ImGui::Checkbox(tr(
"Type 1"),
181 ImGui::Checkbox(tr(
"Type 2"),
183 ImGui::Checkbox(tr(
"Type 3"),
185 ImGui::Text(tr(
"By Stored Placement:"));
186 ImGui::Checkbox(tr(
"Primary / Upper layer (BG1)"),
188 ImGui::Checkbox(tr(
"BG2 overlay / Lower layer (BG2)"),
190 ImGui::Checkbox(tr(
"BG1 overlay (room stream only)"),
199 ImGui::SetNextWindowPos(
201 ImGuiCond_FirstUseEver);
202 ImGui::SetNextWindowSize(ImVec2(250, 0), ImGuiCond_FirstUseEver);
204 ImGuiWindowFlags_NoCollapse)) {
208 auto ensure_bitmap_texture = [
this](
gfx::Bitmap& bitmap) {
209 if (!
renderer_ || !bitmap.is_active() || bitmap.width() <= 0) {
212 if (!bitmap.texture()) {
215 }
else if (bitmap.modified()) {
221 ensure_bitmap_texture(bg1);
222 ensure_bitmap_texture(bg2);
227 ImGui::Text(tr(
"BG1 Bitmap"));
228 ImGui::Text(tr(
" Size: %dx%d"), bg1.width(), bg1.height());
229 ImGui::Text(tr(
" Active: %s"), bg1.is_active() ?
"YES" :
"NO");
230 ImGui::Text(tr(
" Texture: 0x%p"), bg1.texture());
231 ImGui::Text(tr(
" Modified: %s"), bg1.modified() ?
"YES" :
"NO");
234 ImGui::Text(tr(
" Preview:"));
235 ImGui::Image((ImTextureID)(intptr_t)bg1.texture(), ImVec2(128, 128));
239 ImGui::Text(tr(
"BG2 Bitmap"));
240 ImGui::Text(tr(
" Size: %dx%d"), bg2.width(), bg2.height());
241 ImGui::Text(tr(
" Active: %s"), bg2.is_active() ?
"YES" :
"NO");
242 ImGui::Text(tr(
" Texture: 0x%p"), bg2.texture());
243 ImGui::Text(tr(
" Modified: %s"), bg2.modified() ?
"YES" :
"NO");
246 ImGui::Text(tr(
" Preview:"));
247 ImGui::Image((ImTextureID)(intptr_t)bg2.texture(), ImVec2(128, 128));
254 ImGui::SetNextWindowPos(
256 ImGuiCond_FirstUseEver);
257 ImGui::SetNextWindowSize(ImVec2(220, 0), ImGuiCond_FirstUseEver);
259 ImGuiWindowFlags_NoCollapse)) {
261 ImGui::Text(tr(
"Canvas Size: %.0fx%.0f"),
canvas_.
width(),
265 ImGui::Text(tr(
"Layer Visibility (4-way):"));
267 for (
int i = 0; i < 4; ++i) {
269 bool visible = layer_mgr.IsLayerVisible(layer);
270 auto blend = layer_mgr.GetLayerBlendMode(layer);
271 ImGui::Text(
" %s: %s (%s)",
273 visible ?
"VISIBLE" :
"hidden",
278 ImGui::Text(tr(
"Draw Order:"));
279 auto draw_order = layer_mgr.GetDrawOrder();
280 for (
int i = 0; i < 4; ++i) {
281 ImGui::Text(
" %d: %s", i + 1,
284 ImGui::Text(tr(
"BG2 On Top: %s"), layer_mgr.IsBG2OnTop() ?
"YES" :
"NO");
294 const float scale = transform.
scale();
302 ImGui::GetWindowDrawList(), room_origin, scale,
310 ImGui::GetWindowDrawList(), room_origin, scale, room);
315 room_origin, scale, room);
320 ImGui::GetWindowDrawList(), room_origin, scale, room);
325 ImGui::GetWindowDrawList(), room_origin, scale, room,
331 ImGui::GetWindowDrawList(), room_origin, scale, room,
337 ImGui::GetWindowDrawList(), room_origin, scale,
342 ImDrawList* draw_list = ImGui::GetWindowDrawList();
343 const ImVec2 canvas_pos = room_origin;
345 const ImU32 fill_color =
346 ImGui::GetColorU32(ImVec4(info.x, info.y, info.z, 0.25f));
347 const ImU32 border_color =
348 ImGui::GetColorU32(ImVec4(info.x, info.y, info.z, 0.8f));
349 const ImU32 text_bg_color = ImGui::GetColorU32(ImVec4(0, 0, 0, 0.6f));
351 auto is_custom = [](
int id) {
352 return id == 0x31 ||
id == 0x32;
356 if (!is_custom(
static_cast<int>(obj.id_))) {
360 const float px =
static_cast<float>(obj.x()) * 8.0f * scale;
361 const float py =
static_cast<float>(obj.y()) * 8.0f * scale;
362 const float box_w = 16.0f * scale;
363 const float box_h = 16.0f * scale;
364 const ImVec2 p0(canvas_pos.x + px, canvas_pos.y + py);
365 const ImVec2 p1(p0.x + box_w, p0.y + box_h);
367 draw_list->AddRectFilled(p0, p1, fill_color, 2.0f);
368 draw_list->AddRect(p0, p1, border_color, 2.0f, 0, 1.5f);
371 std::snprintf(label,
sizeof(label),
"0x%02X s%d",
372 static_cast<int>(obj.id_),
373 static_cast<int>(obj.size_ & 0x1F));
374 const ImVec2 text_sz = ImGui::CalcTextSize(label);
375 const ImVec2 tp(p0.x + 1.0f, p0.y - text_sz.y - 1.0f);
376 draw_list->AddRectFilled(
377 tp, ImVec2(tp.x + text_sz.x + 2.0f, tp.y + text_sz.y), text_bg_color,
379 draw_list->AddText(tp, border_color, label);
384 const bool show_tracks =
387 if (show_tracks && !tracks.empty()) {
388 ImDrawList* draw_list = ImGui::GetWindowDrawList();
389 const ImVec2 canvas_pos = room_origin;
391 const int active_track =
396 for (
const auto& track : tracks) {
398 static_cast<uint16_t
>(track.start_x),
399 static_cast<uint16_t
>(track.start_y));
400 if (local.room_id != room_id) {
404 ImVec4 marker_color = theme.selection_primary;
405 if (track.id == active_track) {
406 marker_color = theme.status_warning;
409 const float px =
static_cast<float>(local.local_pixel_x) * scale;
410 const float py =
static_cast<float>(local.local_pixel_y) * scale;
411 ImVec2 center(canvas_pos.x + px, canvas_pos.y + py);
412 const float radius = 6.0f * scale;
414 draw_list->AddCircleFilled(center, radius,
415 ImGui::GetColorU32(marker_color));
416 draw_list->AddCircle(center, radius + 2.0f,
417 ImGui::GetColorU32(ImVec4(0, 0, 0, 0.6f)), 0,
420 std::string label = absl::StrFormat(
"T%d", track.id);
422 ImVec2(center.x + 8.0f * scale, center.y - 6.0f * scale),
423 ImGui::GetColorU32(theme.text_primary), label.c_str());
440 const int tile_x = tile_coords.first;
441 const int tile_y = tile_coords.second;
442 if (tile_x < 0 || tile_x >= 64 || tile_y < 0 || tile_y >= 64) {
446 const int canvas_x = tile_x * 8;
447 const int canvas_y = tile_y * 8;
448 const std::pair<uint16_t, uint16_t> camera_coords =
450 const uint16_t camera_x = camera_coords.first;
451 const uint16_t camera_y = camera_coords.second;
455 const ImVec2 mouse_pos = ImGui::GetMousePos();
456 const ImVec2 overlay_pos(mouse_pos.x + 15, mouse_pos.y + 15);
459 ImGui::Text(tr(
"Tile: (%d, %d)"), tile_x, tile_y);
460 ImGui::Text(tr(
"Pixel: (%d, %d)"), canvas_x, canvas_y);
461 ImGui::Text(tr(
"Camera: ($%04X, $%04X)"), camera_x, camera_y);
462 ImGui::Text(tr(
"Sprite: (%d, %d)"), sprite_x, sprite_y);
bool show_coordinate_overlay_
bool show_custom_objects_overlay_
std::bitset< 256 > minecart_sprite_ids_
bool show_track_collision_legend_
void DrawLayerInfoWindow(int room_id)
std::vector< uint16_t > track_tile_order_
ObjectOutlineToggles object_outline_toggles_
bool show_camera_quadrant_overlay_
bool show_room_debug_info_
bool show_water_fill_overlay_
void DrawPersistentDebugWindows(int room_id)
const DungeonRenderingHelpers::CollisionOverlayCache & GetCollisionOverlayCache(int room_id)
bool show_track_gap_overlay_
std::vector< uint16_t > switch_tile_order_
bool show_custom_collision_overlay_
void DrawTextureDebugWindow(zelda3::Room &room, int room_id)
void DrawRoomDebugWindow(zelda3::Room &room, int room_id)
void DrawCoordinateOverlayHud(int room_id)
bool show_minecart_sprite_overlay_
zelda3::RoomLayerManager & GetRoomLayerManager(int room_id)
uint8_t current_entrance_blockset_
bool track_direction_map_enabled_
bool show_track_route_overlay_
DungeonRoomStore * rooms_
gfx::IRenderer * renderer_
bool show_minecart_tracks_
DungeonRenderingHelpers::TrackCollisionConfig track_collision_config_
void DrawObjectPositionOutlines(const gui::CanvasRuntime &rt, const zelda3::Room &room)
MinecartTrackEditorPanel * minecart_track_panel_
void DrawRoomCanvasOverlays(const gui::CanvasRuntime &rt, zelda3::Room &room, int room_id)
bool show_track_collision_overlay_
static void DrawCustomCollisionOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const zelda3::Room &room)
static void DrawTrackGapOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const zelda3::Room &room, const CollisionOverlayCache &cache)
static void DrawTrackCollisionOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const CollisionOverlayCache &cache, const TrackCollisionConfig &config, bool direction_map_enabled, const std::vector< uint16_t > &track_tile_order, const std::vector< uint16_t > &switch_tile_order, bool show_legend)
static void DrawCameraQuadrantOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const zelda3::Room &room)
static void DrawTrackRouteOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const CollisionOverlayCache &cache)
static void DrawMinecartSpriteOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const zelda3::Room &room, const std::bitset< 256 > &minecart_sprite_ids, const TrackCollisionConfig &config)
static void DrawWaterFillOverlay(ImDrawList *draw_list, const ImVec2 &canvas_pos, float scale, const zelda3::Room &room)
bool IsPickingCoordinates() const
int GetPickingTrackIndex() const
const std::vector< MinecartTrack > & GetTracks()
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void ProcessTextureQueue(IRenderer *renderer)
Represents a bitmap image optimized for SNES ROM hacking.
auto global_scale() const
bool IsMouseHovering() const
Editor implementation of DungeonState.
static const char * GetBlendModeName(LayerBlendMode mode)
Get blend mode name.
static const char * GetLayerName(LayerType layer)
Get human-readable name for layer type.
void ReloadGraphics(std::optional< uint8_t > entrance_blockset=std::nullopt)
uint8_t spriteset() const
const std::vector< zelda3::Sprite > & GetSprites() const
const std::vector< RoomObject > & GetTileObjects() const
DungeonState * GetDungeonState()
uint8_t layout_id() const
const AgentUITheme & GetTheme()
constexpr int kThievesTownEastAtticRoomId
std::pair< uint16_t, uint16_t > TileToCameraCoords(int room_id, int tile_x, int tile_y)
Calculate camera coordinates from room and tile position.
constexpr int kSpriteTileSize
CameraToLocalResult CameraToLocalCoords(uint16_t camera_x, uint16_t camera_y)
Editors are the view controllers for the application.
void DrawCanvasHUD(const char *label, const ImVec2 &pos, const ImVec2 &size, std::function< void()> draw_content)
Draw a stylized Heads-Up Display (HUD) for canvas status.
LayerBlendMode
Layer blend modes for compositing.
LayerType
Layer types for the 4-way visibility system.