9#include "absl/strings/str_format.h"
19#include "imgui/imgui.h"
49template <
typename Record>
51 const Record* target) {
52 for (
size_t i = 0; i < records.size(); ++i) {
53 if (&records[i] == target)
59template <
typename Record,
typename Notify>
62 Notify notify,
const char* description) {
63 const Record before = records[row];
65 return absl::OkStatus();
66 auto restore = [&records, row, notify](
const Record& value) -> absl::Status {
67 if (row >= records.size()) {
68 return absl::FailedPreconditionError(
"The edited table row is gone");
71 notify(&records[row]);
72 return absl::OkStatus();
76 before, after, std::move(restore), description));
77 return absl::OkStatus();
99 return "Dungeon Sprite";
110 return absl::FailedPreconditionError(
111 "Native entity editing is unavailable in preview");
114 auto row = FindRecordRow(*records, target);
117 row = FindRecordRow(*records, target);
120 return absl::FailedPreconditionError(
121 "Select the hole's row in the expanded entrance table");
125 return absl::NotFoundError(
"Select a current entrance table row");
129 return CommitRecordEdit(
136 "Edit overworld entrance");
142 return absl::FailedPreconditionError(
143 "Native entity editing is unavailable in preview");
146 const auto row = FindRecordRow(*records, target);
148 return absl::NotFoundError(
"Select a current return table row");
151 return CommitRecordEdit(
158 "Edit overworld return");
167 auto* editor = ctx.editor;
168 if (editor->is_lyra_preview()) {
169 ImGui::TextDisabled(
"Lyra preview: entity editing unavailable");
173 if (editor->current_map_id() >= 0xA0 && editor->current_map_id() < 0xC0) {
174 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.85f, 0.3f, 1.0f));
175 ImGui::Text(
ICON_MD_INFO " Tail Map $%02X Entity Limitations",
176 editor->current_map_id());
177 ImGui::PopStyleColor();
179 "• Items & Bomb Doors: Disabled (requires SRAM event allocation)\n"
180 "• Exits: 1 free slot (full links require ASM exit table v2)\n"
181 "• Entrances: Trigger table v2 in development (Phase P2)\n"
182 "• Sprites: Draft state (actor compilation needed for ROM)");
188 const Rom* rom = editor->rom();
197 :
"Inspect special entrances")) {
199 rom ? std::optional<zelda3::SpecialEntranceInspection>(
201 editor->current_map_id()))
209 if (!inspection.available) {
210 ImGui::TextWrapped(
"%s", inspection.provenance.c_str());
212 ImGui::Text(
"%zu matching positions", inspection.matches.size());
213 for (
size_t i = 0; i < inspection.matches.size(); ++i) {
214 const auto& match = inspection.matches[i];
215 const auto& row = inspection.rows[match.row_index];
217 const std::string name =
221 absl::StrFormat(
"Special area $%02X",
destination))
222 :
"Unresolved destination";
223 ImGui::PushID(
static_cast<int>(i));
224 if (ImGui::Button(absl::StrFormat(
"%s ($%02X) - tile (%d,%d)", name,
228 editor->LocateOverworldTile(match.screen_id, match.world_x,
233 if (ImGui::TreeNode(
"Technical details and provenance")) {
234 ImGui::TextWrapped(
"%s", inspection.provenance.c_str());
235 ImGui::TextWrapped(
"%s", inspection.limitation.c_str());
236 if (inspection.overlay_may_replace_tiles) {
238 "This area has an overlay; the runtime sampled "
239 "tile may depend on overlay state.");
241 for (
const auto& match : inspection.matches) {
242 const auto& row = inspection.rows[match.row_index];
244 "Screen $%02X parent $%02X at (%d,%d): Map16 $%03X, "
245 "word $%04X, mask $%03X -> fake exit $%04X "
246 "(arrival direction $%04X)",
247 match.screen_id, match.parent_area, match.tile_x, match.tile_y,
248 match.tile16_id, match.first_word, match.masked_trigger,
249 row.destination_marker, row.direction);
251 if (ImGui::Button(
"Copy special entrance details")) {
252 std::string details = inspection.provenance +
"\n";
253 for (
const auto& match : inspection.matches) {
254 const auto& row = inspection.rows[match.row_index];
255 details += absl::StrFormat(
256 "screen=$%02X parent=$%02X tile=(%d,%d) map16=$%03X "
257 "word=$%04X mask=$%03X marker=$%04X\n",
258 match.screen_id, match.parent_area, match.tile_x, match.tile_y,
259 match.tile16_id, match.first_word, match.masked_trigger,
260 row.destination_marker);
262 ImGui::SetClipboardText(details.c_str());
270 auto* active_entity = editor->current_entity();
271 if (active_entity ==
nullptr) {
272 ImGui::TextDisabled(tr(
"No entity selected"));
276 ImGui::Text(tr(
"Type: %s"), GetEntityTypeLabel(active_entity->entity_type_));
287 ctx.editor->SetCurrentEntity(entity);
296 auto* editor = ctx.editor;
297 editor->SetCurrentEntity(entity);
308 editor->edit_entrance() =
333 if (!ctx || !entity) {
336 ctx.editor->SetCurrentEntity(entity);
345 auto* editor = ctx.editor;
353 if (entity_to_edit) {
357 if (ImGui::BeginPopupModal(
"Entity Insert Error",
nullptr,
358 ImGuiWindowFlags_AlwaysAutoResize)) {
359 ImGui::TextWrapped(
"%s", editor->insert_error().c_str());
361 if (ImGui::Button(tr(
"OK"), ImVec2(120.0f, 0.0f))) {
362 editor->insert_error().clear();
363 ImGui::CloseCurrentPopup();
369 if (editing_entity &&
371 const auto status = editor->ApplyExitEdit(
373 editor->edit_exit());
375 editor->insert_error() = std::string(status.message());
376 ImGui::OpenPopup(
"Entity Edit Error");
381 if (editing_entity && editing_entity->entity_type_ ==
383 const auto status = editor->ApplyEntranceEdit(
385 editor->edit_entrance());
387 editor->insert_error() = std::string(status.message());
388 ImGui::OpenPopup(
"Entity Edit Error");
393 if (editing_entity &&
395 if (editor->edit_item().deleted) {
396 (void)editor->DeleteSelectedItem();
400 editor->NotifyEntityModified(editing_entity);
405 if (editing_entity && editing_entity->entity_type_ ==
407 *
static_cast<zelda3::Sprite*
>(editing_entity) = editor->edit_sprite();
408 editor->NotifyEntityModified(editing_entity);
412 if (ImGui::BeginPopupModal(
"Entity Edit Error",
nullptr,
413 ImGuiWindowFlags_AlwaysAutoResize)) {
414 ImGui::TextWrapped(
"%s", editor->insert_error().c_str());
415 if (ImGui::Button(tr(
"OK")))
416 ImGui::CloseCurrentPopup();
420 const bool exit_popup_open = ImGui::IsPopupOpen(
422 const bool entrance_popup_open = ImGui::IsPopupOpen(
425 const bool item_popup_open = ImGui::IsPopupOpen(
427 const bool sprite_popup_open = ImGui::IsPopupOpen(
429 if (!exit_popup_open && !entrance_popup_open && !item_popup_open &&
430 !sprite_popup_open) {
438 if (!ctx || !mutation_service)
441 auto* editor = ctx.editor;
442 auto request = editor->TakePendingEntityInsertion();
446 auto res = mutation_service->
InsertEntity(request->type, request->target,
451 editor->NotifyEntityModified(res.entity);
453 editor->insert_error() = res.error_message;
454 ImGui::OpenPopup(
"Entity Insert Error");
463 auto* editor = ctx.editor;
464 auto* active_entity = editor->current_entity();
469 std::vector<gui::MenuItemSpec> items;
472 [&entity_to_edit, active_entity]() { entity_to_edit = active_entity; });
476 items.emplace_back(
"Jump to Room",
ICON_MD_LINK, [editor, exit]() {
477 editor->RequestJumpToRoom(exit->room_id_);
479 }
else if (active_entity->entity_type_ ==
482 items.emplace_back(
"Jump to Entrance",
ICON_MD_LINK, [editor, entrance]() {
483 editor->RequestJumpToEntrance(entrance->entrance_id_);
487 return entity_to_edit;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
UndoManager undo_manager_
Editor-level service responsible for overworld entity mutations.
MutationResult InsertEntity(const std::string &type, ImVec2 pos, int map_id, int game_state, uint8_t item_id=0)
Dispatches entity insertion based on type string.
absl::Status ApplyEntranceEdit(zelda3::OverworldEntrance *target, const zelda3::OverworldEntrance &draft)
zelda3::OverworldEntrance edit_entrance_
void NotifyEntityModified(zelda3::GameEntity *entity)
Notify that an entity has been modified, marking ROM as dirty.
zelda3::Overworld overworld_
zelda3::GameEntity * current_entity_
zelda3::OverworldExit edit_exit_
absl::Status ApplyExitEdit(zelda3::OverworldExit *target, const zelda3::OverworldExit &draft)
Authoritative component for entity editing state and UI.
zelda3::GameEntity * editing_entity_
std::optional< zelda3::SpecialEntranceInspection > special_entrances_
void OpenEditorFor(zelda3::GameEntity *entity)
void ProcessPendingInsertion(EntityMutationService *mutation_service)
static constexpr const char * kContextMenuPopupId
void Draw(bool *p_open) override
Draw the panel content.
zelda3::GameEntity * DrawEntityContextMenu()
const uint8_t * inspected_rom_data_
std::string inspected_filename_
void OpenContextMenuFor(zelda3::GameEntity *entity)
void SetActiveEntity(zelda3::GameEntity *entity)
const Rom * inspected_rom_
Manages undo/redo stacks for a single editor.
void Push(std::unique_ptr< UndoAction > action)
Base class for all overworld and dungeon entities.
enum yaze::zelda3::GameEntity::EntityType entity_type_
Represents an overworld exit that transitions from dungeon to overworld.
bool is_lyra_preview() const
A class for managing sprites in the overworld and underworld.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
absl::Status CommitRecordEdit(std::vector< Record > &records, size_t row, const Record &after, UndoManager &history, Notify notify, const char *description)
std::optional< size_t > FindRecordRow(const std::vector< Record > &records, const Record *target)
const char * GetEntityTypeLabel(zelda3::GameEntity::EntityType type)
bool SameRecord(const zelda3::OverworldEntrance &a, const zelda3::OverworldEntrance &b)
absl::StatusOr< zelda3::OverworldExit > BuildOverworldExitEdit(const zelda3::Overworld &overworld, const std::vector< zelda3::OverworldExit > &records, size_t row, const zelda3::OverworldExit &draft)
bool DrawSpriteEditorPopup(zelda3::Sprite &sprite)
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance)
bool DrawItemEditorPopup(zelda3::OverworldItem &item)
absl::StatusOr< zelda3::OverworldEntrance > BuildOverworldEntranceEdit(const zelda3::Overworld &overworld, const std::vector< zelda3::OverworldEntrance > &records, size_t row, const zelda3::OverworldEntrance &draft, bool legacy_hole_table)
OverworldWindowContext CurrentOverworldWindowContext()
bool DrawExitEditorPopup(zelda3::OverworldExit &exit)
constexpr const char * kOverworld
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.
void RenderMenuItems(const std::vector< MenuItemSpec > &items)
SpecialEntranceInspection InspectSpecialEntrances(const Rom &rom, Overworld &overworld, int map_id)
OverworldRomProfile DetectOverworldRomProfile(const Rom &rom)
#define REGISTER_PANEL(PanelClass)
Auto-registration macro for panels with default constructors.
#define RETURN_IF_ERROR(expr)
Room transition destination.
bool has_expanded_entrances
static std::string GetLabel(const std::string &resource_type, int id, const std::string &default_value="")
Get a label by resource type and ID.