diff --git a/src/game/ChatCommands/Level3.cpp b/src/game/ChatCommands/Level3.cpp index b0f6106d..5335e812 100644 --- a/src/game/ChatCommands/Level3.cpp +++ b/src/game/ChatCommands/Level3.cpp @@ -579,6 +579,10 @@ bool ChatHandler::HandleReloadNpcTextCommand(char* /*args*/) bool ChatHandler::HandleReloadNpcTrainerCommand(char* /*args*/) { + sLog.outString("Re-Loading `npc_trainer_template` Table!"); + sObjectMgr.LoadTrainerTemplates(); + SendGlobalSysMessage("DB table `npc_trainer_template` reloaded.", SEC_MODERATOR); + sLog.outString("Re-Loading `npc_trainer` Table!"); sObjectMgr.LoadTrainers(); SendGlobalSysMessage("DB table `npc_trainer` reloaded.", SEC_MODERATOR); @@ -588,6 +592,10 @@ bool ChatHandler::HandleReloadNpcTrainerCommand(char* /*args*/) bool ChatHandler::HandleReloadNpcVendorCommand(char* /*args*/) { // not safe reload vendor template tables independent... + sLog.outString("Re-Loading `npc_vendor_template` Table!"); + sObjectMgr.LoadVendorTemplates(); + SendGlobalSysMessage("DB table `npc_vendor_template` reloaded.", SEC_MODERATOR); + sLog.outString("Re-Loading `npc_vendor` Table!"); sObjectMgr.LoadVendors(); SendGlobalSysMessage("DB table `npc_vendor` reloaded.", SEC_MODERATOR); diff --git a/src/game/Object/ObjectMgr.cpp b/src/game/Object/ObjectMgr.cpp index ce9acbf2..50393216 100644 --- a/src/game/Object/ObjectMgr.cpp +++ b/src/game/Object/ObjectMgr.cpp @@ -792,7 +792,7 @@ void ObjectMgr::LoadCreatureItemTemplates() } sLog.outString(">> Loaded %u creature item template", sEquipmentStorageItem.GetRecordCount()); - sLog.outString(); + sLog.outString(); } void ObjectMgr::LoadCreatureClassLvlStats() @@ -7994,6 +7994,41 @@ void ObjectMgr::LoadTrainers(char const* tableName, bool isTemplates) sLog.outString(); } +void ObjectMgr::LoadTrainerTemplates() +{ + LoadTrainers("npc_trainer_template", true); + + // post loading check + std::set trainer_ids; + bool hasErrored = false; + + for (CacheTrainerSpellMap::const_iterator tItr = m_mCacheTrainerTemplateSpellMap.begin(); tItr != m_mCacheTrainerTemplateSpellMap.end(); ++tItr) + { trainer_ids.insert(tItr->first); } + + for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i) + { + if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) + { + if (cInfo->TrainerTemplateId) + { + if (m_mCacheTrainerTemplateSpellMap.find(cInfo->TrainerTemplateId) != m_mCacheTrainerTemplateSpellMap.end()) + { trainer_ids.erase(cInfo->TrainerTemplateId); } + else + { + sLog.outErrorDb("Creature (Entry: %u) has TrainerTemplateId = %u for nonexistent trainer template", cInfo->Entry, cInfo->TrainerTemplateId); + hasErrored = true; + } + } + } + } + + for (std::set::const_iterator tItr = trainer_ids.begin(); tItr != trainer_ids.end(); ++tItr) + { sLog.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr); } + + if (hasErrored || !trainer_ids.empty()) // Append extra line in case of reported errors + { sLog.outString(); } +} + void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates) { CacheVendorItemMap& vendorList = isTemplates ? m_mCacheVendorTemplateItemMap : m_mCacheVendorItemMap; @@ -8047,6 +8082,34 @@ void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates) } +void ObjectMgr::LoadVendorTemplates() +{ + LoadVendors("npc_vendor_template", true); + + // post loading check + std::set vendor_ids; + + for (CacheVendorItemMap::const_iterator vItr = m_mCacheVendorTemplateItemMap.begin(); vItr != m_mCacheVendorTemplateItemMap.end(); ++vItr) + { vendor_ids.insert(vItr->first); } + + for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i) + { + if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) + { + if (cInfo->VendorTemplateId) + { + if (m_mCacheVendorTemplateItemMap.find(cInfo->VendorTemplateId) != m_mCacheVendorTemplateItemMap.end()) + { vendor_ids.erase(cInfo->VendorTemplateId); } + else + sLog.outErrorDb("Creature (Entry: %u) has VendorTemplateId = %u for nonexistent vendor template", cInfo->Entry, cInfo->VendorTemplateId); + } + } + } + + for (std::set::const_iterator vItr = vendor_ids.begin(); vItr != vendor_ids.end(); ++vItr) + { sLog.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr); } +} + void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) { m_mGossipMenusMap.clear(); diff --git a/src/game/Object/ObjectMgr.h b/src/game/Object/ObjectMgr.h index c2d7eead..5dd50208 100644 --- a/src/game/Object/ObjectMgr.h +++ b/src/game/Object/ObjectMgr.h @@ -706,10 +706,12 @@ class ObjectMgr void LoadGossipMenus(); + void LoadVendorTemplates(); void LoadVendors() { LoadVendors("npc_vendor", false); } + void LoadTrainerTemplates(); void LoadTrainers() { LoadTrainers("npc_trainer", false); diff --git a/src/game/WorldHandlers/NPCHandler.cpp b/src/game/WorldHandlers/NPCHandler.cpp index 0b7f2302..6be8cf9d 100644 --- a/src/game/WorldHandlers/NPCHandler.cpp +++ b/src/game/WorldHandlers/NPCHandler.cpp @@ -288,13 +288,18 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recv_data) // check present spell in trainer spell list TrainerSpellData const* cSpells = unit->GetTrainerSpells(); + TrainerSpellData const* tSpells = unit->GetTrainerTemplateSpells(); - if (!cSpells) + if (!cSpells && !tSpells) { return; } // Try find spell in npc_trainer TrainerSpell const* trainer_spell = cSpells ? cSpells->Find(spellId) : NULL; + // Not found, try find in npc_trainer_template + if (!trainer_spell && tSpells) + { trainer_spell = tSpells->Find(spellId); } + // Not found anywhere, cheating? if (!trainer_spell) { return; } diff --git a/src/game/WorldHandlers/World.cpp b/src/game/WorldHandlers/World.cpp index a93d13ca..8ad68b9b 100644 --- a/src/game/WorldHandlers/World.cpp +++ b/src/game/WorldHandlers/World.cpp @@ -1262,9 +1262,11 @@ void World::SetInitialWorldSettings() sObjectMgr.LoadGossipMenus(); sLog.outString("Loading Vendors..."); + sObjectMgr.LoadVendorTemplates(); // must be after load ItemTemplate sObjectMgr.LoadVendors(); // must be after load CreatureTemplate, VendorTemplate, and ItemTemplate sLog.outString("Loading Trainers..."); + sObjectMgr.LoadTrainerTemplates(); // must be after load CreatureTemplate sObjectMgr.LoadTrainers(); // must be after load CreatureTemplate, TrainerTemplate sLog.outString("Loading Waypoint scripts..."); // before loading from creature_movement