118 if (state.room_id != target || state.rom != viewer.
rom()) {
119 const int popup_room = state.popup_room_id;
120 const bool popup_open = state.popup_open;
122 state.room_id = target;
123 state.rom = viewer.
rom();
124 state.source_room_id = target == 0 ? 1 : 0;
125 state.popup_room_id = popup_room;
126 state.popup_open = popup_open;
129 ImGui::PushID(
"DungeonRoomTransfer");
130 ImGui::PushID(&viewer);
131 ImGui::PushID(target);
132 const bool valid_target =
136 "Open a room in the canvas to clone or import its contents.");
142 ImGui::TextWrapped(
"Replace contents of room %03X", target);
144 "Selected domains are replaced. Unchecked domains stay in this room.");
145 if (ImGui::Button(
"Copy Room JSON", ImVec2(-1, 0))) {
147 state.error = json.ok() ?
"" : std::string(json.status().message());
148 state.status.clear();
150 ImGui::SetClipboardText(json->c_str());
151 state.status =
"Room JSON copied to clipboard.";
156#ifndef __EMSCRIPTEN__
157 if (ImGui::Button(
"Save Room File...", ImVec2(-1, 0))) {
160 state.error = std::string(json.status().message());
161 state.status.clear();
164 absl::StrFormat(
"room_%03X.yaze-room.json", target),
"json");
167 state.error = result.ok() ?
"" : std::string(result.message());
168 state.status = result.ok() ?
"Room template saved to " + path :
"";
175 const bool editable =
177 ImGui::BeginDisabled(!editable);
178 if (ImGui::RadioButton(
"Clone", !state.import_json)) {
179 state.import_json =
false;
180 InvalidatePreview(state);
184 if (ImGui::RadioButton(
"Import JSON", state.import_json)) {
185 state.import_json =
true;
186 InvalidatePreview(state);
189 if (state.import_json) {
190#ifndef __EMSCRIPTEN__
191 if (ImGui::Button(
"Open Room File...", ImVec2(-1, 0))) {
193 options.
filters.push_back({
"Yaze room template",
"json"});
199 if (ImGui::Button(
"Paste JSON", ImVec2(-1, 0))) {
200 const char* clipboard = ImGui::GetClipboardText();
201 InvalidatePreview(state);
203 state.error =
"The clipboard contains no text.";
206 state.error =
"Room JSON exceeds the 1 MiB document limit.";
208 state.json = clipboard;
212 if (ImGui::InputTextMultiline(
"##RoomJSON", &state.json, ImVec2(-1, 130))) {
213 InvalidatePreview(state);
217 ImGui::TextUnformatted(
"Source room (hex)");
218 ImGui::SetNextItemWidth(-1);
220 &state.source_room_id,
"%03X",
221 ImGuiInputTextFlags_CharsHexadecimal,
222 {reinterpret_cast<uintptr_t>(&viewer),
223 static_cast<uint64_t>(target)})) {
224 InvalidatePreview(state);
229 ImGui::TextUnformatted(
"Replace these domains");
230 for (
const auto& domain : kDomains) {
231 bool enabled = (state.domains & domain.bit) != 0;
232 if (ImGui::Checkbox(domain.label, &enabled)) {
234 state.domains |= domain.bit;
236 state.domains &= ~domain.bit;
239 state.copy_destinations =
false;
241 InvalidatePreview(state);
246 if (ImGui::Checkbox(
"Copy destination links", &state.copy_destinations)) {
247 InvalidatePreview(state);
250 ImGui::EndDisabled();
252 "Destinations are preserved by default. Copied links may need changes "
253 "for the new room.");
254 const bool valid_source =
255 state.import_json ? !state.json.empty() &&
257 : state.source_room_id >= 0 &&
259 state.source_room_id != target;
260 ImGui::BeginDisabled(!valid_source || state.domains == 0);
261 if (ImGui::Button(
"Preview Replacement", ImVec2(-1, 0))) {
262 PreviewReplacement(viewer, state);
265 ImGui::EndDisabled();
266 ImGui::EndDisabled();
270 ImGui::TextWrapped(
"Preview for room %03X", state.preview->target_room_id);
271 DrawCounts(*state.preview);
272 if (!state.preview->changed()) {
273 ImGui::TextWrapped(
"Selected contents already match.");
275 ImGui::BeginDisabled(!editable || !state.preview->changed());
276 const bool apply = ImGui::Button(
"Apply Replacement", ImVec2(-1, 0));
278 ImGui::EndDisabled();
282 const auto plan = state.preview;
284 state.preview.reset();
285 state.error = status.ok() ?
"" : std::string(status.message());
287 status.ok() ?
"Replacement applied. Undo restores this room." :
"";
292 "This view is read-only. Room JSON can still be copied or exported.");
294 if (!valid_source && !state.import_json) {
295 ImGui::TextWrapped(
"Choose a different source room between 000 and 127.");
298 ImGui::TextWrapped(
"Room JSON exceeds the 1 MiB document limit.");
301 "Room JSON reuses numeric asset IDs. Graphics, palettes, layouts and "
302 "messages must already exist in the target project.");
304 "Entrances, incoming links, pit damage, sprite sort mode and reserved "
305 "header data stay in the target room.");
306 if (!state.error.empty()) {
307 ImGui::TextWrapped(
"Room transfer: %s", state.error.c_str());
309 if (state.can_retry_without_properties) {
311 "Keep this room's shared properties and preview the other selected "
312 "contents instead.");
313 ImGui::BeginDisabled(!editable || !valid_source);
314 if (ImGui::Button(
"Preview without room properties", ImVec2(-1, 0))) {
315 state.domains &= ~kTransferMetadata;
316 state.copy_destinations =
false;
317 PreviewReplacement(viewer, state);
320 ImGui::EndDisabled();
322 if (!state.status.empty()) {
323 ImGui::TextWrapped(
"%s", state.status.c_str());