42 void Draw(
bool* p_open)
override {
48 ImGui::TextDisabled(
ICON_MD_INFO " No dungeon rooms loaded.");
53 const int ptr_table_end =
55 const bool collision_table_present =
57 const bool collision_data_region_present =
59 const bool collision_save_supported =
61 if (!collision_table_present) {
63 " Custom collision table missing (use an "
64 "expanded-collision Oracle ROM)");
66 tr(
"Expected ROM >= 0x%X bytes (custom collision pointer table end). "
67 "Current ROM is %zu bytes."),
68 ptr_table_end, rom_size);
70 }
else if (!collision_data_region_present) {
72 " Custom collision data region missing/truncated");
74 tr(
"Expected ROM >= 0x%X bytes (custom collision data soft end). "
75 "Current ROM is %zu bytes."),
82 if (room_id < 0 || room_id >= 296) {
87 auto& room = (*rooms)[room_id];
88 bool has_collision = room.has_custom_collision();
90 ImGui::BeginDisabled(!collision_save_supported);
91 if (ImGui::Button(has_collision ?
"Disable Custom Collision"
92 :
"Enable Custom Collision")) {
93 room.set_has_custom_collision(!has_collision);
100 ImGui::TextUnformatted(tr(
"Authoring"));
103 json_options.
filters.push_back({
"Custom Collision",
"json"});
104 json_options.
filters.push_back({
"All Files",
"*"});
106 ImGui::BeginDisabled(!collision_save_supported);
115 if (!rooms_or.ok()) {
119 const auto imported = std::move(rooms_or.value());
120 for (
const auto& entry : imported) {
121 if (entry.room_id < 0 ||
122 entry.room_id >=
static_cast<int>(rooms->size())) {
129 imported.size(), path.c_str());
132 }
catch (
const std::exception& e) {
147 "custom_collision.json",
"json");
149 std::ofstream file(path);
150 if (!file.is_open()) {
152 absl::StrFormat(
"Cannot write file: %s", path.c_str());
158 exported.size(), path.c_str());
164 ImGui::EndDisabled();
177 if (!collision_save_supported) {
179 " This ROM cannot save custom collision edits "
180 "(expanded collision region missing).");
184 if (ImGui::Checkbox(tr(
"Show Collision Overlay"), &show_overlay)) {
190 tr(
"Painting requires an active interaction context."));
196 ImGui::BeginDisabled(!collision_save_supported);
197 if (ImGui::Checkbox(tr(
"Paint Mode"), &is_painting)) {
204 ImGui::EndDisabled();
207 ImGui::TextColored(theme.text_warning_yellow,
208 tr(
"Click/Drag on canvas to paint"));
213 int brush_radius = std::clamp(state.paint_brush_radius, 0, 8);
214 if (ImGui::SliderInt(tr(
"Brush Radius"), &brush_radius, 0, 8)) {
215 state.paint_brush_radius = brush_radius;
218 ImGui::TextDisabled(tr(
"%dx%d"), (brush_radius * 2) + 1,
219 (brush_radius * 2) + 1);
223 if (ImGui::BeginCombo(tr(
"Collision Type"),
224 absl::StrFormat(
"%02X: %s", current_val,
225 (current_val < tile_types.size()
226 ? tile_types[current_val]
229 for (
int i = 0; i < tile_types.size(); ++i) {
230 bool selected = (current_val == i);
231 if (ImGui::Selectable(
232 absl::StrFormat(
"%02X: %s", i, tile_types[i]).c_str(),
234 state.paint_collision_value =
static_cast<uint8_t
>(i);
241 ImGui::Text(tr(
"Quick Select:"));
242 auto quick_button = [&](
const char* label, uint8_t val) {
243 if (ImGui::Button(label)) {
244 state.paint_collision_value = val;
247 quick_button(
"Floor (00)", 0x00);
249 quick_button(
"Solid (02)", 0x02);
251 quick_button(
"D.Water (08)", 0x08);
252 quick_button(
"S.Water (09)", 0x09);
254 quick_button(
"Pit (1B)", 0x1B);
256 quick_button(
"Spikes (0E)", 0x0E);
260 ImGui::BeginDisabled(!collision_save_supported);
261 if (ImGui::Button(tr(
"Clear All Custom Collision"))) {
262 room.custom_collision().tiles.fill(0);
264 room.custom_collision().has_data =
false;
265 room.MarkCustomCollisionDirty();
268 ImGui::EndDisabled();
270 ImGui::TextWrapped(tr(
271 "Custom collision allows you to override the physics of individual "
272 "8x8 tiles in the room. This is useful for creating water, pits, or "
273 "other effects that don't match the background tiles."));