16using ImGui::BeginChild;
21using ImGui::Selectable;
27 ImVec2 canvas_p0, ImVec2 scrolling,
30 const ImGuiIO& io = ImGui::GetIO();
31 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
32 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
35 float scaled_x = entity.
x_ * scale;
36 float scaled_y = entity.
y_ * scale;
37 float scaled_size = 16.0f * scale;
40 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
41 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
47 const ImGuiIO& io = ImGui::GetIO();
50 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
53 float scaled_x = entity.
x_ * rt.
scale;
54 float scaled_y = entity.
y_ * rt.
scale;
55 float scaled_size = 16.0f * rt.
scale;
58 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
59 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
63 ImVec2 scrolling,
bool free_movement,
float scale) {
65 const ImGuiIO& io = ImGui::GetIO();
66 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
67 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
70 float world_x = mouse_pos.x / scale;
71 float world_y = mouse_pos.y / scale;
74 int grid_size = free_movement ? 8 : 16;
75 int new_x =
static_cast<int>(world_x) / grid_size * grid_size;
76 int new_y =
static_cast<int>(world_y) / grid_size * grid_size;
84 bool set_done =
false;
88 if (ImGui::BeginPopup(
"Entrance Inserter")) {
89 static int entrance_id = 0;
90 if (ImGui::IsWindowAppearing()) {
98 ImGui::CloseCurrentPopup();
103 ImGui::CloseCurrentPopup();
112 static bool set_done =
false;
118 if (ImGui::BeginPopupModal(
121 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
122 if (ImGui::IsWindowAppearing()) {
135 ImGui::Checkbox(
"Is Hole", &entrance.
is_hole_);
139 if (Button(
"Save")) {
141 ImGui::CloseCurrentPopup();
144 if (Button(
"Delete")) {
147 ImGui::CloseCurrentPopup();
150 if (Button(
"Cancel")) {
151 ImGui::CloseCurrentPopup();
160 if (ImGui::BeginPopup(
"Exit Inserter")) {
161 static int exit_id = 0;
162 static int room_id = 0;
163 static int x_pos = 0;
164 static int y_pos = 0;
166 if (ImGui::IsWindowAppearing()) {
173 ImGui::Text(
"Insert New Exit");
181 if (Button(
"Create Exit")) {
184 ImGui::CloseCurrentPopup();
188 if (Button(
"Cancel")) {
189 ImGui::CloseCurrentPopup();
197 static bool set_done =
false;
202 if (ImGui::BeginPopupModal(
204 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
206 static int doorType = 0;
208 static int fancyDoorType = 0;
214 static int centerY = 0;
215 static int centerX = 0;
218 static int linkPosture = 0;
219 static int spriteGFX = 0;
220 static int bgGFX = 0;
221 static int palette = 0;
222 static int sprPal = 0;
224 static int bottom = 0;
226 static int right = 0;
227 static int leftEdgeOfMap = 0;
228 static bool special_exit =
false;
229 static bool show_properties =
false;
231 if (ImGui::IsWindowAppearing()) {
239 centerY = 0; centerX = 0; unk1 = 0; unk2 = 0;
240 linkPosture = 0; spriteGFX = 0; bgGFX = 0;
241 palette = 0; sprPal = 0; top = 0; bottom = 0;
242 left = 0; right = 0; leftEdgeOfMap = 0;
243 special_exit =
false;
244 show_properties =
false;
268 Checkbox(
"Show properties", &show_properties);
269 if (show_properties) {
270 Text(
"Deleted? %s", exit.
deleted_ ?
"true" :
"false");
271 Text(
"Hole? %s", exit.
is_hole_ ?
"true" :
"false");
272 Text(
"Auto-calc scroll/camera? %s",
274 Text(
"Map ID: 0x%02X", exit.
map_id_);
280 if (ImGui::RadioButton(
"None", &doorType, 0)) exit.
door_type_1_ = doorType;
282 if (ImGui::RadioButton(
"Wooden", &doorType, 1)) exit.
door_type_1_ = doorType;
284 if (ImGui::RadioButton(
"Bombable", &doorType, 2)) exit.
door_type_1_ = doorType;
292 if (ImGui::RadioButton(
"None##Fancy", &fancyDoorType, 0)) exit.
door_type_2_ = fancyDoorType;
294 if (ImGui::RadioButton(
"Sanctuary", &fancyDoorType, 1)) exit.
door_type_2_ = fancyDoorType;
296 if (ImGui::RadioButton(
"Palace", &fancyDoorType, 2)) exit.
door_type_2_ = fancyDoorType;
299 if (fancyDoorType != 0) {
306 Checkbox(
"Special exit", &special_exit);
330 ImGui::CloseCurrentPopup();
337 ImGui::CloseCurrentPopup();
344 ImGui::CloseCurrentPopup();
355 if (ImGui::BeginPopup(
"Item Inserter")) {
356 static size_t new_item_id = 0;
358 BeginChild(
"ScrollRegion", ImVec2(150, 150),
true,
359 ImGuiWindowFlags_AlwaysVerticalScrollbar);
370 ImGui::CloseCurrentPopup();
375 ImGui::CloseCurrentPopup();
384 bool set_done =
false;
385 if (ImGui::BeginPopupModal(
387 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
388 BeginChild(
"ScrollRegion", ImVec2(150, 150),
true,
389 ImGuiWindowFlags_AlwaysVerticalScrollbar);
403 ImGui::CloseCurrentPopup();
407 ImGui::CloseCurrentPopup();
413 ImGui::CloseCurrentPopup();
424 static ImGuiTextFilter filter;
425 static std::vector<SpriteItem> items;
429 for (
int i = 0; i < 256; ++i) {
434 filter.Draw(
"Filter", 180);
436 if (ImGui::BeginTable(
"##sprites", 2,
437 ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
438 ImGui::TableSetupColumn(
"ID", ImGuiTableColumnFlags_DefaultSort, 0.0f,
441 ImGui::TableHeadersRow();
444 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) {
445 if (sort_specs->SpecsDirty) {
447 sort_specs->SpecsDirty =
false;
452 for (
const auto& item : items) {
453 if (filter.PassFilter(item.name)) {
454 ImGui::TableNextRow();
455 ImGui::TableSetColumnIndex(0);
457 ImGui::TableSetColumnIndex(1);
459 if (Selectable(item.name, selected_id == item.id,
460 ImGuiSelectableFlags_SpanAllColumns)) {
461 selected_id = item.id;
462 onSpriteSelect(item.id);
471 if (ImGui::BeginPopup(
"Sprite Inserter")) {
472 static int new_sprite_id = 0;
473 static int x_pos = 0;
474 static int y_pos = 0;
476 if (ImGui::IsWindowAppearing()) {
482 ImGui::Text(
"Add New Sprite");
485 BeginChild(
"ScrollRegion", ImVec2(250, 200),
true,
486 ImGuiWindowFlags_AlwaysVerticalScrollbar);
487 DrawSpriteTable([](
int selected_id) { new_sprite_id = selected_id; }, new_sprite_id);
491 ImGui::Text(
"Position:");
495 if (Button(
"Add Sprite")) {
501 ImGui::CloseCurrentPopup();
505 if (Button(
"Cancel")) {
506 ImGui::CloseCurrentPopup();
514 static bool set_done =
false;
519 if (ImGui::BeginPopupModal(
522 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
523 static int selected_id = 0;
524 if (ImGui::IsWindowAppearing()) {
525 selected_id = sprite.
id();
528 BeginChild(
"ScrollRegion", ImVec2(350, 350),
true,
529 ImGuiWindowFlags_AlwaysVerticalScrollbar);
531 Text(
"%s", sprite.
name().c_str());
542 ImGui::CloseCurrentPopup();
547 ImGui::CloseCurrentPopup();
553 ImGui::CloseCurrentPopup();
563 const std::vector<gfx::Tile16>& tiles16,
564 const std::array<uint8_t, 0x200>& all_tiles_types) {
565 static bool set_done =
false;
571 if (ImGui::BeginPopupModal(
574 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
575 static ImGuiTextFilter filter;
576 static int patch_mode = 0;
581 Text(
"Diggable Tiles: %d / 512", diggable_count);
585 filter.Draw(
"Filter by Tile ID", 200);
587 if (Button(
"Clear Filter")) {
592 BeginChild(
"TileList", ImVec2(400, 300),
true,
593 ImGuiWindowFlags_AlwaysVerticalScrollbar);
598 for (uint16_t tile_id = 0;
602 snprintf(id_str,
sizeof(id_str),
"$%03X", tile_id);
604 if (!filter.PassFilter(id_str)) {
608 bool is_diggable = diggable_tiles->
IsDiggable(tile_id);
610 tiles16[tile_id], all_tiles_types);
613 std::optional<gui::StyleColorGuard> dig_color;
615 if (would_be_diggable) {
616 dig_color.emplace(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
618 dig_color.emplace(ImGuiCol_Text, ImVec4(0.8f, 0.8f, 0.2f, 1.0f));
622 if (Checkbox(id_str, &is_diggable)) {
628 if (ImGui::IsItemHovered()) {
629 ImGui::SetTooltip(
"Tile $%03X - %s",
631 would_be_diggable ?
"Auto-detected as diggable"
632 :
"Manually configured");
649 diggable_tiles->
Clear();
650 for (uint16_t tile_id = 0;
659 if (ImGui::IsItemHovered()) {
661 "Set diggable status based on tile types.\n"
662 "A tile is diggable if all 4 component tiles\n"
663 "have type 0x48 or 0x4A (diggable ground).");
670 if (ImGui::IsItemHovered()) {
671 ImGui::SetTooltip(
"Reset to vanilla diggable tiles:\n$034, $035, $071, "
672 "$0DA, $0E1, $0E2, $0F8, $10D, $10E, $10F");
677 diggable_tiles->
Clear();
683 if (ImGui::CollapsingHeader(
"ASM Patch Export")) {
687 ImGui::RadioButton(
"Vanilla", &patch_mode, 0);
689 ImGui::RadioButton(
"ZS Compatible", &patch_mode, 1);
691 ImGui::RadioButton(
"Custom", &patch_mode, 2);
695 if (patch_mode == 2) {
713 std::string patch_content =
727 ImGui::CloseCurrentPopup();
731 ImGui::CloseCurrentPopup();
static std::string GeneratePatch(const DiggableTiles &diggable_tiles, const DiggableTilesPatchConfig &config={})
Generate ASM patch code for the diggable tiles table.
Manages diggable tile state as a 512-bit bitfield.
int GetDiggableCount() const
Get the count of tiles marked as diggable.
void SetVanillaDefaults()
Reset to vanilla diggable tiles.
void SetDiggable(uint16_t tile_id, bool diggable)
Set or clear the diggable bit for a Map16 tile ID.
static bool IsTile16Diggable(const gfx::Tile16 &tile16, const std::array< uint8_t, 0x200 > &all_tiles_types)
Check if a Tile16 should be diggable based on its component tiles.
bool IsDiggable(uint16_t tile_id) const
Check if a Map16 tile ID is marked as diggable.
void Clear()
Clear all diggable bits.
Base class for all overworld and dungeon entities.
Represents an overworld exit that transitions from dungeon to overworld.
void UpdateMapProperties(uint16_t room_map_id, const void *context=nullptr) override
Update entity properties based on map position.
A class for managing sprites in the overworld and underworld.
void UpdateMapProperties(uint16_t map_id, const void *context=nullptr) override
Update entity properties based on map position.
auto set_deleted(bool deleted)
#define ICON_MD_FILE_DOWNLOAD
#define ICON_MD_AUTO_FIX_HIGH
#define ICON_MD_RESTART_ALT
void DrawExitInserterPopup()
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
void DrawItemInsertPopup()
bool DrawEntranceInserterPopup()
void DrawSpriteInserterPopup()
void DrawSpriteTable(std::function< void(int)> onSpriteSelect, int &selected_id)
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
bool DrawDiggableTilesEditorPopup(zelda3::DiggableTiles *diggable_tiles, const std::vector< gfx::Tile16 > &tiles16, const std::array< uint8_t, 0x200 > &all_tiles_types)
Draw popup dialog for editing diggable tiles configuration.
void MoveEntityOnGrid(zelda3::GameEntity *entity, ImVec2 canvas_p0, ImVec2 scrolling, bool free_movement, float scale)
Move entity to grid-aligned position based on mouse.
constexpr float kInputFieldSize
@ SpriteItemColumnID_Name
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
bool IsMouseHoveringOverEntity(const zelda3::GameEntity &entity, ImVec2 canvas_p0, ImVec2 scrolling, float scale)
Check if mouse is hovering over an entity.
constexpr const char * kOverworld
bool InputHexWord(const char *label, uint16_t *data, float input_width, bool no_step)
bool InputHex(const char *label, uint64_t *data)
std::string MakePopupId(size_t session_id, const std::string &editor_name, const std::string &popup_name)
Generate session-aware popup IDs to prevent conflicts in multi-editor layouts.
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
void TextWithSeparators(const absl::string_view &text)
const std::vector< std::string > kSecretItemNames
const std::string kSpriteDefaultNames[256]
constexpr int kMaxDiggableTileId
static const ImGuiTableSortSpecs * s_current_sort_specs
static void SortWithSortSpecs(ImGuiTableSortSpecs *sort_specs, std::vector< SpriteItem > &items)
Configuration for diggable tiles ASM patch generation.
uint32_t freespace_address
bool use_zs_compatible_mode