15using ImGui::BeginChild;
20using ImGui::Selectable;
26 ImVec2 canvas_p0, ImVec2 scrolling,
29 const ImGuiIO& io = ImGui::GetIO();
30 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
31 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
34 float scaled_x = entity.
x_ * scale;
35 float scaled_y = entity.
y_ * scale;
36 float scaled_size = 16.0f * scale;
39 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
40 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
46 const ImGuiIO& io = ImGui::GetIO();
49 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
52 float scaled_x = entity.
x_ * rt.
scale;
53 float scaled_y = entity.
y_ * rt.
scale;
54 float scaled_size = 16.0f * rt.
scale;
57 return mouse_pos.x >= scaled_x && mouse_pos.x <= scaled_x + scaled_size &&
58 mouse_pos.y >= scaled_y && mouse_pos.y <= scaled_y + scaled_size;
62 ImVec2 scrolling,
bool free_movement,
float scale) {
64 const ImGuiIO& io = ImGui::GetIO();
65 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
66 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
69 float world_x = mouse_pos.x / scale;
70 float world_y = mouse_pos.y / scale;
73 int grid_size = free_movement ? 8 : 16;
74 int new_x =
static_cast<int>(world_x) / grid_size * grid_size;
75 int new_y =
static_cast<int>(world_y) / grid_size * grid_size;
83 bool set_done =
false;
87 if (ImGui::BeginPopup(
"Entrance Inserter")) {
88 static int entrance_id = 0;
89 if (ImGui::IsWindowAppearing()) {
97 ImGui::CloseCurrentPopup();
102 ImGui::CloseCurrentPopup();
111 static bool set_done =
false;
117 if (ImGui::BeginPopupModal(
120 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
121 if (ImGui::IsWindowAppearing()) {
134 ImGui::Checkbox(
"Is Hole", &entrance.
is_hole_);
138 if (Button(
"Save")) {
140 ImGui::CloseCurrentPopup();
143 if (Button(
"Delete")) {
146 ImGui::CloseCurrentPopup();
149 if (Button(
"Cancel")) {
150 ImGui::CloseCurrentPopup();
159 if (ImGui::BeginPopup(
"Exit Inserter")) {
160 static int exit_id = 0;
161 static int room_id = 0;
162 static int x_pos = 0;
163 static int y_pos = 0;
165 if (ImGui::IsWindowAppearing()) {
172 ImGui::Text(
"Insert New Exit");
180 if (Button(
"Create Exit")) {
183 ImGui::CloseCurrentPopup();
187 if (Button(
"Cancel")) {
188 ImGui::CloseCurrentPopup();
196 static bool set_done =
false;
201 if (ImGui::BeginPopupModal(
203 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
205 static int doorType = 0;
207 static int fancyDoorType = 0;
213 static int centerY = 0;
214 static int centerX = 0;
217 static int linkPosture = 0;
218 static int spriteGFX = 0;
219 static int bgGFX = 0;
220 static int palette = 0;
221 static int sprPal = 0;
223 static int bottom = 0;
225 static int right = 0;
226 static int leftEdgeOfMap = 0;
227 static bool special_exit =
false;
228 static bool show_properties =
false;
230 if (ImGui::IsWindowAppearing()) {
238 centerY = 0; centerX = 0; unk1 = 0; unk2 = 0;
239 linkPosture = 0; spriteGFX = 0; bgGFX = 0;
240 palette = 0; sprPal = 0; top = 0; bottom = 0;
241 left = 0; right = 0; leftEdgeOfMap = 0;
242 special_exit =
false;
243 show_properties =
false;
267 Checkbox(
"Show properties", &show_properties);
268 if (show_properties) {
269 Text(
"Deleted? %s", exit.
deleted_ ?
"true" :
"false");
270 Text(
"Hole? %s", exit.
is_hole_ ?
"true" :
"false");
271 Text(
"Auto-calc scroll/camera? %s",
273 Text(
"Map ID: 0x%02X", exit.
map_id_);
279 if (ImGui::RadioButton(
"None", &doorType, 0)) exit.
door_type_1_ = doorType;
281 if (ImGui::RadioButton(
"Wooden", &doorType, 1)) exit.
door_type_1_ = doorType;
283 if (ImGui::RadioButton(
"Bombable", &doorType, 2)) exit.
door_type_1_ = doorType;
291 if (ImGui::RadioButton(
"None##Fancy", &fancyDoorType, 0)) exit.
door_type_2_ = fancyDoorType;
293 if (ImGui::RadioButton(
"Sanctuary", &fancyDoorType, 1)) exit.
door_type_2_ = fancyDoorType;
295 if (ImGui::RadioButton(
"Palace", &fancyDoorType, 2)) exit.
door_type_2_ = fancyDoorType;
298 if (fancyDoorType != 0) {
305 Checkbox(
"Special exit", &special_exit);
329 ImGui::CloseCurrentPopup();
336 ImGui::CloseCurrentPopup();
343 ImGui::CloseCurrentPopup();
354 if (ImGui::BeginPopup(
"Item Inserter")) {
355 static size_t new_item_id = 0;
357 BeginChild(
"ScrollRegion", ImVec2(150, 150),
true,
358 ImGuiWindowFlags_AlwaysVerticalScrollbar);
369 ImGui::CloseCurrentPopup();
374 ImGui::CloseCurrentPopup();
383 bool set_done =
false;
384 if (ImGui::BeginPopupModal(
386 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
387 BeginChild(
"ScrollRegion", ImVec2(150, 150),
true,
388 ImGuiWindowFlags_AlwaysVerticalScrollbar);
402 ImGui::CloseCurrentPopup();
406 ImGui::CloseCurrentPopup();
412 ImGui::CloseCurrentPopup();
423 static ImGuiTextFilter filter;
424 static std::vector<SpriteItem> items;
428 for (
int i = 0; i < 256; ++i) {
433 filter.Draw(
"Filter", 180);
435 if (ImGui::BeginTable(
"##sprites", 2,
436 ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
437 ImGui::TableSetupColumn(
"ID", ImGuiTableColumnFlags_DefaultSort, 0.0f,
440 ImGui::TableHeadersRow();
443 if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) {
444 if (sort_specs->SpecsDirty) {
446 sort_specs->SpecsDirty =
false;
451 for (
const auto& item : items) {
452 if (filter.PassFilter(item.name)) {
453 ImGui::TableNextRow();
454 ImGui::TableSetColumnIndex(0);
456 ImGui::TableSetColumnIndex(1);
458 if (Selectable(item.name, selected_id == item.id,
459 ImGuiSelectableFlags_SpanAllColumns)) {
460 selected_id = item.id;
461 onSpriteSelect(item.id);
470 if (ImGui::BeginPopup(
"Sprite Inserter")) {
471 static int new_sprite_id = 0;
472 static int x_pos = 0;
473 static int y_pos = 0;
475 if (ImGui::IsWindowAppearing()) {
481 ImGui::Text(
"Add New Sprite");
484 BeginChild(
"ScrollRegion", ImVec2(250, 200),
true,
485 ImGuiWindowFlags_AlwaysVerticalScrollbar);
486 DrawSpriteTable([](
int selected_id) { new_sprite_id = selected_id; }, new_sprite_id);
490 ImGui::Text(
"Position:");
494 if (Button(
"Add Sprite")) {
500 ImGui::CloseCurrentPopup();
504 if (Button(
"Cancel")) {
505 ImGui::CloseCurrentPopup();
513 static bool set_done =
false;
518 if (ImGui::BeginPopupModal(
521 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
522 static int selected_id = 0;
523 if (ImGui::IsWindowAppearing()) {
524 selected_id = sprite.
id();
527 BeginChild(
"ScrollRegion", ImVec2(350, 350),
true,
528 ImGuiWindowFlags_AlwaysVerticalScrollbar);
530 Text(
"%s", sprite.
name().c_str());
541 ImGui::CloseCurrentPopup();
546 ImGui::CloseCurrentPopup();
552 ImGui::CloseCurrentPopup();
562 const std::vector<gfx::Tile16>& tiles16,
563 const std::array<uint8_t, 0x200>& all_tiles_types) {
564 static bool set_done =
false;
570 if (ImGui::BeginPopupModal(
573 NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
574 static ImGuiTextFilter filter;
575 static int patch_mode = 0;
580 Text(
"Diggable Tiles: %d / 512", diggable_count);
584 filter.Draw(
"Filter by Tile ID", 200);
586 if (Button(
"Clear Filter")) {
591 BeginChild(
"TileList", ImVec2(400, 300),
true,
592 ImGuiWindowFlags_AlwaysVerticalScrollbar);
597 for (uint16_t tile_id = 0;
601 snprintf(id_str,
sizeof(id_str),
"$%03X", tile_id);
603 if (!filter.PassFilter(id_str)) {
607 bool is_diggable = diggable_tiles->
IsDiggable(tile_id);
609 tiles16[tile_id], all_tiles_types);
613 if (would_be_diggable) {
614 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
616 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f, 0.8f, 0.2f, 1.0f));
620 if (Checkbox(id_str, &is_diggable)) {
625 ImGui::PopStyleColor();
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