diff --git a/CMakeLists.txt b/CMakeLists.txt index acaa5ff6..f7a05edf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1298,6 +1298,7 @@ add_executable(wowee_editor tools/editor/npc_spawner.cpp tools/editor/npc_presets.cpp tools/editor/sql_exporter.cpp + tools/editor/server_module_gen.cpp tools/editor/quest_editor.cpp tools/editor/transform_gizmo.cpp tools/editor/zone_manifest.cpp diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 27c8dad0..f124e165 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -10,6 +10,7 @@ #include "pipeline/wowee_collision.hpp" #include "pipeline/wmo_loader.hpp" #include "sql_exporter.hpp" +#include "server_module_gen.hpp" #include "core/coordinates.hpp" #include #include "rendering/vk_context.hpp" diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index d8c612b2..196d9fea 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -9,6 +9,7 @@ #include "pipeline/custom_zone_discovery.hpp" #include "content_pack.hpp" #include "sql_exporter.hpp" +#include "server_module_gen.hpp" #include "wowee_terrain.hpp" #include "pipeline/wowee_terrain_loader.hpp" #include @@ -373,6 +374,14 @@ void EditorUI::renderMenuBar(EditorApp& app) { sqlPath, app.getZoneManifest().mapId); app.showToast("SQL exported: " + sqlPath); } + if (ImGui::MenuItem("Generate Server Module", nullptr, false, app.hasTerrainLoaded())) { + editor::ServerModuleGenerator::generate( + app.getZoneManifest(), + app.getNpcSpawner().getSpawns(), + app.getQuestEditor().getQuests(), + "output"); + app.showToast("Server module: output/mod_wowee_" + app.getLoadedMap()); + } if (ImGui::MenuItem("Export Content Pack (.wcp)", "Ctrl+Shift+E", false, app.hasTerrainLoaded())) { std::string wcpPath = "output/" + app.getLoadedMap() + ".wcp"; app.exportContentPack(wcpPath); diff --git a/tools/editor/server_module_gen.cpp b/tools/editor/server_module_gen.cpp new file mode 100644 index 00000000..2c7cc2bc --- /dev/null +++ b/tools/editor/server_module_gen.cpp @@ -0,0 +1,149 @@ +#include "server_module_gen.hpp" +#include "sql_exporter.hpp" +#include "core/logger.hpp" +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { + +bool ServerModuleGenerator::generate(const ZoneManifest& manifest, + const std::vector& creatures, + const std::vector& quests, + const std::string& outputDir) { + namespace fs = std::filesystem; + std::string dir = outputDir + "/mod_wowee_" + manifest.mapName; + fs::create_directories(dir + "/sql"); + fs::create_directories(dir + "/conf"); + + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + char timeBuf[32]; + std::strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", std::localtime(&time)); + + Config cfg; + cfg.mapId = manifest.mapId; + cfg.mapName = manifest.mapName; + cfg.displayName = manifest.displayName.empty() ? manifest.mapName : manifest.displayName; + + // 1. Generate map registration SQL + { + std::ofstream f(dir + "/sql/01_map.sql"); + f << "-- Wowee Custom Zone: " << cfg.displayName << "\n"; + f << "-- Generated: " << timeBuf << "\n"; + f << "-- Map ID: " << cfg.mapId << "\n\n"; + + f << "-- Register custom map\n"; + f << "INSERT INTO `map_dbc` (`ID`, `MapName`, `MapType`, `MapDescription`) VALUES (" + << cfg.mapId << ", '" << cfg.mapName << "', 0, '" + << cfg.displayName << "') ON DUPLICATE KEY UPDATE `MapName`='" + << cfg.mapName << "';\n\n"; + + f << "-- Register zone area\n"; + f << "INSERT INTO `area_table_dbc` (`ID`, `MapID`, `AreaName`, `ExploreFlag`) VALUES (" + << cfg.zoneId << ", " << cfg.mapId << ", '" + << cfg.displayName << "', 1) ON DUPLICATE KEY UPDATE `AreaName`='" + << cfg.displayName << "';\n"; + } + + // 2. Generate creature + quest SQL + if (!creatures.empty() || !quests.empty()) { + SQLExporter::exportAll(creatures, quests, + dir + "/sql/02_spawns.sql", + cfg.mapId, cfg.startCreatureEntry); + } + + // 3. Generate teleport command SQL + if (!manifest.tiles.empty()) { + std::ofstream f(dir + "/sql/03_teleport.sql"); + f << "-- Teleport location for .tele command\n"; + float tileSize = 533.33333f; + float x = (32.0f - manifest.tiles[0].second) * tileSize; + float y = (32.0f - manifest.tiles[0].first) * tileSize; + f << "INSERT INTO `game_tele` (`name`, `position_x`, `position_y`, " + << "`position_z`, `orientation`, `map`) VALUES ('" + << manifest.mapName << "', " << x << ", " << y << ", " + << manifest.baseHeight + 10.0f << ", 0, " << cfg.mapId + << ") ON DUPLICATE KEY UPDATE `position_x`=" << x << ";\n"; + f << "\n-- Usage: .tele " << manifest.mapName << "\n"; + } + + // 4. Generate zone flags SQL + { + std::ofstream f(dir + "/sql/04_zone_flags.sql"); + f << "-- Zone gameplay flags\n"; + if (manifest.isSanctuary) { + f << "-- Sanctuary zone (no PvP)\n"; + f << "UPDATE `area_table_dbc` SET `Flags` = `Flags` | 0x800 WHERE `ID` = " + << cfg.zoneId << ";\n"; + } + if (manifest.pvpEnabled) { + f << "-- PvP zone\n"; + f << "UPDATE `area_table_dbc` SET `Flags` = `Flags` | 0x40 WHERE `ID` = " + << cfg.zoneId << ";\n"; + } + } + + // 5. Generate worldserver.conf snippet + { + std::ofstream f(dir + "/conf/mod_wowee.conf.dist"); + f << "#\n# Wowee Custom Zone: " << cfg.displayName << "\n"; + f << "# Add this to your worldserver.conf\n#\n\n"; + f << "# Enable custom zone " << cfg.displayName << "\n"; + f << "Wowee." << cfg.mapName << ".Enabled = 1\n"; + f << "Wowee." << cfg.mapName << ".MapId = " << cfg.mapId << "\n"; + f << "Wowee." << cfg.mapName << ".ZoneId = " << cfg.zoneId << "\n\n"; + f << "# Zone settings\n"; + f << "Wowee." << cfg.mapName << ".AllowFlying = " + << (manifest.allowFlying ? 1 : 0) << "\n"; + f << "Wowee." << cfg.mapName << ".PvP = " + << (manifest.pvpEnabled ? 1 : 0) << "\n"; + } + + // 6. Generate server admin README + { + std::ofstream f(dir + "/README.md"); + f << "# " << cfg.displayName << " — Custom Zone for AzerothCore\n\n"; + f << "Generated by Wowee World Editor v1.0.0\n\n"; + f << "## Installation\n\n"; + f << "1. Copy SQL files to your AzerothCore `sql/custom/` directory\n"; + f << "2. Execute in order: `01_map.sql`, `02_spawns.sql`, `03_teleport.sql`, `04_zone_flags.sql`\n"; + f << "3. Copy `conf/mod_wowee.conf.dist` settings to `worldserver.conf`\n"; + f << "4. Restart worldserver\n\n"; + f << "## Details\n\n"; + f << "- **Map ID**: " << cfg.mapId << "\n"; + f << "- **Zone**: " << cfg.displayName << "\n"; + f << "- **Creatures**: " << creatures.size() << "\n"; + f << "- **Quests**: " << quests.size() << "\n"; + f << "- **Tiles**: " << manifest.tiles.size() << "\n"; + if (manifest.allowFlying) f << "- **Flying**: Enabled\n"; + if (manifest.pvpEnabled) f << "- **PvP**: Enabled\n"; + if (manifest.isSanctuary) f << "- **Sanctuary**: Yes\n"; + f << "\n## Teleport\n\n"; + f << "```\n.tele " << manifest.mapName << "\n```\n"; + } + + // 7. Generate manifest JSON + { + nlohmann::json j; + j["format"] = "wowee-server-module-1.0"; + j["zone"] = cfg.displayName; + j["mapId"] = cfg.mapId; + j["creatures"] = creatures.size(); + j["quests"] = quests.size(); + j["tiles"] = manifest.tiles.size(); + j["generated"] = timeBuf; + std::ofstream f(dir + "/module.json"); + f << j.dump(2) << "\n"; + } + + LOG_INFO("Server module generated: ", dir, " (", + creatures.size(), " creatures, ", quests.size(), " quests)"); + return true; +} + +} // namespace editor +} // namespace wowee diff --git a/tools/editor/server_module_gen.hpp b/tools/editor/server_module_gen.hpp new file mode 100644 index 00000000..c32cbc13 --- /dev/null +++ b/tools/editor/server_module_gen.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "zone_manifest.hpp" +#include "npc_spawner.hpp" +#include "quest_editor.hpp" +#include +#include + +namespace wowee { +namespace editor { + +// Generates a complete AzerothCore server module from editor zone data +// Output: SQL + worldserver.conf snippet + README for server admins +class ServerModuleGenerator { +public: + struct Config { + uint32_t mapId = 9000; + uint32_t startCreatureEntry = 100000; + uint32_t startQuestEntry = 100000; + uint32_t zoneId = 10000; + uint32_t areaId = 10001; + std::string mapName; + std::string displayName; + }; + + // Generate complete server module directory + static bool generate(const ZoneManifest& manifest, + const std::vector& creatures, + const std::vector& quests, + const std::string& outputDir); +}; + +} // namespace editor +} // namespace wowee