107 std::optional<int> world_filter;
108 if (
auto world_str = parser.
GetString(
"world")) {
109 std::string w = *world_str;
110 if (w ==
"light" || w ==
"0") {
112 }
else if (w ==
"dark" || w ==
"1") {
114 }
else if (w ==
"special" || w ==
"2") {
116 }
else if (w !=
"all") {
117 return absl::InvalidArgumentError(
118 absl::StrCat(
"Invalid world: ", w,
119 ". Use: light, dark, special, or all"));
128 std::vector<AreaInfo> areas;
129 std::map<int, int> map_to_parent;
132 int world = WorldFromMapId(map_id);
135 if (world_filter.has_value() && world != *world_filter) {
140 if (map ==
nullptr)
continue;
142 int local = LocalIndexFromMapId(map_id);
143 int grid_x = local % kMapsPerRow;
144 int grid_y = local / kMapsPerRow;
147 int min_x = grid_x * kScreenWidth;
148 int min_y = grid_y * kScreenHeight;
149 int max_x = min_x + kScreenWidth;
150 int max_y = min_y + kScreenHeight;
153 auto area_size = map->area_size();
155 max_x = min_x + kScreenWidth * 2;
156 max_y = min_y + kScreenHeight * 2;
158 max_x = min_x + kScreenWidth * 2;
160 max_y = min_y + kScreenHeight * 2;
163 int parent = map->parent();
164 map_to_parent[map_id] = parent;
168 area.
name = GetDefaultAreaName(map_id);
172 area.
size = GetAreaSizeName(area_size);
173 area.
parent_id = parent == map_id ? -1 : parent;
179 areas.push_back(area);
183 std::vector<AreaConnection> connections;
184 std::set<std::pair<int, int>> seen_connections;
188 int world = WorldFromMapId(map_id);
189 if (world_filter.has_value() && world != *world_filter)
continue;
194 int north_area = north_target & 0xFF;
198 int target_world = WorldFromMapId(north_area);
200 if (target_world == world || world == 2 || target_world == 2) {
201 auto key = std::minmax(map_id, north_area);
202 if (seen_connections.find(key) == seen_connections.end()) {
203 seen_connections.insert(key);
211 int local = LocalIndexFromMapId(map_id);
212 int grid_x = local % kMapsPerRow;
213 int grid_y = local / kMapsPerRow;
214 conn.
edge_x = grid_x * kScreenWidth + kScreenWidth / 2;
215 conn.
edge_y = grid_y * kScreenHeight;
218 connections.push_back(conn);
226 int west_area = west_target & 0xFF;
229 int target_world = WorldFromMapId(west_area);
230 if (target_world == world || world == 2 || target_world == 2) {
231 auto key = std::minmax(map_id, west_area);
232 if (seen_connections.find(key) == seen_connections.end()) {
233 seen_connections.insert(key);
240 int local = LocalIndexFromMapId(map_id);
241 int grid_x = local % kMapsPerRow;
242 int grid_y = local / kMapsPerRow;
243 conn.
edge_x = grid_x * kScreenWidth;
244 conn.
edge_y = grid_y * kScreenHeight + kScreenHeight / 2;
247 connections.push_back(conn);
255 int world = WorldFromMapId(map_id);
256 if (world_filter.has_value() && world != *world_filter)
continue;
258 int local = LocalIndexFromMapId(map_id);
259 int grid_x = local % kMapsPerRow;
260 int grid_y = local / kMapsPerRow;
263 int world_offset = (world == 0) ? 0 : (world == 1) ? kDarkWorldOffset : kSpecialWorldOffset;
264 int maps_in_world = (world == 2) ? 32 : 64;
267 if (grid_x < kMapsPerRow - 1) {
268 int east_local = local + 1;
269 if (east_local < maps_in_world) {
270 int east_area = world_offset + east_local;
271 auto key = std::minmax(map_id, east_area);
272 if (seen_connections.find(key) == seen_connections.end()) {
273 seen_connections.insert(key);
279 conn.
edge_x = (grid_x + 1) * kScreenWidth;
280 conn.
edge_y = grid_y * kScreenHeight + kScreenHeight / 2;
283 connections.push_back(conn);
289 int rows_in_world = maps_in_world / kMapsPerRow;
290 if (grid_y < rows_in_world - 1) {
291 int south_local = local + kMapsPerRow;
292 if (south_local < maps_in_world) {
293 int south_area = world_offset + south_local;
294 auto key = std::minmax(map_id, south_area);
295 if (seen_connections.find(key) == seen_connections.end()) {
296 seen_connections.insert(key);
302 conn.
edge_x = grid_x * kScreenWidth + kScreenWidth / 2;
303 conn.
edge_y = (grid_y + 1) * kScreenHeight;
306 connections.push_back(conn);
313 std::vector<EntranceInfo> entrances;
315 const auto& ow_entrances = overworld.
entrances();
316 for (
size_t i = 0; i < ow_entrances.size(); ++i) {
317 const auto& entrance = ow_entrances[i];
318 if (entrance.deleted)
continue;
320 int area_id = entrance.map_id_ & 0xFF;
321 int world = WorldFromMapId(area_id);
323 if (world_filter.has_value() && world != *world_filter)
continue;
326 info.
id =
static_cast<int>(i);
330 info.
is_hole = entrance.is_hole_;
333 constexpr size_t kNumEntranceLabels =
335 if (entrance.entrance_id_ < kNumEntranceLabels) {
338 info.
name = absl::StrFormat(
"Entrance %d", entrance.entrance_id_);
343 uint8_t entrance_id = entrance.entrance_id_;
348 entrances.push_back(info);
352 const auto& holes = overworld.
holes();
353 for (
size_t i = 0; i < holes.size(); ++i) {
354 const auto& hole = holes[i];
356 int area_id = hole.map_id_ & 0xFF;
357 int world = WorldFromMapId(area_id);
359 if (world_filter.has_value() && world != *world_filter)
continue;
362 info.
id =
static_cast<int>(ow_entrances.size() + i);
368 constexpr size_t kNumEntranceLabels =
370 if (hole.entrance_id_ < kNumEntranceLabels) {
371 info.
name = absl::StrFormat(
"%s (Hole)",
374 info.
name = absl::StrFormat(
"Hole %d", hole.entrance_id_);
377 uint8_t entrance_id = hole.entrance_id_;
382 entrances.push_back(info);
386 std::vector<ExitInfo> exits;
387 const auto* ow_exits = overworld.
exits();
389 if (ow_exits !=
nullptr) {
390 for (
const auto& exit : *ow_exits) {
391 int area_id = exit.map_id_ & 0xFF;
392 int world = WorldFromMapId(area_id);
394 if (world_filter.has_value() && world != *world_filter)
continue;
398 info.
name = absl::StrFormat(
"Exit from room 0x%02X", exit.room_id_);
403 exits.push_back(info);
410 formatter.
AddField(
"version",
"1.0.0");
411 formatter.
AddField(
"generated", GetCurrentTimestamp());
416 for (
const auto& area : areas) {
419 formatter.
AddField(
"name", area.name);
420 formatter.
AddField(
"world", GetWorldName(area.world));
423 formatter.
AddField(
"x", area.grid_x);
424 formatter.
AddField(
"y", area.grid_y);
427 formatter.
AddField(
"size", area.size);
429 if (area.parent_id >= 0) {
430 formatter.
AddField(
"parent_id", area.parent_id);
434 formatter.
AddField(
"min_x", area.min_x);
435 formatter.
AddField(
"min_y", area.min_y);
436 formatter.
AddField(
"max_x", area.max_x);
437 formatter.
AddField(
"max_y", area.max_y);
446 for (
const auto& conn : connections) {
448 formatter.
AddField(
"from_area", conn.from_area);
449 formatter.
AddField(
"to_area", conn.to_area);
450 formatter.
AddField(
"direction", conn.direction);
453 formatter.
AddField(
"x", conn.edge_x);
454 formatter.
AddField(
"y", conn.edge_y);
457 formatter.
AddField(
"bidirectional", conn.bidirectional);
464 for (
const auto& entrance : entrances) {
466 formatter.
AddField(
"id", entrance.id);
467 formatter.
AddField(
"name", entrance.name);
468 formatter.
AddField(
"area_id", entrance.area_id);
471 formatter.
AddField(
"x", entrance.position_x);
472 formatter.
AddField(
"y", entrance.position_y);
475 formatter.
AddField(
"dest_room_id", entrance.dest_room_id);
476 formatter.
AddField(
"is_hole", entrance.is_hole);
483 for (
const auto& exit : exits) {
485 formatter.
AddField(
"room_id", exit.room_id);
486 formatter.
AddField(
"name", exit.name);
487 formatter.
AddField(
"return_area_id", exit.return_area_id);
490 formatter.
AddField(
"x", exit.return_x);
491 formatter.
AddField(
"y", exit.return_y);
500 formatter.
AddField(
"total_areas",
static_cast<int>(areas.size()));
501 formatter.
AddField(
"total_connections",
static_cast<int>(connections.size()));
502 formatter.
AddField(
"total_entrances",
static_cast<int>(entrances.size()));
503 formatter.
AddField(
"total_exits",
static_cast<int>(exits.size()));
508 return absl::OkStatus();