diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f900708..8de0b87b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1292,6 +1292,7 @@ add_executable(wowee_editor tools/editor/npc_spawner.cpp tools/editor/npc_presets.cpp tools/editor/transform_gizmo.cpp + tools/editor/zone_manifest.cpp tools/editor/asset_browser.cpp tools/editor/editor_water.cpp tools/editor/editor_markers.cpp diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 53d7c69d..c8e102d2 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -1,5 +1,6 @@ #include "editor_app.hpp" #include "adt_writer.hpp" +#include "zone_manifest.hpp" #include "rendering/vk_context.hpp" #include "pipeline/adt_loader.hpp" #include "pipeline/terrain_mesh.hpp" @@ -596,6 +597,15 @@ void EditorApp::exportZone(const std::string& outputDir) { npcSpawner_.saveToFile(npcPath); } + // Write zone manifest (for client loading) + ZoneManifest manifest; + manifest.mapName = loadedMap_; + manifest.displayName = loadedMap_; + manifest.tiles.push_back({loadedTileX_, loadedTileY_}); + manifest.hasCreatures = (npcSpawner_.spawnCount() > 0); + manifest.baseHeight = terrain_.chunks[0].position[2]; + manifest.save(base + "/zone.json"); + lastSavePath_ = outputDir; showToast("Zone exported to " + base); LOG_INFO("Zone exported to: ", base); diff --git a/tools/editor/editor_app.hpp b/tools/editor/editor_app.hpp index f4ada241..65547ebd 100644 --- a/tools/editor/editor_app.hpp +++ b/tools/editor/editor_app.hpp @@ -8,6 +8,7 @@ #include "object_placer.hpp" #include "npc_spawner.hpp" #include "npc_presets.hpp" +#include "zone_manifest.hpp" #include "asset_browser.hpp" #include "core/window.hpp" #include "pipeline/asset_manager.hpp" diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index c581cab4..739998b1 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -235,9 +235,11 @@ void EditorUI::renderToolbar(EditorApp& app) { } void EditorUI::renderNewTerrainDialog(EditorApp& /*app*/) { - ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(400, 320), ImGuiCond_FirstUseEver); if (ImGui::Begin("New Terrain", &showNewDialog_)) { ImGui::InputText("Map Name", newMapNameBuf_, sizeof(newMapNameBuf_)); + ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1), + "Internal name (no spaces). Used for file paths."); ImGui::InputInt("Tile X", &newTileX_); ImGui::InputInt("Tile Y", &newTileY_); ImGui::SliderFloat("Base Height", &newBaseHeight_, 0.0f, 500.0f); diff --git a/tools/editor/zone_manifest.cpp b/tools/editor/zone_manifest.cpp new file mode 100644 index 00000000..f1e7a873 --- /dev/null +++ b/tools/editor/zone_manifest.cpp @@ -0,0 +1,77 @@ +#include "zone_manifest.hpp" +#include "core/logger.hpp" +#include +#include + +namespace wowee { +namespace editor { + +bool ZoneManifest::save(const std::string& path) const { + auto dir = std::filesystem::path(path).parent_path(); + if (!dir.empty()) std::filesystem::create_directories(dir); + + std::ofstream f(path); + if (!f) { LOG_ERROR("Failed to write zone manifest: ", path); return false; } + + f << "{\n"; + f << " \"mapName\": \"" << mapName << "\",\n"; + f << " \"displayName\": \"" << displayName << "\",\n"; + f << " \"mapId\": " << mapId << ",\n"; + f << " \"biome\": \"" << biome << "\",\n"; + f << " \"baseHeight\": " << baseHeight << ",\n"; + f << " \"hasCreatures\": " << (hasCreatures ? "true" : "false") << ",\n"; + f << " \"description\": \"" << description << "\",\n"; + f << " \"tiles\": ["; + for (size_t i = 0; i < tiles.size(); i++) { + f << "[" << tiles[i].first << "," << tiles[i].second << "]"; + if (i + 1 < tiles.size()) f << ","; + } + f << "],\n"; + f << " \"files\": {\n"; + f << " \"wdt\": \"" << mapName << ".wdt\",\n"; + for (size_t i = 0; i < tiles.size(); i++) { + f << " \"adt_" << tiles[i].first << "_" << tiles[i].second << "\": \"" + << mapName << "_" << tiles[i].first << "_" << tiles[i].second << ".adt\""; + if (i + 1 < tiles.size() || hasCreatures) f << ","; + f << "\n"; + } + if (hasCreatures) + f << " \"creatures\": \"creatures.json\"\n"; + f << " }\n"; + f << "}\n"; + + LOG_INFO("Zone manifest saved: ", path); + return true; +} + +bool ZoneManifest::load(const std::string& path) { + std::ifstream f(path); + if (!f) return false; + std::string content((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + + auto findStr = [&](const std::string& key) -> std::string { + auto pos = content.find("\"" + key + "\""); + if (pos == std::string::npos) return ""; + pos = content.find('"', content.find(':', pos) + 1); + if (pos == std::string::npos) return ""; + auto end = content.find('"', pos + 1); + return content.substr(pos + 1, end - pos - 1); + }; + + mapName = findStr("mapName"); + displayName = findStr("displayName"); + biome = findStr("biome"); + description = findStr("description"); + + auto numPos = content.find("\"mapId\""); + if (numPos != std::string::npos) { + numPos = content.find(':', numPos); + mapId = static_cast(std::stoi(content.substr(numPos + 1))); + } + + return !mapName.empty(); +} + +} // namespace editor +} // namespace wowee diff --git a/tools/editor/zone_manifest.hpp b/tools/editor/zone_manifest.hpp new file mode 100644 index 00000000..703ce24b --- /dev/null +++ b/tools/editor/zone_manifest.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace wowee { +namespace editor { + +struct ZoneManifest { + std::string mapName; + std::string displayName; + uint32_t mapId = 9000; // Custom map IDs start high to avoid conflicts + std::vector> tiles; // (tileX, tileY) pairs + std::string biome; + float baseHeight = 100.0f; + bool hasCreatures = false; + std::string description; + + bool save(const std::string& path) const; + bool load(const std::string& path); +}; + +} // namespace editor +} // namespace wowee