61 ImGui::TextColored(ImVec4(1, 0, 0, 1),
"Project root not set.");
69 ImGui::Text(
"Minecart Track Editor");
81 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f),
93 ImGui::TextDisabled(
"Camera coordinates use $1XXX format (base $1000 + room offset + local position)");
94 ImGui::TextDisabled(
"Hover over dungeon canvas to see coordinates, or click 'Pick' button.");
97 if (ImGui::BeginTable(
"TracksTable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) {
98 ImGui::TableSetupColumn(
"ID", ImGuiTableColumnFlags_WidthFixed, 30.0f);
99 ImGui::TableSetupColumn(
"Room ID", ImGuiTableColumnFlags_WidthFixed, 80.0f);
100 ImGui::TableSetupColumn(
"Camera X", ImGuiTableColumnFlags_WidthFixed, 80.0f);
101 ImGui::TableSetupColumn(
"Camera Y", ImGuiTableColumnFlags_WidthFixed, 80.0f);
102 ImGui::TableSetupColumn(
"Pick", ImGuiTableColumnFlags_WidthFixed, 50.0f);
103 ImGui::TableSetupColumn(
"Go", ImGuiTableColumnFlags_WidthFixed, 40.0f);
104 ImGui::TableHeadersRow();
107 ImGui::TableNextRow();
111 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(80, 80, 0, 100));
114 ImGui::TableNextColumn();
115 ImGui::Text(
"%d", track.id);
117 ImGui::TableNextColumn();
118 uint16_t room_id =
static_cast<uint16_t
>(track.room_id);
120 track.room_id = room_id;
123 ImGui::TableNextColumn();
124 uint16_t start_x =
static_cast<uint16_t
>(track.start_x);
126 track.start_x = start_x;
129 ImGui::TableNextColumn();
130 uint16_t start_y =
static_cast<uint16_t
>(track.start_y);
132 track.start_y = start_y;
136 ImGui::TableNextColumn();
137 ImGui::PushID(track.id);
139 if (is_picking_this) {
140 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.6f, 0.0f, 1.0f));
143 if (is_picking_this) {
149 if (is_picking_this) {
150 ImGui::PopStyleColor();
152 if (ImGui::IsItemHovered()) {
153 ImGui::SetTooltip(is_picking_this ?
"Cancel picking" :
"Pick coordinates from canvas");
158 ImGui::TableNextColumn();
159 ImGui::PushID(track.id + 1000);
165 if (ImGui::IsItemHovered()) {
166 ImGui::SetTooltip(
"Navigate to room $%04X", track.room_id);
176 std::filesystem::path path = std::filesystem::path(
project_root_) /
"Sprites/Objects/data/minecart_tracks.asm";
178 if (!std::filesystem::exists(path)) {
186 std::ifstream file(path);
187 std::stringstream buffer;
188 buffer << file.rdbuf();
189 std::string content = buffer.str();
191 std::vector<int> rooms;
195 if (!
ParseSection(content,
".TrackStartingRooms", rooms) ||
202 size_t count = std::min({rooms.size(), xs.size(), ys.size()});
203 for (
size_t i = 0; i < count; ++i) {
204 tracks_.push_back({(int)i, rooms[i], xs[i], ys[i]});
213 size_t pos = content.find(label);
214 if (pos == std::string::npos)
return false;
217 size_t start = pos + label.length();
220 std::regex dw_regex(R
"(dw\s+((?:\$[0-9A-Fa-f]{4}(?:,\s*)?)+))");
224 std::stringstream ss(content.substr(start));
226 while (std::getline(ss, line)) {
228 size_t trimmed_start = line.find_first_not_of(
" \t");
229 if (trimmed_start != std::string::npos && line[trimmed_start] ==
'.')
break;
232 if (std::regex_search(line, match, dw_regex)) {
233 std::string values_str = match[1];
234 std::stringstream val_ss(values_str);
236 while (std::getline(val_ss, segment,
',')) {
238 segment.erase(0, segment.find_first_not_of(
" \t$"));
241 out_values.push_back(std::stoi(segment,
nullptr, 16));