7#include "absl/strings/str_format.h"
15#include "imgui/imgui.h"
24 if (
rom_ ==
nullptr) {
25 return absl::InvalidArgumentError(
"ROM is null");
52 return absl::OkStatus();
56 if (
rom_ ==
nullptr) {
57 return absl::InvalidArgumentError(
"ROM is null");
61 return absl::InvalidArgumentError(
"Invalid room ID");
87 return absl::OkStatus();
92 return absl::FailedPreconditionError(
"No room loaded");
98 if (!validation_result.is_valid) {
99 std::string error_msg =
"Validation failed";
100 if (!validation_result.errors.empty()) {
101 error_msg +=
": " + validation_result.errors[0];
103 return absl::FailedPreconditionError(error_msg);
113 return absl::FailedPreconditionError(
"No room loaded");
133 return absl::OkStatus();
137 int size,
int layer) {
139 return absl::FailedPreconditionError(
"No room loaded");
144 if (object_type < 0 || object_type > 0xFFF) {
145 return absl::InvalidArgumentError(
"Invalid object type");
149 return absl::InvalidArgumentError(
"Invalid object size");
152 if (layer < kMinLayer || layer >
kMaxLayer) {
153 return absl::InvalidArgumentError(
"Invalid layer");
169 RoomObject new_object(object_type, x, y, size, layer);
177 return absl::FailedPreconditionError(
178 "Object placement would cause collision");
185 if (!add_status.ok()) {
208 return absl::OkStatus();
213 return absl::FailedPreconditionError(
"No room loaded");
217 return absl::OutOfRangeError(
"Object index out of range");
228 if (!remove_status.ok()) {
229 return remove_status;
234 if (selected_index > object_index) {
236 }
else if (selected_index == object_index) {
254 return absl::OkStatus();
259 return absl::FailedPreconditionError(
"No room loaded");
263 return absl::FailedPreconditionError(
"No objects selected");
274 std::sort(sorted_selection.begin(), sorted_selection.end(),
275 std::greater<size_t>());
278 for (
size_t index : sorted_selection) {
279 if (index < current_room_->GetTileObjectCount()) {
292 return absl::OkStatus();
298 return absl::FailedPreconditionError(
"No room loaded");
302 return absl::OutOfRangeError(
"Object index out of range");
323 test_object.
set_x(new_x);
324 test_object.
set_y(new_y);
327 if (i != object_index &&
329 return absl::FailedPreconditionError(
330 "Object move would cause collision");
348 return absl::OkStatus();
354 return absl::FailedPreconditionError(
"No room loaded");
358 return absl::OutOfRangeError(
"Object index out of range");
362 return absl::InvalidArgumentError(
"Invalid object size");
384 return absl::OkStatus();
388 const std::vector<size_t>& indices,
int dx,
int dy) {
390 return absl::FailedPreconditionError(
"No room loaded");
393 if (indices.empty()) {
394 return absl::OkStatus();
404 for (
size_t index : indices) {
409 int new_x =
object.
x() + dx;
410 int new_y =
object.y() + dy;
413 new_x = std::max(0, std::min(63, new_x));
414 new_y = std::max(0, std::min(63, new_y));
428 return absl::OkStatus();
432 const std::vector<size_t>& indices,
int new_layer) {
434 return absl::FailedPreconditionError(
"No room loaded");
437 if (new_layer < kMinLayer || new_layer >
kMaxLayer) {
438 return absl::InvalidArgumentError(
"Invalid layer");
447 for (
size_t index : indices) {
467 return absl::OkStatus();
471 const std::vector<size_t>& indices,
int new_size) {
473 return absl::FailedPreconditionError(
"No room loaded");
477 return absl::InvalidArgumentError(
"Invalid object size");
486 for (
size_t index : indices) {
504 return absl::OkStatus();
524 int new_x =
object.
x() + offset_x;
525 int new_y =
object.y() + offset_y;
528 new_x = std::max(0, std::min(63, new_x));
529 new_y = std::max(0, std::min(63, new_y));
549 const std::vector<size_t>& indices) {
555 for (
size_t index : indices) {
556 if (index < current_room_->GetTileObjectCount()) {
570 std::vector<size_t> new_indices;
582 int new_x = std::min(63, new_obj.
x() + 1);
583 int new_y = std::min(63, new_obj.
y() + 1);
584 new_obj.
set_x(new_x);
585 new_obj.
set_y(new_y);
588 new_indices.push_back(start_index++);
602 return absl::FailedPreconditionError(
"No room loaded");
606 return absl::OutOfRangeError(
"Object index out of range");
610 if (new_type < 0 || new_type > 0xFFF) {
611 return absl::InvalidArgumentError(
"Invalid object type");
621 object.
set_id(
static_cast<int16_t
>(new_type));
631 return absl::OkStatus();
637 return absl::FailedPreconditionError(
"No room loaded");
653 std::vector<RoomObject> new_objects =
658 for (
const auto& new_obj : new_objects) {
661 return absl::FailedPreconditionError(
662 "Template placement would cause collision");
669 for (
const auto& obj : new_objects) {
676 size_t added_count = new_objects.size();
677 for (
size_t i = 0; i < added_count; ++i) {
688 return absl::OkStatus();
694 return absl::FailedPreconditionError(
"No objects selected");
697 std::vector<RoomObject> objects;
698 int min_x = 64, min_y = 64;
702 if (index < current_room_->GetTileObjectCount()) {
704 objects.push_back(obj);
726 return absl::FailedPreconditionError(
"No room loaded");
730 return absl::OkStatus();
753 for (
size_t index : indices) {
760 if (obj.x() < ref_val)
764 if (obj.x() > ref_val)
768 if (obj.y() < ref_val)
772 if (obj.y() > ref_val)
788 ref_val = sum / count;
792 for (
size_t index : indices) {
819 return absl::OkStatus();
825 return absl::FailedPreconditionError(
"No room loaded");
829 return absl::OutOfRangeError(
"Object index out of range");
832 if (new_layer < kMinLayer || new_layer >
kMaxLayer) {
833 return absl::InvalidArgumentError(
"Invalid layer");
845 return absl::OkStatus();
857 return absl::OkStatus();
863 return absl::FailedPreconditionError(
"No room loaded");
878 int layer_delta = delta > 0 ? 1 : -1;
886 return absl::OkStatus();
889 return absl::OkStatus();
900 return absl::OkStatus();
907 if (object_index < current_room_->GetTileObjectCount()) {
918 return absl::OkStatus();
921 return absl::OkStatus();
931 if (current_size < 0x40) {
932 return current_size + 0x10;
933 }
else if (current_size < 0x80) {
934 return current_size + 0x08;
936 return current_size + 0x04;
940 if (current_size > 0x80) {
941 return current_size - 0x04;
942 }
else if (current_size > 0x40) {
943 return current_size - 0x08;
945 return current_size - 0x10;
957 bool shift_pressed) {
959 return absl::FailedPreconditionError(
"No room loaded");
971 if (object_index.has_value()) {
990 if (object_index.has_value()) {
1012 if (object_index.has_value()) {
1023 return absl::OkStatus();
1030 return absl::FailedPreconditionError(
"No room loaded");
1042 if (!undo_status.ok()) {
1053 return absl::FailedPreconditionError(
"No room loaded");
1066 return absl::OkStatus();
1071 return absl::FailedPreconditionError(
"No room loaded");
1080 if (object_index.has_value()) {
1090 return absl::OkStatus();
1107 return absl::OkStatus();
1112 return absl::FailedPreconditionError(
"No room loaded");
1116 return absl::OutOfRangeError(
"Object index out of range");
1133 return absl::OkStatus();
1152 if (object_type >= 0 && object_type <= 0xFFF) {
1161 return std::nullopt;
1168 return static_cast<size_t>(i);
1172 return std::nullopt;
1178 int obj_x =
object.x_;
1179 int obj_y =
object.y_;
1184 if (
object.size_ > 0x80) {
1189 return (x >= obj_x && x < obj_x + obj_width && y >= obj_y &&
1190 y < obj_y + obj_height);
1198 int obj1_x = obj1.
x_ * 16;
1199 int obj1_y = obj1.
y_ * 16;
1203 int obj2_x = obj2.
x_ * 16;
1204 int obj2_y = obj2.
y_ * 16;
1209 if (obj1.
size_ > 0x80) {
1214 if (obj2.
size_ > 0x80) {
1219 return !(obj1_x + obj1_w <= obj2_x || obj2_x + obj2_w <= obj1_x ||
1220 obj1_y + obj1_h <= obj2_y || obj2_y + obj2_h <= obj1_y);
1229 int room_x = screen_x / 16;
1230 int room_y = screen_y / 16;
1232 return {room_x, room_y};
1238 int screen_x = room_x * 16;
1239 int screen_y = room_y * 16;
1241 return {screen_x, screen_y};
1250 if (grid_size <= 0) {
1255 int tile_step = std::max(1, grid_size / 16);
1256 return (coordinate / tile_step) * tile_step;
1275 return absl::FailedPreconditionError(
"No room loaded");
1283 undo_point.
timestamp = std::chrono::steady_clock::now();
1296 return absl::OkStatus();
1301 return absl::FailedPreconditionError(
"Nothing to undo");
1309 current_state.
timestamp = std::chrono::steady_clock::now();
1322 return absl::FailedPreconditionError(
"Nothing to redo");
1330 current_state.
timestamp = std::chrono::steady_clock::now();
1343 return absl::FailedPreconditionError(
"No room loaded");
1365 return absl::OkStatus();
1386static uint32_t BlendColors(uint32_t base, uint32_t tint) {
1387 uint8_t a_tint = (tint >> 24) & 0xFF;
1391 uint8_t r_base = (base >> 16) & 0xFF;
1392 uint8_t g_base = (base >> 8) & 0xFF;
1393 uint8_t b_base = base & 0xFF;
1395 uint8_t r_tint = (tint >> 16) & 0xFF;
1396 uint8_t g_tint = (tint >> 8) & 0xFF;
1397 uint8_t b_tint = tint & 0xFF;
1399 float alpha = a_tint / 255.0f;
1400 uint8_t r = r_base * (1.0f - alpha) + r_tint * alpha;
1401 uint8_t g = g_base * (1.0f - alpha) + g_tint * alpha;
1402 uint8_t b = b_base * (1.0f - alpha) + b_tint * alpha;
1404 return 0xFF000000 | (r << 16) | (g << 8) | b;
1419 int x = obj.
x() * 16;
1420 int y = obj.y() * 16;
1421 int w = 16 + (obj.size() * 4);
1422 int h = 16 + (obj.size() * 4);
1430 for (
int py = y; py < y + h; py++) {
1431 for (
int px = x; px < x + w; px++) {
1432 if (px < canvas.
width() && py < canvas.
height() &&
1433 (px < x + 2 || px >= x + w - 2 || py < y + 2 || py >= y + h - 2)) {
1434 canvas.
SetPixel(px, py, sel_color);
1449 int x = obj.x() * 16;
1450 int y = obj.y() * 16;
1454 uint32_t tint_color = 0xFF000000;
1455 switch (obj.GetLayerValue()) {
1468 uint8_t r = (tint_color >> 16) & 0xFF;
1469 uint8_t g = (tint_color >> 8) & 0xFF;
1470 uint8_t b = tint_color & 0xFF;
1473 for (
int py = y; py < y + h && py < canvas.
height(); py++) {
1474 for (
int px = x; px < x + w && px < canvas.
width(); px++) {
1475 if (px == x || px == x + w - 1 || py == y || py == y + h - 1) {
1476 canvas.
SetPixel(px, py, layer_color);
1493 if (obj_idx < current_room_->GetTileObjectCount()) {
1503 ImGui::TableNextRow();
1504 ImGui::TableNextColumn();
1506 ImGui::TableNextColumn();
1508 ImGui::Text(
"0x%03X", obj.id_);
1510 ImGui::TextColored(theme.text_secondary_gray,
"(%s)", obj_name.c_str());
1514 ImGui::TableNextRow();
1515 ImGui::TableNextColumn();
1516 ImGui::Text(
"Type");
1517 ImGui::TableNextColumn();
1518 ImGui::Text(
"Subtype %d", subtype);
1529 ImGui::TableNextRow();
1530 ImGui::TableNextColumn();
1532 ImGui::TableNextColumn();
1534 ImGui::SetNextItemWidth(-1);
1535 if (ImGui::InputInt(
"##X", &x, 1, 4)) {
1536 if (x >= 0 && x < 64) {
1545 ImGui::TableNextRow();
1546 ImGui::TableNextColumn();
1548 ImGui::TableNextColumn();
1550 ImGui::SetNextItemWidth(-1);
1551 if (ImGui::InputInt(
"##Y", &y, 1, 4)) {
1552 if (y >= 0 && y < 64) {
1569 if (obj.id_ < 0x100) {
1570 ImGui::TableNextRow();
1571 ImGui::TableNextColumn();
1572 ImGui::Text(
"Size");
1573 ImGui::TableNextColumn();
1574 int size = obj.size();
1575 ImGui::SetNextItemWidth(-1);
1576 if (ImGui::SliderInt(
"##Size", &size, 0, 15,
"0x%02X")) {
1585 ImGui::TableNextRow();
1586 ImGui::TableNextColumn();
1587 ImGui::Text(
"Change ID");
1588 ImGui::TableNextColumn();
1590 ImGui::SetNextItemWidth(-1);
1591 if (ImGui::InputInt(
"##ID", &
id, 1, 16,
1592 ImGuiInputTextFlags_CharsHexadecimal)) {
1593 if (
id >= 0 &&
id <= 0xFFF) {
1594 obj.set_id(
static_cast<int16_t
>(
id));
1611 ImGui::TableNextRow();
1612 ImGui::TableNextColumn();
1613 ImGui::Text(
"Draws To");
1614 ImGui::TableNextColumn();
1615 if (semantics.draws_to_both_bgs) {
1616 ImGui::TextColored(theme.text_warning_yellow,
"Both (BG1 + BG2)");
1621 ImGui::TableNextRow();
1622 ImGui::TableNextColumn();
1623 ImGui::Text(
"Layer");
1624 ImGui::TableNextColumn();
1625 int layer = obj.GetLayerValue();
1626 ImGui::SetNextItemWidth(-1);
1627 if (semantics.draws_to_both_bgs) {
1628 ImGui::BeginDisabled();
1630 if (ImGui::Combo(
"##Layer", &layer,
1631 "BG1 (Floor)\0BG2 (Objects)\0BG3 (Overlay)\0")) {
1632 if (!semantics.draws_to_both_bgs) {
1639 if (semantics.draws_to_both_bgs) {
1640 ImGui::EndDisabled();
1643 "This object draws to both BG1 and BG2 in the engine. The stored "
1644 "layer selection does not affect rendering.");
1655 float button_width = (ImGui::GetContentRegionAvail().x - 8) / 2;
1659 ImVec4(theme.status_error.x * 0.7f, theme.status_error.y * 0.7f,
1660 theme.status_error.z * 0.7f, 1.0f)},
1661 {ImGuiCol_ButtonHovered, theme.status_error}});
1662 if (ImGui::Button(
ICON_MD_DELETE " Delete", ImVec2(button_width, 0))) {
1670 ImVec2(button_width, 0))) {
1672 duplicate.
set_x(obj.x() + 1);
1679 ImGui::TextColored(theme.text_warning_yellow,
1687 size_t both_bg_count = 0;
1695 if (both_bg_count > 0) {
1696 ImGui::TextColored(theme.text_warning_yellow,
1698 "won't be affected by layer changes",
1699 both_bg_count, both_bg_count == 1 ?
"" :
"s",
1700 both_bg_count == 1 ?
"s" :
"");
1703 static int batch_layer = 0;
1704 ImGui::SetNextItemWidth(-1);
1705 const bool all_both_bg =
1708 ImGui::BeginDisabled();
1710 if (ImGui::Combo(
"##BatchLayer", &batch_layer,
1711 "BG1 (Floor)\0BG2 (Objects)\0BG3 (Overlay)\0")) {
1715 ImGui::EndDisabled();
1722 static int batch_size = 0x12;
1723 ImGui::SetNextItemWidth(-1);
1724 if (ImGui::InputInt(
"##BatchSize", &batch_size, 1, 16,
1725 ImGuiInputTextFlags_CharsHexadecimal)) {
1733 float nudge_btn_size = (ImGui::GetContentRegionAvail().x - 24) / 4;
1755 float button_width = (ImGui::GetContentRegionAvail().x - 8) / 2;
1759 ImVec4(theme.status_error.x * 0.7f, theme.status_error.y * 0.7f,
1760 theme.status_error.z * 0.7f, 1.0f)},
1761 {ImGuiCol_ButtonHovered, theme.status_error}});
1763 ImVec2(button_width, 0))) {
1771 ImVec2(button_width, 0))) {
1779 ImGui::Begin(
"Layer Controls");
1782 ImGui::Text(
"Current Layer:");
1792 static bool layer_visible[3] = {
true,
true,
true};
1793 ImGui::Text(
"Layer Visibility:");
1794 ImGui::Checkbox(
"Show Layer 0", &layer_visible[0]);
1795 ImGui::Checkbox(
"Show Layer 1", &layer_visible[1]);
1796 ImGui::Checkbox(
"Show Layer 2", &layer_visible[2]);
1812 int count0 = 0, count1 = 0, count2 = 0;
1814 switch (obj.GetLayerValue()) {
1826 ImGui::Text(
"Layer 0: %d objects", count0);
1827 ImGui::Text(
"Layer 1: %d objects", count1);
1828 ImGui::Text(
"Layer 2: %d objects", count2);
1838 return absl::OkStatus();
1849 if (grid_dx == 0 && grid_dy == 0) {
1850 return absl::OkStatus();
1859 int new_x = obj.
x() + grid_dx;
1860 int new_y = obj.y() + grid_dy;
1863 new_x = std::max(0, std::min(63, new_x));
1864 new_y = std::max(0, std::min(63, new_y));
1878 return absl::OkStatus();
1883 return {
false, {}, {
"No room loaded"}};
1892 for (
size_t i = 0; i < objects.size(); i++) {
1893 for (
size_t j = i + 1; j < objects.size(); j++) {
1896 absl::StrFormat(
"Objects at indices %d and %d collide", i, j));
1908 std::vector<std::string> all_issues = result.errors;
1909 all_issues.insert(all_issues.end(), result.warnings.begin(),
1910 result.warnings.end());
1961 return std::make_unique<DungeonObjectEditor>(rom);
1965namespace ObjectCategories {
1969 {
"Walls", {0x10, 0x11, 0x12, 0x13},
"Basic wall objects"},
1970 {
"Floors", {0x20, 0x21, 0x22, 0x23},
"Floor tile objects"},
1971 {
"Decorations", {0x30, 0x31, 0x32, 0x33},
"Decorative objects"},
1972 {
"Interactive", {0xF9, 0xFA, 0xFB},
"Interactive objects like chests"},
1973 {
"Stairs", {0x13, 0x14, 0x15, 0x16},
"Staircase objects"},
1974 {
"Doors", {0x17, 0x18, 0x19, 0x1A},
"Door objects"},
1976 {0xF80, 0xF81, 0xF82, 0xF97},
1977 "Special dungeon objects (Type 3)"}};
1981 const std::string& category_name) {
1984 for (
const auto&
category : categories) {
1985 if (
category.name == category_name) {
1990 return absl::NotFoundError(
"Category not found");
1996 for (
const auto&
category : categories) {
1997 for (
int id :
category.object_ids) {
1998 if (
id == object_id) {
2004 return absl::NotFoundError(
"Object category not found");
2009 info.
id = object_id;
2014 if (object_id >= 0x10 && object_id <= 0x1F) {
2021 }
else if (object_id >= 0x20 && object_id <= 0x2F) {
2022 info.
name =
"Floor";
2028 }
else if (object_id == 0xF9) {
2029 info.
name =
"Small Chest";
2036 info.
name =
"Unknown Object";
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Represents a bitmap image optimized for SNES ROM hacking.
void SetPixel(int x, int y, const SnesColor &color)
Set a pixel at the given x,y coordinates with SNES color.
RAII guard for ImGui style colors.
ValidationResult ValidateRoom()
static constexpr int kDefaultObjectSize
std::function< void(const SelectionState &)> SelectionChangedCallback
absl::Status HandleMouseDrag(int start_x, int start_y, int current_x, int current_y)
absl::Status HandleScrollWheel(int delta, int x, int y, bool ctrl_pressed)
std::pair< int, int > ScreenToRoomCoordinates(int screen_x, int screen_y)
int GetNextSize(int current_size, int delta)
void SetRoomChangedCallback(RoomChangedCallback callback)
std::pair< int, int > RoomToScreenCoordinates(int room_x, int room_y)
absl::Status InsertObject(int x, int y, int object_type, int size=0x12, int layer=0)
EditingState editing_state_
static constexpr int kMaxLayer
ObjectTemplateManager template_manager_
const std::vector< ObjectTemplate > & GetTemplates() const
absl::Status BatchMoveObjects(const std::vector< size_t > &indices, int dx, int dy)
DungeonObjectEditor(Rom *rom)
absl::Status ChangeObjectLayer(size_t object_index, int new_layer)
absl::Status BatchChangeObjectLayer(const std::vector< size_t > &indices, int new_layer)
absl::Status DeleteObject(size_t object_index)
void CopySelectedObjects(const std::vector< size_t > &indices)
SelectionState selection_state_
absl::Status InitializeEditor()
void SetSelectionChangedCallback(SelectionChangedCallback callback)
absl::Status SelectObject(int screen_x, int screen_y)
std::optional< size_t > DuplicateObject(size_t object_index, int offset_x=1, int offset_y=1)
absl::Status AlignSelectedObjects(Alignment alignment)
absl::Status CreateUndoPoint()
std::vector< UndoPoint > undo_history_
static constexpr size_t kMaxUndoHistory
bool IsValidSize(int size)
std::optional< size_t > FindObjectAt(int room_x, int room_y)
static constexpr int kMaxObjectSize
absl::Status AddToSelection(size_t object_index)
std::function< void()> RoomChangedCallback
std::vector< RoomObject > clipboard_
static constexpr int kMinLayer
ObjectChangedCallback object_changed_callback_
DungeonValidator validator_
void RenderLayerVisualization(gfx::Bitmap &canvas)
std::vector< std::string > GetValidationErrors()
absl::Status LoadRoom(int room_id)
std::unique_ptr< Room > owned_room_
void SetConfig(const EditorConfig &config)
std::function< void(size_t object_index, const RoomObject &object)> ObjectChangedCallback
std::vector< UndoPoint > redo_history_
absl::Status HandleDragOperation(int current_x, int current_y)
absl::Status HandleSizeEdit(int delta, int x, int y)
bool ObjectsCollide(const RoomObject &obj1, const RoomObject &obj2)
void SetExternalRoom(Room *room)
absl::Status ClearSelection()
bool IsObjectAtPosition(const RoomObject &object, int x, int y)
void SetCurrentLayer(int layer)
void SetObjectChangedCallback(ObjectChangedCallback callback)
std::vector< size_t > PasteObjects()
absl::Status HandleMouseRelease(int x, int y)
void UpdatePreviewObject()
absl::Status InsertTemplate(const ObjectTemplate &tmpl, int x, int y)
absl::Status MoveObject(size_t object_index, int new_x, int new_y)
absl::Status ResizeObject(size_t object_index, int new_size)
std::optional< RoomObject > preview_object_
absl::Status BatchResizeObjects(const std::vector< size_t > &indices, int new_size)
static constexpr int kMinObjectSize
absl::Status ChangeObjectType(size_t object_index, int new_type)
void SetCurrentObjectType(int object_type)
absl::Status DeleteSelectedObjects()
SelectionChangedCallback selection_changed_callback_
int SnapToGrid(int coordinate)
absl::Status ApplyUndoPoint(const UndoPoint &undo_point)
absl::Status HandleMouseClick(int x, int y, bool left_button, bool right_button, bool shift_pressed)
void RenderSelectionHighlight(gfx::Bitmap &canvas)
absl::Status CreateTemplateFromSelection(const std::string &name, const std::string &description)
RoomChangedCallback room_changed_callback_
void RenderLayerControls()
ValidationResult ValidateRoom(const Room &room)
static ObjectTemplate CreateFromObjects(const std::string &name, const std::string &description, const std::vector< RoomObject > &objects, int origin_x, int origin_y)
const std::vector< ObjectTemplate > & GetTemplates() const
std::vector< RoomObject > InstantiateTemplate(const ObjectTemplate &tmpl, int x, int y, Rom *rom)
absl::Status LoadTemplates(const std::string &directory_path)
absl::Status SaveTemplate(const ObjectTemplate &tmpl, const std::string &directory_path)
void set_size(uint8_t size)
absl::Status RemoveObject(size_t index)
size_t GetTileObjectCount() const
absl::Status SaveObjects()
RoomObject & GetTileObject(size_t index)
const std::vector< RoomObject > & GetTileObjects() const
void SetTileObjects(const std::vector< RoomObject > &objects)
absl::Status AddObject(const RoomObject &object)
void RemoveTileObject(size_t index)
#define ICON_MD_ARROW_FORWARD
#define ICON_MD_OPEN_WITH
#define ICON_MD_ARROW_DOWNWARD
#define ICON_MD_ASPECT_RATIO
#define ICON_MD_ARROW_UPWARD
#define ICON_MD_ARROW_BACK
#define ICON_MD_SELECT_ALL
#define ICON_MD_CONTENT_COPY
#define ICON_MD_DELETE_SWEEP
const AgentUITheme & GetTheme()
void PropertyRow(const char *label, const char *value)
void SectionHeader(const char *icon, const char *label, const ImVec4 &color)
bool BeginPropertyTable(const char *id, int columns, ImGuiTableFlags extra_flags)
void HelpMarker(const char *desc)
std::vector< ObjectCategory > GetObjectCategories()
Get all available object categories.
absl::StatusOr< ObjectInfo > GetObjectInfo(int object_id)
absl::StatusOr< std::string > GetObjectCategory(int object_id)
Get category for a specific object.
absl::StatusOr< std::vector< int > > GetObjectsInCategory(const std::string &category_name)
Get objects in a specific category.
ObjectLayerSemantics GetObjectLayerSemantics(const RoomObject &object)
std::unique_ptr< DungeonObjectEditor > CreateDungeonObjectEditor(Rom *rom)
Factory function to create dungeon object editor.
int GetObjectSubtype(int object_id)
std::string GetObjectName(int object_id)
constexpr int kNumberOfRooms
const char * EffectiveBgLayerLabel(EffectiveBgLayer layer)
bool show_collision_bounds
bool show_selection_highlight
std::vector< size_t > selected_objects
std::vector< RoomObject > objects
std::chrono::steady_clock::time_point timestamp
std::vector< std::pair< int, int > > valid_sizes
std::vector< int > valid_layers
std::vector< std::string > errors