#include "game/zone_manager.hpp" #include "core/logger.hpp" #include #include #include #include namespace wowee { namespace game { // Resolve "assets/Original Music/" to an absolute path, or return empty static std::string resolveOriginalMusic(const char* filename) { namespace fs = std::filesystem; fs::path rel = fs::path("assets") / "Original Music" / filename; if (fs::exists(rel)) return fs::canonical(rel).string(); fs::path abs = fs::current_path() / rel; if (fs::exists(abs)) return fs::canonical(abs).string(); return ""; } // Helper: prefix with "file:" so the renderer knows to use playFilePath static std::string filePrefix(const std::string& path) { if (path.empty()) return ""; return "file:" + path; } void ZoneManager::initialize() { // Resolve original music paths at startup auto om = [](const char* name) -> std::string { std::string path = resolveOriginalMusic(name); return path.empty() ? "" : filePrefix(path); }; std::string omWanderwewill = om("Wanderwewill.mp3"); std::string omYouNoTake = om("You No Take Candle!.mp3"); std::string omGoldBooty = om("Gold on the Tide in Booty Bay.mp3"); std::string omLanterns = om("Lanterns Over Lordaeron.mp3"); std::string omBarrens = om("The Barrens Has No End.mp3"); std::string omBoneCollector = om("The Bone Collector.mp3"); std::string omLootTheDogs = om("Loot the Dogs.mp3"); std::string omOneMorePull = om("One More Pull.mp3"); std::string omRollNeedGreed = om("Roll Need Greed.mp3"); std::string omRunBackPolka = om("RunBackPolka.mp3"); std::string omWhoPulled = om("WHO PULLED_.mp3"); // Elwynn Forest (zone 12) ZoneInfo elwynn; elwynn.id = 12; elwynn.name = "Elwynn Forest"; elwynn.musicPaths = { "Sound\\Music\\ZoneMusic\\Forest\\DayForest01.mp3", "Sound\\Music\\ZoneMusic\\Forest\\DayForest02.mp3", "Sound\\Music\\ZoneMusic\\Forest\\DayForest03.mp3", }; if (!omWanderwewill.empty()) elwynn.musicPaths.push_back(omWanderwewill); if (!omYouNoTake.empty()) elwynn.musicPaths.push_back(omYouNoTake); zones[12] = elwynn; // Stormwind City (zone 1519) ZoneInfo stormwind; stormwind.id = 1519; stormwind.name = "Stormwind City"; stormwind.musicPaths = { "Sound\\Music\\CityMusic\\Stormwind\\stormwind04-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind05-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind06-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind07-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind08-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind09-zone.mp3", "Sound\\Music\\CityMusic\\Stormwind\\stormwind10-zone.mp3", }; zones[1519] = stormwind; // Dun Morogh (zone 1) - neighboring zone ZoneInfo dunmorogh; dunmorogh.id = 1; dunmorogh.name = "Dun Morogh"; dunmorogh.musicPaths = { "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain01.mp3", "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain02.mp3", "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain03.mp3", }; if (!omRunBackPolka.empty()) dunmorogh.musicPaths.push_back(omRunBackPolka); zones[1] = dunmorogh; // Westfall (zone 40) ZoneInfo westfall; westfall.id = 40; westfall.name = "Westfall"; westfall.musicPaths = { "Sound\\Music\\ZoneMusic\\Plains\\DayPlains01.mp3", "Sound\\Music\\ZoneMusic\\Plains\\DayPlains02.mp3", "Sound\\Music\\ZoneMusic\\Plains\\DayPlains03.mp3", }; if (!omYouNoTake.empty()) westfall.musicPaths.push_back(omYouNoTake); zones[40] = westfall; // Tirisfal Glades (zone 85) ZoneInfo tirisfal; tirisfal.id = 85; tirisfal.name = "Tirisfal Glades"; tirisfal.musicPaths = { "Sound\\Music\\ZoneMusic\\UndeadForest\\UndeadForest01.mp3", "Sound\\Music\\ZoneMusic\\UndeadForest\\UndeadForest02.mp3", "Sound\\Music\\ZoneMusic\\UndeadForest\\UndeadForest03.mp3", }; if (!omLanterns.empty()) tirisfal.musicPaths.push_back(omLanterns); zones[85] = tirisfal; // Undercity (zone 1497) ZoneInfo undercity; undercity.id = 1497; undercity.name = "Undercity"; undercity.musicPaths = { "Sound\\Music\\CityMusic\\Undercity\\Undercity01-zone.mp3", "Sound\\Music\\CityMusic\\Undercity\\Undercity02-zone.mp3", "Sound\\Music\\CityMusic\\Undercity\\Undercity03-zone.mp3", }; if (!omLanterns.empty()) undercity.musicPaths.push_back(omLanterns); zones[1497] = undercity; // The Barrens (zone 17) ZoneInfo barrens; barrens.id = 17; barrens.name = "The Barrens"; barrens.musicPaths = { "Sound\\Music\\ZoneMusic\\Desert\\DayDesert01.mp3", "Sound\\Music\\ZoneMusic\\Desert\\DayDesert02.mp3", "Sound\\Music\\ZoneMusic\\Desert\\DayDesert03.mp3", }; if (!omBarrens.empty()) barrens.musicPaths.push_back(omBarrens); zones[17] = barrens; // Stranglethorn Vale (zone 33) ZoneInfo stranglethorn; stranglethorn.id = 33; stranglethorn.name = "Stranglethorn Vale"; stranglethorn.musicPaths = { "Sound\\Music\\ZoneMusic\\Jungle\\DayJungle01.mp3", "Sound\\Music\\ZoneMusic\\Jungle\\DayJungle02.mp3", "Sound\\Music\\ZoneMusic\\Jungle\\DayJungle03.mp3", }; if (!omGoldBooty.empty()) stranglethorn.musicPaths.push_back(omGoldBooty); zones[33] = stranglethorn; // Duskwood (zone 10) ZoneInfo duskwood; duskwood.id = 10; duskwood.name = "Duskwood"; duskwood.musicPaths = { "Sound\\Music\\ZoneMusic\\HauntedForest\\HauntedForest01.mp3", "Sound\\Music\\ZoneMusic\\HauntedForest\\HauntedForest02.mp3", "Sound\\Music\\ZoneMusic\\HauntedForest\\HauntedForest03.mp3", }; if (!omBoneCollector.empty()) duskwood.musicPaths.push_back(omBoneCollector); zones[10] = duskwood; // Burning Steppes (zone 46) ZoneInfo burningSteppes; burningSteppes.id = 46; burningSteppes.name = "Burning Steppes"; burningSteppes.musicPaths = { "Sound\\Music\\ZoneMusic\\BarrenDry\\DayBarrenDry01.mp3", "Sound\\Music\\ZoneMusic\\BarrenDry\\DayBarrenDry02.mp3", }; if (!omOneMorePull.empty()) burningSteppes.musicPaths.push_back(omOneMorePull); if (!omWhoPulled.empty()) burningSteppes.musicPaths.push_back(omWhoPulled); if (!omLootTheDogs.empty()) burningSteppes.musicPaths.push_back(omLootTheDogs); zones[46] = burningSteppes; // Searing Gorge (zone 51) ZoneInfo searingGorge; searingGorge.id = 51; searingGorge.name = "Searing Gorge"; searingGorge.musicPaths = { "Sound\\Music\\ZoneMusic\\BarrenDry\\DayBarrenDry01.mp3", "Sound\\Music\\ZoneMusic\\BarrenDry\\DayBarrenDry02.mp3", }; if (!omWhoPulled.empty()) searingGorge.musicPaths.push_back(omWhoPulled); if (!omOneMorePull.empty()) searingGorge.musicPaths.push_back(omOneMorePull); if (!omLootTheDogs.empty()) searingGorge.musicPaths.push_back(omLootTheDogs); zones[51] = searingGorge; // Ironforge (zone 1537) ZoneInfo ironforge; ironforge.id = 1537; ironforge.name = "Ironforge"; ironforge.musicPaths = { "Sound\\Music\\CityMusic\\Ironforge\\Ironforge01-zone.mp3", "Sound\\Music\\CityMusic\\Ironforge\\Ironforge02-zone.mp3", "Sound\\Music\\CityMusic\\Ironforge\\Ironforge03-zone.mp3", }; if (!omRunBackPolka.empty()) ironforge.musicPaths.push_back(omRunBackPolka); if (!omRollNeedGreed.empty()) ironforge.musicPaths.push_back(omRollNeedGreed); zones[1537] = ironforge; // Loch Modan (zone 38) ZoneInfo lochModan; lochModan.id = 38; lochModan.name = "Loch Modan"; lochModan.musicPaths = { "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain01.mp3", "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain02.mp3", "Sound\\Music\\ZoneMusic\\Mountain\\DayMountain03.mp3", }; if (!omRollNeedGreed.empty()) lochModan.musicPaths.push_back(omRollNeedGreed); zones[38] = lochModan; // --- Kalimdor zones --- // Orgrimmar (zone 1637) ZoneInfo orgrimmar; orgrimmar.id = 1637; orgrimmar.name = "Orgrimmar"; orgrimmar.musicPaths = { "Sound\\Music\\CityMusic\\Orgrimmar\\orgrimmar01-zone.mp3", "Sound\\Music\\CityMusic\\Orgrimmar\\orgrimmar02-zone.mp3", "Sound\\Music\\CityMusic\\Orgrimmar\\orgrimmar03-zone.mp3", }; if (!omWhoPulled.empty()) orgrimmar.musicPaths.push_back(omWhoPulled); if (!omOneMorePull.empty()) orgrimmar.musicPaths.push_back(omOneMorePull); zones[1637] = orgrimmar; // Durotar (zone 14) ZoneInfo durotar; durotar.id = 14; durotar.name = "Durotar"; durotar.musicPaths = { "Sound\\Music\\ZoneMusic\\Desert\\DayDesert01.mp3", "Sound\\Music\\ZoneMusic\\Desert\\DayDesert02.mp3", "Sound\\Music\\ZoneMusic\\Desert\\DayDesert03.mp3", }; if (!omBarrens.empty()) durotar.musicPaths.push_back(omBarrens); zones[14] = durotar; // Mulgore (zone 215) ZoneInfo mulgore; mulgore.id = 215; mulgore.name = "Mulgore"; mulgore.musicPaths = { "Sound\\Music\\ZoneMusic\\Plains\\DayPlains01.mp3", "Sound\\Music\\ZoneMusic\\Plains\\DayPlains02.mp3", "Sound\\Music\\ZoneMusic\\Plains\\DayPlains03.mp3", }; if (!omWanderwewill.empty()) mulgore.musicPaths.push_back(omWanderwewill); if (!omBarrens.empty()) mulgore.musicPaths.push_back(omBarrens); zones[215] = mulgore; // Thunder Bluff (zone 1638) ZoneInfo thunderBluff; thunderBluff.id = 1638; thunderBluff.name = "Thunder Bluff"; thunderBluff.musicPaths = { "Sound\\Music\\CityMusic\\ThunderBluff\\ThunderBluff01-zone.mp3", "Sound\\Music\\CityMusic\\ThunderBluff\\ThunderBluff02-zone.mp3", "Sound\\Music\\CityMusic\\ThunderBluff\\ThunderBluff03-zone.mp3", }; if (!omWanderwewill.empty()) thunderBluff.musicPaths.push_back(omWanderwewill); zones[1638] = thunderBluff; // Darkshore (zone 148) ZoneInfo darkshore; darkshore.id = 148; darkshore.name = "Darkshore"; darkshore.musicPaths = { "Sound\\Music\\ZoneMusic\\NightElf\\NightElf01.mp3", "Sound\\Music\\ZoneMusic\\NightElf\\NightElf02.mp3", "Sound\\Music\\ZoneMusic\\NightElf\\NightElf03.mp3", }; if (!omBoneCollector.empty()) darkshore.musicPaths.push_back(omBoneCollector); if (!omLanterns.empty()) darkshore.musicPaths.push_back(omLanterns); zones[148] = darkshore; // Teldrassil (zone 141) ZoneInfo teldrassil; teldrassil.id = 141; teldrassil.name = "Teldrassil"; teldrassil.musicPaths = { "Sound\\Music\\ZoneMusic\\NightElf\\NightElf01.mp3", "Sound\\Music\\ZoneMusic\\NightElf\\NightElf02.mp3", "Sound\\Music\\ZoneMusic\\NightElf\\NightElf03.mp3", }; if (!omWanderwewill.empty()) teldrassil.musicPaths.push_back(omWanderwewill); zones[141] = teldrassil; // Darnassus (zone 1657) ZoneInfo darnassus; darnassus.id = 1657; darnassus.name = "Darnassus"; darnassus.musicPaths = { "Sound\\Music\\CityMusic\\Darnassus\\Darnassus01-zone.mp3", "Sound\\Music\\CityMusic\\Darnassus\\Darnassus02-zone.mp3", "Sound\\Music\\CityMusic\\Darnassus\\Darnassus03-zone.mp3", }; zones[1657] = darnassus; // Tile-to-zone mappings for Azeroth (Eastern Kingdoms) // Elwynn Forest tiles for (int tx = 31; tx <= 34; tx++) { for (int ty = 48; ty <= 51; ty++) { tileToZone[tx * 100 + ty] = 12; // Elwynn } } // Stormwind City tiles (northern part of Elwynn area) tileToZone[31 * 100 + 47] = 1519; tileToZone[32 * 100 + 47] = 1519; tileToZone[33 * 100 + 47] = 1519; // Westfall tiles (west of Elwynn) for (int ty = 48; ty <= 51; ty++) { tileToZone[35 * 100 + ty] = 40; tileToZone[36 * 100 + ty] = 40; } // Dun Morogh tiles (south/east of Elwynn) for (int tx = 31; tx <= 34; tx++) { tileToZone[tx * 100 + 52] = 1; tileToZone[tx * 100 + 53] = 1; } // Duskwood tiles (south of Elwynn) for (int tx = 33; tx <= 36; tx++) { tileToZone[tx * 100 + 52] = 10; tileToZone[tx * 100 + 53] = 10; } // Tirisfal Glades tiles (northern Eastern Kingdoms) for (int tx = 28; tx <= 31; tx++) { for (int ty = 38; ty <= 41; ty++) { tileToZone[tx * 100 + ty] = 85; } } // Stranglethorn Vale tiles (south of Westfall/Duskwood) for (int tx = 33; tx <= 36; tx++) { for (int ty = 54; ty <= 58; ty++) { tileToZone[tx * 100 + ty] = 33; } } // Burning Steppes tiles (east of Redridge, north of Blackrock) for (int tx = 29; tx <= 31; tx++) { tileToZone[tx * 100 + 52] = 46; tileToZone[tx * 100 + 53] = 46; } // Searing Gorge tiles (north of Burning Steppes) for (int tx = 29; tx <= 31; tx++) { tileToZone[tx * 100 + 50] = 51; tileToZone[tx * 100 + 51] = 51; } // Loch Modan tiles (east of Dun Morogh) for (int tx = 35; tx <= 37; tx++) { tileToZone[tx * 100 + 52] = 38; tileToZone[tx * 100 + 53] = 38; } // The Barrens tiles (Kalimdor - large zone) for (int tx = 17; tx <= 22; tx++) { for (int ty = 28; ty <= 35; ty++) { tileToZone[tx * 100 + ty] = 17; } } // --- Kalimdor tile mappings --- // Durotar tiles (east coast, near Orgrimmar) for (int tx = 19; tx <= 22; tx++) { for (int ty = 25; ty <= 28; ty++) { tileToZone[tx * 100 + ty] = 14; } } // Orgrimmar tiles (within Durotar) tileToZone[20 * 100 + 26] = 1637; tileToZone[21 * 100 + 26] = 1637; tileToZone[20 * 100 + 27] = 1637; tileToZone[21 * 100 + 27] = 1637; // Mulgore tiles (south of Barrens) for (int tx = 15; tx <= 18; tx++) { for (int ty = 33; ty <= 36; ty++) { tileToZone[tx * 100 + ty] = 215; } } // Thunder Bluff tiles (within Mulgore) tileToZone[16 * 100 + 34] = 1638; tileToZone[17 * 100 + 34] = 1638; // Darkshore tiles (northwest Kalimdor coast) for (int tx = 14; tx <= 17; tx++) { for (int ty = 19; ty <= 24; ty++) { tileToZone[tx * 100 + ty] = 148; } } // Teldrassil tiles (island off Darkshore) for (int tx = 13; tx <= 15; tx++) { for (int ty = 15; ty <= 18; ty++) { tileToZone[tx * 100 + ty] = 141; } } // Darnassus tiles (within Teldrassil) tileToZone[14 * 100 + 16] = 1657; tileToZone[14 * 100 + 17] = 1657; std::srand(static_cast(std::time(nullptr))); LOG_INFO("Zone manager initialized: ", zones.size(), " zones, ", tileToZone.size(), " tile mappings"); } uint32_t ZoneManager::getZoneId(int tileX, int tileY) const { int key = tileX * 100 + tileY; auto it = tileToZone.find(key); if (it != tileToZone.end()) { return it->second; } return 0; // Unknown zone } const ZoneInfo* ZoneManager::getZoneInfo(uint32_t zoneId) const { auto it = zones.find(zoneId); if (it != zones.end()) { return &it->second; } return nullptr; } std::string ZoneManager::getRandomMusic(uint32_t zoneId) { auto it = zones.find(zoneId); if (it == zones.end() || it->second.musicPaths.empty()) { return ""; } // Build filtered pool: exclude file: (original soundtrack) tracks if disabled const auto& all = it->second.musicPaths; std::vector pool; pool.reserve(all.size()); for (const auto& p : all) { if (!useOriginalSoundtrack_ && p.rfind("file:", 0) == 0) continue; pool.push_back(&p); } // Fall back to full list if filtering left nothing if (pool.empty()) { for (const auto& p : all) pool.push_back(&p); } if (pool.size() == 1) { lastPlayedMusic_ = *pool[0]; return lastPlayedMusic_; } // Avoid playing the same track back-to-back const std::string* pick = pool[0]; for (int attempts = 0; attempts < 5; ++attempts) { pick = pool[std::rand() % pool.size()]; if (*pick != lastPlayedMusic_) break; } lastPlayedMusic_ = *pick; return lastPlayedMusic_; } std::vector ZoneManager::getAllMusicPaths() const { std::vector out; std::unordered_set seen; for (const auto& [zoneId, zone] : zones) { (void)zoneId; for (const auto& path : zone.musicPaths) { if (path.empty()) continue; if (seen.insert(path).second) { out.push_back(path); } } } return out; } } // namespace game } // namespace wowee