diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 72b220c2..71b7c89b 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -997,7 +997,7 @@ void EditorApp::exportZone(const std::string& outputDir) { // Write zone manifest (for client loading) // Scan output directory for all exported tiles (includes adjacent tiles) - ZoneManifest manifest; + ZoneManifest& manifest = zoneManifest_; manifest.mapName = loadedMap_; manifest.displayName = loadedMap_; manifest.tiles.push_back({loadedTileX_, loadedTileY_}); diff --git a/tools/editor/editor_app.hpp b/tools/editor/editor_app.hpp index 47b6ae8d..a922e8b9 100644 --- a/tools/editor/editor_app.hpp +++ b/tools/editor/editor_app.hpp @@ -150,6 +150,7 @@ private: float autoSaveInterval_ = 300.0f; bool autoSaveEnabled_ = true; bool showQuitConfirm_ = false; + ZoneManifest zoneManifest_; // Recent zones struct RecentZone { std::string mapName; int tileX; int tileY; }; @@ -162,6 +163,7 @@ public: void showToast(const std::string& msg, float duration = 3.0f); const std::vector& getToasts() const { return toasts_; } const std::vector& getRecentZones() const { return recentZones_; } + ZoneManifest& getZoneManifest() { return zoneManifest_; } bool isAutoSaveEnabled() const { return autoSaveEnabled_; } void setAutoSaveEnabled(bool v) { autoSaveEnabled_ = v; } float getAutoSaveInterval() const { return autoSaveInterval_; } diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 55b8292c..1c0c0462 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -2473,6 +2473,51 @@ void EditorUI::renderPropertiesPanel(EditorApp& app) { ImGui::Text("Height: %.0f-%.0f (avg %.0f)", minH, maxH, sumH / count); } + // Zone audio configuration + if (app.hasTerrainLoaded() && ImGui::CollapsingHeader("Zone Audio")) { + auto& manifest = app.getZoneManifest(); + static char musicBuf[256] = {}; + static char ambDayBuf[256] = {}; + static char ambNightBuf[256] = {}; + std::strncpy(musicBuf, manifest.musicTrack.c_str(), sizeof(musicBuf) - 1); + std::strncpy(ambDayBuf, manifest.ambienceDay.c_str(), sizeof(ambDayBuf) - 1); + std::strncpy(ambNightBuf, manifest.ambienceNight.c_str(), sizeof(ambNightBuf) - 1); + + if (ImGui::InputText("Music##audio", musicBuf, sizeof(musicBuf))) + manifest.musicTrack = musicBuf; + if (ImGui::InputText("Ambience (Day)##audio", ambDayBuf, sizeof(ambDayBuf))) + manifest.ambienceDay = ambDayBuf; + if (ImGui::InputText("Ambience (Night)##audio", ambNightBuf, sizeof(ambNightBuf))) + manifest.ambienceNight = ambNightBuf; + ImGui::SliderFloat("Music Vol", &manifest.musicVolume, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat("Ambience Vol", &manifest.ambienceVolume, 0.0f, 1.0f, "%.2f"); + + if (ImGui::BeginCombo("Presets##audioPreset", "Select...")) { + if (ImGui::Selectable("Elwynn Forest")) { + manifest.musicTrack = "Sound\\Music\\ZoneMusic\\Forest\\ForestDay.mp3"; + manifest.ambienceDay = "Sound\\Ambience\\GlueScreenAmbience.wav"; + } + if (ImGui::Selectable("Durotar")) { + manifest.musicTrack = "Sound\\Music\\ZoneMusic\\Barrens\\BarrensDay.mp3"; + manifest.ambienceDay = "Sound\\Ambience\\GlueScreenAmbience.wav"; + } + if (ImGui::Selectable("Darkshore")) { + manifest.musicTrack = "Sound\\Music\\ZoneMusic\\Darkshore\\DarkshoreDay.mp3"; + manifest.ambienceDay = "Sound\\Ambience\\GlueScreenAmbience.wav"; + } + if (ImGui::Selectable("Dungeon")) { + manifest.musicTrack = "Sound\\Music\\ZoneMusic\\Dungeon\\DungeonAmbience.mp3"; + manifest.ambienceDay = ""; + } + if (ImGui::Selectable("None (silent)")) { + manifest.musicTrack = ""; + manifest.ambienceDay = ""; + manifest.ambienceNight = ""; + } + ImGui::EndCombo(); + } + } + if (app.getTerrainEditor().hasUnsavedChanges()) ImGui::TextColored(ImVec4(1, 0.8f, 0.3f, 1), "* Unsaved (Ctrl+S to save)"); else diff --git a/tools/editor/zone_manifest.cpp b/tools/editor/zone_manifest.cpp index 2104f335..03a1be9b 100644 --- a/tools/editor/zone_manifest.cpp +++ b/tools/editor/zone_manifest.cpp @@ -44,6 +44,17 @@ bool ZoneManifest::save(const std::string& path) const { if (hasCreatures) files["creatures"] = "creatures.json"; j["files"] = files; + // Audio configuration + if (!musicTrack.empty() || !ambienceDay.empty()) { + nlohmann::json audio; + if (!musicTrack.empty()) audio["music"] = musicTrack; + if (!ambienceDay.empty()) audio["ambienceDay"] = ambienceDay; + if (!ambienceNight.empty()) audio["ambienceNight"] = ambienceNight; + audio["musicVolume"] = musicVolume; + audio["ambienceVolume"] = ambienceVolume; + j["audio"] = audio; + } + std::ofstream f(path); if (!f) { LOG_ERROR("Failed to write zone manifest: ", path); return false; } f << j.dump(2) << "\n"; @@ -76,6 +87,16 @@ bool ZoneManifest::load(const std::string& path) { } } + // Audio configuration + if (j.contains("audio")) { + const auto& a = j["audio"]; + musicTrack = a.value("music", ""); + ambienceDay = a.value("ambienceDay", ""); + ambienceNight = a.value("ambienceNight", ""); + musicVolume = a.value("musicVolume", 0.7f); + ambienceVolume = a.value("ambienceVolume", 0.5f); + } + return !mapName.empty(); } catch (const std::exception& e) { LOG_ERROR("Failed to parse zone manifest: ", e.what()); diff --git a/tools/editor/zone_manifest.hpp b/tools/editor/zone_manifest.hpp index 703ce24b..9270b0ca 100644 --- a/tools/editor/zone_manifest.hpp +++ b/tools/editor/zone_manifest.hpp @@ -17,6 +17,13 @@ struct ZoneManifest { bool hasCreatures = false; std::string description; + // Audio configuration + std::string musicTrack; // Background music file path + std::string ambienceDay; // Daytime ambient sound + std::string ambienceNight; // Nighttime ambient sound + float musicVolume = 0.7f; + float ambienceVolume = 0.5f; + bool save(const std::string& path) const; bool load(const std::string& path); };