mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 09:33:51 +00:00
feat(editor): add --info-tilemap for project-wide ADT grid visualization
Renders the WoW 64x64 ADT coordinate grid as ASCII art showing which
tiles are claimed by which zones. Useful for spotting tile-coord
collisions before two zones ship overlapping content, and for
'where am I working?' overview of multi-zone projects:
wowee_editor --info-tilemap custom_zones
Tilemap: custom_zones
zones : 2
tiles used : 5
collisions : 0 (multiple zones claiming same tile)
legend : D=Desert F=Forest
0 1 2 3 4 5 6
0123456789012345678901234567890123456789012345678901234567890123
y=30 ..............................FFDD..............................
y=31 ...............................F................................
Glyphs are first-letter-of-zone uppercased; collision tiles render
as '*'. Empty rows are skipped to keep output bounded for projects
in one corner of the map (skipping all 60+ blank rows of the full
64x64 grid).
Exit 1 on collisions so CI can gate before merging two PRs that
both add tiles in the same area. JSON mode emits per-tile claims
for programmatic consumption.
Verified on a 2-zone project: Forest (3 tiles) + Desert (2 tiles)
correctly rendered, no collisions detected, glyphs F/D placed at
the right grid coords.
This commit is contained in:
parent
1718c2333f
commit
f73b377b4d
1 changed files with 123 additions and 2 deletions
|
|
@ -426,6 +426,8 @@ static void printUsage(const char* argv0) {
|
|||
std::printf(" --list-zones [--json] List discovered custom zones and exit\n");
|
||||
std::printf(" --zone-stats <projectDir> [--json]\n");
|
||||
std::printf(" Aggregate counts across every zone in <projectDir>\n");
|
||||
std::printf(" --info-tilemap <projectDir> [--json]\n");
|
||||
std::printf(" ASCII-render the 64x64 WoW ADT grid showing tile claims by zone\n");
|
||||
std::printf(" --list-zone-deps <zoneDir> [--json]\n");
|
||||
std::printf(" List external M2/WMO model paths a zone references (objects + WOB doodads)\n");
|
||||
std::printf(" --export-zone-deps-md <zoneDir> [out.md]\n");
|
||||
|
|
@ -672,8 +674,8 @@ int main(int argc, char* argv[]) {
|
|||
"--export-zone-summary-md", "--export-quest-graph",
|
||||
"--export-zone-csv", "--export-zone-html",
|
||||
"--scaffold-zone", "--add-tile", "--remove-tile", "--list-tiles",
|
||||
"--for-each-zone", "--zone-stats", "--list-zone-deps",
|
||||
"--check-zone-refs", "--export-zone-deps-md",
|
||||
"--for-each-zone", "--zone-stats", "--info-tilemap",
|
||||
"--list-zone-deps", "--check-zone-refs", "--export-zone-deps-md",
|
||||
"--add-creature", "--add-object", "--add-quest",
|
||||
"--add-quest-objective", "--add-quest-reward-item", "--set-quest-reward",
|
||||
"--remove-quest-objective", "--clone-quest", "--clone-creature",
|
||||
|
|
@ -9511,6 +9513,125 @@ int main(int argc, char* argv[]) {
|
|||
r.bytes / kKB);
|
||||
}
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--info-tilemap") == 0 && i + 1 < argc) {
|
||||
// Visualize the WoW 64x64 ADT grid showing which tiles are
|
||||
// claimed by which zones across a project. Useful for
|
||||
// spotting tile-coord collisions before two zones try to
|
||||
// ship overlapping content, and for getting a 'where am I
|
||||
// working?' overview of a multi-zone project.
|
||||
std::string projectDir = argv[++i];
|
||||
bool jsonOut = (i + 1 < argc &&
|
||||
std::strcmp(argv[i + 1], "--json") == 0);
|
||||
if (jsonOut) i++;
|
||||
namespace fs = std::filesystem;
|
||||
if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) {
|
||||
std::fprintf(stderr,
|
||||
"info-tilemap: %s is not a directory\n", projectDir.c_str());
|
||||
return 1;
|
||||
}
|
||||
// Map (tx, ty) -> vector<zone names> so collision overlaps
|
||||
// are visible. Walk every zone in the project.
|
||||
std::map<std::pair<int,int>, std::vector<std::string>> claims;
|
||||
std::vector<std::string> zones;
|
||||
for (const auto& entry : fs::directory_iterator(projectDir)) {
|
||||
if (!entry.is_directory()) continue;
|
||||
if (!fs::exists(entry.path() / "zone.json")) continue;
|
||||
wowee::editor::ZoneManifest zm;
|
||||
if (!zm.load((entry.path() / "zone.json").string())) continue;
|
||||
std::string zname = zm.mapName.empty()
|
||||
? entry.path().filename().string() : zm.mapName;
|
||||
zones.push_back(zname);
|
||||
for (const auto& [tx, ty] : zm.tiles) {
|
||||
if (tx >= 0 && tx < 64 && ty >= 0 && ty < 64) {
|
||||
claims[{tx, ty}].push_back(zname);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Per-zone label glyph: first letter of the zone name,
|
||||
// uppercased so different zones get distinct chars in the
|
||||
// grid. Multi-letter overlap collapses to '*'.
|
||||
std::map<std::string, char> zoneGlyph;
|
||||
char nextGlyph = 'A';
|
||||
for (const auto& z : zones) {
|
||||
if (zoneGlyph.count(z)) continue;
|
||||
if (!z.empty() && std::isalpha(static_cast<unsigned char>(z[0]))) {
|
||||
zoneGlyph[z] = static_cast<char>(std::toupper(static_cast<unsigned char>(z[0])));
|
||||
} else {
|
||||
zoneGlyph[z] = nextGlyph++;
|
||||
if (nextGlyph > 'Z') nextGlyph = 'a';
|
||||
}
|
||||
}
|
||||
int collisions = 0;
|
||||
for (const auto& [coord, owners] : claims) {
|
||||
if (owners.size() > 1) collisions++;
|
||||
}
|
||||
if (jsonOut) {
|
||||
nlohmann::json j;
|
||||
j["projectDir"] = projectDir;
|
||||
j["zoneCount"] = zones.size();
|
||||
j["claimedTiles"] = claims.size();
|
||||
j["collisions"] = collisions;
|
||||
nlohmann::json claimsJson = nlohmann::json::array();
|
||||
for (const auto& [coord, owners] : claims) {
|
||||
claimsJson.push_back({{"x", coord.first},
|
||||
{"y", coord.second},
|
||||
{"zones", owners}});
|
||||
}
|
||||
j["claims"] = claimsJson;
|
||||
std::printf("%s\n", j.dump(2).c_str());
|
||||
return collisions == 0 ? 0 : 1;
|
||||
}
|
||||
std::printf("Tilemap: %s\n", projectDir.c_str());
|
||||
std::printf(" zones : %zu\n", zones.size());
|
||||
std::printf(" tiles used : %zu\n", claims.size());
|
||||
std::printf(" collisions : %d (multiple zones claiming same tile)\n",
|
||||
collisions);
|
||||
std::printf(" legend :");
|
||||
for (const auto& [name, glyph] : zoneGlyph) {
|
||||
std::printf(" %c=%s", glyph, name.c_str());
|
||||
}
|
||||
std::printf("\n\n");
|
||||
// Render 64x64 grid. Print column header in groups of 10
|
||||
// for readability.
|
||||
std::printf(" ");
|
||||
for (int x = 0; x < 64; ++x) {
|
||||
std::printf("%c", (x % 10 == 0) ? '0' + (x / 10) : ' ');
|
||||
}
|
||||
std::printf("\n");
|
||||
std::printf(" ");
|
||||
for (int x = 0; x < 64; ++x) std::printf("%d", x % 10);
|
||||
std::printf("\n");
|
||||
for (int y = 0; y < 64; ++y) {
|
||||
// Skip rows that have no tiles claimed — keeps the
|
||||
// output bounded for projects in one corner of the map.
|
||||
bool rowHasContent = false;
|
||||
for (int x = 0; x < 64 && !rowHasContent; ++x) {
|
||||
if (claims.count({x, y})) rowHasContent = true;
|
||||
}
|
||||
if (!rowHasContent) continue;
|
||||
std::printf(" y=%2d ", y);
|
||||
for (int x = 0; x < 64; ++x) {
|
||||
auto it = claims.find({x, y});
|
||||
if (it == claims.end()) {
|
||||
std::printf(".");
|
||||
} else if (it->second.size() > 1) {
|
||||
std::printf("*"); // collision
|
||||
} else {
|
||||
std::printf("%c", zoneGlyph[it->second[0]]);
|
||||
}
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
if (collisions > 0) {
|
||||
std::printf("\n COLLISIONS:\n");
|
||||
for (const auto& [coord, owners] : claims) {
|
||||
if (owners.size() < 2) continue;
|
||||
std::printf(" (%d, %d) claimed by:", coord.first, coord.second);
|
||||
for (const auto& o : owners) std::printf(" %s", o.c_str());
|
||||
std::printf("\n");
|
||||
}
|
||||
}
|
||||
return collisions == 0 ? 0 : 1;
|
||||
} else if (std::strcmp(argv[i], "--list-zone-deps") == 0 && i + 1 < argc) {
|
||||
// Enumerate every external model path a zone references —
|
||||
// both directly placed (objects.json) and indirectly via
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue