40 void Draw(
bool* p_open)
override {
46 ImGui::TextDisabled(
ICON_MD_INFO " No dungeon rooms loaded.");
51 const int ptr_table_end =
53 const bool collision_table_present =
55 const bool collision_data_region_present =
57 const bool collision_save_supported =
59 if (!collision_table_present) {
61 " Custom collision table missing (use an "
62 "expanded-collision Oracle ROM)");
64 "Expected ROM >= 0x%X bytes (custom collision pointer table end). "
65 "Current ROM is %zu bytes.",
66 ptr_table_end, rom_size);
68 }
else if (!collision_data_region_present) {
70 " Custom collision data region missing/truncated");
72 "Expected ROM >= 0x%X bytes (custom collision data soft end). "
73 "Current ROM is %zu bytes.",
80 if (room_id < 0 || room_id >= 296) {
85 auto& room = (*rooms)[room_id];
86 bool has_collision = room.has_custom_collision();
88 ImGui::BeginDisabled(!collision_save_supported);
89 if (ImGui::Button(has_collision ?
"Disable Custom Collision"
90 :
"Enable Custom Collision")) {
91 room.set_has_custom_collision(!has_collision);
98 ImGui::TextUnformatted(
"Authoring");
101 json_options.
filters.push_back({
"Custom Collision",
"json"});
102 json_options.
filters.push_back({
"All Files",
"*"});
104 ImGui::BeginDisabled(!collision_save_supported);
113 if (!rooms_or.ok()) {
117 const auto imported = std::move(rooms_or.value());
118 for (
const auto& entry : imported) {
119 if (entry.room_id < 0 ||
120 entry.room_id >=
static_cast<int>(rooms->size())) {
127 imported.size(), path.c_str());
130 }
catch (
const std::exception& e) {
145 "custom_collision.json",
"json");
147 std::ofstream file(path);
148 if (!file.is_open()) {
150 absl::StrFormat(
"Cannot write file: %s", path.c_str());
156 exported.size(), path.c_str());
162 ImGui::EndDisabled();
175 if (!collision_save_supported) {
177 " This ROM cannot save custom collision edits "
178 "(expanded collision region missing).");
182 if (ImGui::Checkbox(
"Show Collision Overlay", &show_overlay)) {
187 ImGui::TextDisabled(
"Painting requires an active interaction context.");
193 ImGui::BeginDisabled(!collision_save_supported);
194 if (ImGui::Checkbox(
"Paint Mode", &is_painting)) {
201 ImGui::EndDisabled();
204 ImGui::TextColored(theme.text_warning_yellow,
205 "Click/Drag on canvas to paint");
210 int brush_radius = std::clamp(state.paint_brush_radius, 0, 8);
211 if (ImGui::SliderInt(
"Brush Radius", &brush_radius, 0, 8)) {
212 state.paint_brush_radius = brush_radius;
215 ImGui::TextDisabled(
"%dx%d", (brush_radius * 2) + 1,
216 (brush_radius * 2) + 1);
220 if (ImGui::BeginCombo(
"Collision Type",
221 absl::StrFormat(
"%02X: %s", current_val,
222 (current_val < tile_types.size()
223 ? tile_types[current_val]
226 for (
int i = 0; i < tile_types.size(); ++i) {
227 bool selected = (current_val == i);
228 if (ImGui::Selectable(
229 absl::StrFormat(
"%02X: %s", i, tile_types[i]).c_str(),
231 state.paint_collision_value =
static_cast<uint8_t
>(i);
238 ImGui::Text(
"Quick Select:");
239 auto quick_button = [&](
const char* label, uint8_t val) {
240 if (ImGui::Button(label)) {
241 state.paint_collision_value = val;
244 quick_button(
"Floor (00)", 0x00);
246 quick_button(
"Solid (02)", 0x02);
248 quick_button(
"D.Water (08)", 0x08);
249 quick_button(
"S.Water (09)", 0x09);
251 quick_button(
"Pit (1B)", 0x1B);
253 quick_button(
"Spikes (0E)", 0x0E);
257 ImGui::BeginDisabled(!collision_save_supported);
258 if (ImGui::Button(
"Clear All Custom Collision")) {
259 room.custom_collision().tiles.fill(0);
261 room.custom_collision().has_data =
false;
262 room.MarkCustomCollisionDirty();
265 ImGui::EndDisabled();
268 "Custom collision allows you to override the physics of individual "
269 "8x8 tiles in the room. This is useful for creating water, pits, or "
270 "other effects that don't match the background tiles.");
276 const std::array<zelda3::Room, 0x128>& rooms) {
277 std::vector<zelda3::CustomCollisionRoomEntry> out;
279 for (
int rid = 0; rid < static_cast<int>(rooms.size()); ++rid) {
280 const auto& room = rooms[rid];
286 const auto& map = room.custom_collision().tiles;
287 for (
size_t off = 0; off < map.size(); ++off) {
288 const uint8_t val = map[off];
293 entry.
tiles.push_back(
299 out.push_back(std::move(entry));