43 void Draw(
bool* p_open)
override {
49 ImGui::TextDisabled(
ICON_MD_INFO " No dungeon rooms loaded.");
54 const int ptr_table_end =
56 const bool collision_table_present =
58 const bool collision_data_region_present =
60 const bool collision_save_supported =
62 if (!collision_table_present) {
64 " Custom collision table missing (use an "
65 "expanded-collision Oracle ROM)");
67 tr(
"Expected ROM >= 0x%X bytes (custom collision pointer table end). "
68 "Current ROM is %zu bytes."),
69 ptr_table_end, rom_size);
71 }
else if (!collision_data_region_present) {
73 " Custom collision data region missing/truncated");
75 tr(
"Expected ROM >= 0x%X bytes (custom collision data soft end). "
76 "Current ROM is %zu bytes."),
83 if (room_id < 0 || room_id >= 296) {
88 auto& room = (*rooms)[room_id];
89 bool has_collision = room.has_custom_collision();
91 ImGui::BeginDisabled(!collision_save_supported);
92 if (ImGui::Button(has_collision ?
"Disable Custom Collision"
93 :
"Enable Custom Collision")) {
94 room.set_has_custom_collision(!has_collision);
101 ImGui::TextUnformatted(tr(
"Authoring"));
104 json_options.
filters.push_back({
"Custom Collision",
"json"});
105 json_options.
filters.push_back({
"All Files",
"*"});
107 ImGui::BeginDisabled(!collision_save_supported);
116 if (!rooms_or.ok()) {
120 const auto imported = std::move(rooms_or.value());
121 for (
const auto& entry : imported) {
122 if (entry.room_id < 0 ||
123 entry.room_id >=
static_cast<int>(rooms->size())) {
130 imported.size(), path.c_str());
133 }
catch (
const std::exception& e) {
148 "custom_collision.json",
"json");
150 std::ofstream file(path);
151 if (!file.is_open()) {
153 absl::StrFormat(
"Cannot write file: %s", path.c_str());
159 exported.size(), path.c_str());
165 ImGui::EndDisabled();
178 if (!collision_save_supported) {
180 " This ROM cannot save custom collision edits "
181 "(expanded collision region missing).");
185 if (ImGui::Checkbox(tr(
"Show Collision Overlay"), &show_overlay)) {
191 tr(
"Painting requires an active interaction context."));
197 ImGui::BeginDisabled(!collision_save_supported);
198 if (ImGui::Checkbox(tr(
"Paint Mode"), &is_painting)) {
205 ImGui::EndDisabled();
208 ImGui::TextColored(theme.text_warning_yellow,
209 tr(
"Click/Drag on canvas to paint"));
214 int brush_radius = std::clamp(state.paint_brush_radius, 0, 8);
215 if (ImGui::SliderInt(tr(
"Brush Radius"), &brush_radius, 0, 8)) {
216 state.paint_brush_radius = brush_radius;
219 ImGui::TextDisabled(tr(
"%dx%d"), (brush_radius * 2) + 1,
220 (brush_radius * 2) + 1);
224 if (ImGui::BeginCombo(tr(
"Collision Type"),
225 absl::StrFormat(
"%02X: %s", current_val,
226 (current_val < tile_types.size()
227 ? tile_types[current_val]
230 for (
int i = 0; i < tile_types.size(); ++i) {
231 bool selected = (current_val == i);
232 if (ImGui::Selectable(
233 absl::StrFormat(
"%02X: %s", i, tile_types[i]).c_str(),
235 state.paint_collision_value =
static_cast<uint8_t
>(i);
242 ImGui::Text(tr(
"Quick Select:"));
243 auto quick_button = [&](
const char* label, uint8_t val) {
244 if (ImGui::Button(label)) {
245 state.paint_collision_value = val;
248 quick_button(
"Floor (00)", 0x00);
250 quick_button(
"Solid (02)", 0x02);
252 quick_button(
"D.Water (08)", 0x08);
253 quick_button(
"S.Water (09)", 0x09);
255 quick_button(
"Pit (1B)", 0x1B);
257 quick_button(
"Spikes (0E)", 0x0E);
261 ImGui::BeginDisabled(!collision_save_supported);
262 if (ImGui::Button(tr(
"Clear All Custom Collision"))) {
263 room.custom_collision().tiles.fill(0);
265 room.custom_collision().has_data =
false;
266 room.MarkCustomCollisionDirty();
269 ImGui::EndDisabled();
271 ImGui::TextWrapped(tr(
272 "Custom collision allows you to override the physics of individual "
273 "8x8 tiles in the room. This is useful for creating water, pits, or "
274 "other effects that don't match the background tiles."));