Updated Copyright year to 2013
[getmangos.git] / src / game / ObjectMgr.cpp
blob6baff3d257a0dfee318040d22edc42ee780dce3a
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "ObjectMgr.h"
20 #include "Database/DatabaseEnv.h"
21 #include "Policies/SingletonImp.h"
23 #include "SQLStorages.h"
24 #include "Log.h"
25 #include "MapManager.h"
26 #include "ObjectGuid.h"
27 #include "ScriptMgr.h"
28 #include "SpellMgr.h"
29 #include "UpdateMask.h"
30 #include "World.h"
31 #include "Group.h"
32 #include "ArenaTeam.h"
33 #include "Transports.h"
34 #include "ProgressBar.h"
35 #include "Language.h"
36 #include "PoolManager.h"
37 #include "GameEventMgr.h"
38 #include "Spell.h"
39 #include "Chat.h"
40 #include "AccountMgr.h"
41 #include "MapPersistentStateMgr.h"
42 #include "SpellAuras.h"
43 #include "Util.h"
44 #include "WaypointManager.h"
45 #include "GossipDef.h"
46 #include "Mail.h"
47 #include "InstanceData.h"
48 #include "DB2Structure.h"
49 #include "DB2Stores.h"
51 #include <limits>
53 INSTANTIATE_SINGLETON_1(ObjectMgr);
55 bool normalizePlayerName(std::string& name)
57 if (name.empty())
58 return false;
60 wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME + 1];
61 size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
63 if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
64 return false;
66 wstr_buf[0] = wcharToUpper(wstr_buf[0]);
67 for (size_t i = 1; i < wstr_len; ++i)
68 wstr_buf[i] = wcharToLower(wstr_buf[i]);
70 if (!WStrToUtf8(wstr_buf, wstr_len, name))
71 return false;
73 return true;
76 LanguageDesc lang_description[LANGUAGES_COUNT] =
78 { LANG_ADDON, 0, 0 },
79 { LANG_UNIVERSAL, 0, 0 },
80 { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
81 { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
82 { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
83 { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
84 { LANG_COMMON, 668, SKILL_LANG_COMMON },
85 { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
86 { LANG_TITAN, 816, SKILL_LANG_TITAN },
87 { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
88 { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
89 { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
90 { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
91 { LANG_TROLL, 7341, SKILL_LANG_TROLL },
92 { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
93 { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
94 { LANG_ZOMBIE, 0, 0 },
95 { LANG_GNOMISH_BINARY, 0, 0 },
96 { LANG_GOBLIN_BINARY, 0, 0 },
97 { LANG_WORGEN, 69270, SKILL_LANG_WORGEN },
98 { LANG_GOBLIN, 69269, SKILL_LANG_GOBLIN }
101 LanguageDesc const* GetLanguageDescByID(uint32 lang)
103 for (int i = 0; i < LANGUAGES_COUNT; ++i)
105 if (uint32(lang_description[i].lang_id) == lang)
106 return &lang_description[i];
109 return NULL;
112 bool SpellClickInfo::IsFitToRequirements(Player const* player) const
114 if (questStart)
116 // not in expected required quest state
117 if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)))
118 return false;
121 if (questEnd)
123 // not in expected forbidden quest state
124 if (!player || player->GetQuestRewardStatus(questEnd))
125 return false;
128 return true;
131 template<typename T>
132 T IdGenerator<T>::Generate()
134 if (m_nextGuid >= std::numeric_limits<T>::max() - 1)
136 sLog.outError("%s guid overflow!! Can't continue, shutting down server. ", m_name);
137 World::StopNow(ERROR_EXIT_CODE);
139 return m_nextGuid++;
142 template uint32 IdGenerator<uint32>::Generate();
143 template uint64 IdGenerator<uint64>::Generate();
145 ObjectMgr::ObjectMgr() :
146 m_ArenaTeamIds("Arena team ids"),
147 m_AuctionIds("Auction ids"),
148 m_EquipmentSetIds("Equipment set ids"),
149 m_GuildIds("Guild ids"),
150 m_MailIds("Mail ids"),
151 m_PetNumbers("Pet numbers"),
152 m_FirstTemporaryCreatureGuid(1),
153 m_FirstTemporaryGameObjectGuid(1)
157 ObjectMgr::~ObjectMgr()
159 for (QuestMap::iterator i = mQuestTemplates.begin(); i != mQuestTemplates.end(); ++i)
160 delete i->second;
162 for (PetLevelInfoMap::iterator i = petInfo.begin(); i != petInfo.end(); ++i)
163 delete[] i->second;
165 for (int race = 0; race < MAX_RACES; ++race)
166 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
167 delete[] playerInfo[race][class_].levelInfo;
169 // free objects
170 for (GroupMap::iterator itr = mGroupMap.begin(); itr != mGroupMap.end(); ++itr)
171 delete itr->second;
173 for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
174 delete itr->second;
176 for (CacheVendorItemMap::iterator itr = m_mCacheVendorTemplateItemMap.begin(); itr != m_mCacheVendorTemplateItemMap.end(); ++itr)
177 itr->second.Clear();
179 for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
180 itr->second.Clear();
182 for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
183 itr->second.Clear();
186 Group* ObjectMgr::GetGroupById(uint32 id) const
188 GroupMap::const_iterator itr = mGroupMap.find(id);
189 if (itr != mGroupMap.end())
190 return itr->second;
192 return NULL;
195 ArenaTeam* ObjectMgr::GetArenaTeamById(uint32 arenateamid) const
197 ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid);
198 if (itr != mArenaTeamMap.end())
199 return itr->second;
201 return NULL;
204 ArenaTeam* ObjectMgr::GetArenaTeamByName(const std::string& arenateamname) const
206 for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
207 if (itr->second->GetName() == arenateamname)
208 return itr->second;
210 return NULL;
213 ArenaTeam* ObjectMgr::GetArenaTeamByCaptain(ObjectGuid guid) const
215 for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
216 if (itr->second->GetCaptainGuid() == guid)
217 return itr->second;
219 return NULL;
222 void ObjectMgr::LoadCreatureLocales()
224 mCreatureLocaleMap.clear(); // need for reload case
226 QueryResult* result = WorldDatabase.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
228 if (!result)
230 BarGoLink bar(1);
232 bar.step();
234 sLog.outString();
235 sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
236 return;
239 BarGoLink bar(result->GetRowCount());
243 Field* fields = result->Fetch();
244 bar.step();
246 uint32 entry = fields[0].GetUInt32();
248 if (!GetCreatureTemplate(entry))
250 ERROR_DB_STRICT_LOG("Table `locales_creature` has data for not existed creature entry %u, skipped.", entry);
251 continue;
254 CreatureLocale& data = mCreatureLocaleMap[entry];
256 for (int i = 1; i < MAX_LOCALE; ++i)
258 std::string str = fields[1 + 2 * (i - 1)].GetCppString();
259 if (!str.empty())
261 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
262 if (idx >= 0)
264 if ((int32)data.Name.size() <= idx)
265 data.Name.resize(idx + 1);
267 data.Name[idx] = str;
270 str = fields[1 + 2 * (i - 1) + 1].GetCppString();
271 if (!str.empty())
273 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
274 if (idx >= 0)
276 if ((int32)data.SubName.size() <= idx)
277 data.SubName.resize(idx + 1);
279 data.SubName[idx] = str;
284 while (result->NextRow());
286 delete result;
288 sLog.outString();
289 sLog.outString(">> Loaded " SIZEFMTD " creature locale strings", mCreatureLocaleMap.size());
292 void ObjectMgr::LoadGossipMenuItemsLocales()
294 mGossipMenuItemsLocaleMap.clear(); // need for reload case
296 QueryResult* result = WorldDatabase.Query("SELECT menu_id,id,"
297 "option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
298 "option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
299 "option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
300 "option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
301 "FROM locales_gossip_menu_option");
303 if (!result)
305 BarGoLink bar(1);
307 bar.step();
309 sLog.outString();
310 sLog.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
311 return;
314 BarGoLink bar(result->GetRowCount());
318 Field* fields = result->Fetch();
319 bar.step();
321 uint16 menuId = fields[0].GetUInt16();
322 uint16 id = fields[1].GetUInt16();
324 GossipMenuItemsMapBounds bounds = GetGossipMenuItemsMapBounds(menuId);
326 bool found = false;
327 if (bounds.first != bounds.second)
329 for (GossipMenuItemsMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
331 if (itr->second.id == id)
333 found = true;
334 break;
339 if (!found)
341 ERROR_DB_STRICT_LOG("Table `locales_gossip_menu_option` has data for nonexistent gossip menu %u item %u, skipped.", menuId, id);
342 continue;
345 GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId, id)];
347 for (int i = 1; i < MAX_LOCALE; ++i)
349 std::string str = fields[2 + 2 * (i - 1)].GetCppString();
350 if (!str.empty())
352 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
353 if (idx >= 0)
355 if ((int32)data.OptionText.size() <= idx)
356 data.OptionText.resize(idx + 1);
358 data.OptionText[idx] = str;
361 str = fields[2 + 2 * (i - 1) + 1].GetCppString();
362 if (!str.empty())
364 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
365 if (idx >= 0)
367 if ((int32)data.BoxText.size() <= idx)
368 data.BoxText.resize(idx + 1);
370 data.BoxText[idx] = str;
375 while (result->NextRow());
377 delete result;
379 sLog.outString();
380 sLog.outString(">> Loaded " SIZEFMTD " gossip_menu_option locale strings", mGossipMenuItemsLocaleMap.size());
383 void ObjectMgr::LoadPointOfInterestLocales()
385 mPointOfInterestLocaleMap.clear(); // need for reload case
387 QueryResult* result = WorldDatabase.Query("SELECT entry,icon_name_loc1,icon_name_loc2,icon_name_loc3,icon_name_loc4,icon_name_loc5,icon_name_loc6,icon_name_loc7,icon_name_loc8 FROM locales_points_of_interest");
389 if (!result)
391 BarGoLink bar(1);
393 bar.step();
395 sLog.outString();
396 sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
397 return;
400 BarGoLink bar(result->GetRowCount());
404 Field* fields = result->Fetch();
405 bar.step();
407 uint32 entry = fields[0].GetUInt32();
409 if (!GetPointOfInterest(entry))
411 ERROR_DB_STRICT_LOG("Table `locales_points_of_interest` has data for nonexistent POI entry %u, skipped.", entry);
412 continue;
415 PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];
417 for (int i = 1; i < MAX_LOCALE; ++i)
419 std::string str = fields[i].GetCppString();
420 if (str.empty())
421 continue;
423 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
424 if (idx >= 0)
426 if ((int32)data.IconName.size() <= idx)
427 data.IconName.resize(idx + 1);
429 data.IconName[idx] = str;
433 while (result->NextRow());
435 delete result;
437 sLog.outString();
438 sLog.outString(">> Loaded " SIZEFMTD " points_of_interest locale strings", mPointOfInterestLocaleMap.size());
441 struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader, SQLStorage>
443 template<class D>
444 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
446 dst = D(sScriptMgr.GetScriptId(src));
450 void ObjectMgr::LoadCreatureTemplates()
452 SQLCreatureLoader loader;
453 loader.Load(sCreatureStorage);
455 sLog.outString(">> Loaded %u creature definitions", sCreatureStorage.GetRecordCount());
456 sLog.outString();
458 std::set<uint32> difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures
459 std::set<uint32> hasDifficultyEntries[MAX_DIFFICULTY - 1]; // already loaded creatures with difficulty 1 values
461 // check data correctness
462 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
464 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
465 if (!cInfo)
466 continue;
468 bool ok = true; // bool to allow continue outside this loop
469 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
471 if (!cInfo->DifficultyEntry[diff])
472 continue;
473 ok = false; // will be set to true at the end of this loop again
475 CreatureInfo const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
476 if (!difficultyInfo)
478 sLog.outErrorDb("Creature (Entry: %u) have `difficulty_entry_%u`=%u but creature entry %u not exist.",
479 i, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
480 continue;
483 if (difficultyEntries[diff].find(i) != difficultyEntries[diff].end())
485 sLog.outErrorDb("Creature (Entry: %u) listed as difficulty %u but have value in `difficulty_entry_%u`.", i, diff + 1, diff + 1);
486 continue;
489 bool ok2 = true;
490 for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
492 ok2 = false;
493 if (difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != difficultyEntries[diff2].end())
495 sLog.outErrorDb("Creature (Entry: %u) already listed as difficulty %u for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
496 continue;
499 if (hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != hasDifficultyEntries[diff2].end())
501 sLog.outErrorDb("Creature (Entry: %u) have `difficulty_entry_%u`=%u but creature entry %u have difficulty %u entry also.",
502 i, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
503 continue;
505 ok2 = true;
507 if (!ok2)
508 continue;
510 if (cInfo->unit_class != difficultyInfo->unit_class)
512 sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in difficulty %u mode (Entry: %u, class %u).",
513 i, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
514 continue;
517 if (cInfo->npcflag != difficultyInfo->npcflag)
519 sLog.outErrorDb("Creature (Entry: %u) has different `npcflag` in difficulty %u mode (Entry: %u).", i, diff + 1, cInfo->DifficultyEntry[diff]);
520 continue;
523 if (cInfo->trainer_class != difficultyInfo->trainer_class)
525 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in difficulty %u mode (Entry: %u).", i, diff + 1, cInfo->DifficultyEntry[diff]);
526 continue;
529 if (cInfo->trainer_race != difficultyInfo->trainer_race)
531 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in difficulty %u mode (Entry: %u).", i, diff + 1, cInfo->DifficultyEntry[diff]);
532 continue;
535 if (cInfo->trainer_type != difficultyInfo->trainer_type)
537 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_type` in difficulty %u mode (Entry: %u).", i, diff + 1, cInfo->DifficultyEntry[diff]);
538 continue;
541 if (cInfo->trainer_spell != difficultyInfo->trainer_spell)
543 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", i, diff + 1, cInfo->DifficultyEntry[diff]);
544 continue;
547 if (difficultyInfo->AIName && *difficultyInfo->AIName)
549 sLog.outErrorDb("Difficulty %u mode creature (Entry: %u) has `AIName`, but in any case will used difficulty 0 mode creature (Entry: %u) AIName.",
550 diff + 1, cInfo->DifficultyEntry[diff], i);
551 continue;
554 if (difficultyInfo->ScriptID)
556 sLog.outErrorDb("Difficulty %u mode creature (Entry: %u) has `ScriptName`, but in any case will used difficulty 0 mode creature (Entry: %u) ScriptName.",
557 diff + 1, cInfo->DifficultyEntry[diff], i);
558 continue;
561 hasDifficultyEntries[diff].insert(i);
562 difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
563 ok = true;
565 if (!ok)
566 continue;
568 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
569 if (!factionTemplate)
570 sLog.outErrorDb("Creature (Entry: %u) has nonexistent faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
572 factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
573 if (!factionTemplate)
574 sLog.outErrorDb("Creature (Entry: %u) has nonexistent faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
576 for (int k = 0; k < MAX_KILL_CREDIT; ++k)
578 if (cInfo->KillCredit[k])
580 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
582 sLog.outErrorDb("Creature (Entry: %u) has nonexistent creature entry in `KillCredit%d` (%u)", cInfo->Entry, k + 1, cInfo->KillCredit[k]);
583 const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0;
588 // used later for scale
589 CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
591 for (int i = 0; i < MAX_CREATURE_MODEL; ++i)
593 if (cInfo->ModelId[i])
595 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[i]);
596 if (!displayEntry)
598 sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_%d (%u), can crash client", cInfo->Entry, i + 1, cInfo->ModelId[i]);
599 const_cast<CreatureInfo*>(cInfo)->ModelId[i] = 0;
601 else if (!displayScaleEntry)
602 displayScaleEntry = displayEntry;
604 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[i]);
605 if (!minfo)
606 sLog.outErrorDb("Creature (Entry: %u) are using modelid_%d (%u), but creature_model_info are missing for this model.", cInfo->Entry, i + 1, cInfo->ModelId[i]);
610 if (!displayScaleEntry)
611 sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid in modelid_1/modelid_2/modelid_3/modelid_4", cInfo->Entry);
613 // use below code for 0-checks for unit_class
614 if (!cInfo->unit_class)
615 ERROR_DB_STRICT_LOG("Creature (Entry: %u) not has proper unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
616 else if (((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
617 sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
619 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
621 sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`", cInfo->Entry, cInfo->dmgschool);
622 const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
625 if (cInfo->baseattacktime == 0)
626 const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
628 if (cInfo->rangeattacktime == 0)
629 const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
631 if (cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK)
633 sLog.outDebug("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, but it is expected to be set in run-time based at `npc_spellclick_spells` contents.", cInfo->Entry, UNIT_NPC_FLAG_SPELLCLICK);
634 const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
637 if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
638 sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u", cInfo->Entry, cInfo->trainer_type);
640 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
642 sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`", cInfo->Entry, cInfo->type);
643 const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
646 // must exist or used hidden but used in data horse case
647 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
649 sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`", cInfo->Entry, cInfo->family);
650 const_cast<CreatureInfo*>(cInfo)->family = 0;
653 if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
655 sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly", cInfo->Entry, cInfo->InhabitType);
656 const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
659 if (cInfo->PetSpellDataId)
661 CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
662 if (!spellDataId)
663 sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
666 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
668 sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.", cInfo->Entry, cInfo->MovementType);
669 const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
672 if (cInfo->vehicleId && !sVehicleStore.LookupEntry(cInfo->vehicleId))
674 sLog.outErrorDb("Creature (Entry: %u) has non-existing vehicle_id (%u), set to 0.", cInfo->Entry, cInfo->vehicleId);
675 const_cast<CreatureInfo*>(cInfo)->vehicleId = 0;
678 if (cInfo->equipmentId > 0) // 0 no equipment
680 if (!GetEquipmentInfo(cInfo->equipmentId))
682 sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
683 const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
687 if (cInfo->vendorId > 0)
689 if (!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
690 sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with vendor_id %u but not have flag UNIT_NPC_FLAG_VENDOR (%u), vendor items will ignored.", cInfo->Entry, cInfo->vendorId, UNIT_NPC_FLAG_VENDOR);
693 /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
694 if (cInfo->scale <= 0.0f)
696 if (displayScaleEntry)
697 const_cast<CreatureInfo*>(cInfo)->scale = displayScaleEntry->scale;
698 else
699 const_cast<CreatureInfo*>(cInfo)->scale = DEFAULT_OBJECT_SCALE;
704 void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
706 // Now add the auras, format "spell1 spell2 ..."
707 char* p, *s;
708 std::vector<int> val;
709 s = p = (char*)reinterpret_cast<char const*>(addon->auras);
710 if (p)
712 while (p[0] != 0)
714 ++p;
715 if (p[0] == ' ')
717 val.push_back(atoi(s));
718 s = ++p;
721 if (p != s)
722 val.push_back(atoi(s));
724 // free char* loaded memory
725 delete[](char*)reinterpret_cast<char const*>(addon->auras);
728 // empty list
729 if (val.empty())
731 addon->auras = NULL;
732 return;
735 // replace by new structures array
736 const_cast<uint32*&>(addon->auras) = new uint32[val.size() + 1];
738 uint32 i = 0;
739 for (uint32 j = 0; j < val.size(); ++j)
741 uint32& cAura = const_cast<uint32&>(addon->auras[i]);
742 cAura = uint32(val[j]);
744 SpellEntry const* AdditionalSpellInfo = sSpellStore.LookupEntry(cAura);
745 if (!AdditionalSpellInfo)
747 sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura, table);
748 continue;
751 if (std::find(&addon->auras[0], &addon->auras[i], cAura) != &addon->auras[i])
753 sLog.outErrorDb("Creature (%s: %u) has duplicate spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura, table);
754 continue;
757 ++i;
760 // fill terminator element (after last added)
761 const_cast<uint32&>(addon->auras[i]) = 0;
764 void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment)
766 creatureaddons.Load();
768 sLog.outString(">> Loaded %u %s", creatureaddons.GetRecordCount(), comment);
769 sLog.outString();
771 // check data correctness and convert 'auras'
772 for (uint32 i = 1; i < creatureaddons.GetMaxEntry(); ++i)
774 CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i);
775 if (!addon)
776 continue;
778 if (addon->mount)
780 if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount))
782 sLog.outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->mount, creatureaddons.GetTableName());
783 const_cast<CreatureDataAddon*>(addon)->mount = 0;
787 if (addon->sheath_state > SHEATH_STATE_RANGED)
788 sLog.outErrorDb("Creature (%s %u) has unknown sheath state (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->sheath_state, creatureaddons.GetTableName());
790 if (!sEmotesStore.LookupEntry(addon->emote))
792 sLog.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName());
793 const_cast<CreatureDataAddon*>(addon)->emote = 0;
796 ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName);
800 void ObjectMgr::LoadCreatureAddons()
802 LoadCreatureAddons(sCreatureInfoAddonStorage, "Entry", "creature template addons");
804 // check entry ids
805 for (uint32 i = 1; i < sCreatureInfoAddonStorage.GetMaxEntry(); ++i)
806 if (CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i))
807 if (!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
808 sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`", addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());
810 LoadCreatureAddons(sCreatureDataAddonStorage, "GUID", "creature addons");
812 // check entry ids
813 for (uint32 i = 1; i < sCreatureDataAddonStorage.GetMaxEntry(); ++i)
814 if (CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i))
815 if (mCreatureDataMap.find(addon->guidOrEntry) == mCreatureDataMap.end())
816 sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`", addon->guidOrEntry);
819 void ObjectMgr::LoadEquipmentTemplates()
821 sEquipmentStorage.Load();
823 for (uint32 i = 0; i < sEquipmentStorage.GetMaxEntry(); ++i)
825 EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i);
827 if (!eqInfo)
828 continue;
830 for (uint8 j = 0; j < 3; ++j)
832 if (!eqInfo->equipentry[j])
833 continue;
835 ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]);
837 if (!dbcitem)
839 sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j+1, i);
840 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
841 continue;
844 if (dbcitem->InventoryType != INVTYPE_WEAPON &&
845 dbcitem->InventoryType != INVTYPE_SHIELD &&
846 dbcitem->InventoryType != INVTYPE_RANGED &&
847 dbcitem->InventoryType != INVTYPE_2HWEAPON &&
848 dbcitem->InventoryType != INVTYPE_WEAPONMAINHAND &&
849 dbcitem->InventoryType != INVTYPE_WEAPONOFFHAND &&
850 dbcitem->InventoryType != INVTYPE_HOLDABLE &&
851 dbcitem->InventoryType != INVTYPE_THROWN &&
852 dbcitem->InventoryType != INVTYPE_RANGEDRIGHT &&
853 dbcitem->InventoryType != INVTYPE_RELIC)
855 sLog.outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo->equipentry[j], j+1, i);
856 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
861 sLog.outString(">> Loaded %u equipment template", sEquipmentStorage.GetRecordCount());
862 sLog.outString();
865 // generally models that does not have a gender(2), or has alternative model for same gender
866 uint32 ObjectMgr::GetCreatureModelAlternativeModel(uint32 modelId) const
868 if (const CreatureModelInfo* modelInfo = GetCreatureModelInfo(modelId))
869 return modelInfo->modelid_alternative;
871 return 0;
874 CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id) const
876 CreatureModelInfo const* minfo = GetCreatureModelInfo(display_id);
877 if (!minfo)
878 return NULL;
880 // If a model for another gender exists, 50% chance to use it
881 if (minfo->modelid_other_gender != 0 && urand(0, 1) == 0)
883 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
884 if (!minfo_tmp)
886 sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
887 return minfo; // not fatal, just use the previous one
889 else
890 return minfo_tmp;
892 else
893 return minfo;
896 uint32 ObjectMgr::GetModelForRace(uint32 sourceModelId, uint32 racemask)
898 uint32 modelId = 0;
900 CreatureModelRaceMapBounds bounds = m_mCreatureModelRaceMap.equal_range(sourceModelId);
902 for (CreatureModelRaceMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
904 if (!(itr->second.racemask & racemask))
905 continue;
907 if (itr->second.creature_entry)
909 const CreatureInfo* cInfo = GetCreatureTemplate(itr->second.creature_entry);
910 modelId = Creature::ChooseDisplayId(cInfo);
912 else
914 modelId = itr->second.modelid_racial;
918 return modelId;
921 void ObjectMgr::LoadCreatureModelInfo()
923 sCreatureModelStorage.Load();
925 // post processing
926 for (uint32 i = 1; i < sCreatureModelStorage.GetMaxEntry(); ++i)
928 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
929 if (!minfo)
930 continue;
932 if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
933 sLog.outErrorDb("Table `creature_model_info` has model for nonexistent model id (%u).", minfo->modelid);
935 if (minfo->gender >= MAX_GENDER)
937 sLog.outErrorDb("Table `creature_model_info` has invalid gender (%u) for model id (%u).", uint32(minfo->gender), minfo->modelid);
938 const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
941 if (minfo->modelid_other_gender)
943 if (minfo->modelid_other_gender == minfo->modelid)
945 sLog.outErrorDb("Table `creature_model_info` has redundant modelid_other_gender model (%u) defined for model id %u.", minfo->modelid_other_gender, minfo->modelid);
946 const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
948 else if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
950 sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_gender model (%u) defined for model id %u.", minfo->modelid_other_gender, minfo->modelid);
951 const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
955 if (minfo->modelid_alternative)
957 if (minfo->modelid_alternative == minfo->modelid)
959 sLog.outErrorDb("Table `creature_model_info` has redundant modelid_alternative model (%u) defined for model id %u.", minfo->modelid_alternative, minfo->modelid);
960 const_cast<CreatureModelInfo*>(minfo)->modelid_alternative = 0;
962 else if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_alternative))
964 sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_alternative model (%u) defined for model id %u.", minfo->modelid_alternative, minfo->modelid);
965 const_cast<CreatureModelInfo*>(minfo)->modelid_alternative = 0;
970 // character races expected have model info data in table
971 for (uint32 race = 1; race < sChrRacesStore.GetNumRows(); ++race)
973 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
974 if (!raceEntry)
975 continue;
977 if (!((1 << (race - 1)) & RACEMASK_ALL_PLAYABLE))
978 continue;
980 if (CreatureModelInfo const* minfo = GetCreatureModelInfo(raceEntry->model_f))
982 if (minfo->gender != GENDER_FEMALE)
983 sLog.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u female model id %u", minfo->gender, race, raceEntry->model_f);
985 if (minfo->modelid_other_gender != raceEntry->model_m)
986 sLog.outErrorDb("Table `creature_model_info` have wrong other gender model id %u for character race %u female model id %u", minfo->modelid_other_gender, race, raceEntry->model_f);
988 if (minfo->bounding_radius <= 0.0f)
990 sLog.outErrorDb("Table `creature_model_info` have wrong bounding_radius %f for character race %u female model id %u, use %f instead", minfo->bounding_radius, race, raceEntry->model_f, DEFAULT_WORLD_OBJECT_SIZE);
991 const_cast<CreatureModelInfo*>(minfo)->bounding_radius = DEFAULT_WORLD_OBJECT_SIZE;
994 if (minfo->combat_reach != 1.5f)
996 sLog.outErrorDb("Table `creature_model_info` have wrong combat_reach %f for character race %u female model id %u, expected always 1.5f", minfo->combat_reach, race, raceEntry->model_f);
997 const_cast<CreatureModelInfo*>(minfo)->combat_reach = 1.5f;
1000 else
1001 sLog.outErrorDb("Table `creature_model_info` expect have data for character race %u female model id %u", race, raceEntry->model_f);
1003 if (CreatureModelInfo const* minfo = GetCreatureModelInfo(raceEntry->model_m))
1005 if (minfo->gender != GENDER_MALE)
1006 sLog.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u male model id %u", minfo->gender, race, raceEntry->model_m);
1008 if (minfo->modelid_other_gender != raceEntry->model_f)
1009 sLog.outErrorDb("Table `creature_model_info` have wrong other gender model id %u for character race %u male model id %u", minfo->modelid_other_gender, race, raceEntry->model_m);
1011 if (minfo->bounding_radius <= 0.0f)
1013 sLog.outErrorDb("Table `creature_model_info` have wrong bounding_radius %f for character race %u male model id %u, use %f instead", minfo->bounding_radius, race, raceEntry->model_f, DEFAULT_WORLD_OBJECT_SIZE);
1014 const_cast<CreatureModelInfo*>(minfo)->bounding_radius = DEFAULT_WORLD_OBJECT_SIZE;
1017 if (minfo->combat_reach != 1.5f)
1019 sLog.outErrorDb("Table `creature_model_info` have wrong combat_reach %f for character race %u male model id %u, expected always 1.5f", minfo->combat_reach, race, raceEntry->model_m);
1020 const_cast<CreatureModelInfo*>(minfo)->combat_reach = 1.5f;
1023 else
1024 sLog.outErrorDb("Table `creature_model_info` expect have data for character race %u male model id %u", race, raceEntry->model_m);
1028 sLog.outString(">> Loaded %u creature model based info", sCreatureModelStorage.GetRecordCount());
1029 sLog.outString();
1032 void ObjectMgr::LoadCreatureModelRace()
1034 m_mCreatureModelRaceMap.clear(); // can be used for reload
1036 QueryResult* result = WorldDatabase.Query("SELECT modelid, racemask, creature_entry, modelid_racial FROM creature_model_race");
1038 if (!result)
1040 BarGoLink bar(1);
1042 bar.step();
1044 sLog.outString();
1045 sLog.outErrorDb(">> Loaded creature_model_race, table is empty!");
1046 return;
1049 BarGoLink bar(result->GetRowCount());
1051 uint32 count = 0;
1053 // model, racemask
1054 std::map<uint32, uint32> model2raceMask;
1058 bar.step();
1060 Field* fields = result->Fetch();
1062 CreatureModelRace raceData;
1064 raceData.modelid = fields[0].GetUInt32();
1065 raceData.racemask = fields[1].GetUInt32();
1066 raceData.creature_entry = fields[2].GetUInt32();
1067 raceData.modelid_racial = fields[3].GetUInt32();
1069 if (!sCreatureDisplayInfoStore.LookupEntry(raceData.modelid))
1071 sLog.outErrorDb("Table `creature_model_race` has model for nonexistent model id (%u), skipping", raceData.modelid);
1072 continue;
1075 if (!sCreatureModelStorage.LookupEntry<CreatureModelInfo>(raceData.modelid))
1077 sLog.outErrorDb("Table `creature_model_race` modelid %u does not exist in creature_model_info, skipping", raceData.modelid);
1078 continue;
1081 if (!raceData.racemask)
1083 sLog.outErrorDb("Table `creature_model_race` modelid %u has no racemask defined, skipping", raceData.modelid);
1084 continue;
1087 if (!(raceData.racemask & RACEMASK_ALL_PLAYABLE))
1089 sLog.outErrorDb("Table `creature_model_race` modelid %u include invalid racemask, skipping", raceData.modelid);
1090 continue;
1093 std::map<uint32, uint32>::const_iterator model2Race = model2raceMask.find(raceData.modelid);
1095 // can't have same mask for same model several times
1096 if (model2Race != model2raceMask.end())
1098 if (model2Race->second & raceData.racemask)
1100 sLog.outErrorDb("Table `creature_model_race` modelid %u with racemask %u has mask already included for same modelid, skipping", raceData.modelid, raceData.racemask);
1101 continue;
1105 model2raceMask[raceData.modelid] |= raceData.racemask;
1107 // creature_entry is the prefered way
1108 if (raceData.creature_entry)
1110 if (raceData.modelid_racial)
1111 sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for modelid %u but a creature_entry are already defined, modelid_racial will never be used.", raceData.modelid, raceData.modelid_racial);
1113 if (!sCreatureStorage.LookupEntry<CreatureInfo>(raceData.creature_entry))
1115 sLog.outErrorDb("Table `creature_model_race` modelid %u has creature_entry for nonexistent creature_template (%u), skipping", raceData.modelid, raceData.creature_entry);
1116 continue;
1119 else if (raceData.modelid_racial)
1121 if (!sCreatureDisplayInfoStore.LookupEntry(raceData.modelid_racial))
1123 sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for nonexistent model id (%u), skipping", raceData.modelid, raceData.modelid_racial);
1124 continue;
1127 if (!sCreatureModelStorage.LookupEntry<CreatureModelInfo>(raceData.modelid_racial))
1129 sLog.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial %u, but are not defined in creature_model_info, skipping", raceData.modelid, raceData.modelid_racial);
1130 continue;
1133 else
1135 sLog.outErrorDb("Table `creature_model_race` modelid %u does not have either creature_entry or modelid_racial defined, skipping", raceData.modelid);
1136 continue;
1139 m_mCreatureModelRaceMap.insert(CreatureModelRaceMap::value_type(raceData.modelid, raceData));
1141 ++count;
1143 while (result->NextRow());
1145 delete result;
1147 sLog.outString();
1148 sLog.outString(">> Loaded %u creature_model_race entries", count);
1151 void ObjectMgr::LoadCreatures()
1153 uint32 count = 0;
1154 // 0 1 2 3
1155 QueryResult* result = WorldDatabase.Query("SELECT creature.guid, creature.id, map, modelid,"
1156 // 4 5 6 7 8 9 10 11
1157 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
1158 // 12 13 14 15 16 17 18
1159 "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event,"
1160 // 19 20
1161 "pool_creature.pool_entry, pool_creature_template.pool_entry "
1162 "FROM creature "
1163 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1164 "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid "
1165 "LEFT OUTER JOIN pool_creature_template ON creature.id = pool_creature_template.id");
1167 if (!result)
1169 BarGoLink bar(1);
1171 bar.step();
1173 sLog.outString();
1174 sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
1175 return;
1178 // build single time for check creature data
1179 std::set<uint32> difficultyCreatures[MAX_DIFFICULTY - 1];
1180 for (uint32 i = 0; i < sCreatureStorage.GetMaxEntry(); ++i)
1181 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1182 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1; ++diff)
1183 if (cInfo->DifficultyEntry[diff])
1184 difficultyCreatures[diff].insert(cInfo->DifficultyEntry[diff]);
1186 // build single time for check spawnmask
1187 std::map<uint32, uint32> spawnMasks;
1188 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
1189 if (sMapStore.LookupEntry(i))
1190 for (int k = 0; k < MAX_DIFFICULTY; ++k)
1191 if (GetMapDifficultyData(i, Difficulty(k)))
1192 spawnMasks[i] |= (1 << k);
1193 // Map 0 was removed from dbc as of 4.x.x
1194 spawnMasks[0] = 1 << REGULAR_DIFFICULTY;
1196 BarGoLink bar(result->GetRowCount());
1200 Field* fields = result->Fetch();
1201 bar.step();
1203 uint32 guid = fields[ 0].GetUInt32();
1204 uint32 entry = fields[ 1].GetUInt32();
1206 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
1207 if (!cInfo)
1209 sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
1210 continue;
1213 CreatureData& data = mCreatureDataMap[guid];
1215 data.id = entry;
1216 data.mapid = fields[ 2].GetUInt32();
1217 data.modelid_override = fields[ 3].GetUInt32();
1218 data.equipmentId = fields[ 4].GetUInt32();
1219 data.posX = fields[ 5].GetFloat();
1220 data.posY = fields[ 6].GetFloat();
1221 data.posZ = fields[ 7].GetFloat();
1222 data.orientation = fields[ 8].GetFloat();
1223 data.spawntimesecs = fields[ 9].GetUInt32();
1224 data.spawndist = fields[10].GetFloat();
1225 data.currentwaypoint = fields[11].GetUInt32();
1226 data.curhealth = fields[12].GetUInt32();
1227 data.curmana = fields[13].GetUInt32();
1228 data.is_dead = fields[14].GetBool();
1229 data.movementType = fields[15].GetUInt8();
1230 data.spawnMask = fields[16].GetUInt8();
1231 data.phaseMask = fields[17].GetUInt16();
1232 int16 gameEvent = fields[18].GetInt16();
1233 int16 GuidPoolId = fields[19].GetInt16();
1234 int16 EntryPoolId = fields[20].GetInt16();
1236 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
1237 if (!mapEntry)
1239 sLog.outErrorDb("Table `creature` have creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
1240 continue;
1243 if (data.spawnMask & ~spawnMasks[data.mapid])
1244 sLog.outErrorDb("Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid);
1246 bool ok = true;
1247 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
1249 if (difficultyCreatures[diff].find(data.id) != difficultyCreatures[diff].end())
1251 sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as difficulty %u template (entry: %u) in `creature_template`, skipped.",
1252 guid, diff + 1, data.id);
1253 ok = false;
1256 if (!ok)
1257 continue;
1259 if (data.modelid_override > 0 && !sCreatureDisplayInfoStore.LookupEntry(data.modelid_override))
1261 sLog.outErrorDb("Table `creature` GUID %u (entry %u) has model for nonexistent model id (%u), set to 0.", guid, data.id, data.modelid_override);
1262 data.modelid_override = 0;
1265 if (data.equipmentId > 0) // -1 no equipment, 0 use default
1267 if (!GetEquipmentInfo(data.equipmentId))
1269 sLog.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
1270 data.equipmentId = -1;
1274 if (cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
1276 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.", guid, data.id, data.curhealth, cInfo->minhealth);
1277 data.curhealth = cInfo->minhealth;
1280 if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
1282 if (!mapEntry || !mapEntry->IsDungeon())
1283 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND (%u) but creature are not in instance.",
1284 guid, data.id, CREATURE_FLAG_EXTRA_INSTANCE_BIND);
1287 if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_AGGRO_ZONE)
1289 if (!mapEntry || !mapEntry->IsDungeon())
1290 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_AGGRO_ZONE (%u) but creature are not in instance.",
1291 guid, data.id, CREATURE_FLAG_EXTRA_AGGRO_ZONE);
1294 if (data.curmana < cInfo->minmana)
1296 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.", guid, data.id, data.curmana, cInfo->minmana);
1297 data.curmana = cInfo->minmana;
1300 if (data.spawndist < 0.0f)
1302 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
1303 data.spawndist = 0.0f;
1305 else if (data.movementType == RANDOM_MOTION_TYPE)
1307 if (data.spawndist == 0.0f)
1309 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid, data.id);
1310 data.movementType = IDLE_MOTION_TYPE;
1313 else if (data.movementType == IDLE_MOTION_TYPE)
1315 if (data.spawndist != 0.0f)
1317 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
1318 data.spawndist = 0.0f;
1322 if (data.phaseMask == 0)
1324 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
1325 data.phaseMask = 1;
1328 if (gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1329 AddCreatureToGrid(guid, &data);
1331 ++count;
1334 while (result->NextRow());
1336 delete result;
1338 sLog.outString();
1339 sLog.outString(">> Loaded " SIZEFMTD " creatures", mCreatureDataMap.size());
1342 void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
1344 uint8 mask = data->spawnMask;
1345 for (uint8 i = 0; mask != 0; ++i, mask >>= 1)
1347 if (mask & 1)
1349 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1350 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1352 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
1353 cell_guids.creatures.insert(guid);
1358 void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
1360 uint8 mask = data->spawnMask;
1361 for (uint8 i = 0; mask != 0; ++i, mask >>= 1)
1363 if (mask & 1)
1365 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1366 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1368 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
1369 cell_guids.creatures.erase(guid);
1374 void ObjectMgr::LoadGameObjects()
1376 uint32 count = 0;
1378 // 0 1 2 3 4 5 6
1379 QueryResult* result = WorldDatabase.Query("SELECT gameobject.guid, gameobject.id, map, position_x, position_y, position_z, orientation,"
1380 // 7 8 9 10 11 12 13 14 15 16
1381 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event,"
1382 // 17 18
1383 "pool_gameobject.pool_entry, pool_gameobject_template.pool_entry "
1384 "FROM gameobject "
1385 "LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1386 "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid "
1387 "LEFT OUTER JOIN pool_gameobject_template ON gameobject.id = pool_gameobject_template.id");
1389 if (!result)
1391 BarGoLink bar(1);
1393 bar.step();
1395 sLog.outString();
1396 sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1397 return;
1400 // build single time for check spawnmask
1401 std::map<uint32, uint32> spawnMasks;
1402 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
1403 if (sMapStore.LookupEntry(i))
1404 for (int k = 0; k < MAX_DIFFICULTY; ++k)
1405 if (GetMapDifficultyData(i, Difficulty(k)))
1406 spawnMasks[i] |= (1 << k);
1407 // Map 0 was removed from dbc as of 4.x.x
1408 spawnMasks[0] = 1 << REGULAR_DIFFICULTY;
1410 BarGoLink bar(result->GetRowCount());
1414 Field* fields = result->Fetch();
1415 bar.step();
1417 uint32 guid = fields[ 0].GetUInt32();
1418 uint32 entry = fields[ 1].GetUInt32();
1420 GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
1421 if (!gInfo)
1423 sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
1424 continue;
1427 if (!gInfo->displayId)
1429 switch (gInfo->type)
1431 // can be invisible always and then not req. display id in like case
1432 case GAMEOBJECT_TYPE_TRAP:
1433 case GAMEOBJECT_TYPE_SPELL_FOCUS:
1434 break;
1435 default:
1436 sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have displayId == 0 and then will always invisible in game.", guid, entry, gInfo->type);
1437 break;
1440 else if (!sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
1442 sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
1443 continue;
1446 GameObjectData& data = mGameObjectDataMap[guid];
1448 data.id = entry;
1449 data.mapid = fields[ 2].GetUInt32();
1450 data.posX = fields[ 3].GetFloat();
1451 data.posY = fields[ 4].GetFloat();
1452 data.posZ = fields[ 5].GetFloat();
1453 data.orientation = fields[ 6].GetFloat();
1454 data.rotation.x = fields[ 7].GetFloat();
1455 data.rotation.y = fields[ 8].GetFloat();
1456 data.rotation.z = fields[ 9].GetFloat();
1457 data.rotation.w = fields[10].GetFloat();
1458 data.spawntimesecs = fields[11].GetInt32();
1459 data.animprogress = fields[12].GetUInt32();
1460 uint32 go_state = fields[13].GetUInt32();
1461 data.spawnMask = fields[14].GetUInt8();
1462 data.phaseMask = fields[15].GetUInt16();
1463 int16 gameEvent = fields[16].GetInt16();
1464 int16 GuidPoolId = fields[17].GetInt16();
1465 int16 EntryPoolId = fields[18].GetInt16();
1467 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
1468 if (!mapEntry)
1470 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that spawned at nonexistent map (Id: %u), skip", guid, data.id, data.mapid);
1471 continue;
1474 if (data.spawnMask & ~spawnMasks[data.mapid])
1475 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid);
1477 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
1479 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.", guid, data.id);
1482 if (go_state >= MAX_GO_STATE)
1484 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
1485 continue;
1487 data.go_state = GOState(go_state);
1489 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
1491 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.x (%f) value, skip", guid, data.id, data.rotation.x);
1492 continue;
1495 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
1497 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.y (%f) value, skip", guid, data.id, data.rotation.y);
1498 continue;
1501 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
1503 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.z (%f) value, skip", guid, data.id, data.rotation.z);
1504 continue;
1507 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
1509 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.w (%f) value, skip", guid, data.id, data.rotation.w);
1510 continue;
1513 if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
1515 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
1516 continue;
1519 if (data.phaseMask == 0)
1521 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
1522 data.phaseMask = 1;
1525 if (gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1526 AddGameobjectToGrid(guid, &data);
1528 ++count;
1531 while (result->NextRow());
1533 delete result;
1535 sLog.outString();
1536 sLog.outString(">> Loaded " SIZEFMTD " gameobjects", mGameObjectDataMap.size());
1539 void ObjectMgr::LoadGameObjectAddon()
1541 sGameObjectDataAddonStorage.Load();
1543 sLog.outString(">> Loaded %u gameobject addons", sGameObjectDataAddonStorage.GetRecordCount());
1544 sLog.outString();
1546 for (uint32 i = 1; i < sGameObjectDataAddonStorage.GetMaxEntry(); ++i)
1548 GameObjectDataAddon const* addon = sGameObjectDataAddonStorage.LookupEntry<GameObjectDataAddon>(i);
1549 if (!addon)
1550 continue;
1552 if (!GetGODataPair(addon->guid))
1554 sLog.outErrorDb("Gameobject (GUID: %u) does not exist but has a record in `gameobject_addon`", addon->guid);
1555 continue;
1558 if (!addon->path_rotation.isUnit())
1560 sLog.outErrorDb("Gameobject (GUID: %u) has invalid path rotation", addon->guid);
1561 const_cast<GameObjectDataAddon*>(addon)->path_rotation = QuaternionData(0.f, 0.f, 0.f, 1.f);
1566 void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
1568 uint8 mask = data->spawnMask;
1569 for (uint8 i = 0; mask != 0; ++i, mask >>= 1)
1571 if (mask & 1)
1573 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1574 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1576 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
1577 cell_guids.gameobjects.insert(guid);
1582 void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
1584 uint8 mask = data->spawnMask;
1585 for (uint8 i = 0; mask != 0; ++i, mask >>= 1)
1587 if (mask & 1)
1589 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1590 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1592 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
1593 cell_guids.gameobjects.erase(guid);
1598 // name must be checked to correctness (if received) before call this function
1599 ObjectGuid ObjectMgr::GetPlayerGuidByName(std::string name) const
1601 ObjectGuid guid;
1603 CharacterDatabase.escape_string(name);
1605 // Player name safe to sending to DB (checked at login) and this function using
1606 QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
1607 if (result)
1609 guid = ObjectGuid(HIGHGUID_PLAYER, (*result)[0].GetUInt32());
1611 delete result;
1614 return guid;
1617 bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string& name) const
1619 // prevent DB access for online player
1620 if (Player* player = GetPlayer(guid))
1622 name = player->GetName();
1623 return true;
1626 uint32 lowguid = guid.GetCounter();
1628 QueryResult* result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", lowguid);
1630 if (result)
1632 name = (*result)[0].GetCppString();
1633 delete result;
1634 return true;
1637 return false;
1640 Team ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const
1642 // prevent DB access for online player
1643 if (Player* player = GetPlayer(guid))
1644 return Player::TeamForRace(player->getRace());
1646 uint32 lowguid = guid.GetCounter();
1648 QueryResult* result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", lowguid);
1650 if (result)
1652 uint8 race = (*result)[0].GetUInt8();
1653 delete result;
1654 return Player::TeamForRace(race);
1657 return TEAM_NONE;
1660 uint32 ObjectMgr::GetPlayerAccountIdByGUID(ObjectGuid guid) const
1662 if (!guid.IsPlayer())
1663 return 0;
1665 // prevent DB access for online player
1666 if (Player* player = GetPlayer(guid))
1667 return player->GetSession()->GetAccountId();
1669 uint32 lowguid = guid.GetCounter();
1671 QueryResult* result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", lowguid);
1672 if (result)
1674 uint32 acc = (*result)[0].GetUInt32();
1675 delete result;
1676 return acc;
1679 return 0;
1682 uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
1684 QueryResult* result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
1685 if (result)
1687 uint32 acc = (*result)[0].GetUInt32();
1688 delete result;
1689 return acc;
1692 return 0;
1695 void ObjectMgr::LoadItemLocales()
1697 mItemLocaleMap.clear(); // need for reload case
1699 QueryResult* result = WorldDatabase.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
1701 if (!result)
1703 BarGoLink bar(1);
1705 bar.step();
1707 sLog.outString();
1708 sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
1709 return;
1712 BarGoLink bar(result->GetRowCount());
1716 Field* fields = result->Fetch();
1717 bar.step();
1719 uint32 entry = fields[0].GetUInt32();
1721 if (!GetItemPrototype(entry))
1723 ERROR_DB_STRICT_LOG("Table `locales_item` has data for nonexistent item entry %u, skipped.", entry);
1724 continue;
1727 ItemLocale& data = mItemLocaleMap[entry];
1729 for (int i = 1; i < MAX_LOCALE; ++i)
1731 std::string str = fields[1 + 2 * (i - 1)].GetCppString();
1732 if (!str.empty())
1734 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1735 if (idx >= 0)
1737 if ((int32)data.Name.size() <= idx)
1738 data.Name.resize(idx + 1);
1740 data.Name[idx] = str;
1744 str = fields[1 + 2 * (i - 1) + 1].GetCppString();
1745 if (!str.empty())
1747 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1748 if (idx >= 0)
1750 if ((int32)data.Description.size() <= idx)
1751 data.Description.resize(idx + 1);
1753 data.Description[idx] = str;
1758 while (result->NextRow());
1760 delete result;
1762 sLog.outString();
1763 sLog.outString(">> Loaded " SIZEFMTD " Item locale strings", mItemLocaleMap.size());
1766 struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader, SQLStorage>
1768 template<class D>
1769 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
1771 dst = D(sScriptMgr.GetScriptId(src));
1775 void ObjectMgr::LoadItemPrototypes()
1777 SQLItemLoader loader;
1778 loader.Load(sItemStorage);
1779 sLog.outString(">> Loaded %u item prototypes", sItemStorage.GetRecordCount());
1780 sLog.outString();
1782 // check data correctness
1783 for (uint32 i = 1; i < sItemStorage.GetMaxEntry(); ++i)
1785 ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
1786 ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
1787 if(!proto)
1789 /* to many errors, and possible not all items really used in game
1790 if (dbcitem)
1791 sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
1793 continue;
1796 if (dbcitem)
1798 if(proto->Class != dbcitem->Class)
1800 sLog.outErrorDb("Item (Entry: %u) not correct class %u, must be %u (still using DB value).",i,proto->Class,dbcitem->Class);
1801 // It safe let use Class from DB
1803 /* disabled: have some strange wrong cases for Subclass values.
1804 for enable also uncomment Subclass field in ItemEntry structure and in Itemfmt[]
1805 if(proto->SubClass != dbcitem->SubClass)
1807 sLog.outErrorDb("Item (Entry: %u) not correct (Class: %u, Sub: %u) pair, must be (Class: %u, Sub: %u) (still using DB value).",i,proto->Class,proto->SubClass,dbcitem->Class,dbcitem->SubClass);
1808 // It safe let use Subclass from DB
1812 if(proto->Unk0 != dbcitem->Unk0)
1814 sLog.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i,proto->Unk0,dbcitem->Unk0);
1815 // It safe let use Unk0 from DB
1818 if(proto->Material != dbcitem->Material)
1820 sLog.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i,proto->Material,dbcitem->Material);
1821 // It safe let use Material from DB
1824 if(proto->InventoryType != dbcitem->InventoryType)
1826 sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
1827 // It safe let use InventoryType from DB
1830 if(proto->DisplayInfoID != dbcitem->DisplayId)
1832 sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
1833 const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
1835 if(proto->Sheath != dbcitem->Sheath)
1837 sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i,proto->Sheath,dbcitem->Sheath);
1838 const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
1841 else
1843 sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existing items).", i);
1846 if (proto->Class >= MAX_ITEM_CLASS)
1848 sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)", i, proto->Class);
1849 const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC;
1852 if (proto->SubClass >= MaxItemSubclassValues[proto->Class])
1854 sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u", i, proto->SubClass, proto->Class);
1855 const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
1858 if (proto->Quality >= MAX_ITEM_QUALITY)
1860 sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)", i, proto->Quality);
1861 const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
1864 if (proto->Flags2 & ITEM_FLAG2_HORDE_ONLY)
1866 if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE))
1867 if ((proto->AllowableRace & faction->BaseRepRaceMask[0]) == 0)
1868 sLog.outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAG2_HORDE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.",
1869 i, proto->AllowableRace, ITEM_FLAG2_HORDE_ONLY);
1871 if (proto->Flags2 & ITEM_FLAG2_ALLIANCE_ONLY)
1872 sLog.outErrorDb("Item (Entry: %u) have in `Flags2` flags ITEM_FLAG2_ALLIANCE_ONLY (%u) and ITEM_FLAG2_HORDE_ONLY (%u) in Flags field, this is wrong combination.",
1873 i, ITEM_FLAG2_ALLIANCE_ONLY, ITEM_FLAG2_HORDE_ONLY);
1875 else if (proto->Flags2 & ITEM_FLAG2_ALLIANCE_ONLY)
1877 if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE))
1878 if ((proto->AllowableRace & faction->BaseRepRaceMask[0]) == 0)
1879 sLog.outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAG2_ALLIANCE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.",
1880 i, proto->AllowableRace, ITEM_FLAG2_ALLIANCE_ONLY);
1883 if (proto->BuyCount <= 0)
1885 sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", i, proto->BuyCount);
1886 const_cast<ItemPrototype*>(proto)->BuyCount = 1;
1889 if (proto->InventoryType >= MAX_INVTYPE)
1891 sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)", i, proto->InventoryType);
1892 const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
1895 if (proto->InventoryType != INVTYPE_NON_EQUIP)
1897 if (proto->Flags & ITEM_FLAG_LOOTABLE)
1899 sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_LOOTABLE (%u), flag removed.", i, ITEM_FLAG_LOOTABLE);
1900 const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_LOOTABLE;
1903 if (proto->Flags & ITEM_FLAG_MILLABLE)
1905 sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_MILLABLE (%u), flag removed.", i, ITEM_FLAG_MILLABLE);
1906 const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_MILLABLE;
1909 if (proto->Flags & ITEM_FLAG_PROSPECTABLE)
1911 sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_PROSPECTABLE (%u), flag removed.", i, ITEM_FLAG_PROSPECTABLE);
1912 const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_PROSPECTABLE;
1915 else if (proto->InventoryType != INVTYPE_BAG)
1917 if (proto->ContainerSlots > 0)
1919 sLog.outErrorDb("Non-container item (Entry: %u) has ContainerSlots (%u), set to 0.", i, proto->ContainerSlots);
1920 const_cast<ItemPrototype*>(proto)->ContainerSlots = 0;
1924 if (proto->RequiredSkill >= MAX_SKILL_TYPE)
1926 sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)", i, proto->RequiredSkill);
1927 const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
1931 // can be used in equip slot, as page read use in inventory, or spell casting at use
1932 bool req = proto->InventoryType != INVTYPE_NON_EQUIP || proto->PageText;
1933 if (!req)
1935 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
1937 if (proto->Spells[j].SpellId)
1939 req = true;
1940 break;
1945 if (req)
1947 if (!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
1948 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.", i, proto->AllowableClass);
1950 if (!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
1951 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.", i, proto->AllowableRace);
1955 if (proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
1957 sLog.outErrorDb("Item (Entry: %u) have wrong (nonexistent) spell in RequiredSpell (%u)", i, proto->RequiredSpell);
1958 const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
1961 if (proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
1962 sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", i, proto->RequiredReputationRank);
1964 if (proto->RequiredReputationFaction)
1966 if (!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
1968 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", i, proto->RequiredReputationFaction);
1969 const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
1972 if (proto->RequiredReputationRank == MIN_REPUTATION_RANK)
1973 sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", i);
1975 else if (proto->RequiredReputationRank > MIN_REPUTATION_RANK)
1976 sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.", i);
1978 if (proto->MaxCount < -1)
1980 sLog.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.", i, proto->MaxCount);
1981 const_cast<ItemPrototype*>(proto)->MaxCount = -1;
1984 if (proto->Stackable == 0)
1986 sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.", i, proto->Stackable);
1987 const_cast<ItemPrototype*>(proto)->Stackable = 1;
1989 else if (proto->Stackable < -1)
1991 sLog.outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.", i, proto->Stackable);
1992 const_cast<ItemPrototype*>(proto)->Stackable = -1;
1994 else if (proto->Stackable > 1000)
1996 sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (1000).", i, proto->Stackable);
1997 const_cast<ItemPrototype*>(proto)->Stackable = 1000;
2000 if (proto->ContainerSlots)
2002 if (proto->ContainerSlots > MAX_BAG_SIZE)
2004 sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", i, proto->ContainerSlots, MAX_BAG_SIZE);
2005 const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
2009 for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j)
2011 // for ItemStatValue != 0
2012 if (proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
2014 sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
2015 const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
2018 switch (proto->ItemStat[j].ItemStatType)
2020 case ITEM_MOD_HEALTH:
2021 case ITEM_MOD_DEFENSE_SKILL_RATING:
2022 case ITEM_MOD_BLOCK_RATING:
2023 case ITEM_MOD_HIT_MELEE_RATING:
2024 case ITEM_MOD_HIT_RANGED_RATING:
2025 case ITEM_MOD_HIT_SPELL_RATING:
2026 case ITEM_MOD_CRIT_MELEE_RATING:
2027 case ITEM_MOD_CRIT_SPELL_RATING:
2028 case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
2029 case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
2030 case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
2031 case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
2032 case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
2033 case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
2034 case ITEM_MOD_HASTE_MELEE_RATING:
2035 case ITEM_MOD_HASTE_RANGED_RATING:
2036 case ITEM_MOD_HASTE_SPELL_RATING:
2037 case ITEM_MOD_HIT_TAKEN_RATING:
2038 case ITEM_MOD_CRIT_TAKEN_RATING:
2039 case ITEM_MOD_FERAL_ATTACK_POWER:
2040 case ITEM_MOD_SPELL_HEALING_DONE:
2041 case ITEM_MOD_SPELL_DAMAGE_DONE:
2042 case ITEM_MOD_MANA_REGENERATION:
2043 case ITEM_MOD_ARMOR_PENETRATION_RATING:
2044 case ITEM_MOD_BLOCK_VALUE:
2045 sLog.outErrorDb("Item (Entry: %u) has deprecated stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
2046 break;
2047 default:
2048 break;
2052 if (proto->DamageType >= MAX_SPELL_SCHOOL)
2054 sLog.outErrorDb("Item (Entry: %u) has wrong damagetype (%u)", i, proto->DamageType);
2055 const_cast<ItemPrototype*>(proto)->DamageType = 0;
2058 // special format
2059 if ((proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET))
2061 // spell_1
2062 if (proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
2064 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format", i, 0 + 1, proto->Spells[0].SpellTrigger);
2065 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
2066 const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2067 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
2068 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2071 // spell_2 have learning spell
2072 if (proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
2074 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.", i, 1 + 1, proto->Spells[1].SpellTrigger);
2075 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
2076 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
2077 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2079 else if (!proto->Spells[1].SpellId)
2081 sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.", i, 1 + 1);
2082 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
2083 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2085 else
2087 SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
2088 if (!spellInfo)
2090 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)", i, 1 + 1, proto->Spells[1].SpellId);
2091 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
2092 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
2093 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2095 // allowed only in special format
2096 else if ((proto->Spells[1].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[1].SpellId == SPELL_ID_GENERIC_LEARN_PET))
2098 sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i, 1 + 1, proto->Spells[1].SpellId);
2099 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
2100 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
2101 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2105 // spell_3*,spell_4*,spell_5* is empty
2106 for (int j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j)
2108 if (proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
2110 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
2111 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2112 const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2114 else if (proto->Spells[j].SpellId != 0)
2116 sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format", i, j + 1, proto->Spells[j].SpellId);
2117 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2121 // normal spell list
2122 else
2124 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
2126 if (proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
2128 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
2129 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2130 const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2132 // on hit can be sued only at weapon
2133 else if (proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
2135 if (proto->Class != ITEM_CLASS_WEAPON)
2136 sLog.outErrorDb("Item (Entry: %u) isn't weapon (Class: %u) but has on hit spelltrigger_%d (%u), it will not triggered.", i, proto->Class, j + 1, proto->Spells[j].SpellTrigger);
2139 if (proto->Spells[j].SpellId)
2141 SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
2142 if (!spellInfo)
2144 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)", i, j + 1, proto->Spells[j].SpellId);
2145 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2147 // allowed only in special format
2148 else if ((proto->Spells[j].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[j].SpellId == SPELL_ID_GENERIC_LEARN_PET))
2150 sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i, j + 1, proto->Spells[j].SpellId);
2151 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2157 if (proto->Bonding >= MAX_BIND_TYPE)
2158 sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)", i, proto->Bonding);
2160 if (proto->PageText)
2162 if (!sPageTextStore.LookupEntry<PageText>(proto->PageText))
2163 sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i, proto->PageText);
2166 if (proto->LockID && !sLockStore.LookupEntry(proto->LockID))
2167 sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)", i, proto->LockID);
2169 if (proto->Sheath >= MAX_SHEATHETYPE)
2171 sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)", i, proto->Sheath);
2172 const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
2175 if (proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
2177 sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", i, proto->RandomProperty);
2178 const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
2181 if (proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
2183 sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)", i, proto->RandomSuffix);
2184 const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
2187 // item can have not null only one from field values
2188 if (proto->RandomProperty && proto->RandomSuffix)
2190 sLog.outErrorDb("Item (Entry: %u) have RandomProperty==%u and RandomSuffix==%u, but must have one from field = 0",
2191 proto->ItemId, proto->RandomProperty, proto->RandomSuffix);
2192 const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
2195 if (proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
2197 sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)", i, proto->ItemSet);
2198 const_cast<ItemPrototype*>(proto)->ItemSet = 0;
2201 if (proto->Area && !GetAreaEntryByAreaID(proto->Area))
2202 sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)", i, proto->Area);
2204 if (proto->Map && !sMapStore.LookupEntry(proto->Map))
2205 sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)", i, proto->Map);
2207 if (proto->BagFamily)
2209 // check bits
2210 for (uint32 j = 0; j < sizeof(proto->BagFamily) * 8; ++j)
2212 uint32 mask = 1 << j;
2213 if (!(proto->BagFamily & mask))
2214 continue;
2216 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j + 1);
2217 if (!bf)
2219 sLog.outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", i);
2220 const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
2221 continue;
2224 /*if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask)
2226 CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId);
2227 if(!ctEntry)
2229 sLog.outErrorDb("Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit",i);
2230 const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
2236 if (proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
2237 sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)", i, proto->TotemCategory);
2239 for (int j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
2241 if (proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
2243 sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)", i, j + 1, proto->Socket[j].Color);
2244 const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
2248 if (proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
2249 sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)", i, proto->GemProperties);
2251 if (proto->RequiredDisenchantSkill < -1)
2253 sLog.outErrorDb("Item (Entry: %u) has wrong RequiredDisenchantSkill (%i), set to (-1).", i, proto->RequiredDisenchantSkill);
2254 const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
2256 else if (proto->RequiredDisenchantSkill != -1)
2258 if (proto->Quality > ITEM_QUALITY_EPIC || proto->Quality < ITEM_QUALITY_UNCOMMON)
2260 ERROR_DB_STRICT_LOG("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable quality (%u), reset it.",
2261 i, proto->RequiredDisenchantSkill, proto->Quality);
2262 const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
2264 else if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR)
2266 // some wrong data in wdb for unused items
2267 ERROR_DB_STRICT_LOG("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable item class (%u), reset it.",
2268 i, proto->RequiredDisenchantSkill, proto->Class);
2269 const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
2273 if (proto->DisenchantID)
2275 if (proto->Quality > ITEM_QUALITY_EPIC || proto->Quality < ITEM_QUALITY_UNCOMMON)
2277 sLog.outErrorDb("Item (Entry: %u) has wrong quality (%u) for disenchanting, remove disenchanting loot id.", i, proto->Quality);
2278 const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
2280 else if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR)
2282 sLog.outErrorDb("Item (Entry: %u) has wrong item class (%u) for disenchanting, remove disenchanting loot id.", i, proto->Class);
2283 const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
2285 else if (proto->RequiredDisenchantSkill < 0)
2287 sLog.outErrorDb("Item (Entry: %u) marked as non-disenchantable by RequiredDisenchantSkill == -1, remove disenchanting loot id.", i);
2288 const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
2291 else
2293 // lot DB cases
2294 if (proto->RequiredDisenchantSkill >= 0)
2295 ERROR_DB_STRICT_LOG("Item (Entry: %u) marked as disenchantable by RequiredDisenchantSkill, but not have disenchanting loot id.", i);
2298 if (proto->FoodType >= MAX_PET_DIET)
2300 sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)", i, proto->FoodType);
2301 const_cast<ItemPrototype*>(proto)->FoodType = 0;
2304 if (proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory))
2306 sLog.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)", i, proto->ItemLimitCategory);
2307 const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0;
2310 if (proto->HolidayId && !sHolidaysStore.LookupEntry(proto->HolidayId))
2312 sLog.outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i, proto->HolidayId);
2313 const_cast<ItemPrototype*>(proto)->HolidayId = 0;
2316 if (proto->ExtraFlags)
2318 if (proto->ExtraFlags & ~ITEM_EXTRA_ALL)
2319 sLog.outErrorDb("Item (Entry: %u) has wrong ExtraFlags (%u) with unused bits set", i, proto->ExtraFlags);
2321 if (proto->ExtraFlags & ITEM_EXTRA_NON_CONSUMABLE)
2323 bool can_be_need = false;
2324 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
2326 if (proto->Spells[j].SpellCharges < 0)
2328 can_be_need = true;
2329 break;
2333 if (!can_be_need)
2335 sLog.outErrorDb("Item (Entry: %u) has redundant non-consumable flag in ExtraFlags, item not have negative charges", i);
2336 const_cast<ItemPrototype*>(proto)->ExtraFlags &= ~ITEM_EXTRA_NON_CONSUMABLE;
2340 if (proto->ExtraFlags & ITEM_EXTRA_REAL_TIME_DURATION)
2342 if (proto->Duration == 0)
2344 sLog.outErrorDb("Item (Entry: %u) has redundant real-time duration flag in ExtraFlags, item not have duration", i);
2345 const_cast<ItemPrototype*>(proto)->ExtraFlags &= ~ITEM_EXTRA_REAL_TIME_DURATION;
2351 // check some dbc referenced items (avoid duplicate reports)
2352 std::set<uint32> notFoundOutfit;
2353 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
2355 CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
2356 if (!entry)
2357 continue;
2359 for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
2361 if (entry->ItemId[j] <= 0)
2362 continue;
2364 uint32 item_id = entry->ItemId[j];
2366 if (!GetItemPrototype(item_id))
2367 if (item_id != 40582) // nonexistent item by default but referenced in DBC, skip it from errors
2368 notFoundOutfit.insert(item_id);
2372 for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
2373 sLog.outErrorDb("Item (Entry: %u) not exist in `item_template` but referenced in `CharStartOutfit.dbc`", *itr);
2376 void ObjectMgr::LoadItemConverts()
2378 m_ItemConvert.clear(); // needed for reload case
2380 uint32 count = 0;
2382 QueryResult* result = WorldDatabase.Query("SELECT entry,item FROM item_convert");
2384 if (!result)
2386 BarGoLink bar(1);
2388 bar.step();
2390 sLog.outString();
2391 sLog.outErrorDb(">> Loaded 0 Item converts . DB table `item_convert` is empty.");
2392 return;
2395 BarGoLink bar(result->GetRowCount());
2399 Field* fields = result->Fetch();
2400 bar.step();
2402 uint32 itemEntry = fields[0].GetUInt32();
2403 uint32 itemTargetId = fields[1].GetUInt32();
2405 ItemPrototype const* pItemEntryProto = sItemStorage.LookupEntry<ItemPrototype>(itemEntry);
2406 if (!pItemEntryProto)
2408 sLog.outErrorDb("Table `item_convert`: Item %u not exist in `item_template`.", itemEntry);
2409 continue;
2412 ItemPrototype const* pItemTargetProto = sItemStorage.LookupEntry<ItemPrototype>(itemTargetId);
2413 if (!pItemTargetProto)
2415 sLog.outErrorDb("Table `item_convert`: Item target %u for original item %u not exist in `item_template`.", itemTargetId, itemEntry);
2416 continue;
2419 // 2 cases when item convert used
2420 // Boa item with reputation requirement
2421 if ((!(pItemEntryProto->Flags & ITEM_FLAG_BOA) || !pItemEntryProto->RequiredReputationFaction) &&
2422 // convertion to another team/race
2423 (pItemTargetProto->AllowableRace & pItemEntryProto->AllowableRace))
2425 sLog.outErrorDb("Table `item_convert` not appropriate item %u conversion to %u. Table can be used for BoA items requirement drop or for conversion to another race/team use.", itemEntry, itemTargetId);
2426 continue;
2429 m_ItemConvert[itemEntry] = itemTargetId;
2431 ++count;
2433 while (result->NextRow());
2435 delete result;
2437 sLog.outString();
2438 sLog.outString(">> Loaded %u Item converts", count);
2441 void ObjectMgr::LoadItemExpireConverts()
2443 m_ItemExpireConvert.clear(); // needed for reload case
2445 uint32 count = 0;
2447 QueryResult* result = WorldDatabase.Query("SELECT entry,item FROM item_expire_convert");
2449 if (!result)
2451 BarGoLink bar(1);
2453 bar.step();
2455 sLog.outString();
2456 sLog.outErrorDb(">> Loaded 0 Item expire converts . DB table `item_expire_convert` is empty.");
2457 return;
2460 BarGoLink bar(result->GetRowCount());
2464 Field* fields = result->Fetch();
2465 bar.step();
2467 uint32 itemEntry = fields[0].GetUInt32();
2468 uint32 itemTargetId = fields[1].GetUInt32();
2470 ItemPrototype const* pItemEntryProto = sItemStorage.LookupEntry<ItemPrototype>(itemEntry);
2471 if (!pItemEntryProto)
2473 sLog.outErrorDb("Table `item_expire_convert`: Item %u not exist in `item_template`.", itemEntry);
2474 continue;
2477 ItemPrototype const* pItemTargetProto = sItemStorage.LookupEntry<ItemPrototype>(itemTargetId);
2478 if (!pItemTargetProto)
2480 sLog.outErrorDb("Table `item_expire_convert`: Item target %u for original item %u not exist in `item_template`.", itemTargetId, itemEntry);
2481 continue;
2484 // Expire convert possible only for items with duration
2485 if (pItemEntryProto->Duration == 0)
2487 sLog.outErrorDb("Table `item_expire_convert` not appropriate item %u conversion to %u. Table can be used for items with duration.", itemEntry, itemTargetId);
2488 continue;
2491 m_ItemExpireConvert[itemEntry] = itemTargetId;
2493 ++count;
2495 while (result->NextRow());
2497 delete result;
2499 sLog.outString();
2500 sLog.outString(">> Loaded %u Item expire converts", count);
2503 void ObjectMgr::LoadItemRequiredTarget()
2505 m_ItemRequiredTarget.clear(); // needed for reload case
2507 uint32 count = 0;
2509 QueryResult* result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target");
2511 if (!result)
2513 BarGoLink bar(1);
2515 bar.step();
2517 sLog.outString();
2518 sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty.");
2519 return;
2522 BarGoLink bar(result->GetRowCount());
2526 Field* fields = result->Fetch();
2527 bar.step();
2529 uint32 uiItemId = fields[0].GetUInt32();
2530 uint32 uiType = fields[1].GetUInt32();
2531 uint32 uiTargetEntry = fields[2].GetUInt32();
2533 ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId);
2535 if (!pItemProto)
2537 sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.", uiItemId, uiTargetEntry);
2538 continue;
2541 bool bIsItemSpellValid = false;
2543 for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
2545 if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId))
2547 if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
2549 SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(pSpellInfo->Id);
2550 if (bounds.first != bounds.second)
2551 break;
2553 for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
2555 SpellEffectEntry const* spellEffect = pSpellInfo->GetSpellEffect(SpellEffectIndex(j));
2556 if(!spellEffect)
2557 continue;
2559 if (spellEffect->EffectImplicitTargetA == TARGET_CHAIN_DAMAGE ||
2560 spellEffect->EffectImplicitTargetB == TARGET_CHAIN_DAMAGE ||
2561 spellEffect->EffectImplicitTargetA == TARGET_DUELVSPLAYER ||
2562 spellEffect->EffectImplicitTargetB == TARGET_DUELVSPLAYER)
2564 bIsItemSpellValid = true;
2565 break;
2568 if (bIsItemSpellValid)
2569 break;
2574 if (!bIsItemSpellValid)
2576 sLog.outErrorDb("Table `item_required_target`: Spell used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in `spell_script_target` or doesn't have item spelltrigger.", uiItemId);
2577 continue;
2580 if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE)
2582 sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.", uiType, uiTargetEntry);
2583 continue;
2586 if (!uiTargetEntry)
2588 sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).", uiType);
2589 continue;
2592 if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry))
2594 sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.", uiTargetEntry);
2595 continue;
2598 m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId, ItemRequiredTarget(ItemRequiredTargetType(uiType), uiTargetEntry)));
2600 ++count;
2602 while (result->NextRow());
2604 delete result;
2606 sLog.outString();
2607 sLog.outString(">> Loaded %u Item required targets", count);
2610 void ObjectMgr::LoadPetLevelInfo()
2612 // Loading levels data
2614 // 0 1 2 3 4 5 6 7 8 9
2615 QueryResult* result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2617 uint32 count = 0;
2619 if (!result)
2621 BarGoLink bar(1);
2622 bar.step();
2624 sLog.outString();
2625 sLog.outString(">> Loaded %u level pet stats definitions", count);
2626 sLog.outErrorDb("Error loading `pet_levelstats` table or empty table.");
2627 return;
2630 BarGoLink bar(result->GetRowCount());
2634 Field* fields = result->Fetch();
2636 uint32 creature_id = fields[0].GetUInt32();
2637 if (!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
2639 sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
2640 continue;
2643 uint32 current_level = fields[1].GetUInt32();
2644 if (current_level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2646 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2647 sLog.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
2648 else
2650 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
2651 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2653 continue;
2655 else if (current_level < 1)
2657 sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
2658 continue;
2661 PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
2663 if (pInfoMapEntry == NULL)
2664 pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)];
2666 // data for level 1 stored in [0] array element, ...
2667 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
2669 pLevelInfo->health = fields[2].GetUInt16();
2670 pLevelInfo->mana = fields[3].GetUInt16();
2671 pLevelInfo->armor = fields[9].GetUInt16();
2673 for (int i = 0; i < MAX_STATS; ++i)
2675 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
2678 bar.step();
2679 ++count;
2681 while (result->NextRow());
2683 delete result;
2685 sLog.outString();
2686 sLog.outString(">> Loaded %u level pet stats definitions", count);
2689 // Fill gaps and check integrity
2690 for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
2692 PetLevelInfo* pInfo = itr->second;
2694 // fatal error if no level 1 data
2695 if (!pInfo || pInfo[0].health == 0)
2697 sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!", itr->first);
2698 Log::WaitBeforeContinueIfNeed();
2699 exit(1);
2702 // fill level gaps
2703 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
2705 if (pInfo[level].health == 0)
2707 sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
2708 pInfo[level] = pInfo[level - 1];
2714 PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
2716 if (level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2717 level = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL);
2719 PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
2720 if (itr == petInfo.end())
2721 return NULL;
2723 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
2726 void ObjectMgr::LoadPlayerInfo()
2728 // Load playercreate
2730 // 0 1 2 3 4 5 6 7 8
2731 QueryResult* result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation, phaseMap FROM playercreateinfo");
2733 uint32 count = 0;
2735 if (!result)
2737 BarGoLink bar(1);
2739 sLog.outString();
2740 sLog.outString(">> Loaded %u player create definitions", count);
2741 sLog.outErrorDb("Error loading `playercreateinfo` table or empty table.");
2742 Log::WaitBeforeContinueIfNeed();
2743 exit(1);
2746 BarGoLink bar(result->GetRowCount());
2750 Field* fields = result->Fetch();
2752 uint32 current_race = fields[0].GetUInt32();
2753 uint32 current_class = fields[1].GetUInt32();
2754 uint32 mapId = fields[2].GetUInt32();
2755 uint32 areaId = fields[3].GetUInt32();
2756 float positionX = fields[4].GetFloat();
2757 float positionY = fields[5].GetFloat();
2758 float positionZ = fields[6].GetFloat();
2759 float orientation = fields[7].GetFloat();
2760 uint16 phaseMap = fields[8].GetUInt16();
2762 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2763 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2765 sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
2766 continue;
2769 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2770 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2772 sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
2773 continue;
2776 // accept DB data only for valid position (and non instanceable)
2777 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
2779 sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
2780 continue;
2783 if (sMapStore.LookupEntry(mapId)->Instanceable())
2785 sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
2786 continue;
2789 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2791 pInfo->mapId = mapId;
2792 pInfo->areaId = areaId;
2793 pInfo->positionX = positionX;
2794 pInfo->positionY = positionY;
2795 pInfo->positionZ = positionZ;
2796 pInfo->orientation = orientation;
2797 pInfo->phaseMap = phaseMap;
2799 pInfo->displayId_m = rEntry->model_m;
2800 pInfo->displayId_f = rEntry->model_f;
2802 bar.step();
2803 ++count;
2805 while (result->NextRow());
2807 delete result;
2809 sLog.outString();
2810 sLog.outString(">> Loaded %u player create definitions", count);
2813 // Load playercreate items
2815 // 0 1 2 3
2816 QueryResult* result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2818 uint32 count = 0;
2820 if (!result)
2822 BarGoLink bar(1);
2824 bar.step();
2826 sLog.outString();
2827 sLog.outString(">> Loaded %u custom player create items", count);
2829 else
2831 BarGoLink bar(result->GetRowCount());
2835 Field* fields = result->Fetch();
2837 uint32 current_race = fields[0].GetUInt32();
2838 uint32 current_class = fields[1].GetUInt32();
2840 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2841 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2843 sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
2844 continue;
2847 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2848 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2850 sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
2851 continue;
2854 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2856 uint32 item_id = fields[2].GetUInt32();
2858 if (!GetItemPrototype(item_id))
2860 sLog.outErrorDb("Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.", item_id, current_race, current_class);
2861 continue;
2864 uint32 amount = fields[3].GetUInt32();
2866 if (!amount)
2868 sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
2869 continue;
2872 pInfo->item.push_back(PlayerCreateInfoItem(item_id, amount));
2874 bar.step();
2875 ++count;
2877 while (result->NextRow());
2879 delete result;
2881 sLog.outString();
2882 sLog.outString(">> Loaded %u custom player create items", count);
2886 // Load playercreate spells
2888 // 0 1 2
2889 QueryResult* result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell");
2891 uint32 count = 0;
2893 if (!result)
2895 BarGoLink bar(1);
2897 sLog.outString();
2898 sLog.outString(">> Loaded %u player create spells", count);
2899 sLog.outErrorDb("Error loading `playercreateinfo_spell` table or empty table.");
2901 else
2903 BarGoLink bar(result->GetRowCount());
2907 Field* fields = result->Fetch();
2909 uint32 current_race = fields[0].GetUInt32();
2910 uint32 current_class = fields[1].GetUInt32();
2912 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2913 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2915 sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.", current_race);
2916 continue;
2919 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2920 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2922 sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.", current_class);
2923 continue;
2926 uint32 spell_id = fields[2].GetUInt32();
2927 if (!sSpellStore.LookupEntry(spell_id))
2929 sLog.outErrorDb("Non existing spell %u in `playercreateinfo_spell` table, ignoring.", spell_id);
2930 continue;
2933 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2934 pInfo->spell.push_back(spell_id);
2936 bar.step();
2937 ++count;
2939 while (result->NextRow());
2941 delete result;
2943 sLog.outString();
2944 sLog.outString(">> Loaded %u player create spells", count);
2948 // Load playercreate actions
2950 // 0 1 2 3 4
2951 QueryResult* result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
2953 uint32 count = 0;
2955 if (!result)
2957 BarGoLink bar(1);
2959 sLog.outString();
2960 sLog.outString(">> Loaded %u player create actions", count);
2961 sLog.outErrorDb("Error loading `playercreateinfo_action` table or empty table.");
2963 else
2965 BarGoLink bar(result->GetRowCount());
2969 Field* fields = result->Fetch();
2971 uint32 current_race = fields[0].GetUInt32();
2972 uint32 current_class = fields[1].GetUInt32();
2974 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2975 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2977 sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
2978 continue;
2981 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2982 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2984 sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
2985 continue;
2988 uint8 action_button = fields[2].GetUInt8();
2989 uint32 action = fields[3].GetUInt32();
2990 uint8 action_type = fields[4].GetUInt8();
2992 if (!Player::IsActionButtonDataValid(action_button, action, action_type, NULL))
2993 continue;
2995 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2996 pInfo->action.push_back(PlayerCreateInfoAction(action_button, action, action_type));
2998 bar.step();
2999 ++count;
3001 while (result->NextRow());
3003 delete result;
3005 sLog.outString();
3006 sLog.outString(">> Loaded %u player create actions", count);
3010 // Loading levels data (class/race dependent)
3012 // 0 1 2 3 4 5 6 7
3013 QueryResult* result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
3015 uint32 count = 0;
3017 if (!result)
3019 BarGoLink bar(1);
3021 sLog.outString();
3022 sLog.outString(">> Loaded %u level stats definitions", count);
3023 sLog.outErrorDb("Error loading `player_levelstats` table or empty table.");
3024 Log::WaitBeforeContinueIfNeed();
3025 exit(1);
3028 BarGoLink bar(result->GetRowCount());
3032 Field* fields = result->Fetch();
3034 uint32 current_race = fields[0].GetUInt32();
3035 uint32 current_class = fields[1].GetUInt32();
3037 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
3038 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
3040 sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.", current_race);
3041 continue;
3044 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
3045 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
3047 sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.", current_class);
3048 continue;
3051 uint32 current_level = fields[2].GetUInt32();
3052 if (current_level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3054 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3055 sLog.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3056 else
3058 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.", current_level);
3059 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3061 continue;
3064 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
3066 if (!pInfo->levelInfo)
3067 pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)];
3069 PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level - 1];
3071 for (int i = 0; i < MAX_STATS; ++i)
3072 pLevelInfo->stats[i] = fields[i + 3].GetUInt8();
3074 bar.step();
3075 ++count;
3077 while (result->NextRow());
3079 delete result;
3081 sLog.outString();
3082 sLog.outString(">> Loaded %u level stats definitions", count);
3085 // Fill gaps and check integrity
3086 for (int race = 0; race < MAX_RACES; ++race)
3088 // skip nonexistent races
3089 if (!((1 << (race - 1)) & RACEMASK_ALL_PLAYABLE) || !sChrRacesStore.LookupEntry(race))
3090 continue;
3092 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
3094 // skip nonexistent classes
3095 if (!((1 << (class_ - 1)) & CLASSMASK_ALL_PLAYABLE) || !sChrClassesStore.LookupEntry(class_))
3096 continue;
3098 PlayerInfo* pInfo = &playerInfo[race][class_];
3100 // skip non loaded combinations
3101 if (!pInfo->displayId_m || !pInfo->displayId_f)
3102 continue;
3104 // skip expansion races if not playing with expansion
3105 if (sWorld.getConfig(CONFIG_UINT32_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
3106 continue;
3108 // skip expansion classes if not playing with expansion
3109 if (sWorld.getConfig(CONFIG_UINT32_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
3110 continue;
3112 // skip expansion races if not playing with expansion
3113 if (sWorld.getConfig(CONFIG_UINT32_EXPANSION) < 3 && (race == RACE_WORGEN || race == RACE_GOBLIN))
3114 continue;
3116 // fatal error if no level 1 data
3117 if (!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0)
3119 sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!", race, class_);
3120 Log::WaitBeforeContinueIfNeed();
3121 exit(1);
3124 // fill level gaps
3125 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3127 if (pInfo->levelInfo[level].stats[0] == 0)
3129 sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
3130 pInfo->levelInfo[level] = pInfo->levelInfo[level - 1];
3136 // Loading xp per level data
3138 mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL));
3139 for (uint32 level = 0; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3140 mPlayerXPperLevel[level] = 0;
3142 // 0 1
3143 QueryResult* result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
3145 uint32 count = 0;
3147 if (!result)
3149 BarGoLink bar(1);
3151 sLog.outString();
3152 sLog.outString(">> Loaded %u xp for level definitions", count);
3153 sLog.outErrorDb("Error loading `player_xp_for_level` table or empty table.");
3154 Log::WaitBeforeContinueIfNeed();
3155 exit(1);
3158 BarGoLink bar(result->GetRowCount());
3162 Field* fields = result->Fetch();
3164 uint32 current_level = fields[0].GetUInt32();
3165 uint32 current_xp = fields[1].GetUInt32();
3167 if (current_level >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3169 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3170 sLog.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3171 else
3173 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
3174 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3176 continue;
3178 // PlayerXPperLevel
3179 mPlayerXPperLevel[current_level] = current_xp;
3180 bar.step();
3181 ++count;
3183 while (result->NextRow());
3185 delete result;
3187 sLog.outString();
3188 sLog.outString(">> Loaded %u xp for level definitions", count);
3191 // fill level gaps
3192 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3194 if (mPlayerXPperLevel[level] == 0)
3196 sLog.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
3197 mPlayerXPperLevel[level] = mPlayerXPperLevel[level - 1] + 100;
3202 void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, uint32& baseHP, uint32& baseMana) const
3204 if (level < 1 || class_ >= MAX_CLASSES)
3205 return;
3207 if (level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3208 level = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL);
3210 GtOCTBaseHPByClassEntry const* hp = sGtOCTBaseHPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1);
3211 GtOCTBaseMPByClassEntry const* mp = sGtOCTBaseMPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1);
3213 if (!hp || !mp)
3215 sLog.outError("Tried to get non-existant Class-Level combination data for base hp/mp. Class %u Level %u", class_, level);
3216 return;
3219 baseHP = uint32(hp->ratio);
3220 baseMana = uint32(mp->ratio);
3223 void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
3225 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
3226 return;
3228 PlayerInfo const* pInfo = &playerInfo[race][class_];
3229 if (pInfo->displayId_m == 0 || pInfo->displayId_f == 0)
3230 return;
3232 if (level <= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3233 *info = pInfo->levelInfo[level - 1];
3234 else
3235 BuildPlayerLevelInfo(race, class_, level, info);
3238 void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
3240 // base data (last known level)
3241 *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) - 1];
3243 for (int lvl = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
3245 switch (_class)
3247 case CLASS_WARRIOR:
3248 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3249 info->stats[STAT_STAMINA] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3250 info->stats[STAT_AGILITY] += (lvl > 36 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3251 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3252 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3253 break;
3254 case CLASS_PALADIN:
3255 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1 : 0);
3256 info->stats[STAT_STAMINA] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3257 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 7 && !(lvl % 2) ? 1 : 0));
3258 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl % 2) ? 1 : 0);
3259 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1 : 0);
3260 break;
3261 case CLASS_HUNTER:
3262 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1 : 0);
3263 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3264 info->stats[STAT_AGILITY] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3265 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl % 2) ? 1 : 0);
3266 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3267 break;
3268 case CLASS_ROGUE:
3269 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1 : 0);
3270 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3271 info->stats[STAT_AGILITY] += (lvl > 16 ? 2 : (lvl > 1 ? 1 : 0));
3272 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl % 2) ? 1 : 0);
3273 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3274 break;
3275 case CLASS_PRIEST:
3276 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3277 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3278 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3279 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2 : (lvl > 1 ? 1 : 0));
3280 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1 : 0);
3281 break;
3282 case CLASS_SHAMAN:
3283 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3284 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3285 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl % 2) ? 1 : 0);
3286 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1 : 0);
3287 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1 : 0);
3288 break;
3289 case CLASS_MAGE:
3290 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3291 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3292 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3293 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2 : (lvl > 1 ? 1 : 0));
3294 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3295 break;
3296 case CLASS_WARLOCK:
3297 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3298 info->stats[STAT_STAMINA] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3299 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3300 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3301 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3302 break;
3303 case CLASS_DRUID:
3304 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3305 info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
3306 info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3307 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
3308 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
3313 /* ********************************************************************************************* */
3314 /* * Static Wrappers */
3315 /* ********************************************************************************************* */
3316 GameObjectInfo const* ObjectMgr::GetGameObjectInfo(uint32 id) { return sGOStorage.LookupEntry<GameObjectInfo>(id); }
3317 Player* ObjectMgr::GetPlayer(const char* name) { return ObjectAccessor::FindPlayerByName(name); }
3318 Player* ObjectMgr::GetPlayer(ObjectGuid guid, bool inWorld /*=true*/) { return ObjectAccessor::FindPlayer(guid, inWorld); }
3319 CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id) { return sCreatureStorage.LookupEntry<CreatureInfo>(id); }
3320 CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid) { return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid); }
3321 EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry) { return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry); }
3322 CreatureDataAddon const* ObjectMgr::GetCreatureAddon(uint32 lowguid) { return sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(lowguid); }
3323 CreatureDataAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry) { return sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(entry); }
3324 ItemPrototype const* ObjectMgr::GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry<ItemPrototype>(id); }
3325 InstanceTemplate const* ObjectMgr::GetInstanceTemplate(uint32 map) { return sInstanceTemplate.LookupEntry<InstanceTemplate>(map); }
3326 WorldTemplate const* ObjectMgr::GetWorldTemplate(uint32 map) { return sWorldTemplate.LookupEntry<WorldTemplate>(map); }
3328 /* ********************************************************************************************* */
3329 /* * Loading Functions */
3330 /* ********************************************************************************************* */
3331 void ObjectMgr::LoadArenaTeams()
3333 uint32 count = 0;
3335 // 0 1 2 3 4 5
3336 QueryResult* result = CharacterDatabase.Query("SELECT arena_team.arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,"
3337 // 6 7 8 9 10 11 12 13 14
3338 "EmblemColor,BorderStyle,BorderColor, rating,games_week,wins_week,games_season,wins_season,rank "
3339 "FROM arena_team LEFT JOIN arena_team_stats ON arena_team.arenateamid = arena_team_stats.arenateamid ORDER BY arena_team.arenateamid ASC");
3341 if (!result)
3344 BarGoLink bar(1);
3346 bar.step();
3348 sLog.outString();
3349 sLog.outString(">> Loaded %u arenateam definitions", count);
3350 return;
3353 // load arena_team members
3354 QueryResult* arenaTeamMembersResult = CharacterDatabase.Query(
3355 // 0 1 2 3 4 5 6 7 8
3356 "SELECT arenateamid,member.guid,played_week,wons_week,played_season,wons_season,personal_rating,name,class "
3357 "FROM arena_team_member member LEFT JOIN characters chars on member.guid = chars.guid ORDER BY member.arenateamid ASC");
3359 BarGoLink bar(result->GetRowCount());
3364 bar.step();
3365 ++count;
3367 ArenaTeam* newArenaTeam = new ArenaTeam;
3368 if (!newArenaTeam->LoadArenaTeamFromDB(result) ||
3369 !newArenaTeam->LoadMembersFromDB(arenaTeamMembersResult))
3371 newArenaTeam->Disband(NULL);
3372 delete newArenaTeam;
3373 continue;
3375 AddArenaTeam(newArenaTeam);
3377 while (result->NextRow());
3379 delete result;
3380 delete arenaTeamMembersResult;
3382 sLog.outString();
3383 sLog.outString(">> Loaded %u arenateam definitions", count);
3386 void ObjectMgr::LoadGroups()
3388 // -- loading groups --
3389 uint32 count = 0;
3390 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
3391 QueryResult* result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty, leaderGuid, groupId FROM groups");
3393 if (!result)
3395 BarGoLink bar(1);
3397 bar.step();
3399 sLog.outString();
3400 sLog.outString(">> Loaded %u group definitions", count);
3401 return;
3404 BarGoLink bar(result->GetRowCount());
3408 bar.step();
3409 Field* fields = result->Fetch();
3410 ++count;
3411 Group* group = new Group;
3412 if (!group->LoadGroupFromDB(fields))
3414 group->Disband();
3415 delete group;
3416 continue;
3418 AddGroup(group);
3420 while (result->NextRow());
3422 delete result;
3424 sLog.outString();
3425 sLog.outString(">> Loaded %u group definitions", count);
3427 // -- loading members --
3428 count = 0;
3429 // 0 1 2 3
3430 result = CharacterDatabase.Query("SELECT memberGuid, assistant, subgroup, groupId FROM group_member ORDER BY groupId");
3431 if (!result)
3433 BarGoLink bar2(1);
3434 bar2.step();
3436 else
3438 Group* group = NULL; // used as cached pointer for avoid relookup group for each member
3440 BarGoLink bar2(result->GetRowCount());
3443 bar2.step();
3444 Field* fields = result->Fetch();
3445 ++count;
3447 uint32 memberGuidlow = fields[0].GetUInt32();
3448 ObjectGuid memberGuid = ObjectGuid(HIGHGUID_PLAYER, memberGuidlow);
3449 bool assistent = fields[1].GetBool();
3450 uint8 subgroup = fields[2].GetUInt8();
3451 uint32 groupId = fields[3].GetUInt32();
3452 if (!group || group->GetId() != groupId)
3454 group = GetGroupById(groupId);
3455 if (!group)
3457 sLog.outErrorDb("Incorrect entry in group_member table : no group with Id %d for member %s!",
3458 groupId, memberGuid.GetString().c_str());
3459 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow);
3460 continue;
3464 if (!group->LoadMemberFromDB(memberGuidlow, subgroup, assistent))
3466 sLog.outErrorDb("Incorrect entry in group_member table : member %s cannot be added to group (Id: %u)!",
3467 memberGuid.GetString().c_str(), groupId);
3468 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow);
3471 while (result->NextRow());
3472 delete result;
3475 // clean groups
3476 // TODO: maybe delete from the DB before loading in this case
3477 for (GroupMap::iterator itr = mGroupMap.begin(); itr != mGroupMap.end();)
3479 if (itr->second->GetMembersCount() < 2)
3481 itr->second->Disband();
3482 delete itr->second;
3483 mGroupMap.erase(itr++);
3485 else
3486 ++itr;
3489 // -- loading instances --
3490 count = 0;
3491 result = CharacterDatabase.Query(
3492 // 0 1 2 3 4 5
3493 "SELECT group_instance.leaderGuid, map, instance, permanent, instance.difficulty, resettime, "
3494 // 6
3495 "(SELECT COUNT(*) FROM character_instance WHERE guid = group_instance.leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1), "
3496 // 7 8
3497 " groups.groupId, instance.encountersMask "
3498 "FROM group_instance LEFT JOIN instance ON instance = id LEFT JOIN groups ON groups.leaderGUID = group_instance.leaderGUID ORDER BY leaderGuid"
3501 if (!result)
3503 BarGoLink bar2(1);
3504 bar2.step();
3506 else
3508 Group* group = NULL; // used as cached pointer for avoid relookup group for each member
3510 BarGoLink bar2(result->GetRowCount());
3513 bar2.step();
3514 Field* fields = result->Fetch();
3515 ++count;
3517 uint32 leaderGuidLow = fields[0].GetUInt32();
3518 uint32 mapId = fields[1].GetUInt32();
3519 Difficulty diff = (Difficulty)fields[4].GetUInt8();
3520 uint32 groupId = fields[7].GetUInt32();
3522 if (!group || group->GetId() != groupId)
3524 // find group id in map by leader low guid
3525 group = GetGroupById(groupId);
3526 if (!group)
3528 sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", leaderGuidLow);
3529 continue;
3533 MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
3534 if (!mapEntry || !mapEntry->IsDungeon())
3536 sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", mapId);
3537 continue;
3540 if (diff >= (mapEntry->IsRaid() ? MAX_RAID_DIFFICULTY : MAX_DUNGEON_DIFFICULTY))
3542 sLog.outErrorDb("Wrong dungeon difficulty use in group_instance table: %d", diff + 1);
3543 diff = REGULAR_DIFFICULTY; // default for both difficaly types
3546 DungeonPersistentState* state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, fields[2].GetUInt32(), Difficulty(diff), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true, true, fields[8].GetUInt32());
3547 group->BindToInstance(state, fields[3].GetBool(), true);
3549 while (result->NextRow());
3550 delete result;
3553 sLog.outString();
3554 sLog.outString(">> Loaded %u group-instance binds total", count);
3556 sLog.outString();
3557 sLog.outString(">> Loaded %u group members total", count);
3560 void ObjectMgr::LoadQuests()
3562 // For reload case
3563 for (QuestMap::const_iterator itr = mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
3564 delete itr->second;
3566 mQuestTemplates.clear();
3568 m_ExclusiveQuestGroups.clear();
3570 // 0 1 2 3 4 5 6 7 8 9
3571 QueryResult* result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, MinLevel, QuestLevel, Type, RequiredClasses, RequiredRaces, RequiredSkill, RequiredSkillValue,"
3572 // 10 11 12 13 14 15 16 17
3573 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
3574 // 18 19 20 21 22 23 24 25 26
3575 "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain,"
3576 // 27 28 29 30
3577 "RewXPId, SrcItemId, SrcItemCount, SrcSpell,"
3578 // 31 32 33 34 35 36 37 38 39 40 41
3579 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, CompletedText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
3580 // 42 43 44 45 46 47 48 49 50 51 52 53
3581 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemId5, ReqItemId6, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4, ReqItemCount5, ReqItemCount6,"
3582 // 54 55 56 57 58 59 60 61
3583 "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4,"
3584 // 62 63 64 65 66 67 68 69
3585 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
3586 // 70 71 72 73
3587 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
3588 // 74 75 76 77 78 79
3589 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
3590 // 80 81 82 83 84 85
3591 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
3592 // 86 87 88 89 90 91 92 93
3593 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
3594 // 94 95 96 97 98
3595 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5,"
3596 // 99 100 101 102 103
3597 "RewRepValueId1, RewRepValueId2, RewRepValueId3, RewRepValueId4, RewRepValueId5,"
3598 // 104 105 106 107 108
3599 "RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
3600 // 109 110 111 112 113 114
3601 "RewHonorAddition, RewHonorMultiplier, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast,"
3602 // 115 116 117 118 119 120
3603 "RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
3604 // 121 122 123 124 125 126 127 128
3605 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4,"
3606 // 129 130 131 132 133 134
3607 "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
3608 // 135 136 137 138
3609 "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4,"
3610 // 139 140
3611 "StartScript, CompleteScript, "
3612 // 141 142 143 144 145 146 147
3613 "ReqSpellLearned, PortraitGiver, PortraitTurnIn, PortraitGiverName, PortraitGiverText, PortraitTurnInName, PortraitTurnInText, "
3614 // 148 149 150 151 152 153 154 155
3615 "ReqCurrencyId1, ReqCurrencyId2, ReqCurrencyId3, ReqCurrencyId4, ReqCurrencyCount1, ReqCurrencyCount2, ReqCurrencyCount3, ReqCurrencyCount4, "
3616 // 156 157 158 159 160 161 162 163
3617 "RewCurrencyId1, RewCurrencyId2, RewCurrencyId3, RewCurrencyId4, RewCurrencyCount1, RewCurrencyCount2, RewCurrencyCount3, RewCurrencyCount4, "
3618 // 164 165 166 167
3619 "RewSkill, RewSkillValue, SoundAccept, SoundTurnIn "
3620 " FROM quest_template");
3621 if (!result)
3623 BarGoLink bar(1);
3624 bar.step();
3626 sLog.outString();
3627 sLog.outString(">> Loaded 0 quests definitions");
3628 sLog.outErrorDb("`quest_template` table is empty!");
3629 return;
3632 // create multimap previous quest for each existing quest
3633 // some quests can have many previous maps set by NextQuestId in previous quest
3634 // for example set of race quests can lead to single not race specific quest
3635 BarGoLink bar(result->GetRowCount());
3638 bar.step();
3639 Field* fields = result->Fetch();
3641 Quest* newQuest = new Quest(fields);
3642 mQuestTemplates[newQuest->GetQuestId()] = newQuest;
3644 while (result->NextRow());
3646 delete result;
3648 // Post processing
3650 std::map<uint32, uint32> usedMailTemplates;
3652 for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
3654 Quest* qinfo = iter->second;
3656 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3658 if (qinfo->GetQuestMethod() >= 3)
3660 sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
3663 if (qinfo->m_SpecialFlags > QUEST_SPECIAL_FLAG_DB_ALLOWED)
3665 sLog.outErrorDb("Quest %u has `SpecialFlags` = %u, above max flags not allowed for database.", qinfo->GetQuestId(), qinfo->m_SpecialFlags);
3668 if (qinfo->HasQuestFlag(QUEST_FLAGS_DAILY) && qinfo->HasQuestFlag(QUEST_FLAGS_WEEKLY))
3670 sLog.outErrorDb("Weekly Quest %u is marked as daily quest in `QuestFlags`, removed daily flag.", qinfo->GetQuestId());
3671 qinfo->m_QuestFlags &= ~QUEST_FLAGS_DAILY;
3674 if (qinfo->HasQuestFlag(QUEST_FLAGS_DAILY))
3676 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE))
3678 sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3679 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE);
3683 if (qinfo->HasQuestFlag(QUEST_FLAGS_WEEKLY))
3685 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE))
3687 sLog.outErrorDb("Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3688 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE);
3692 if (qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_MONTHLY))
3694 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE))
3696 sLog.outErrorDb("Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3697 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE);
3701 if (qinfo->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED))
3703 // at auto-reward can be rewarded only RewChoiceItemId[0]
3704 for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
3706 if (uint32 id = qinfo->RewChoiceItemId[j])
3708 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3709 qinfo->GetQuestId(), j + 1, id, j + 1);
3710 // no changes, quest ignore this data
3715 // client quest log visual (area case)
3716 if (qinfo->ZoneOrSort > 0)
3718 if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
3720 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
3721 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3722 // no changes, quest not dependent from this value but can have problems at client
3725 // client quest log visual (sort case)
3726 if (qinfo->ZoneOrSort < 0)
3728 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
3729 if (!qSort)
3731 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
3732 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3733 // no changes, quest not dependent from this value but can have problems at client (note some may be 0, we must allow this so no check)
3737 // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
3738 if (qinfo->RequiredClasses)
3740 if (!(qinfo->RequiredClasses & CLASSMASK_ALL_PLAYABLE))
3742 sLog.outErrorDb("Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->RequiredClasses);
3743 qinfo->RequiredClasses = 0;
3747 // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
3748 if (qinfo->RequiredRaces)
3750 if (!(qinfo->RequiredRaces & RACEMASK_ALL_PLAYABLE))
3752 sLog.outErrorDb("Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->RequiredRaces);
3753 qinfo->RequiredRaces = 0;
3757 // RequiredSkill, can be 0
3758 if (qinfo->RequiredSkill)
3760 if (!sSkillLineStore.LookupEntry(qinfo->RequiredSkill))
3762 sLog.outErrorDb("Quest %u has `RequiredSkill` = %u but this skill does not exist",
3763 qinfo->GetQuestId(), qinfo->RequiredSkill);
3767 if (qinfo->RequiredSkillValue)
3769 if (qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue())
3771 sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
3772 qinfo->GetQuestId(), qinfo->RequiredSkillValue, sWorld.GetConfigMaxSkillValue());
3773 // no changes, quest can't be done for this requirement
3776 // else Skill quests can have 0 skill level, this is ok
3778 if (qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
3780 sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
3781 qinfo->GetQuestId(), qinfo->RepObjectiveFaction, qinfo->RepObjectiveFaction);
3782 // no changes, quest can't be done for this requirement
3785 if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
3787 sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3788 qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
3789 // no changes, quest can't be done for this requirement
3792 if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
3794 sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3795 qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
3796 // no changes, quest can't be done for this requirement
3799 if (qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
3801 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
3802 qinfo->GetQuestId(), qinfo->RequiredMinRepValue, ReputationMgr::Reputation_Cap);
3803 // no changes, quest can't be done for this requirement
3806 if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
3808 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
3809 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
3810 // no changes, quest can't be done for this requirement
3813 if (!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0)
3815 sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
3816 qinfo->GetQuestId(), qinfo->RepObjectiveValue);
3817 // warning
3820 if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0)
3822 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
3823 qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
3824 // warning
3827 if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0)
3829 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3830 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
3831 // warning
3834 if (qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
3836 sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
3837 qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
3838 qinfo->CharTitleId = 0;
3839 // quest can't reward this title
3842 if (qinfo->SrcItemId)
3844 if (!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
3846 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
3847 qinfo->GetQuestId(), qinfo->SrcItemId, qinfo->SrcItemId);
3848 qinfo->SrcItemId = 0; // quest can't be done for this requirement
3850 else if (qinfo->SrcItemCount == 0)
3852 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
3853 qinfo->GetQuestId(), qinfo->SrcItemId);
3854 qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward compatibility with DB
3857 else if (qinfo->SrcItemCount > 0)
3859 sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
3860 qinfo->GetQuestId(), qinfo->SrcItemCount);
3861 qinfo->SrcItemCount = 0; // no quest work changes in fact
3864 if (qinfo->SrcSpell)
3866 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
3867 if (!spellInfo)
3869 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
3870 qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3871 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3873 else if (!SpellMgr::IsSpellValid(spellInfo))
3875 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
3876 qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3877 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3881 for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
3883 if (uint32 id = qinfo->ReqItemId[j])
3885 if (qinfo->ReqItemCount[j] == 0)
3887 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
3888 qinfo->GetQuestId(), j + 1, id, j + 1);
3889 // no changes, quest can't be done for this requirement
3892 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER);
3894 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3896 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3897 qinfo->GetQuestId(), j + 1, id, id);
3898 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3901 else if (qinfo->ReqItemCount[j] > 0)
3903 sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
3904 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqItemCount[j]);
3905 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3909 for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
3911 if (uint32 id = qinfo->ReqSourceId[j])
3913 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3915 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
3916 qinfo->GetQuestId(), j + 1, id, id);
3917 // no changes, quest can't be done for this requirement
3920 else
3922 if (qinfo->ReqSourceCount[j] > 0)
3924 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
3925 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqSourceCount[j]);
3926 // no changes, quest ignore this data
3931 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3933 if (uint32 id = qinfo->ReqSpell[j])
3935 SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
3936 if (!spellInfo)
3938 sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3939 qinfo->GetQuestId(), j + 1, id, id);
3940 continue;
3943 if (!qinfo->ReqCreatureOrGOId[j])
3945 bool found = false;
3946 for (int k = 0; k < MAX_EFFECT_INDEX; ++k)
3948 SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(k));
3949 if(!spellEffect)
3950 continue;
3952 if ((spellEffect->Effect == SPELL_EFFECT_QUEST_COMPLETE && uint32(spellEffect->EffectMiscValue) == qinfo->QuestId) ||
3953 spellEffect->Effect == SPELL_EFFECT_SEND_EVENT)
3955 found = true;
3956 break;
3960 if (found)
3962 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
3964 sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->QuestId, j + 1, j + 1);
3966 // this will prevent quest completing without objective
3967 const_cast<Quest*>(qinfo)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
3970 else
3972 sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest can't be done.",
3973 qinfo->GetQuestId(), j + 1, id, j + 1, id);
3974 // no changes, quest can't be done for this requirement
3980 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3982 int32 id = qinfo->ReqCreatureOrGOId[j];
3983 if (id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
3985 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
3986 qinfo->GetQuestId(), j + 1, id, uint32(-id));
3987 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3990 if (id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
3992 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
3993 qinfo->GetQuestId(), j + 1, id, uint32(id));
3994 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3997 if (id)
3999 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
4001 qinfo->SetSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO));
4003 if (!qinfo->ReqCreatureOrGOCount[j])
4005 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
4006 qinfo->GetQuestId(), j + 1, id, j + 1);
4007 // no changes, quest can be incorrectly done, but we already report this
4010 else if (qinfo->ReqCreatureOrGOCount[j] > 0)
4012 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
4013 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqCreatureOrGOCount[j]);
4014 // no changes, quest ignore this data
4018 bool choice_found = false;
4019 for (int j = QUEST_REWARD_CHOICES_COUNT - 1; j >= 0; --j)
4021 if (uint32 id = qinfo->RewChoiceItemId[j])
4023 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
4025 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4026 qinfo->GetQuestId(), j + 1, id, id);
4027 qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
4029 else
4030 choice_found = true;
4032 if (!qinfo->RewChoiceItemCount[j])
4034 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
4035 qinfo->GetQuestId(), j + 1, id, j + 1);
4036 // no changes, quest can't be done
4039 else if (choice_found) // client crash if have gap in item reward choices
4041 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemId%d` = %u, client can crash at like data.",
4042 qinfo->GetQuestId(), j + 1, j + 2, qinfo->RewChoiceItemId[j + 1]);
4043 // fill gap by clone later filled choice
4044 qinfo->RewChoiceItemId[j] = qinfo->RewChoiceItemId[j + 1];
4045 qinfo->RewChoiceItemCount[j] = qinfo->RewChoiceItemCount[j + 1];
4047 else if (qinfo->RewChoiceItemCount[j] > 0)
4049 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
4050 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewChoiceItemCount[j]);
4051 // no changes, quest ignore this data
4055 for (int j = 0; j < QUEST_REWARDS_COUNT; ++j)
4057 if (uint32 id = qinfo->RewItemId[j])
4059 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
4061 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4062 qinfo->GetQuestId(), j + 1, id, id);
4063 qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
4066 if (!qinfo->RewItemCount[j])
4068 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
4069 qinfo->GetQuestId(), j + 1, id, j + 1);
4070 // no changes
4073 else if (qinfo->RewItemCount[j] > 0)
4075 sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
4076 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewItemCount[j]);
4077 // no changes, quest ignore this data
4081 for (int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
4083 if (qinfo->RewRepFaction[j])
4085 if (abs(qinfo->RewRepValueId[j]) > 9)
4086 sLog.outErrorDb("Quest %u has RewRepValueId%d = %i but value is not valid.", qinfo->GetQuestId(), j + 1, qinfo->RewRepValueId[j]);
4088 if (!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
4090 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
4091 qinfo->GetQuestId(), j + 1, qinfo->RewRepFaction[j] , qinfo->RewRepFaction[j]);
4092 qinfo->RewRepFaction[j] = 0; // quest will not reward this
4095 else if (qinfo->RewRepValue[j] != 0)
4097 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %i.",
4098 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewRepValue[j]);
4099 // no changes, quest ignore this data
4103 if (qinfo->RewSpell)
4105 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
4107 if (!spellInfo)
4109 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
4110 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
4111 qinfo->RewSpell = 0; // no spell reward will display for this quest
4113 else if (!SpellMgr::IsSpellValid(spellInfo))
4115 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.",
4116 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
4117 qinfo->RewSpell = 0; // no spell reward will display for this quest
4119 else if (GetTalentSpellCost(qinfo->RewSpell))
4121 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4122 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
4123 qinfo->RewSpell = 0; // no spell reward will display for this quest
4127 if (qinfo->RewSpellCast)
4129 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
4131 if (!spellInfo)
4133 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
4134 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
4135 qinfo->RewSpellCast = 0; // no spell will be casted on player
4137 else if (!SpellMgr::IsSpellValid(spellInfo))
4139 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
4140 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
4141 qinfo->RewSpellCast = 0; // no spell will be casted on player
4143 else if (GetTalentSpellCost(qinfo->RewSpellCast))
4145 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4146 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
4147 qinfo->RewSpellCast = 0; // no spell will be casted on player
4151 if (qinfo->RewMailTemplateId)
4153 if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
4155 sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
4156 qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId);
4157 qinfo->RewMailTemplateId = 0; // no mail will send to player
4158 qinfo->RewMailDelaySecs = 0; // no mail will send to player
4160 else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
4162 std::map<uint32, uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
4163 sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
4164 qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId, used_mt_itr->second);
4165 qinfo->RewMailTemplateId = 0; // no mail will send to player
4166 qinfo->RewMailDelaySecs = 0; // no mail will send to player
4168 else
4169 usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
4172 if (qinfo->NextQuestInChain)
4174 QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
4175 if (qNextItr == mQuestTemplates.end())
4177 sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
4178 qinfo->GetQuestId(), qinfo->NextQuestInChain , qinfo->NextQuestInChain);
4179 qinfo->NextQuestInChain = 0;
4181 else
4182 qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
4185 // fill additional data stores
4186 if (qinfo->PrevQuestId)
4188 if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
4190 sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
4192 else
4194 qinfo->prevQuests.push_back(qinfo->PrevQuestId);
4198 if (qinfo->NextQuestId)
4200 QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
4201 if (qNextItr == mQuestTemplates.end())
4203 sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
4205 else
4207 int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
4208 qNextItr->second->prevQuests.push_back(signedQuestId);
4212 if (qinfo->RewSkill)
4214 if (!sSkillLineStore.LookupEntry(qinfo->RewSkill))
4216 sLog.outErrorDb("Quest %u has `RewSkill` = %u but this skill does not exist",
4217 qinfo->GetQuestId(), qinfo->RewSkill);
4218 qinfo->RewSkill = 0;
4219 qinfo->RewSkillValue = 0;
4223 if (qinfo->RewSkillValue)
4225 if (qinfo->RewSkillValue > sWorld.GetConfigMaxSkillValue())
4227 sLog.outErrorDb("Quest %u has `RewSkillValue` = %u which is more than max possible skill value %u.",
4228 qinfo->GetQuestId(), qinfo->RewSkillValue, sWorld.GetConfigMaxSkillValue());
4231 else
4233 if (qinfo->RewSkill)
4235 sLog.outErrorDb("Quest %u has `RewSkillValue` = %u, but `RewSkill` exists and is %u.",
4236 qinfo->GetQuestId(), qinfo->RewSkillValue, qinfo->RewSkill);
4237 qinfo->RewSkill = 0;
4241 if (qinfo->ReqSpellLearned)
4243 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->ReqSpellLearned);
4245 if (!spellInfo)
4247 sLog.outErrorDb("Quest %u has `ReqSpellLearned` = %u but spell %u does not exist, quest will not have a spell requirement.",
4248 qinfo->GetQuestId(), qinfo->ReqSpellLearned, qinfo->ReqSpellLearned);
4249 qinfo->ReqSpellLearned = 0;
4251 else if (!SpellMgr::IsSpellValid(spellInfo))
4253 sLog.outErrorDb("Quest %u has `ReqSpellLearned` = %u but spell %u is broken, quest will not have a spell requirement.",
4254 qinfo->GetQuestId(), qinfo->ReqSpellLearned, qinfo->ReqSpellLearned);
4255 qinfo->ReqSpellLearned = 0;
4259 for (int j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j)
4261 if (qinfo->ReqCurrencyId[j])
4263 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER);
4265 CurrencyTypesEntry const * currencyEntry = sCurrencyTypesStore.LookupEntry(qinfo->ReqCurrencyId[j]);
4266 if (!currencyEntry)
4268 sLog.outErrorDb("Quest %u has `ReqCurrencyId%d` = %u but currency with entry %u does not exist, quest can not be completed.",
4269 qinfo->GetQuestId(), j + 1, qinfo->ReqCurrencyId[j], qinfo->ReqCurrencyId[j]);
4270 qinfo->ReqCurrencyId[j] = 0;
4271 qinfo->ReqCurrencyCount[j] = 0;
4273 else
4275 if (!qinfo->ReqCurrencyCount[j])
4277 sLog.outErrorDb("Quest %u has `ReqCurrencyId%d` = %u but `ReqCurrencyCount%d` = %u.",
4278 qinfo->GetQuestId(), j + 1, qinfo->ReqCurrencyId[j], j + 1, qinfo->ReqCurrencyCount[j]);
4279 qinfo->ReqCurrencyId[j] = 0;
4281 else if (currencyEntry->TotalCap && qinfo->ReqCurrencyCount[j] > currencyEntry->TotalCap)
4283 sLog.outErrorDb("Quest %u has `ReqCurrencyCount%d` = %u but currency %u has max count %u.",
4284 qinfo->GetQuestId(), j + 1, qinfo->ReqCurrencyCount[j], qinfo->ReqCurrencyId[j], currencyEntry->TotalCap);
4285 qinfo->ReqCurrencyCount[j] = currencyEntry->TotalCap;
4289 else if (qinfo->ReqCurrencyCount[j])
4291 if (!qinfo->ReqCurrencyId[j])
4293 sLog.outErrorDb("Quest %u has `ReqCurrencyId%d` = 0 but `ReqCurrencyCount%d` = %u.",
4294 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqCurrencyCount[j]);
4295 qinfo->ReqCurrencyCount[j] = 0;
4300 for (int j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
4302 if (qinfo->RewCurrencyId[j])
4304 CurrencyTypesEntry const * currencyEntry = sCurrencyTypesStore.LookupEntry(qinfo->RewCurrencyId[j]);
4305 if (!currencyEntry)
4307 sLog.outErrorDb("Quest %u has `RewCurrencyId%d` = %u but currency with entry %u does not exist, quest will not reward that currency.",
4308 qinfo->GetQuestId(), j + 1, qinfo->RewCurrencyId[j], qinfo->RewCurrencyId[j]);
4309 qinfo->RewCurrencyId[j] = 0;
4310 qinfo->RewCurrencyCount[j] = 0;
4312 else if (!qinfo->RewCurrencyCount[j])
4314 sLog.outErrorDb("Quest %u has `RewCurrencyId%d` = %u but `RewCurrencyCount%d` = %u.",
4315 qinfo->GetQuestId(), j + 1, qinfo->RewCurrencyId[j], j + 1, qinfo->RewCurrencyCount[j]);
4316 qinfo->RewCurrencyId[j] = 0;
4319 else if (qinfo->RewCurrencyCount[j])
4321 if (!qinfo->RewCurrencyId[j])
4323 sLog.outErrorDb("Quest %u has `RewCurrencyId%d` = 0 but `RewCurrencyCount%d` = %u.",
4324 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewCurrencyCount[j]);
4325 qinfo->RewCurrencyCount[j] = 0;
4330 if (qinfo->SoundAcceptId)
4332 SoundEntriesEntry const * soundEntry = sSoundEntriesStore.LookupEntry(qinfo->SoundAcceptId);
4333 if (!soundEntry)
4335 sLog.outErrorDb("Quest %u has `SoundAcceptId` = %u but sound with that entry does not exists.",
4336 qinfo->GetQuestId(), qinfo->SoundAcceptId);
4337 qinfo->SoundAcceptId = 0;
4341 if (qinfo->SoundTurnInId)
4343 SoundEntriesEntry const * soundEntry = sSoundEntriesStore.LookupEntry(qinfo->SoundTurnInId);
4344 if (!soundEntry)
4346 sLog.outErrorDb("Quest %u has `SoundTurnInId` = %u but sound with that entry does not exists.",
4347 qinfo->GetQuestId(), qinfo->SoundTurnInId);
4348 qinfo->SoundTurnInId = 0;
4352 if (qinfo->ExclusiveGroup)
4353 m_ExclusiveQuestGroups.insert(ExclusiveQuestGroupsMap::value_type(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
4355 if (qinfo->LimitTime)
4356 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_TIMED);
4359 // check QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
4360 for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
4362 SpellEntry const* spellInfo = sSpellStore.LookupEntry(i);
4363 if (!spellInfo)
4364 continue;
4366 for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
4368 SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(j));
4369 if(!spellEffect)
4370 continue;
4371 if (spellEffect->Effect != SPELL_EFFECT_QUEST_COMPLETE)
4372 continue;
4374 uint32 quest_id = spellEffect->EffectMiscValue;
4376 Quest const* quest = GetQuestTemplate(quest_id);
4378 // some quest referenced in spells not exist (outdated spells)
4379 if (!quest)
4380 continue;
4382 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
4384 sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest does not have SpecialFlags QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT (2) set. Quest SpecialFlags should be corrected to enable this objective.", spellInfo->Id, quest_id);
4386 // The below forced alteration has been disabled because of spell 33824 / quest 10162.
4387 // A startup error will still occur with proper data in quest_template, but it will be possible to sucessfully complete the quest with the expected data.
4389 // this will prevent quest completing without objective
4390 // const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
4395 sLog.outString();
4396 sLog.outString(">> Loaded " SIZEFMTD " quests definitions", mQuestTemplates.size());
4399 void ObjectMgr::LoadQuestLocales()
4401 mQuestLocaleMap.clear(); // need for reload case
4403 QueryResult* result = WorldDatabase.Query("SELECT entry,"
4404 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
4405 "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,CompletedText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,PortraitGiverName_loc1,PortraitGiverText_loc1,PortraitTurnInName_loc1,PortraitTurnInText_loc1,"
4406 "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,CompletedText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,PortraitGiverName_loc2,PortraitGiverText_loc2,PortraitTurnInName_loc2,PortraitTurnInText_loc2,"
4407 "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,CompletedText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,PortraitGiverName_loc3,PortraitGiverText_loc3,PortraitTurnInName_loc3,PortraitTurnInText_loc3,"
4408 "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,CompletedText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,PortraitGiverName_loc4,PortraitGiverText_loc4,PortraitTurnInName_loc4,PortraitTurnInText_loc4,"
4409 "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,CompletedText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,PortraitGiverName_loc5,PortraitGiverText_loc5,PortraitTurnInName_loc5,PortraitTurnInText_loc5,"
4410 "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,CompletedText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,PortraitGiverName_loc6,PortraitGiverText_loc6,PortraitTurnInName_loc6,PortraitTurnInText_loc6,"
4411 "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,CompletedText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,PortraitGiverName_loc7,PortraitGiverText_loc7,PortraitTurnInName_loc7,PortraitTurnInText_loc7,"
4412 "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,CompletedText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8,PortraitGiverName_loc8,PortraitGiverText_loc8,PortraitTurnInName_loc8,PortraitTurnInText_loc8"
4413 " FROM locales_quest"
4416 if (!result)
4418 BarGoLink bar(1);
4420 bar.step();
4422 sLog.outString();
4423 sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
4424 return;
4427 BarGoLink bar(result->GetRowCount());
4431 Field* fields = result->Fetch();
4432 bar.step();
4434 uint32 entry = fields[0].GetUInt32();
4436 if (!GetQuestTemplate(entry))
4438 ERROR_DB_STRICT_LOG("Table `locales_quest` has data for nonexistent quest entry %u, skipped.", entry);
4439 continue;
4442 QuestLocale& data = mQuestLocaleMap[entry];
4444 for (int i = 1; i < MAX_LOCALE; ++i)
4446 std::string str = fields[1 + 15 * (i - 1)].GetCppString();
4447 if (!str.empty())
4449 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4450 if (idx >= 0)
4452 if ((int32)data.Title.size() <= idx)
4453 data.Title.resize(idx + 1);
4455 data.Title[idx] = str;
4458 str = fields[1 + 15 * (i - 1) + 1].GetCppString();
4459 if (!str.empty())
4461 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4462 if (idx >= 0)
4464 if ((int32)data.Details.size() <= idx)
4465 data.Details.resize(idx + 1);
4467 data.Details[idx] = str;
4470 str = fields[1 + 15 * (i - 1) + 2].GetCppString();
4471 if (!str.empty())
4473 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4474 if (idx >= 0)
4476 if ((int32)data.Objectives.size() <= idx)
4477 data.Objectives.resize(idx + 1);
4479 data.Objectives[idx] = str;
4482 str = fields[1 + 15 * (i - 1) + 3].GetCppString();
4483 if (!str.empty())
4485 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4486 if (idx >= 0)
4488 if ((int32)data.OfferRewardText.size() <= idx)
4489 data.OfferRewardText.resize(idx + 1);
4491 data.OfferRewardText[idx] = str;
4494 str = fields[1 + 15 * (i - 1) + 4].GetCppString();
4495 if (!str.empty())
4497 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4498 if (idx >= 0)
4500 if ((int32)data.RequestItemsText.size() <= idx)
4501 data.RequestItemsText.resize(idx + 1);
4503 data.RequestItemsText[idx] = str;
4506 str = fields[1 + 15 * (i - 1) + 5].GetCppString();
4507 if (!str.empty())
4509 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4510 if (idx >= 0)
4512 if ((int32)data.EndText.size() <= idx)
4513 data.EndText.resize(idx + 1);
4515 data.EndText[idx] = str;
4518 str = fields[1 + 15 * (i - 1) + 6].GetCppString();
4519 if (!str.empty())
4521 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4522 if (idx >= 0)
4524 if ((int32)data.CompletedText.size() <= idx)
4525 data.CompletedText.resize(idx + 1);
4527 data.CompletedText[idx] = str;
4530 for (int k = 0; k < 4; ++k)
4532 str = fields[1 + 15 * (i - 1) + 7 + k].GetCppString();
4533 if (!str.empty())
4535 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4536 if (idx >= 0)
4538 if ((int32)data.ObjectiveText[k].size() <= idx)
4539 data.ObjectiveText[k].resize(idx + 1);
4541 data.ObjectiveText[k][idx] = str;
4545 str = fields[1 + 15 * (i - 1) + 11].GetCppString();
4546 if (!str.empty())
4548 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4549 if (idx >= 0)
4551 if ((int32)data.PortraitGiverName.size() <= idx)
4552 data.PortraitGiverName.resize(idx + 1);
4554 data.PortraitGiverName[idx] = str;
4557 str = fields[1 + 15 * (i - 1) + 12].GetCppString();
4558 if (!str.empty())
4560 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4561 if (idx >= 0)
4563 if ((int32)data.PortraitGiverText.size() <= idx)
4564 data.PortraitGiverText.resize(idx + 1);
4566 data.PortraitGiverText[idx] = str;
4569 str = fields[1 + 15 * (i - 1) + 13].GetCppString();
4570 if (!str.empty())
4572 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4573 if (idx >= 0)
4575 if ((int32)data.PortraitTurnInName.size() <= idx)
4576 data.PortraitTurnInName.resize(idx + 1);
4578 data.PortraitTurnInName[idx] = str;
4581 str = fields[1 + 15 * (i - 1) + 14].GetCppString();
4582 if (!str.empty())
4584 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4585 if (idx >= 0)
4587 if ((int32)data.PortraitTurnInText.size() <= idx)
4588 data.PortraitTurnInText.resize(idx + 1);
4590 data.PortraitTurnInText[idx] = str;
4595 while (result->NextRow());
4597 delete result;
4599 sLog.outString();
4600 sLog.outString(">> Loaded " SIZEFMTD " Quest locale strings", mQuestLocaleMap.size());
4603 void ObjectMgr::LoadPageTexts()
4605 sPageTextStore.Load();
4606 sLog.outString(">> Loaded %u page texts", sPageTextStore.GetRecordCount());
4607 sLog.outString();
4609 for (uint32 i = 1; i < sPageTextStore.GetMaxEntry(); ++i)
4611 // check data correctness
4612 PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
4613 if (!page)
4614 continue;
4616 if (page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
4618 sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i, page->Next_Page);
4619 continue;
4622 // detect circular reference
4623 std::set<uint32> checkedPages;
4624 for (PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
4626 if (!pageItr->Next_Page)
4627 break;
4628 checkedPages.insert(pageItr->Page_ID);
4629 if (checkedPages.find(pageItr->Next_Page) != checkedPages.end())
4631 std::ostringstream ss;
4632 ss << "The text page(s) ";
4633 for (std::set<uint32>::iterator itr = checkedPages.begin(); itr != checkedPages.end(); ++itr)
4634 ss << *itr << " ";
4635 ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4636 << pageItr->Page_ID << " to 0";
4637 sLog.outErrorDb("%s", ss.str().c_str());
4638 const_cast<PageText*>(pageItr)->Next_Page = 0;
4639 break;
4645 void ObjectMgr::LoadPageTextLocales()
4647 mPageTextLocaleMap.clear(); // need for reload case
4649 QueryResult* result = WorldDatabase.Query("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
4651 if (!result)
4653 BarGoLink bar(1);
4655 bar.step();
4657 sLog.outString();
4658 sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
4659 return;
4662 BarGoLink bar(result->GetRowCount());
4666 Field* fields = result->Fetch();
4667 bar.step();
4669 uint32 entry = fields[0].GetUInt32();
4671 if (!sPageTextStore.LookupEntry<PageText>(entry))
4673 ERROR_DB_STRICT_LOG("Table `locales_page_text` has data for nonexistent page text entry %u, skipped.", entry);
4674 continue;
4677 PageTextLocale& data = mPageTextLocaleMap[entry];
4679 for (int i = 1; i < MAX_LOCALE; ++i)
4681 std::string str = fields[i].GetCppString();
4682 if (str.empty())
4683 continue;
4685 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4686 if (idx >= 0)
4688 if ((int32)data.Text.size() <= idx)
4689 data.Text.resize(idx + 1);
4691 data.Text[idx] = str;
4696 while (result->NextRow());
4698 delete result;
4700 sLog.outString();
4701 sLog.outString(">> Loaded " SIZEFMTD " PageText locale strings", mPageTextLocaleMap.size());
4704 void ObjectMgr::LoadInstanceEncounters()
4706 m_DungeonEncounters.clear(); // need for reload case
4708 QueryResult* result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
4710 if (!result)
4712 BarGoLink bar(1);
4714 bar.step();
4716 sLog.outString();
4717 sLog.outString(">> Loaded 0 Instance Encounters. DB table `instance_encounters` is empty.");
4718 return;
4721 BarGoLink bar(result->GetRowCount());
4725 Field* fields = result->Fetch();
4726 bar.step();
4728 uint32 entry = fields[0].GetUInt32();
4729 DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
4731 if (!dungeonEncounter)
4733 sLog.outErrorDb("Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
4734 continue;
4737 uint8 creditType = fields[1].GetUInt8();
4738 uint32 creditEntry = fields[2].GetUInt32();
4739 switch (creditType)
4741 case ENCOUNTER_CREDIT_KILL_CREATURE:
4743 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creditEntry);
4744 if (!cInfo)
4746 sLog.outErrorDb("Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]);
4747 continue;
4749 break;
4751 case ENCOUNTER_CREDIT_CAST_SPELL:
4753 if (!sSpellStore.LookupEntry(creditEntry))
4755 // skip spells that aren't in dbc for now
4756 // sLog.outErrorDb("Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]);
4757 continue;
4759 break;
4761 default:
4762 sLog.outErrorDb("Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName[0]);
4763 continue;
4765 uint32 lastEncounterDungeon = fields[3].GetUInt32();
4767 m_DungeonEncounters.insert(DungeonEncounterMap::value_type(creditEntry, new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon)));
4770 while (result->NextRow());
4772 delete result;
4774 sLog.outString();
4775 sLog.outString(">> Loaded " SIZEFMTD " Instance Encounters", m_DungeonEncounters.size());
4778 struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader, SQLStorage>
4780 template<class D>
4781 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
4783 dst = D(sScriptMgr.GetScriptId(src));
4787 void ObjectMgr::LoadInstanceTemplate()
4789 SQLInstanceLoader loader;
4790 loader.Load(sInstanceTemplate);
4792 for (uint32 i = 0; i < sInstanceTemplate.GetMaxEntry(); ++i)
4794 InstanceTemplate const* temp = GetInstanceTemplate(i);
4795 if (!temp)
4796 continue;
4798 MapEntry const* mapEntry = sMapStore.LookupEntry(temp->map);
4799 if (!mapEntry)
4801 sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
4802 sInstanceTemplate.EraseEntry(i);
4803 continue;
4806 if (!mapEntry->Instanceable())
4808 sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: non-instanceable mapid %d for template!", temp->map);
4809 sInstanceTemplate.EraseEntry(i);
4810 continue;
4813 if (temp->parent > 0)
4815 // check existence
4816 MapEntry const* parentEntry = sMapStore.LookupEntry(temp->parent);
4817 if (!parentEntry)
4819 sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad parent map id %u for instance template %d template!",
4820 temp->parent, temp->map);
4821 const_cast<InstanceTemplate*>(temp)->parent = 0;
4822 continue;
4825 if (parentEntry->IsContinent())
4827 sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: parent point to continent map id %u for instance template %d template, ignored, need be set only for non-continent parents!",
4828 parentEntry->MapID, temp->map);
4829 const_cast<InstanceTemplate*>(temp)->parent = 0;
4830 continue;
4835 sLog.outString(">> Loaded %u Instance Template definitions", sInstanceTemplate.GetRecordCount());
4836 sLog.outString();
4839 struct SQLWorldLoader : public SQLStorageLoaderBase<SQLWorldLoader, SQLStorage>
4841 template<class D>
4842 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
4844 dst = D(sScriptMgr.GetScriptId(src));
4848 void ObjectMgr::LoadWorldTemplate()
4850 SQLWorldLoader loader;
4851 loader.Load(sWorldTemplate, false);
4853 for (uint32 i = 0; i < sWorldTemplate.GetMaxEntry(); ++i)
4855 WorldTemplate const* temp = GetWorldTemplate(i);
4856 if (!temp)
4857 continue;
4859 MapEntry const* mapEntry = sMapStore.LookupEntry(temp->map);
4860 if (!mapEntry)
4862 sLog.outErrorDb("ObjectMgr::LoadWorldTemplate: bad mapid %d for template!", temp->map);
4863 sWorldTemplate.EraseEntry(i);
4864 continue;
4867 if (mapEntry->Instanceable())
4869 sLog.outErrorDb("ObjectMgr::LoadWorldTemplate: instanceable mapid %d for template!", temp->map);
4870 sWorldTemplate.EraseEntry(i);
4871 continue;
4875 sLog.outString(">> Loaded %u World Template definitions", sWorldTemplate.GetRecordCount());
4876 sLog.outString();
4879 void ObjectMgr::LoadConditions()
4881 SQLWorldLoader loader;
4882 loader.Load(sConditionStorage);
4884 for (uint32 i = 0; i < sConditionStorage.GetMaxEntry(); ++i)
4886 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(i);
4887 if (!condition)
4888 continue;
4890 if (!condition->IsValid())
4892 sLog.outErrorDb("ObjectMgr::LoadConditions: invalid condition_entry %u, skip", i);
4893 sConditionStorage.EraseEntry(i);
4894 continue;
4898 sLog.outString(">> Loaded %u Condition definitions", sConditionStorage.GetRecordCount());
4899 sLog.outString();
4902 GossipText const* ObjectMgr::GetGossipText(uint32 Text_ID) const
4904 GossipTextMap::const_iterator itr = mGossipText.find(Text_ID);
4905 if (itr != mGossipText.end())
4906 return &itr->second;
4907 return NULL;
4910 void ObjectMgr::LoadGossipText()
4912 QueryResult* result = WorldDatabase.Query("SELECT * FROM npc_text");
4914 int count = 0;
4915 if (!result)
4917 BarGoLink bar(1);
4918 bar.step();
4920 sLog.outString();
4921 sLog.outString(">> Loaded %u npc texts", count);
4922 return;
4925 int cic;
4927 BarGoLink bar(result->GetRowCount());
4931 ++count;
4932 cic = 0;
4934 Field* fields = result->Fetch();
4936 bar.step();
4938 uint32 Text_ID = fields[cic++].GetUInt32();
4939 if (!Text_ID)
4941 sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
4942 continue;
4945 GossipText& gText = mGossipText[Text_ID];
4947 for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
4949 gText.Options[i].Text_0 = fields[cic++].GetCppString();
4950 gText.Options[i].Text_1 = fields[cic++].GetCppString();
4952 gText.Options[i].Language = fields[cic++].GetUInt32();
4953 gText.Options[i].Probability = fields[cic++].GetFloat();
4955 for (int j = 0; j < 3; ++j)
4957 gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32();
4958 gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt32();
4962 while (result->NextRow());
4964 sLog.outString();
4965 sLog.outString(">> Loaded %u npc texts", count);
4966 delete result;
4969 void ObjectMgr::LoadGossipTextLocales()
4971 mNpcTextLocaleMap.clear(); // need for reload case
4973 QueryResult* result = WorldDatabase.Query("SELECT entry,"
4974 "Text0_0_loc1,Text0_1_loc1,Text1_0_loc1,Text1_1_loc1,Text2_0_loc1,Text2_1_loc1,Text3_0_loc1,Text3_1_loc1,Text4_0_loc1,Text4_1_loc1,Text5_0_loc1,Text5_1_loc1,Text6_0_loc1,Text6_1_loc1,Text7_0_loc1,Text7_1_loc1,"
4975 "Text0_0_loc2,Text0_1_loc2,Text1_0_loc2,Text1_1_loc2,Text2_0_loc2,Text2_1_loc2,Text3_0_loc2,Text3_1_loc1,Text4_0_loc2,Text4_1_loc2,Text5_0_loc2,Text5_1_loc2,Text6_0_loc2,Text6_1_loc2,Text7_0_loc2,Text7_1_loc2,"
4976 "Text0_0_loc3,Text0_1_loc3,Text1_0_loc3,Text1_1_loc3,Text2_0_loc3,Text2_1_loc3,Text3_0_loc3,Text3_1_loc1,Text4_0_loc3,Text4_1_loc3,Text5_0_loc3,Text5_1_loc3,Text6_0_loc3,Text6_1_loc3,Text7_0_loc3,Text7_1_loc3,"
4977 "Text0_0_loc4,Text0_1_loc4,Text1_0_loc4,Text1_1_loc4,Text2_0_loc4,Text2_1_loc4,Text3_0_loc4,Text3_1_loc1,Text4_0_loc4,Text4_1_loc4,Text5_0_loc4,Text5_1_loc4,Text6_0_loc4,Text6_1_loc4,Text7_0_loc4,Text7_1_loc4,"
4978 "Text0_0_loc5,Text0_1_loc5,Text1_0_loc5,Text1_1_loc5,Text2_0_loc5,Text2_1_loc5,Text3_0_loc5,Text3_1_loc1,Text4_0_loc5,Text4_1_loc5,Text5_0_loc5,Text5_1_loc5,Text6_0_loc5,Text6_1_loc5,Text7_0_loc5,Text7_1_loc5,"
4979 "Text0_0_loc6,Text0_1_loc6,Text1_0_loc6,Text1_1_loc6,Text2_0_loc6,Text2_1_loc6,Text3_0_loc6,Text3_1_loc1,Text4_0_loc6,Text4_1_loc6,Text5_0_loc6,Text5_1_loc6,Text6_0_loc6,Text6_1_loc6,Text7_0_loc6,Text7_1_loc6,"
4980 "Text0_0_loc7,Text0_1_loc7,Text1_0_loc7,Text1_1_loc7,Text2_0_loc7,Text2_1_loc7,Text3_0_loc7,Text3_1_loc1,Text4_0_loc7,Text4_1_loc7,Text5_0_loc7,Text5_1_loc7,Text6_0_loc7,Text6_1_loc7,Text7_0_loc7,Text7_1_loc7, "
4981 "Text0_0_loc8,Text0_1_loc8,Text1_0_loc8,Text1_1_loc8,Text2_0_loc8,Text2_1_loc8,Text3_0_loc8,Text3_1_loc1,Text4_0_loc8,Text4_1_loc8,Text5_0_loc8,Text5_1_loc8,Text6_0_loc8,Text6_1_loc8,Text7_0_loc8,Text7_1_loc8 "
4982 " FROM locales_npc_text");
4984 if (!result)
4986 BarGoLink bar(1);
4988 bar.step();
4990 sLog.outString();
4991 sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
4992 return;
4995 BarGoLink bar(result->GetRowCount());
4999 Field* fields = result->Fetch();
5000 bar.step();
5002 uint32 entry = fields[0].GetUInt32();
5004 if (!GetGossipText(entry))
5006 ERROR_DB_STRICT_LOG("Table `locales_npc_text` has data for nonexistent gossip text entry %u, skipped.", entry);
5007 continue;
5010 NpcTextLocale& data = mNpcTextLocaleMap[entry];
5012 for (int i = 1; i < MAX_LOCALE; ++i)
5014 for (int j = 0; j < 8; ++j)
5016 std::string str0 = fields[1 + 8 * 2 * (i - 1) + 2 * j].GetCppString();
5017 if (!str0.empty())
5019 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5020 if (idx >= 0)
5022 if ((int32)data.Text_0[j].size() <= idx)
5023 data.Text_0[j].resize(idx + 1);
5025 data.Text_0[j][idx] = str0;
5028 std::string str1 = fields[1 + 8 * 2 * (i - 1) + 2 * j + 1].GetCppString();
5029 if (!str1.empty())
5031 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5032 if (idx >= 0)
5034 if ((int32)data.Text_1[j].size() <= idx)
5035 data.Text_1[j].resize(idx + 1);
5037 data.Text_1[j][idx] = str1;
5043 while (result->NextRow());
5045 delete result;
5047 sLog.outString();
5048 sLog.outString(">> Loaded " SIZEFMTD " NpcText locale strings", mNpcTextLocaleMap.size());
5051 // not very fast function but it is called only once a day, or on starting-up
5052 void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
5054 time_t basetime = time(NULL);
5055 DEBUG_LOG("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
5056 // delete all old mails without item and without body immediately, if starting server
5057 if (!serverUp)
5058 CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD "' AND has_items = '0' AND body = ''", (uint64)basetime);
5059 // 0 1 2 3 4 5 6 7 8
5060 QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD "'", (uint64)basetime);
5061 if (!result)
5063 BarGoLink bar(1);
5064 bar.step();
5065 sLog.outString();
5066 sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
5067 return; // any mails need to be returned or deleted
5070 // std::ostringstream delitems, delmails; // will be here for optimization
5071 // bool deletemail = false, deleteitem = false;
5072 // delitems << "DELETE FROM item_instance WHERE guid IN ( ";
5073 // delmails << "DELETE FROM mail WHERE id IN ( "
5075 BarGoLink bar(result->GetRowCount());
5076 uint32 count = 0;
5077 Field* fields;
5081 bar.step();
5083 fields = result->Fetch();
5084 Mail* m = new Mail;
5085 m->messageID = fields[0].GetUInt32();
5086 m->messageType = fields[1].GetUInt8();
5087 m->sender = fields[2].GetUInt32();
5088 m->receiverGuid = ObjectGuid(HIGHGUID_PLAYER, fields[3].GetUInt32());
5089 bool has_items = fields[4].GetBool();
5090 m->expire_time = (time_t)fields[5].GetUInt64();
5091 m->deliver_time = 0;
5092 m->COD = fields[6].GetUInt32();
5093 m->checked = fields[7].GetUInt32();
5094 m->mailTemplateId = fields[8].GetInt16();
5096 Player* pl = 0;
5097 if (serverUp)
5098 pl = GetPlayer(m->receiverGuid);
5099 if (pl)
5101 // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
5102 // his in mailbox and he has already listed his mails )
5103 delete m;
5104 continue;
5106 // delete or return mail:
5107 if (has_items)
5109 QueryResult* resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
5110 if (resultItems)
5114 Field* fields2 = resultItems->Fetch();
5116 uint32 item_guid_low = fields2[0].GetUInt32();
5117 uint32 item_template = fields2[1].GetUInt32();
5119 m->AddItem(item_guid_low, item_template);
5121 while (resultItems->NextRow());
5123 delete resultItems;
5125 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
5126 if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
5128 // mail open and then not returned
5129 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
5130 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
5132 else
5134 // mail will be returned:
5135 CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'",
5136 m->receiverGuid.GetCounter(), m->sender, (uint64)(basetime + 30 * DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
5137 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
5139 // update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
5140 CharacterDatabase.PExecute("UPDATE mail_items SET receiver = %u WHERE item_guid = '%u'", m->sender, itr2->item_guid);
5141 CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = %u WHERE guid = '%u'", m->sender, itr2->item_guid);
5143 delete m;
5144 continue;
5148 // deletemail = true;
5149 // delmails << m->messageID << ", ";
5150 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
5151 delete m;
5152 ++count;
5154 while (result->NextRow());
5155 delete result;
5157 sLog.outString();
5158 sLog.outString(">> Loaded %u mails", count);
5161 void ObjectMgr::LoadQuestAreaTriggers()
5163 mQuestAreaTriggerMap.clear(); // need for reload case
5165 QueryResult* result = WorldDatabase.Query("SELECT id,quest FROM areatrigger_involvedrelation");
5167 uint32 count = 0;
5169 if (!result)
5171 BarGoLink bar(1);
5172 bar.step();
5174 sLog.outString();
5175 sLog.outString(">> Loaded %u quest trigger points", count);
5176 return;
5179 BarGoLink bar(result->GetRowCount());
5183 ++count;
5184 bar.step();
5186 Field* fields = result->Fetch();
5188 uint32 trigger_ID = fields[0].GetUInt32();
5189 uint32 quest_ID = fields[1].GetUInt32();
5191 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
5192 if (!atEntry)
5194 sLog.outErrorDb("Table `areatrigger_involvedrelation` has area trigger (ID: %u) not listed in `AreaTrigger.dbc`.", trigger_ID);
5195 continue;
5198 Quest const* quest = GetQuestTemplate(quest_ID);
5199 if (!quest)
5201 sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
5202 continue;
5205 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
5207 sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
5209 // this will prevent quest completing without objective
5210 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
5212 // continue; - quest modified to required objective and trigger can be allowed.
5215 mQuestAreaTriggerMap[trigger_ID] = quest_ID;
5218 while (result->NextRow());
5220 delete result;
5222 sLog.outString();
5223 sLog.outString(">> Loaded %u quest trigger points", count);
5226 void ObjectMgr::LoadTavernAreaTriggers()
5228 mTavernAreaTriggerSet.clear(); // need for reload case
5230 QueryResult* result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
5232 uint32 count = 0;
5234 if (!result)
5236 BarGoLink bar(1);
5237 bar.step();
5239 sLog.outString();
5240 sLog.outString(">> Loaded %u tavern triggers", count);
5241 return;
5244 BarGoLink bar(result->GetRowCount());
5248 ++count;
5249 bar.step();
5251 Field* fields = result->Fetch();
5253 uint32 Trigger_ID = fields[0].GetUInt32();
5255 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5256 if (!atEntry)
5258 sLog.outErrorDb("Table `areatrigger_tavern` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID);
5259 continue;
5262 mTavernAreaTriggerSet.insert(Trigger_ID);
5264 while (result->NextRow());
5266 delete result;
5268 sLog.outString();
5269 sLog.outString(">> Loaded %u tavern triggers", count);
5272 uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, Team team)
5274 bool found = false;
5275 float dist;
5276 uint32 id = 0;
5278 for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
5280 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
5281 if (!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0])
5282 continue;
5284 uint8 field = (uint8)((i - 1) / 8);
5285 uint8 submask = 1 << ((i - 1) % 8);
5287 // skip not taxi network nodes
5288 if ((sTaxiNodesMask[field] & submask) == 0)
5289 continue;
5291 float dist2 = (node->x - x) * (node->x - x) + (node->y - y) * (node->y - y) + (node->z - z) * (node->z - z);
5292 if (found)
5294 if (dist2 < dist)
5296 dist = dist2;
5297 id = i;
5300 else
5302 found = true;
5303 dist = dist2;
5304 id = i;
5308 return id;
5311 void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32& path, uint32& cost)
5313 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
5314 if (src_i == sTaxiPathSetBySource.end())
5316 path = 0;
5317 cost = 0;
5318 return;
5321 TaxiPathSetForSource& pathSet = src_i->second;
5323 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
5324 if (dest_i == pathSet.end())
5326 path = 0;
5327 cost = 0;
5328 return;
5331 cost = dest_i->second.price;
5332 path = dest_i->second.ID;
5335 uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, Team team, bool allowed_alt_team /* = false */)
5337 uint16 mount_entry = 0;
5339 // select mount creature id
5340 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
5341 if (node)
5343 if (team == ALLIANCE)
5345 mount_entry = node->MountCreatureID[1];
5346 if (!mount_entry && allowed_alt_team)
5347 mount_entry = node->MountCreatureID[0];
5349 else if (team == HORDE)
5351 mount_entry = node->MountCreatureID[0];
5353 if (!mount_entry && allowed_alt_team)
5354 mount_entry = node->MountCreatureID[1];
5358 CreatureInfo const* mount_info = GetCreatureTemplate(mount_entry);
5359 if (!mount_info)
5360 return 0;
5362 uint16 mount_id = Creature::ChooseDisplayId(mount_info);
5363 if (!mount_id)
5364 return 0;
5366 CreatureModelInfo const* minfo = GetCreatureModelRandomGender(mount_id);
5367 if (minfo)
5368 mount_id = minfo->modelid;
5370 return mount_id;
5373 void ObjectMgr::LoadGraveyardZones()
5375 mGraveYardMap.clear(); // need for reload case
5377 QueryResult* result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
5379 uint32 count = 0;
5381 if (!result)
5383 BarGoLink bar(1);
5384 bar.step();
5386 sLog.outString();
5387 sLog.outString(">> Loaded %u graveyard-zone links", count);
5388 return;
5391 BarGoLink bar(result->GetRowCount());
5395 ++count;
5396 bar.step();
5398 Field* fields = result->Fetch();
5400 uint32 safeLocId = fields[0].GetUInt32();
5401 uint32 zoneId = fields[1].GetUInt32();
5402 uint32 team = fields[2].GetUInt32();
5404 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
5405 if (!entry)
5407 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
5408 continue;
5411 AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId);
5412 if (!areaEntry)
5414 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.", zoneId);
5415 continue;
5418 if (areaEntry->zone != 0)
5420 sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.", zoneId);
5421 continue;
5424 if (team != TEAM_BOTH_ALLOWED && team != HORDE && team != ALLIANCE)
5426 sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.", team);
5427 continue;
5430 if (!AddGraveYardLink(safeLocId, zoneId, Team(team), false))
5431 sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
5433 while (result->NextRow());
5435 delete result;
5437 sLog.outString();
5438 sLog.outString(">> Loaded %u graveyard-zone links", count);
5441 WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, Team team)
5443 // search for zone associated closest graveyard
5444 uint32 zoneId = sTerrainMgr.GetZoneId(MapId, x, y, z);
5446 // Simulate std. algorithm:
5447 // found some graveyard associated to (ghost_zone,ghost_map)
5449 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5450 // then check faction
5451 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5452 // then check faction
5453 GraveYardMapBounds bounds = mGraveYardMap.equal_range(zoneId);
5455 if (bounds.first == bounds.second)
5457 sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, uint32(team));
5458 return NULL;
5461 // at corpse map
5462 bool foundNear = false;
5463 float distNear;
5464 WorldSafeLocsEntry const* entryNear = NULL;
5466 // at entrance map for corpse map
5467 bool foundEntr = false;
5468 float distEntr;
5469 WorldSafeLocsEntry const* entryEntr = NULL;
5471 // some where other
5472 WorldSafeLocsEntry const* entryFar = NULL;
5474 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
5476 for (GraveYardMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
5478 GraveYardData const& data = itr->second;
5480 // Checked on load
5481 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
5483 // skip enemy faction graveyard
5484 // team == TEAM_BOTH_ALLOWED case can be at call from .neargrave
5485 // TEAM_INVALID != team for all teams
5486 if (data.team != TEAM_BOTH_ALLOWED && data.team != team && team != TEAM_BOTH_ALLOWED)
5487 continue;
5489 // find now nearest graveyard at other (continent) map
5490 if (MapId != entry->map_id)
5492 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5493 if (!mapEntry ||
5494 mapEntry->ghost_entrance_map < 0 ||
5495 uint32(mapEntry->ghost_entrance_map) != entry->map_id ||
5496 (mapEntry->ghost_entrance_x == 0 && mapEntry->ghost_entrance_y == 0))
5498 // not have any coordinates for check distance anyway
5499 entryFar = entry;
5500 continue;
5503 // at entrance map calculate distance (2D);
5504 float dist2 = (entry->x - mapEntry->ghost_entrance_x) * (entry->x - mapEntry->ghost_entrance_x)
5505 + (entry->y - mapEntry->ghost_entrance_y) * (entry->y - mapEntry->ghost_entrance_y);
5506 if (foundEntr)
5508 if (dist2 < distEntr)
5510 distEntr = dist2;
5511 entryEntr = entry;
5514 else
5516 foundEntr = true;
5517 distEntr = dist2;
5518 entryEntr = entry;
5521 // find now nearest graveyard at same map
5522 else
5524 float dist2 = (entry->x - x) * (entry->x - x) + (entry->y - y) * (entry->y - y) + (entry->z - z) * (entry->z - z);
5525 if (foundNear)
5527 if (dist2 < distNear)
5529 distNear = dist2;
5530 entryNear = entry;
5533 else
5535 foundNear = true;
5536 distNear = dist2;
5537 entryNear = entry;
5542 if (entryNear)
5543 return entryNear;
5545 if (entryEntr)
5546 return entryEntr;
5548 return entryFar;
5551 GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId) const
5553 GraveYardMapBounds bounds = mGraveYardMap.equal_range(zoneId);
5555 for (GraveYardMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
5557 if (itr->second.safeLocId == id)
5558 return &itr->second;
5561 return NULL;
5564 bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, Team team, bool inDB)
5566 if (FindGraveYardData(id, zoneId)) // This ensures that (safeLoc)Id, zoneId is unique in mGraveYardMap
5567 return false;
5569 // add link to loaded data
5570 GraveYardData data;
5571 data.safeLocId = id;
5572 data.team = team;
5574 mGraveYardMap.insert(GraveYardMap::value_type(zoneId, data));
5576 // add link to DB
5577 if (inDB)
5578 WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) VALUES ('%u', '%u','%u')", id, zoneId, uint32(team));
5580 return true;
5583 void ObjectMgr::SetGraveYardLinkTeam(uint32 id, uint32 zoneId, Team team)
5585 std::pair<GraveYardMap::iterator, GraveYardMap::iterator> bounds = mGraveYardMap.equal_range(zoneId);
5587 for (GraveYardMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
5589 GraveYardData& data = itr->second;
5591 // skip not matching safezone id
5592 if (data.safeLocId != id)
5593 continue;
5595 data.team = team; // Validate link
5596 return;
5599 if (team == TEAM_INVALID)
5600 return;
5602 // Link expected but not exist.
5603 sLog.outErrorDb("ObjectMgr::SetGraveYardLinkTeam called for safeLoc %u, zoneId %u, but no graveyard link for this found in database.", id, zoneId);
5604 AddGraveYardLink(id, zoneId, team); // Add to prevent further error message and correct mechanismn
5607 void ObjectMgr::LoadAreaTriggerTeleports()
5609 mAreaTriggers.clear(); // need for reload case
5611 uint32 count = 0;
5613 // 0 1 2 3 4 5 6 7 8 9 10 11 12
5614 QueryResult* result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_quest_done_heroic, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
5615 if (!result)
5618 BarGoLink bar(1);
5620 bar.step();
5622 sLog.outString();
5623 sLog.outString(">> Loaded %u area trigger teleport definitions", count);
5624 return;
5627 BarGoLink bar(result->GetRowCount());
5631 Field* fields = result->Fetch();
5633 bar.step();
5635 ++count;
5637 uint32 Trigger_ID = fields[0].GetUInt32();
5639 AreaTrigger at;
5641 at.requiredLevel = fields[1].GetUInt8();
5642 at.requiredItem = fields[2].GetUInt32();
5643 at.requiredItem2 = fields[3].GetUInt32();
5644 at.heroicKey = fields[4].GetUInt32();
5645 at.heroicKey2 = fields[5].GetUInt32();
5646 at.requiredQuest = fields[6].GetUInt32();
5647 at.requiredQuestHeroic = fields[7].GetUInt32();
5648 at.target_mapId = fields[8].GetUInt32();
5649 at.target_X = fields[9].GetFloat();
5650 at.target_Y = fields[10].GetFloat();
5651 at.target_Z = fields[11].GetFloat();
5652 at.target_Orientation = fields[12].GetFloat();
5654 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5655 if (!atEntry)
5657 sLog.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID);
5658 continue;
5661 if (at.requiredItem)
5663 ItemPrototype const* pProto = GetItemPrototype(at.requiredItem);
5664 if (!pProto)
5666 sLog.outError("Table `areatrigger_teleport` has nonexistent key item %u for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
5667 at.requiredItem = 0;
5671 if (at.requiredItem2)
5673 ItemPrototype const* pProto = GetItemPrototype(at.requiredItem2);
5674 if (!pProto)
5676 sLog.outError("Table `areatrigger_teleport` has nonexistent second key item %u for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
5677 at.requiredItem2 = 0;
5681 if (at.heroicKey)
5683 ItemPrototype const* pProto = GetItemPrototype(at.heroicKey);
5684 if (!pProto)
5686 sLog.outError("Table `areatrigger_teleport` has nonexistent heroic key item %u for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
5687 at.heroicKey = 0;
5691 if (at.heroicKey2)
5693 ItemPrototype const* pProto = GetItemPrototype(at.heroicKey2);
5694 if (!pProto)
5696 sLog.outError("Table `areatrigger_teleport` has nonexistent heroic second key item %u for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
5697 at.heroicKey2 = 0;
5701 if (at.requiredQuest)
5703 QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuest);
5704 if (qReqItr == mQuestTemplates.end())
5706 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent required quest %u for trigger %u, remove quest done requirement.", at.requiredQuest, Trigger_ID);
5707 at.requiredQuest = 0;
5711 if (at.requiredQuestHeroic)
5713 QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuestHeroic);
5714 if (qReqItr == mQuestTemplates.end())
5716 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent required heroic quest %u for trigger %u, remove quest done requirement.", at.requiredQuestHeroic, Trigger_ID);
5717 at.requiredQuestHeroic = 0;
5721 MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
5722 if (!mapEntry)
5724 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent target map (ID: %u) for Area trigger (ID:%u).", at.target_mapId, Trigger_ID);
5725 continue;
5728 if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
5730 sLog.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) without target coordinates.", Trigger_ID);
5731 continue;
5734 mAreaTriggers[Trigger_ID] = at;
5737 while (result->NextRow());
5739 delete result;
5741 sLog.outString();
5742 sLog.outString(">> Loaded %u area trigger teleport definitions", count);
5746 * Searches for the areatrigger which teleports players out of the given map (only direct to continent)
5748 AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 map_id) const
5750 const MapEntry* mapEntry = sMapStore.LookupEntry(map_id);
5751 if (!mapEntry || mapEntry->ghost_entrance_map < 0)
5752 return NULL;
5754 // Try to find one that teleports to the map we want to enter
5755 std::list<AreaTrigger const*> ghostTrigger;
5756 AreaTrigger const* compareTrigger = NULL;
5757 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5759 if (itr->second.target_mapId == uint32(mapEntry->ghost_entrance_map))
5761 ghostTrigger.push_back(&itr->second);
5762 // First run, only consider AreaTrigger that teleport in the proper map
5763 if ((!compareTrigger || itr->second.IsLessOrEqualThan(compareTrigger)) && sAreaTriggerStore.LookupEntry(itr->first)->mapid == map_id)
5765 if (itr->second.IsMinimal())
5766 return &itr->second;
5768 compareTrigger = &itr->second;
5772 if (compareTrigger)
5773 return compareTrigger;
5775 // Second attempt: take one fitting
5776 for (std::list<AreaTrigger const*>::const_iterator itr = ghostTrigger.begin(); itr != ghostTrigger.end(); ++itr)
5778 if (!compareTrigger || (*itr)->IsLessOrEqualThan(compareTrigger))
5780 if ((*itr)->IsMinimal())
5781 return *itr;
5783 compareTrigger = *itr;
5786 return compareTrigger;
5790 * Searches for the areatrigger which teleports players to the given map
5792 AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
5794 AreaTrigger const* compareTrigger = NULL;
5795 MapEntry const* mEntry = sMapStore.LookupEntry(Map);
5797 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5799 if (itr->second.target_mapId == Map)
5801 if (mEntry->Instanceable())
5803 // Remark that IsLessOrEqualThan is no total order, and a->IsLeQ(b) != !b->IsLeQ(a)
5804 if (!compareTrigger || compareTrigger->IsLessOrEqualThan(&itr->second))
5805 compareTrigger = &itr->second;
5807 else
5809 if (!compareTrigger || itr->second.IsLessOrEqualThan(compareTrigger))
5811 if (itr->second.IsMinimal())
5812 return &itr->second;
5814 compareTrigger = &itr->second;
5819 return compareTrigger;
5822 void ObjectMgr::PackGroupIds()
5824 // this routine renumbers groups in such a way so they start from 1 and go up
5826 // obtain set of all groups
5827 std::set<uint32> groupIds;
5829 // all valid ids are in the instance table
5830 // any associations to ids not in this table are assumed to be
5831 // cleaned already in CleanupInstances
5832 QueryResult* result = CharacterDatabase.Query("SELECT groupId FROM groups");
5833 if (result)
5837 Field* fields = result->Fetch();
5839 uint32 id = fields[0].GetUInt32();
5841 if (id == 0)
5843 CharacterDatabase.BeginTransaction();
5844 CharacterDatabase.PExecute("DELETE FROM groups WHERE groupId = '%u'", id);
5845 CharacterDatabase.PExecute("DELETE FROM group_member WHERE groupId = '%u'", id);
5846 CharacterDatabase.CommitTransaction();
5847 continue;
5850 groupIds.insert(id);
5852 while (result->NextRow());
5853 delete result;
5856 BarGoLink bar(groupIds.size() + 1);
5857 bar.step();
5859 uint32 groupId = 1;
5860 // we do assume std::set is sorted properly on integer value
5861 for (std::set<uint32>::iterator i = groupIds.begin(); i != groupIds.end(); ++i)
5863 if (*i != groupId)
5865 // remap group id
5866 CharacterDatabase.BeginTransaction();
5867 CharacterDatabase.PExecute("UPDATE groups SET groupId = '%u' WHERE groupId = '%u'", groupId, *i);
5868 CharacterDatabase.PExecute("UPDATE group_member SET groupId = '%u' WHERE groupId = '%u'", groupId, *i);
5869 CharacterDatabase.CommitTransaction();
5872 ++groupId;
5873 bar.step();
5876 sLog.outString(">> Group Ids remapped, next group id is %u", groupId);
5877 sLog.outString();
5880 void ObjectMgr::SetHighestGuids()
5882 QueryResult* result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
5883 if (result)
5885 m_CharGuids.Set((*result)[0].GetUInt32() + 1);
5886 delete result;
5889 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
5890 if (result)
5892 m_FirstTemporaryCreatureGuid = (*result)[0].GetUInt32() + 1;
5893 delete result;
5896 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
5897 if (result)
5899 m_ItemGuids.Set((*result)[0].GetUInt32() + 1);
5900 delete result;
5903 result = CharacterDatabase.Query("SELECT MAX(id) FROM instance");
5904 if (result)
5906 m_InstanceGuids.Set((*result)[0].GetUInt32() + 1);
5907 delete result;
5910 // Cleanup other tables from nonexistent guids (>=m_hiItemGuid)
5911 CharacterDatabase.BeginTransaction();
5912 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5913 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5914 CharacterDatabase.PExecute("DELETE FROM auction WHERE itemguid >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5915 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5916 CharacterDatabase.CommitTransaction();
5918 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
5919 if (result)
5921 m_FirstTemporaryGameObjectGuid = (*result)[0].GetUInt32() + 1;
5922 delete result;
5925 result = CharacterDatabase.Query("SELECT MAX(id) FROM auction");
5926 if (result)
5928 m_AuctionIds.Set((*result)[0].GetUInt32() + 1);
5929 delete result;
5932 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
5933 if (result)
5935 m_MailIds.Set((*result)[0].GetUInt32() + 1);
5936 delete result;
5939 result = CharacterDatabase.Query("SELECT MAX(guid) FROM corpse");
5940 if (result)
5942 m_CorpseGuids.Set((*result)[0].GetUInt32() + 1);
5943 delete result;
5946 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
5947 if (result)
5949 m_ArenaTeamIds.Set((*result)[0].GetUInt32() + 1);
5950 delete result;
5953 result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
5954 if (result)
5956 m_EquipmentSetIds.Set((*result)[0].GetUInt64() + 1);
5957 delete result;
5960 result = CharacterDatabase.Query("SELECT MAX(guildid) FROM guild");
5961 if (result)
5963 m_GuildIds.Set((*result)[0].GetUInt32() + 1);
5964 delete result;
5967 result = CharacterDatabase.Query("SELECT MAX(groupId) FROM groups");
5968 if (result)
5970 m_GroupGuids.Set((*result)[0].GetUInt32() + 1);
5971 delete result;
5974 // setup reserved ranges for static guids spawn
5975 m_StaticCreatureGuids.Set(m_FirstTemporaryCreatureGuid);
5976 m_FirstTemporaryCreatureGuid += sWorld.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_CREATURE);
5978 m_StaticGameObjectGuids.Set(m_FirstTemporaryGameObjectGuid);
5979 m_FirstTemporaryGameObjectGuid += sWorld.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_GAMEOBJECT);
5982 void ObjectMgr::LoadGameObjectLocales()
5984 mGameObjectLocaleMap.clear(); // need for reload case
5986 QueryResult* result = WorldDatabase.Query("SELECT entry,"
5987 "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
5988 "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
5989 "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
5991 if (!result)
5993 BarGoLink bar(1);
5995 bar.step();
5997 sLog.outString();
5998 sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
5999 return;
6002 BarGoLink bar(result->GetRowCount());
6006 Field* fields = result->Fetch();
6007 bar.step();
6009 uint32 entry = fields[0].GetUInt32();
6011 if (!GetGameObjectInfo(entry))
6013 ERROR_DB_STRICT_LOG("Table `locales_gameobject` has data for nonexistent gameobject entry %u, skipped.", entry);
6014 continue;
6017 GameObjectLocale& data = mGameObjectLocaleMap[entry];
6019 for (int i = 1; i < MAX_LOCALE; ++i)
6021 std::string str = fields[i].GetCppString();
6022 if (!str.empty())
6024 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
6025 if (idx >= 0)
6027 if ((int32)data.Name.size() <= idx)
6028 data.Name.resize(idx + 1);
6030 data.Name[idx] = str;
6035 for (int i = 1; i < MAX_LOCALE; ++i)
6037 std::string str = fields[i + (MAX_LOCALE - 1)].GetCppString();
6038 if (!str.empty())
6040 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
6041 if (idx >= 0)
6043 if ((int32)data.CastBarCaption.size() <= idx)
6044 data.CastBarCaption.resize(idx + 1);
6046 data.CastBarCaption[idx] = str;
6052 while (result->NextRow());
6054 delete result;
6056 sLog.outString();
6057 sLog.outString(">> Loaded " SIZEFMTD " gameobject locale strings", mGameObjectLocaleMap.size());
6060 struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader, SQLStorage>
6062 template<class D>
6063 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
6065 dst = D(sScriptMgr.GetScriptId(src));
6069 inline void CheckGOLockId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
6071 if (sLockStore.LookupEntry(dataN))
6072 return;
6074 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
6075 goInfo->id, goInfo->type, N, dataN, dataN);
6078 inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
6080 if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN))
6082 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
6083 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
6084 goInfo->id, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
6086 else
6087 // too many error reports about nonexistent trap templates
6088 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
6089 goInfo->id, goInfo->type, N, dataN, dataN);
6092 inline void CheckGOSpellId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
6094 if (sSpellStore.LookupEntry(dataN))
6095 return;
6097 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
6098 goInfo->id, goInfo->type, N, dataN, dataN);
6101 inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo, uint32 const& dataN, uint32 N)
6103 if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR))
6104 return;
6106 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
6107 goInfo->id, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR);
6109 // prevent client and server unexpected work
6110 const_cast<uint32&>(dataN) = 0;
6113 inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
6115 // 0/1 correct values
6116 if (dataN <= 1)
6117 return;
6119 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.",
6120 goInfo->id, goInfo->type, N, dataN);
6123 inline void CheckGOConsumable(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
6125 // 0/1 correct values
6126 if (dataN <= 1)
6127 return;
6129 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
6130 goInfo->id, goInfo->type, N, dataN);
6133 inline void CheckAndFixGOCaptureMinTime(GameObjectInfo const* goInfo, uint32 const& dataN, uint32 N)
6135 if (dataN > 0)
6136 return;
6138 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) has data%d=%u but minTime field value must be > 0.",
6139 goInfo->id, goInfo->type, N, dataN);
6141 // prevent division through 0 exception
6142 const_cast<uint32&>(dataN) = 1;
6145 void ObjectMgr::LoadGameobjectInfo()
6147 SQLGameObjectLoader loader;
6148 loader.Load(sGOStorage);
6150 // some checks
6151 for (SQLStorageBase::SQLSIterator<GameObjectInfo> itr = sGOStorage.getDataBegin<GameObjectInfo>(); itr < sGOStorage.getDataEnd<GameObjectInfo>(); ++itr)
6153 GameObjectInfo const* goInfo = itr.getValue();
6155 if (goInfo->size <= 0.0f) // prevent use too small scales
6157 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have too small size=%f",
6158 goInfo->id, goInfo->type, goInfo->size);
6159 const_cast<GameObjectInfo*>(goInfo)->size = DEFAULT_OBJECT_SCALE;
6162 // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore
6164 switch (goInfo->type)
6166 case GAMEOBJECT_TYPE_DOOR: // 0
6168 if (goInfo->door.lockId)
6169 CheckGOLockId(goInfo, goInfo->door.lockId, 1);
6170 CheckGONoDamageImmuneId(goInfo, goInfo->door.noDamageImmune, 3);
6171 break;
6173 case GAMEOBJECT_TYPE_BUTTON: // 1
6175 if (goInfo->button.lockId)
6176 CheckGOLockId(goInfo, goInfo->button.lockId, 1);
6177 if (goInfo->button.linkedTrapId) // linked trap
6178 CheckGOLinkedTrapId(goInfo, goInfo->button.linkedTrapId, 3);
6179 CheckGONoDamageImmuneId(goInfo, goInfo->button.noDamageImmune, 4);
6180 break;
6182 case GAMEOBJECT_TYPE_QUESTGIVER: // 2
6184 if (goInfo->questgiver.lockId)
6185 CheckGOLockId(goInfo, goInfo->questgiver.lockId, 0);
6186 CheckGONoDamageImmuneId(goInfo, goInfo->questgiver.noDamageImmune, 5);
6187 break;
6189 case GAMEOBJECT_TYPE_CHEST: // 3
6191 if (goInfo->chest.lockId)
6192 CheckGOLockId(goInfo, goInfo->chest.lockId, 0);
6194 CheckGOConsumable(goInfo, goInfo->chest.consumable, 3);
6196 if (goInfo->chest.linkedTrapId) // linked trap
6197 CheckGOLinkedTrapId(goInfo, goInfo->chest.linkedTrapId, 7);
6198 break;
6200 case GAMEOBJECT_TYPE_TRAP: // 6
6202 if (goInfo->trap.lockId)
6203 CheckGOLockId(goInfo, goInfo->trap.lockId, 0);
6204 /* disable check for while, too many nonexistent spells
6205 if (goInfo->trap.spellId) // spell
6206 CheckGOSpellId(goInfo,goInfo->trap.spellId,3);
6208 break;
6210 case GAMEOBJECT_TYPE_CHAIR: // 7
6211 CheckAndFixGOChairHeightId(goInfo, goInfo->chair.height, 1);
6212 break;
6213 case GAMEOBJECT_TYPE_SPELL_FOCUS: // 8
6215 if (goInfo->spellFocus.focusId)
6217 if (!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
6218 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
6219 goInfo->id, goInfo->type, goInfo->spellFocus.focusId, goInfo->spellFocus.focusId);
6222 if (goInfo->spellFocus.linkedTrapId) // linked trap
6223 CheckGOLinkedTrapId(goInfo, goInfo->spellFocus.linkedTrapId, 2);
6224 break;
6226 case GAMEOBJECT_TYPE_GOOBER: // 10
6228 if (goInfo->goober.lockId)
6229 CheckGOLockId(goInfo, goInfo->goober.lockId, 0);
6231 CheckGOConsumable(goInfo, goInfo->goober.consumable, 3);
6233 if (goInfo->goober.pageId) // pageId
6235 if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
6236 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
6237 goInfo->id, goInfo->type, goInfo->goober.pageId, goInfo->goober.pageId);
6239 /* disable check for while, too many nonexistent spells
6240 if (goInfo->goober.spellId) // spell
6241 CheckGOSpellId(goInfo,goInfo->goober.spellId,10);
6243 CheckGONoDamageImmuneId(goInfo, goInfo->goober.noDamageImmune, 11);
6244 if (goInfo->goober.linkedTrapId) // linked trap
6245 CheckGOLinkedTrapId(goInfo, goInfo->goober.linkedTrapId, 12);
6246 break;
6248 case GAMEOBJECT_TYPE_AREADAMAGE: // 12
6250 if (goInfo->areadamage.lockId)
6251 CheckGOLockId(goInfo, goInfo->areadamage.lockId, 0);
6252 break;
6254 case GAMEOBJECT_TYPE_CAMERA: // 13
6256 if (goInfo->camera.lockId)
6257 CheckGOLockId(goInfo, goInfo->camera.lockId, 0);
6258 break;
6260 case GAMEOBJECT_TYPE_MO_TRANSPORT: // 15
6262 if (goInfo->moTransport.taxiPathId)
6264 if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
6265 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
6266 goInfo->id, goInfo->type, goInfo->moTransport.taxiPathId, goInfo->moTransport.taxiPathId);
6268 break;
6270 case GAMEOBJECT_TYPE_SUMMONING_RITUAL: // 18
6272 /* disable check for while, too many nonexistent spells
6273 // always must have spell
6274 CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1);
6276 break;
6278 case GAMEOBJECT_TYPE_SPELLCASTER: // 22
6280 // always must have spell
6281 CheckGOSpellId(goInfo, goInfo->spellcaster.spellId, 0);
6282 break;
6284 case GAMEOBJECT_TYPE_FLAGSTAND: // 24
6286 if (goInfo->flagstand.lockId)
6287 CheckGOLockId(goInfo, goInfo->flagstand.lockId, 0);
6288 CheckGONoDamageImmuneId(goInfo, goInfo->flagstand.noDamageImmune, 5);
6289 break;
6291 case GAMEOBJECT_TYPE_FISHINGHOLE: // 25
6293 if (goInfo->fishinghole.lockId)
6294 CheckGOLockId(goInfo, goInfo->fishinghole.lockId, 4);
6295 break;
6297 case GAMEOBJECT_TYPE_FLAGDROP: // 26
6299 if (goInfo->flagdrop.lockId)
6300 CheckGOLockId(goInfo, goInfo->flagdrop.lockId, 0);
6301 CheckGONoDamageImmuneId(goInfo, goInfo->flagdrop.noDamageImmune, 3);
6302 break;
6304 case GAMEOBJECT_TYPE_CAPTURE_POINT: // 29
6306 CheckAndFixGOCaptureMinTime(goInfo, goInfo->capturePoint.minTime, 16);
6307 break;
6309 case GAMEOBJECT_TYPE_BARBER_CHAIR: // 32
6310 CheckAndFixGOChairHeightId(goInfo, goInfo->barberChair.chairheight, 0);
6311 break;
6315 sLog.outString(">> Loaded %u game object templates", sGOStorage.GetRecordCount());
6316 sLog.outString();
6319 void ObjectMgr::LoadExplorationBaseXP()
6321 uint32 count = 0;
6322 QueryResult* result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
6324 if (!result)
6326 BarGoLink bar(1);
6328 bar.step();
6330 sLog.outString();
6331 sLog.outString(">> Loaded %u BaseXP definitions", count);
6332 return;
6335 BarGoLink bar(result->GetRowCount());
6339 bar.step();
6341 Field* fields = result->Fetch();
6342 uint32 level = fields[0].GetUInt32();
6343 uint32 basexp = fields[1].GetUInt32();
6344 mBaseXPTable[level] = basexp;
6345 ++count;
6347 while (result->NextRow());
6349 delete result;
6351 sLog.outString();
6352 sLog.outString(">> Loaded %u BaseXP definitions", count);
6355 uint32 ObjectMgr::GetBaseXP(uint32 level) const
6357 BaseXPMap::const_iterator itr = mBaseXPTable.find(level);
6358 return itr != mBaseXPTable.end() ? itr->second : 0;
6361 uint32 ObjectMgr::GetXPForLevel(uint32 level) const
6363 if (level < mPlayerXPperLevel.size())
6364 return mPlayerXPperLevel[level];
6365 return 0;
6368 void ObjectMgr::LoadPetNames()
6370 uint32 count = 0;
6371 QueryResult* result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
6373 if (!result)
6375 BarGoLink bar(1);
6377 bar.step();
6379 sLog.outString();
6380 sLog.outString(">> Loaded %u pet name parts", count);
6381 return;
6384 BarGoLink bar(result->GetRowCount());
6388 bar.step();
6390 Field* fields = result->Fetch();
6391 std::string word = fields[0].GetString();
6392 uint32 entry = fields[1].GetUInt32();
6393 bool half = fields[2].GetBool();
6394 if (half)
6395 PetHalfName1[entry].push_back(word);
6396 else
6397 PetHalfName0[entry].push_back(word);
6398 ++count;
6400 while (result->NextRow());
6401 delete result;
6403 sLog.outString();
6404 sLog.outString(">> Loaded %u pet name parts", count);
6407 void ObjectMgr::LoadPetNumber()
6409 QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
6410 if (result)
6412 Field* fields = result->Fetch();
6413 m_PetNumbers.Set(fields[0].GetUInt32() + 1);
6414 delete result;
6417 BarGoLink bar(1);
6418 bar.step();
6420 sLog.outString();
6421 sLog.outString(">> Loaded the max pet number: %d", m_PetNumbers.GetNextAfterMaxUsed() - 1);
6424 std::string ObjectMgr::GeneratePetName(uint32 entry)
6426 std::vector<std::string>& list0 = PetHalfName0[entry];
6427 std::vector<std::string>& list1 = PetHalfName1[entry];
6429 if (list0.empty() || list1.empty())
6431 CreatureInfo const* cinfo = GetCreatureTemplate(entry);
6432 char const* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
6433 if (!petname)
6434 petname = cinfo->Name;
6435 return std::string(petname);
6438 return *(list0.begin() + urand(0, list0.size() - 1)) + *(list1.begin() + urand(0, list1.size() - 1));
6441 void ObjectMgr::LoadCorpses()
6443 uint32 count = 0;
6444 // 0 1 2 3 4 5 6
6445 QueryResult* result = CharacterDatabase.Query("SELECT corpse.guid, player, corpse.position_x, corpse.position_y, corpse.position_z, corpse.orientation, corpse.map, "
6446 // 7 8 9 10 11 12 13 14 15 16 17 18
6447 "time, corpse_type, instance, phaseMask, gender, race, class, playerBytes, playerBytes2, equipmentCache, guildId, playerFlags FROM corpse "
6448 "JOIN characters ON player = characters.guid "
6449 "LEFT JOIN guild_member ON player=guild_member.guid WHERE corpse_type <> 0");
6451 if (!result)
6453 BarGoLink bar(1);
6455 bar.step();
6457 sLog.outString();
6458 sLog.outString(">> Loaded %u corpses", count);
6459 return;
6462 BarGoLink bar(result->GetRowCount());
6466 bar.step();
6468 Field* fields = result->Fetch();
6470 uint32 guid = fields[0].GetUInt32();
6472 Corpse* corpse = new Corpse;
6473 if (!corpse->LoadFromDB(guid, fields))
6475 delete corpse;
6476 continue;
6479 sObjectAccessor.AddCorpse(corpse);
6481 ++count;
6483 while (result->NextRow());
6484 delete result;
6486 sLog.outString();
6487 sLog.outString(">> Loaded %u corpses", count);
6490 void ObjectMgr::LoadReputationRewardRate()
6492 m_RepRewardRateMap.clear(); // for reload case
6494 uint32 count = 0;
6495 QueryResult* result = WorldDatabase.Query("SELECT faction, quest_rate, creature_rate, spell_rate FROM reputation_reward_rate");
6497 if (!result)
6499 BarGoLink bar(1);
6501 bar.step();
6503 sLog.outString();
6504 sLog.outErrorDb(">> Loaded `reputation_reward_rate`, table is empty!");
6505 return;
6508 BarGoLink bar(result->GetRowCount());
6512 bar.step();
6514 Field* fields = result->Fetch();
6516 uint32 factionId = fields[0].GetUInt32();
6518 RepRewardRate repRate;
6520 repRate.quest_rate = fields[1].GetFloat();
6521 repRate.creature_rate = fields[2].GetFloat();
6522 repRate.spell_rate = fields[3].GetFloat();
6524 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
6525 if (!factionEntry)
6527 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
6528 continue;
6531 if (repRate.quest_rate < 0.0f)
6533 sLog.outErrorDb("Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.quest_rate, factionId);
6534 continue;
6537 if (repRate.creature_rate < 0.0f)
6539 sLog.outErrorDb("Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creature_rate, factionId);
6540 continue;
6543 if (repRate.spell_rate < 0.0f)
6545 sLog.outErrorDb("Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spell_rate, factionId);
6546 continue;
6549 m_RepRewardRateMap[factionId] = repRate;
6551 ++count;
6553 while (result->NextRow());
6555 delete result;
6557 sLog.outString();
6558 sLog.outString(">> Loaded %u reputation_reward_rate", count);
6561 void ObjectMgr::LoadReputationOnKill()
6563 uint32 count = 0;
6565 // 0 1 2
6566 QueryResult* result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
6567 // 3 4 5 6 7 8 9
6568 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
6569 "FROM creature_onkill_reputation");
6571 if (!result)
6573 BarGoLink bar(1);
6575 bar.step();
6577 sLog.outString();
6578 sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
6579 return;
6582 BarGoLink bar(result->GetRowCount());
6586 Field* fields = result->Fetch();
6587 bar.step();
6589 uint32 creature_id = fields[0].GetUInt32();
6591 ReputationOnKillEntry repOnKill;
6592 repOnKill.repfaction1 = fields[1].GetUInt32();
6593 repOnKill.repfaction2 = fields[2].GetUInt32();
6594 repOnKill.is_teamaward1 = fields[3].GetBool();
6595 repOnKill.reputation_max_cap1 = fields[4].GetUInt32();
6596 repOnKill.repvalue1 = fields[5].GetInt32();
6597 repOnKill.is_teamaward2 = fields[6].GetBool();
6598 repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
6599 repOnKill.repvalue2 = fields[8].GetInt32();
6600 repOnKill.team_dependent = fields[9].GetUInt8();
6602 if (!GetCreatureTemplate(creature_id))
6604 sLog.outErrorDb("Table `creature_onkill_reputation` have data for nonexistent creature entry (%u), skipped", creature_id);
6605 continue;
6608 if (repOnKill.repfaction1)
6610 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
6611 if (!factionEntry1)
6613 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.repfaction1);
6614 continue;
6618 if (repOnKill.repfaction2)
6620 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
6621 if (!factionEntry2)
6623 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.repfaction2);
6624 continue;
6628 mRepOnKill[creature_id] = repOnKill;
6630 ++count;
6632 while (result->NextRow());
6634 delete result;
6636 sLog.outString();
6637 sLog.outString(">> Loaded %u creature award reputation definitions", count);
6640 void ObjectMgr::LoadReputationSpilloverTemplate()
6642 m_RepSpilloverTemplateMap.clear(); // for reload case
6644 uint32 count = 0;
6645 QueryResult* result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4 FROM reputation_spillover_template");
6647 if (!result)
6649 BarGoLink bar(1);
6651 bar.step();
6653 sLog.outString();
6654 sLog.outString(">> Loaded `reputation_spillover_template`, table is empty.");
6655 return;
6658 BarGoLink bar(result->GetRowCount());
6662 bar.step();
6664 Field* fields = result->Fetch();
6666 uint32 factionId = fields[0].GetUInt32();
6668 RepSpilloverTemplate repTemplate;
6670 repTemplate.faction[0] = fields[1].GetUInt32();
6671 repTemplate.faction_rate[0] = fields[2].GetFloat();
6672 repTemplate.faction_rank[0] = fields[3].GetUInt32();
6673 repTemplate.faction[1] = fields[4].GetUInt32();
6674 repTemplate.faction_rate[1] = fields[5].GetFloat();
6675 repTemplate.faction_rank[1] = fields[6].GetUInt32();
6676 repTemplate.faction[2] = fields[7].GetUInt32();
6677 repTemplate.faction_rate[2] = fields[8].GetFloat();
6678 repTemplate.faction_rank[2] = fields[9].GetUInt32();
6679 repTemplate.faction[3] = fields[10].GetUInt32();
6680 repTemplate.faction_rate[3] = fields[11].GetFloat();
6681 repTemplate.faction_rank[3] = fields[12].GetUInt32();
6683 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
6685 if (!factionEntry)
6687 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
6688 continue;
6691 if (factionEntry->team == 0)
6693 sLog.outErrorDb("Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId);
6694 continue;
6697 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
6699 if (repTemplate.faction[i])
6701 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
6703 if (!factionSpillover)
6705 sLog.outErrorDb("Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate.faction[i], factionId);
6706 continue;
6709 if (factionSpillover->reputationListID < 0)
6711 sLog.outErrorDb("Spillover faction (faction.dbc) %u for faction %u in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate.faction[i], factionId);
6712 continue;
6715 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
6717 sLog.outErrorDb("Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
6718 continue;
6723 FactionEntry const* factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]);
6724 if (repTemplate.faction[0] && !factionEntry0)
6726 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]);
6727 continue;
6729 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]);
6730 if (repTemplate.faction[1] && !factionEntry1)
6732 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]);
6733 continue;
6735 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]);
6736 if (repTemplate.faction[2] && !factionEntry2)
6738 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]);
6739 continue;
6741 FactionEntry const* factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]);
6742 if (repTemplate.faction[3] && !factionEntry3)
6744 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]);
6745 continue;
6748 m_RepSpilloverTemplateMap[factionId] = repTemplate;
6750 ++count;
6752 while (result->NextRow());
6754 delete result;
6756 sLog.outString();
6757 sLog.outString(">> Loaded %u reputation_spillover_template", count);
6760 void ObjectMgr::LoadPointsOfInterest()
6762 mPointsOfInterest.clear(); // need for reload case
6764 uint32 count = 0;
6766 // 0 1 2 3 4 5
6767 QueryResult* result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
6769 if (!result)
6771 BarGoLink bar(1);
6773 bar.step();
6775 sLog.outString();
6776 sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
6777 return;
6780 BarGoLink bar(result->GetRowCount());
6784 Field* fields = result->Fetch();
6785 bar.step();
6787 uint32 point_id = fields[0].GetUInt32();
6789 PointOfInterest POI;
6790 POI.x = fields[1].GetFloat();
6791 POI.y = fields[2].GetFloat();
6792 POI.icon = fields[3].GetUInt32();
6793 POI.flags = fields[4].GetUInt32();
6794 POI.data = fields[5].GetUInt32();
6795 POI.icon_name = fields[6].GetCppString();
6797 if (!MaNGOS::IsValidMapCoord(POI.x, POI.y))
6799 sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id, POI.x, POI.y);
6800 continue;
6803 mPointsOfInterest[point_id] = POI;
6805 ++count;
6807 while (result->NextRow());
6809 delete result;
6811 sLog.outString();
6812 sLog.outString(">> Loaded %u Points of Interest definitions", count);
6815 void ObjectMgr::LoadQuestPOI()
6817 mQuestPOIMap.clear(); // need for reload case
6819 uint32 count = 0;
6821 // 0 1 2 3 4 5 6 7
6822 QueryResult* result = WorldDatabase.Query("SELECT questId, poiId, objIndex, mapId, mapAreaId, floorId, unk3, unk4 FROM quest_poi");
6824 if (!result)
6826 BarGoLink bar(1);
6828 bar.step();
6830 sLog.outString();
6831 sLog.outErrorDb(">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
6832 return;
6835 BarGoLink bar(result->GetRowCount());
6839 Field* fields = result->Fetch();
6840 bar.step();
6842 uint32 questId = fields[0].GetUInt32();
6843 uint32 poiId = fields[1].GetUInt32();
6844 int32 objIndex = fields[2].GetInt32();
6845 uint32 mapId = fields[3].GetUInt32();
6846 uint32 mapAreaId = fields[4].GetUInt32();
6847 uint32 floorId = fields[5].GetUInt32();
6848 uint32 unk3 = fields[6].GetUInt32();
6849 uint32 unk4 = fields[7].GetUInt32();
6851 QuestPOI POI(poiId, objIndex, mapId, mapAreaId, floorId, unk3, unk4);
6853 mQuestPOIMap[questId].push_back(POI);
6855 ++count;
6857 while (result->NextRow());
6859 delete result;
6861 QueryResult* points = WorldDatabase.Query("SELECT questId, poiId, x, y FROM quest_poi_points");
6863 if (points)
6867 Field* pointFields = points->Fetch();
6869 uint32 questId = pointFields[0].GetUInt32();
6870 uint32 poiId = pointFields[1].GetUInt32();
6871 int32 x = pointFields[2].GetInt32();
6872 int32 y = pointFields[3].GetInt32();
6874 QuestPOIVector& vect = mQuestPOIMap[questId];
6876 for (QuestPOIVector::iterator itr = vect.begin(); itr != vect.end(); ++itr)
6878 if (itr->PoiId != poiId)
6879 continue;
6881 QuestPOIPoint point(x, y);
6882 itr->points.push_back(point);
6883 break;
6886 while (points->NextRow());
6888 delete points;
6891 sLog.outString();
6892 sLog.outString(">> Loaded %u quest POI definitions", count);
6895 void ObjectMgr::LoadQuestPhaseMaps()
6897 mQuestPhaseMap.clear(); // need for reload case
6899 uint32 count = 0;
6901 // 0 1
6902 QueryResult *result = WorldDatabase.Query("SELECT questId, map, phase FROM quest_phase_maps");
6904 if (!result)
6906 BarGoLink bar(1);
6908 bar.step();
6910 sLog.outString();
6911 sLog.outErrorDb(">> Loaded 0 quest phase maps definitions. DB table `quest_phase_maps` is empty.");
6912 return;
6915 BarGoLink bar(result->GetRowCount());
6919 Field *fields = result->Fetch();
6920 bar.step();
6922 uint32 questId = fields[0].GetUInt32();
6923 uint16 mapId = fields[1].GetUInt16();
6924 uint32 phase = fields[2].GetUInt32();
6926 QuestPhaseMaps QuestPhase(mapId, phase);
6928 mQuestPhaseMap[questId].push_back(QuestPhase);
6930 ++count;
6931 } while (result->NextRow());
6933 delete result;
6935 sLog.outString();
6936 sLog.outString(">> Loaded %u quest phase maps definitions", count);
6939 void ObjectMgr::LoadNPCSpellClickSpells()
6941 uint32 count = 0;
6943 mSpellClickInfoMap.clear();
6944 // 0 1 2 3 4 5
6945 QueryResult* result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells");
6947 if (!result)
6949 BarGoLink bar(1);
6951 bar.step();
6953 sLog.outString();
6954 sLog.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
6955 return;
6958 BarGoLink bar(result->GetRowCount());
6962 Field* fields = result->Fetch();
6963 bar.step();
6965 uint32 npc_entry = fields[0].GetUInt32();
6966 CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry);
6967 if (!cInfo)
6969 sLog.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
6970 continue;
6973 uint32 spellid = fields[1].GetUInt32();
6974 SpellEntry const* spellinfo = sSpellStore.LookupEntry(spellid);
6975 if (!spellinfo)
6977 sLog.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid);
6978 continue;
6981 uint32 quest_start = fields[2].GetUInt32();
6983 // quest might be 0 to enable spellclick independent of any quest
6984 if (quest_start)
6986 if (mQuestTemplates.find(quest_start) == mQuestTemplates.end())
6988 sLog.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start);
6989 continue;
6994 bool quest_start_active = fields[3].GetBool();
6996 uint32 quest_end = fields[4].GetUInt32();
6997 // quest might be 0 to enable spellclick active infinity after start quest
6998 if (quest_end)
7000 if (mQuestTemplates.find(quest_end) == mQuestTemplates.end())
7002 sLog.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end);
7003 continue;
7008 uint8 castFlags = fields[5].GetUInt8();
7009 SpellClickInfo info;
7010 info.spellId = spellid;
7011 info.questStart = quest_start;
7012 info.questStartCanActive = quest_start_active;
7013 info.questEnd = quest_end;
7014 info.castFlags = castFlags;
7015 mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info));
7017 // mark creature template as spell clickable
7018 const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;
7020 ++count;
7022 while (result->NextRow());
7024 delete result;
7026 sLog.outString();
7027 sLog.outString(">> Loaded %u spellclick definitions", count);
7030 void ObjectMgr::LoadWeatherZoneChances()
7032 uint32 count = 0;
7034 // 0 1 2 3 4 5 6 7 8 9 10 11 12
7035 QueryResult* result = WorldDatabase.Query("SELECT zone, spring_rain_chance, spring_snow_chance, spring_storm_chance, summer_rain_chance, summer_snow_chance, summer_storm_chance, fall_rain_chance, fall_snow_chance, fall_storm_chance, winter_rain_chance, winter_snow_chance, winter_storm_chance FROM game_weather");
7037 if (!result)
7039 BarGoLink bar(1);
7041 bar.step();
7043 sLog.outString();
7044 sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
7045 return;
7048 BarGoLink bar(result->GetRowCount());
7052 Field* fields = result->Fetch();
7053 bar.step();
7055 uint32 zone_id = fields[0].GetUInt32();
7057 WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
7059 for (int season = 0; season < WEATHER_SEASONS; ++season)
7061 wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE - 1) + 1].GetUInt32();
7062 wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE - 1) + 2].GetUInt32();
7063 wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE - 1) + 3].GetUInt32();
7065 if (wzc.data[season].rainChance > 100)
7067 wzc.data[season].rainChance = 25;
7068 sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%", zone_id, season);
7071 if (wzc.data[season].snowChance > 100)
7073 wzc.data[season].snowChance = 25;
7074 sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%", zone_id, season);
7077 if (wzc.data[season].stormChance > 100)
7079 wzc.data[season].stormChance = 25;
7080 sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%", zone_id, season);
7084 ++count;
7086 while (result->NextRow());
7088 delete result;
7090 sLog.outString();
7091 sLog.outString(">> Loaded %u weather definitions", count);
7094 void ObjectMgr::DeleteCreatureData(uint32 guid)
7096 // remove mapid*cellid -> guid_set map
7097 CreatureData const* data = GetCreatureData(guid);
7098 if (data)
7099 RemoveCreatureFromGrid(guid, data);
7101 mCreatureDataMap.erase(guid);
7104 void ObjectMgr::DeleteGOData(uint32 guid)
7106 // remove mapid*cellid -> guid_set map
7107 GameObjectData const* data = GetGOData(guid);
7108 if (data)
7109 RemoveGameobjectFromGrid(guid, data);
7111 mGameObjectDataMap.erase(guid);
7114 void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
7116 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7117 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid, 0)][cellid];
7118 cell_guids.corpses[player_guid] = instance;
7121 void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
7123 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7124 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid, 0)][cellid];
7125 cell_guids.corpses.erase(player_guid);
7128 void ObjectMgr::LoadQuestRelationsHelper(QuestRelationsMap& map, char const* table)
7130 map.clear(); // need for reload case
7132 uint32 count = 0;
7134 QueryResult* result = WorldDatabase.PQuery("SELECT id,quest FROM %s", table);
7136 if (!result)
7138 BarGoLink bar(1);
7140 bar.step();
7142 sLog.outString();
7143 sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.", table, table);
7144 return;
7147 BarGoLink bar(result->GetRowCount());
7151 Field* fields = result->Fetch();
7152 bar.step();
7154 uint32 id = fields[0].GetUInt32();
7155 uint32 quest = fields[1].GetUInt32();
7157 if (mQuestTemplates.find(quest) == mQuestTemplates.end())
7159 sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.", table, quest, id);
7160 continue;
7163 map.insert(QuestRelationsMap::value_type(id, quest));
7165 ++count;
7167 while (result->NextRow());
7169 delete result;
7171 sLog.outString();
7172 sLog.outString(">> Loaded %u quest relations from %s", count, table);
7175 void ObjectMgr::LoadGameobjectQuestRelations()
7177 LoadQuestRelationsHelper(m_GOQuestRelations, "gameobject_questrelation");
7179 for (QuestRelationsMap::iterator itr = m_GOQuestRelations.begin(); itr != m_GOQuestRelations.end(); ++itr)
7181 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
7182 if (!goInfo)
7183 sLog.outErrorDb("Table `gameobject_questrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr->first, itr->second);
7184 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
7185 sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
7189 void ObjectMgr::LoadGameobjectInvolvedRelations()
7191 LoadQuestRelationsHelper(m_GOQuestInvolvedRelations, "gameobject_involvedrelation");
7193 for (QuestRelationsMap::iterator itr = m_GOQuestInvolvedRelations.begin(); itr != m_GOQuestInvolvedRelations.end(); ++itr)
7195 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
7196 if (!goInfo)
7197 sLog.outErrorDb("Table `gameobject_involvedrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr->first, itr->second);
7198 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
7199 sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
7203 void ObjectMgr::LoadCreatureQuestRelations()
7205 LoadQuestRelationsHelper(m_CreatureQuestRelations, "creature_questrelation");
7207 for (QuestRelationsMap::iterator itr = m_CreatureQuestRelations.begin(); itr != m_CreatureQuestRelations.end(); ++itr)
7209 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
7210 if (!cInfo)
7211 sLog.outErrorDb("Table `creature_questrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
7212 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
7213 sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
7217 void ObjectMgr::LoadCreatureInvolvedRelations()
7219 LoadQuestRelationsHelper(m_CreatureQuestInvolvedRelations, "creature_involvedrelation");
7221 for (QuestRelationsMap::iterator itr = m_CreatureQuestInvolvedRelations.begin(); itr != m_CreatureQuestInvolvedRelations.end(); ++itr)
7223 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
7224 if (!cInfo)
7225 sLog.outErrorDb("Table `creature_involvedrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
7226 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
7227 sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
7231 void ObjectMgr::LoadReservedPlayersNames()
7233 m_ReservedNames.clear(); // need for reload case
7235 QueryResult* result = WorldDatabase.Query("SELECT name FROM reserved_name");
7237 uint32 count = 0;
7239 if (!result)
7241 BarGoLink bar(1);
7242 bar.step();
7244 sLog.outString();
7245 sLog.outString(">> Loaded %u reserved player names", count);
7246 return;
7249 BarGoLink bar(result->GetRowCount());
7251 Field* fields;
7254 bar.step();
7255 fields = result->Fetch();
7256 std::string name = fields[0].GetCppString();
7258 std::wstring wstr;
7259 if (!Utf8toWStr(name, wstr))
7261 sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str());
7262 continue;
7265 wstrToLower(wstr);
7267 m_ReservedNames.insert(wstr);
7268 ++count;
7270 while (result->NextRow());
7272 delete result;
7274 sLog.outString();
7275 sLog.outString(">> Loaded %u reserved player names", count);
7278 bool ObjectMgr::IsReservedName(const std::string& name) const
7280 std::wstring wstr;
7281 if (!Utf8toWStr(name, wstr))
7282 return false;
7284 wstrToLower(wstr);
7286 return m_ReservedNames.find(wstr) != m_ReservedNames.end();
7289 enum LanguageType
7291 LT_BASIC_LATIN = 0x0000,
7292 LT_EXTENDEN_LATIN = 0x0001,
7293 LT_CYRILLIC = 0x0002,
7294 LT_EAST_ASIA = 0x0004,
7295 LT_ANY = 0xFFFF
7298 static LanguageType GetRealmLanguageType(bool create)
7300 switch (sWorld.getConfig(CONFIG_UINT32_REALM_ZONE))
7302 case REALM_ZONE_UNKNOWN: // any language
7303 case REALM_ZONE_DEVELOPMENT:
7304 case REALM_ZONE_TEST_SERVER:
7305 case REALM_ZONE_QA_SERVER:
7306 return LT_ANY;
7307 case REALM_ZONE_UNITED_STATES: // extended-Latin
7308 case REALM_ZONE_OCEANIC:
7309 case REALM_ZONE_LATIN_AMERICA:
7310 case REALM_ZONE_ENGLISH:
7311 case REALM_ZONE_GERMAN:
7312 case REALM_ZONE_FRENCH:
7313 case REALM_ZONE_SPANISH:
7314 return LT_EXTENDEN_LATIN;
7315 case REALM_ZONE_KOREA: // East-Asian
7316 case REALM_ZONE_TAIWAN:
7317 case REALM_ZONE_CHINA:
7318 return LT_EAST_ASIA;
7319 case REALM_ZONE_RUSSIAN: // Cyrillic
7320 return LT_CYRILLIC;
7321 default:
7322 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
7326 bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
7328 if (strictMask == 0) // any language, ignore realm
7330 if (isExtendedLatinString(wstr, numericOrSpace))
7331 return true;
7332 if (isCyrillicString(wstr, numericOrSpace))
7333 return true;
7334 if (isEastAsianString(wstr, numericOrSpace))
7335 return true;
7336 return false;
7339 if (strictMask & 0x2) // realm zone specific
7341 LanguageType lt = GetRealmLanguageType(create);
7342 if (lt & LT_EXTENDEN_LATIN)
7343 if (isExtendedLatinString(wstr, numericOrSpace))
7344 return true;
7345 if (lt & LT_CYRILLIC)
7346 if (isCyrillicString(wstr, numericOrSpace))
7347 return true;
7348 if (lt & LT_EAST_ASIA)
7349 if (isEastAsianString(wstr, numericOrSpace))
7350 return true;
7353 if (strictMask & 0x1) // basic Latin
7355 if (isBasicLatinString(wstr, numericOrSpace))
7356 return true;
7359 return false;
7362 uint8 ObjectMgr::CheckPlayerName(const std::string& name, bool create)
7364 std::wstring wname;
7365 if (!Utf8toWStr(name, wname))
7366 return CHAR_NAME_INVALID_CHARACTER;
7368 if (wname.size() > MAX_PLAYER_NAME)
7369 return CHAR_NAME_TOO_LONG;
7371 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_PLAYER_NAME);
7372 if (wname.size() < minName)
7373 return CHAR_NAME_TOO_SHORT;
7375 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_PLAYER_NAMES);
7376 if (!isValidString(wname, strictMask, false, create))
7377 return CHAR_NAME_MIXED_LANGUAGES;
7379 return CHAR_NAME_SUCCESS;
7382 bool ObjectMgr::IsValidCharterName(const std::string& name)
7384 std::wstring wname;
7385 if (!Utf8toWStr(name, wname))
7386 return false;
7388 if (wname.size() > MAX_CHARTER_NAME)
7389 return false;
7391 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_CHARTER_NAME);
7392 if (wname.size() < minName)
7393 return false;
7395 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_CHARTER_NAMES);
7397 return isValidString(wname, strictMask, true);
7400 PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name)
7402 std::wstring wname;
7403 if (!Utf8toWStr(name, wname))
7404 return PET_NAME_INVALID;
7406 if (wname.size() > MAX_PET_NAME)
7407 return PET_NAME_TOO_LONG;
7409 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_PET_NAME);
7410 if (wname.size() < minName)
7411 return PET_NAME_TOO_SHORT;
7413 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_PET_NAMES);
7414 if (!isValidString(wname, strictMask, false))
7415 return PET_NAME_MIXED_LANGUAGES;
7417 return PET_NAME_SUCCESS;
7420 int ObjectMgr::GetIndexForLocale(LocaleConstant loc)
7422 if (loc == LOCALE_enUS)
7423 return -1;
7425 for (size_t i = 0; i < m_LocalForIndex.size(); ++i)
7426 if (m_LocalForIndex[i] == loc)
7427 return i;
7429 return -1;
7432 LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
7434 if (i < 0 || i >= (int32)m_LocalForIndex.size())
7435 return LOCALE_enUS;
7437 return m_LocalForIndex[i];
7440 int ObjectMgr::GetOrNewIndexForLocale(LocaleConstant loc)
7442 if (loc == LOCALE_enUS)
7443 return -1;
7445 for (size_t i = 0; i < m_LocalForIndex.size(); ++i)
7446 if (m_LocalForIndex[i] == loc)
7447 return i;
7449 m_LocalForIndex.push_back(loc);
7450 return m_LocalForIndex.size() - 1;
7453 void ObjectMgr::LoadGameObjectForQuests()
7455 mGameObjectForQuestSet.clear(); // need for reload case
7457 if (!sGOStorage.GetMaxEntry())
7459 BarGoLink bar(1);
7460 bar.step();
7461 sLog.outString();
7462 sLog.outString(">> Loaded 0 GameObjects for quests");
7463 return;
7466 BarGoLink bar(sGOStorage.GetMaxEntry() - 1);
7467 uint32 count = 0;
7469 // collect GO entries for GO that must activated
7470 for (uint32 go_entry = 1; go_entry < sGOStorage.GetMaxEntry(); ++go_entry)
7472 bar.step();
7473 GameObjectInfo const* goInfo = GetGameObjectInfo(go_entry);
7474 if (!goInfo)
7475 continue;
7477 switch (goInfo->type)
7479 case GAMEOBJECT_TYPE_QUESTGIVER:
7481 if (m_GOQuestRelations.find(go_entry) != m_GOQuestRelations.end() ||
7482 m_GOQuestInvolvedRelations.find(go_entry) != m_GOQuestInvolvedRelations.end())
7484 mGameObjectForQuestSet.insert(go_entry);
7485 ++count;
7488 break;
7490 case GAMEOBJECT_TYPE_CHEST:
7492 // scan GO chest with loot including quest items
7493 uint32 loot_id = goInfo->GetLootId();
7495 // always activate to quest, GO may not have loot, OR find if GO has loot for quest.
7496 if (goInfo->chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
7498 mGameObjectForQuestSet.insert(go_entry);
7499 ++count;
7501 break;
7503 case GAMEOBJECT_TYPE_GENERIC:
7505 if (goInfo->_generic.questID) // quest related objects, has visual effects
7507 mGameObjectForQuestSet.insert(go_entry);
7508 ++count;
7510 break;
7512 case GAMEOBJECT_TYPE_SPELL_FOCUS:
7514 if (goInfo->spellFocus.questID) // quest related objects, has visual effect
7516 mGameObjectForQuestSet.insert(go_entry);
7517 ++count;
7519 break;
7521 case GAMEOBJECT_TYPE_GOOBER:
7523 if (goInfo->goober.questId) // quests objects
7525 mGameObjectForQuestSet.insert(go_entry);
7526 ++count;
7528 break;
7530 default:
7531 break;
7535 sLog.outString();
7536 sLog.outString(">> Loaded %u GameObjects for quests", count);
7539 bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
7541 int32 start_value = min_value;
7542 int32 end_value = max_value;
7543 // some string can have negative indexes range
7544 if (start_value < 0)
7546 if (end_value >= start_value)
7548 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7549 return false;
7552 // real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1
7553 std::swap(start_value, end_value);
7554 ++start_value;
7555 ++end_value;
7557 else
7559 if (start_value >= end_value)
7561 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7562 return false;
7566 // cleanup affected map part for reloading case
7567 for (MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
7569 if (itr->first >= start_value && itr->first < end_value)
7570 mMangosStringLocaleMap.erase(itr++);
7571 else
7572 ++itr;
7575 QueryResult* result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s", table);
7577 if (!result)
7579 BarGoLink bar(1);
7581 bar.step();
7583 sLog.outString();
7584 if (min_value == MIN_MANGOS_STRING_ID) // error only in case internal strings
7585 sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.", table);
7586 else
7587 sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.", table);
7588 return false;
7591 uint32 count = 0;
7593 BarGoLink bar(result->GetRowCount());
7597 Field* fields = result->Fetch();
7598 bar.step();
7600 int32 entry = fields[0].GetInt32();
7602 if (entry == 0)
7604 sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.", table);
7605 continue;
7607 else if (entry < start_value || entry >= end_value)
7609 sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.", table, entry, min_value, max_value);
7610 continue;
7613 MangosStringLocale& data = mMangosStringLocaleMap[entry];
7615 if (data.Content.size() > 0)
7617 sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.", table, entry);
7618 continue;
7621 data.Content.resize(1);
7622 ++count;
7624 // 0 -> default, idx in to idx+1
7625 data.Content[0] = fields[1].GetCppString();
7627 for (int i = 1; i < MAX_LOCALE; ++i)
7629 std::string str = fields[i + 1].GetCppString();
7630 if (!str.empty())
7632 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7633 if (idx >= 0)
7635 // 0 -> default, idx in to idx+1
7636 if ((int32)data.Content.size() <= idx + 1)
7637 data.Content.resize(idx + 2);
7639 data.Content[idx + 1] = str;
7644 while (result->NextRow());
7646 delete result;
7648 sLog.outString();
7649 if (min_value == MIN_MANGOS_STRING_ID)
7650 sLog.outString(">> Loaded %u MaNGOS strings from table %s", count, table);
7651 else
7652 sLog.outString(">> Loaded %u string templates from %s", count, table);
7654 return true;
7657 const char* ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
7659 // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
7660 // Content[0] always exist if exist MangosStringLocale
7661 if (MangosStringLocale const* msl = GetMangosStringLocale(entry))
7663 if ((int32)msl->Content.size() > locale_idx + 1 && !msl->Content[locale_idx + 1].empty())
7664 return msl->Content[locale_idx + 1].c_str();
7665 else
7666 return msl->Content[0].c_str();
7669 if (entry > MIN_DB_SCRIPT_STRING_ID)
7670 sLog.outErrorDb("Entry %i not found in `db_script_string` table.", entry);
7671 else if (entry > 0)
7672 sLog.outErrorDb("Entry %i not found in `mangos_string` table.", entry);
7673 else if (entry > MAX_CREATURE_AI_TEXT_STRING_ID)
7674 sLog.outErrorDb("Entry %i not found in `creature_ai_texts` table.", entry);
7675 else
7676 sLog.outErrorDb("Mangos string entry %i not found in DB.", entry);
7677 return "<error>";
7680 void ObjectMgr::LoadFishingBaseSkillLevel()
7682 mFishingBaseForArea.clear(); // for reload case
7684 uint32 count = 0;
7685 QueryResult* result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
7687 if (!result)
7689 BarGoLink bar(1);
7691 bar.step();
7693 sLog.outString();
7694 sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
7695 return;
7698 BarGoLink bar(result->GetRowCount());
7702 bar.step();
7704 Field* fields = result->Fetch();
7705 uint32 entry = fields[0].GetUInt32();
7706 int32 skill = fields[1].GetInt32();
7708 AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
7709 if (!fArea)
7711 sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
7712 continue;
7715 mFishingBaseForArea[entry] = skill;
7716 ++count;
7718 while (result->NextRow());
7720 delete result;
7722 sLog.outString();
7723 sLog.outString(">> Loaded %u areas for fishing base skill level", count);
7726 // Check if a player meets condition conditionId
7727 bool ObjectMgr::IsPlayerMeetToCondition(Player const* pPlayer, uint16 conditionId) const
7729 if (!pPlayer)
7730 return false; // player not present, return false
7732 if (const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(conditionId))
7733 return condition->Meets(pPlayer);
7735 return false;
7738 bool ObjectMgr::CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names)
7740 for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
7742 std::wstring wname;
7743 if (!Utf8toWStr(names.name[i], wname))
7744 return false;
7746 if (mainpart != GetMainPartOfName(wname, i + 1))
7747 return false;
7749 return true;
7752 // Checks if player meets the condition
7753 bool PlayerCondition::Meets(Player const* player) const
7755 if (!player)
7756 return false; // player not present, return false
7758 switch (m_condition)
7760 case CONDITION_NOT:
7761 // Checked on load
7762 return !sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player);
7763 case CONDITION_OR:
7764 // Checked on load
7765 return sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player) || sConditionStorage.LookupEntry<PlayerCondition>(m_value2)->Meets(player);
7766 case CONDITION_AND:
7767 // Checked on load
7768 return sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player) && sConditionStorage.LookupEntry<PlayerCondition>(m_value2)->Meets(player);
7769 case CONDITION_NONE:
7770 return true; // empty condition, always met
7771 case CONDITION_AURA:
7772 return player->HasAura(m_value1, SpellEffectIndex(m_value2));
7773 case CONDITION_ITEM:
7774 return player->HasItemCount(m_value1, m_value2);
7775 case CONDITION_ITEM_EQUIPPED:
7776 return player->HasItemOrGemWithIdEquipped(m_value1, 1);
7777 case CONDITION_AREAID:
7779 uint32 zone, area;
7780 player->GetZoneAndAreaId(zone, area);
7781 return (zone == m_value1 || area == m_value1) == (m_value2 == 0);
7783 case CONDITION_REPUTATION_RANK_MIN:
7785 FactionEntry const* faction = sFactionStore.LookupEntry(m_value1);
7786 return faction && player->GetReputationMgr().GetRank(faction) >= ReputationRank(m_value2);
7788 case CONDITION_TEAM:
7789 return uint32(player->GetTeam()) == m_value1;
7790 case CONDITION_SKILL:
7791 return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) >= m_value2;
7792 case CONDITION_QUESTREWARDED:
7793 return player->GetQuestRewardStatus(m_value1);
7794 case CONDITION_QUESTTAKEN:
7796 return player->IsCurrentQuest(m_value1, m_value2);
7798 case CONDITION_AD_COMMISSION_AURA:
7800 Unit::SpellAuraHolderMap const& auras = player->GetSpellAuraHolderMap();
7801 for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
7802 if ((itr->second->GetSpellProto()->HasAttribute(SPELL_ATTR_CASTABLE_WHILE_MOUNTED) || itr->second->GetSpellProto()->HasAttribute(SPELL_ATTR_UNK4)) && itr->second->GetSpellProto()->SpellVisual[0] == 3580)
7803 return true;
7804 return false;
7806 case CONDITION_NO_AURA:
7807 return !player->HasAura(m_value1, SpellEffectIndex(m_value2));
7808 case CONDITION_ACTIVE_GAME_EVENT:
7809 return sGameEventMgr.IsActiveEvent(m_value1);
7810 case CONDITION_AREA_FLAG:
7812 if (AreaTableEntry const* pAreaEntry = GetAreaEntryByAreaID(player->GetAreaId()))
7814 if ((!m_value1 || (pAreaEntry->flags & m_value1)) && (!m_value2 || !(pAreaEntry->flags & m_value2)))
7815 return true;
7817 return false;
7819 case CONDITION_RACE_CLASS:
7820 if ((!m_value1 || (player->getRaceMask() & m_value1)) && (!m_value2 || (player->getClassMask() & m_value2)))
7821 return true;
7822 return false;
7823 case CONDITION_LEVEL:
7825 switch (m_value2)
7827 case 0: return player->getLevel() == m_value1;
7828 case 1: return player->getLevel() >= m_value1;
7829 case 2: return player->getLevel() <= m_value1;
7831 return false;
7833 case CONDITION_NOITEM:
7834 return !player->HasItemCount(m_value1, m_value2);
7835 case CONDITION_SPELL:
7837 switch (m_value2)
7839 case 0: return player->HasSpell(m_value1);
7840 case 1: return !player->HasSpell(m_value1);
7842 return false;
7844 case CONDITION_INSTANCE_SCRIPT:
7846 // have meaning only for specific map instance script so ignore other maps
7847 if (player->GetMapId() != m_value1)
7848 return false;
7849 if (InstanceData* data = player->GetInstanceData())
7850 return data->CheckConditionCriteriaMeet(player, m_value1, m_value2);
7851 return false;
7853 case CONDITION_QUESTAVAILABLE:
7855 if (Quest const* quest = sObjectMgr.GetQuestTemplate(m_value1))
7856 return player->CanTakeQuest(quest, false);
7857 else
7858 return false;
7860 case CONDITION_ACHIEVEMENT:
7862 switch (m_value2)
7864 case 0: return player->GetAchievementMgr().HasAchievement(m_value1);
7865 case 1: return !player->GetAchievementMgr().HasAchievement(m_value1);
7867 return false;
7869 case CONDITION_ACHIEVEMENT_REALM:
7871 AchievementEntry const* ach = sAchievementStore.LookupEntry(m_value1);
7872 switch (m_value2)
7874 case 0: return sAchievementMgr.IsRealmCompleted(ach);
7875 case 1: return !sAchievementMgr.IsRealmCompleted(ach);
7877 return false;
7879 case CONDITION_QUEST_NONE:
7881 if (!player->IsCurrentQuest(m_value1) && !player->GetQuestRewardStatus(m_value1))
7882 return true;
7883 return false;
7885 case CONDITION_ITEM_WITH_BANK:
7886 return player->HasItemCount(m_value1, m_value2, true);
7887 case CONDITION_NOITEM_WITH_BANK:
7888 return !player->HasItemCount(m_value1, m_value2, true);
7889 case CONDITION_NOT_ACTIVE_GAME_EVENT:
7890 return !sGameEventMgr.IsActiveEvent(m_value1);
7891 case CONDITION_ACTIVE_HOLIDAY:
7892 return sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1));
7893 case CONDITION_NOT_ACTIVE_HOLIDAY:
7894 return !sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1));
7895 case CONDITION_LEARNABLE_ABILITY:
7897 // Already know the spell
7898 if (player->HasSpell(m_value1))
7899 return false;
7901 // If item defined, check if player has the item already.
7902 if (m_value2)
7904 // Hard coded item count. This should be ok, since the intention with this condition is to have
7905 // a all-in-one check regarding items that learn some ability (primary/secondary tradeskills).
7906 // Commonly, items like this is unique and/or are not expected to be obtained more than once.
7907 if (player->HasItemCount(m_value2, 1, true))
7908 return false;
7911 bool isSkillOk = false;
7913 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(m_value1);
7915 for (SkillLineAbilityMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
7917 const SkillLineAbilityEntry* skillInfo = itr->second;
7919 if (!skillInfo)
7920 continue;
7922 // doesn't have skill
7923 if (!player->HasSkill(skillInfo->skillId))
7924 return false;
7926 // doesn't match class
7927 if (skillInfo->classmask && (skillInfo->classmask & player->getClassMask()) == 0)
7928 return false;
7930 // doesn't match race
7931 if (skillInfo->racemask && (skillInfo->racemask & player->getRaceMask()) == 0)
7932 return false;
7934 // skill level too low
7935 if (skillInfo->min_value > player->GetSkillValue(skillInfo->skillId))
7936 return false;
7938 isSkillOk = true;
7939 break;
7942 if (isSkillOk)
7943 return true;
7945 return false;
7947 case CONDITION_SKILL_BELOW:
7948 if (m_value2 == 1)
7949 return !player->HasSkill(m_value1);
7950 else
7951 return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) < m_value2;
7952 case CONDITION_REPUTATION_RANK_MAX:
7954 FactionEntry const* faction = sFactionStore.LookupEntry(m_value1);
7955 return faction && player->GetReputationMgr().GetRank(faction) <= ReputationRank(m_value2);
7957 case CONDITION_COMPLETED_ENCOUNTER:
7959 if (!player->GetMap()->IsDungeon())
7961 sLog.outErrorDb("CONDITION_COMPLETED_ENCOUNTER (entry %u) is used outside of a dungeon (on Map %u) by %s", m_entry, player->GetMapId(), player->GetGuidStr().c_str());
7962 return false;
7965 uint32 completedEncounterMask = ((DungeonMap*)player->GetMap())->GetPersistanceState()->GetCompletedEncountersMask();
7966 DungeonEncounterEntry const* dbcEntry1 = sDungeonEncounterStore.LookupEntry(m_value1);
7967 DungeonEncounterEntry const* dbcEntry2 = sDungeonEncounterStore.LookupEntry(m_value2);
7968 // Check that on proper map
7969 if (dbcEntry1->mapId != player->GetMapId())
7971 sLog.outErrorDb("CONDITION_COMPLETED_ENCOUNTER (entry %u, DungeonEncounterEntry %u) is used on wrong map (used on Map %u) by %s", m_entry, m_value1, player->GetMapId(), player->GetGuidStr().c_str());
7972 return false;
7974 // Select matching difficulties
7975 if (player->GetDifficulty(player->GetMap()->IsRaid()) != Difficulty(dbcEntry1->Difficulty))
7976 dbcEntry1 = NULL;
7977 if (dbcEntry2 && player->GetDifficulty(player->GetMap()->IsRaid()) != Difficulty(dbcEntry2->Difficulty))
7978 dbcEntry2 = NULL;
7980 return completedEncounterMask & ((dbcEntry1 ? 1 << dbcEntry1->encounterIndex : 0) | (dbcEntry2 ? 1 << dbcEntry2->encounterIndex : 0));
7982 default:
7983 return false;
7987 // Verification of condition values validity
7988 bool PlayerCondition::IsValid(uint16 entry, ConditionType condition, uint32 value1, uint32 value2)
7990 switch (condition)
7992 case CONDITION_NOT:
7994 if (value1 >= entry)
7996 sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry, condition, value1);
7997 return false;
7999 const PlayerCondition* condition1 = sConditionStorage.LookupEntry<PlayerCondition>(value1);
8000 if (!condition1)
8002 sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has value1 %u without proper condition, skipped", entry, condition, value1);
8003 return false;
8005 break;
8007 case CONDITION_OR:
8008 case CONDITION_AND:
8010 if (value1 >= entry)
8012 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry, condition, value1);
8013 return false;
8015 if (value2 >= entry)
8017 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value2 %u, must be lower than entry, skipped", entry, condition, value2);
8018 return false;
8020 const PlayerCondition* condition1 = sConditionStorage.LookupEntry<PlayerCondition>(value1);
8021 if (!condition1)
8023 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value1 %u without proper condition, skipped", entry, condition, value1);
8024 return false;
8026 const PlayerCondition* condition2 = sConditionStorage.LookupEntry<PlayerCondition>(value2);
8027 if (!condition2)
8029 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value2 %u without proper condition, skipped", entry, condition, value2);
8030 return false;
8032 break;
8034 case CONDITION_AURA:
8036 if (!sSpellStore.LookupEntry(value1))
8038 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
8039 return false;
8041 if (value2 >= MAX_EFFECT_INDEX)
8043 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry, condition, value2, MAX_EFFECT_INDEX - 1);
8044 return false;
8046 break;
8048 case CONDITION_ITEM:
8049 case CONDITION_NOITEM:
8050 case CONDITION_ITEM_WITH_BANK:
8051 case CONDITION_NOITEM_WITH_BANK:
8053 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value1);
8054 if (!proto)
8056 sLog.outErrorDb("Item condition (entry %u, type %u) requires to have non existing item (%u), skipped", entry, condition, value1);
8057 return false;
8060 if (value2 < 1)
8062 sLog.outErrorDb("Item condition (entry %u, type %u) useless with count < 1, skipped", entry, condition);
8063 return false;
8065 break;
8067 case CONDITION_ITEM_EQUIPPED:
8069 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value1);
8070 if (!proto)
8072 sLog.outErrorDb("ItemEquipped condition (entry %u, type %u) requires to have non existing item (%u) equipped, skipped", entry, condition, value1);
8073 return false;
8075 break;
8077 case CONDITION_AREAID:
8079 AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
8080 if (!areaEntry)
8082 sLog.outErrorDb("Zone condition (entry %u, type %u) requires to be in non existing area (%u), skipped", entry, condition, value1);
8083 return false;
8086 if (value2 > 1)
8088 sLog.outErrorDb("Zone condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2);
8089 return false;
8091 break;
8093 case CONDITION_REPUTATION_RANK_MIN:
8094 case CONDITION_REPUTATION_RANK_MAX:
8096 FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
8097 if (!factionEntry)
8099 sLog.outErrorDb("Reputation condition (entry %u, type %u) requires to have reputation non existing faction (%u), skipped", entry, condition, value1);
8100 return false;
8103 if (value2 >= MAX_REPUTATION_RANK)
8105 sLog.outErrorDb("Reputation condition (entry %u, type %u) has invalid rank requirement (value2 = %u) - must be between %u and %u, skipped", entry, condition, value2, MIN_REPUTATION_RANK, MAX_REPUTATION_RANK - 1);
8106 return false;
8108 break;
8110 case CONDITION_TEAM:
8112 if (value1 != ALLIANCE && value1 != HORDE)
8114 sLog.outErrorDb("Team condition (entry %u, type %u) specifies unknown team (%u), skipped", entry, condition, value1);
8115 return false;
8117 break;
8119 case CONDITION_SKILL:
8120 case CONDITION_SKILL_BELOW:
8122 SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(value1);
8123 if (!pSkill)
8125 sLog.outErrorDb("Skill condition (entry %u, type %u) specifies non-existing skill (%u), skipped", entry, condition, value1);
8126 return false;
8128 if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue())
8130 sLog.outErrorDb("Skill condition (entry %u, type %u) specifies invalid skill value (%u), skipped", entry, condition, value2);
8131 return false;
8133 break;
8135 case CONDITION_QUESTREWARDED:
8136 case CONDITION_QUESTTAKEN:
8137 case CONDITION_QUESTAVAILABLE:
8138 case CONDITION_QUEST_NONE:
8140 Quest const* Quest = sObjectMgr.GetQuestTemplate(value1);
8141 if (!Quest)
8143 sLog.outErrorDb("Quest condition (entry %u, type %u) specifies non-existing quest (%u), skipped", entry, condition, value1);
8144 return false;
8147 if (value2 && condition != CONDITION_QUESTTAKEN)
8148 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2);
8149 break;
8151 case CONDITION_AD_COMMISSION_AURA:
8153 if (value1)
8154 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value1 (%u)!", entry, condition, value1);
8155 if (value2)
8156 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2);
8157 break;
8159 case CONDITION_NO_AURA:
8161 if (!sSpellStore.LookupEntry(value1))
8163 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
8164 return false;
8166 if (value2 > MAX_EFFECT_INDEX)
8168 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry, condition, value2, MAX_EFFECT_INDEX - 1);
8169 return false;
8171 break;
8173 case CONDITION_ACTIVE_GAME_EVENT:
8174 case CONDITION_NOT_ACTIVE_GAME_EVENT:
8176 if (!sGameEventMgr.IsValidEvent(value1))
8178 sLog.outErrorDb("(Not)Active event condition (entry %u, type %u) requires existing event id (%u), skipped", entry, condition, value1);
8179 return false;
8181 break;
8183 case CONDITION_AREA_FLAG:
8185 if (!value1 && !value2)
8187 sLog.outErrorDb("Area flag condition (entry %u, type %u) has both values like 0, skipped", entry, condition);
8188 return false;
8190 break;
8192 case CONDITION_RACE_CLASS:
8194 if (!value1 && !value2)
8196 sLog.outErrorDb("Race_class condition (entry %u, type %u) has both values like 0, skipped", entry, condition);
8197 return false;
8200 if (value1 && !(value1 & RACEMASK_ALL_PLAYABLE))
8202 sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid player class %u, skipped", entry, condition, value1);
8203 return false;
8206 if (value2 && !(value2 & CLASSMASK_ALL_PLAYABLE))
8208 sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid race mask %u, skipped", entry, condition, value2);
8209 return false;
8211 break;
8213 case CONDITION_LEVEL:
8215 if (!value1 || value1 > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
8217 sLog.outErrorDb("Level condition (entry %u, type %u)has invalid level %u, skipped", entry, condition, value1);
8218 return false;
8221 if (value2 > 2)
8223 sLog.outErrorDb("Level condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", entry, condition, value2);
8224 return false;
8227 break;
8229 case CONDITION_SPELL:
8231 if (!sSpellStore.LookupEntry(value1))
8233 sLog.outErrorDb("Spell condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
8234 return false;
8237 if (value2 > 1)
8239 sLog.outErrorDb("Spell condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2);
8240 return false;
8243 break;
8245 case CONDITION_INSTANCE_SCRIPT:
8247 MapEntry const* mapEntry = sMapStore.LookupEntry(value1);
8248 if (!mapEntry || !mapEntry->IsDungeon())
8250 sLog.outErrorDb("Instance script condition (entry %u, type %u) has nonexistent map id %u as first arg, skipped", entry, condition, value1);
8251 return false;
8254 break;
8256 case CONDITION_ACHIEVEMENT:
8257 case CONDITION_ACHIEVEMENT_REALM:
8259 if (!sAchievementStore.LookupEntry(value1))
8261 sLog.outErrorDb("Achievement condition (entry %u, type %u) requires to have non existing achievement (Id: %d), skipped", entry, condition, value1);
8262 return false;
8265 if (value2 > 1)
8267 sLog.outErrorDb("Achievement condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2);
8268 return false;
8271 break;
8273 case CONDITION_ACTIVE_HOLIDAY:
8274 case CONDITION_NOT_ACTIVE_HOLIDAY:
8276 if (!sHolidaysStore.LookupEntry(value1))
8278 sLog.outErrorDb("(Not)Active holiday condition (entry %u, type %u) requires existing holiday id (%u), skipped", entry, condition, value1);
8279 return false;
8281 break;
8283 case CONDITION_LEARNABLE_ABILITY:
8285 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(value1);
8287 if (bounds.first == bounds.second)
8289 sLog.outErrorDb("Learnable ability condition (entry %u, type %u) has spell id %u defined, but this spell is not listed in SkillLineAbility and can not be used, skipping.", entry, condition, value1);
8290 return false;
8293 if (value2)
8295 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value2);
8296 if (!proto)
8298 sLog.outErrorDb("Learnable ability condition (entry %u, type %u) has item entry %u defined but item does not exist, skipping.", entry, condition, value2);
8299 return false;
8303 break;
8305 case CONDITION_COMPLETED_ENCOUNTER:
8307 DungeonEncounterEntry const* dbcEntry1 = sDungeonEncounterStore.LookupEntry(value1);
8308 DungeonEncounterEntry const* dbcEntry2 = sDungeonEncounterStore.LookupEntry(value2);
8309 if (!dbcEntry1)
8311 sLog.outErrorDb("Completed Encounter condition (entry %u, type %u) has an unknown DungeonEncounter entry %u defined (in value1), skipping.", entry, condition, value1);
8312 return false;
8314 if (value2 && !dbcEntry2)
8316 sLog.outErrorDb("Completed Encounter condition (entry %u, type %u) has an unknown DungeonEncounter entry %u defined (in value2), skipping.", entry, condition, value2);
8317 return false;
8319 if (dbcEntry2 && dbcEntry1->mapId != dbcEntry2->mapId)
8321 sLog.outErrorDb("Completed Encounter condition (entry %u, type %u) has different mapIds for both encounters, skipping.", entry, condition);
8322 return false;
8324 break;
8326 case CONDITION_NONE:
8327 break;
8328 default:
8329 sLog.outErrorDb("Condition entry %u has bad type of %d, skipped ", entry, condition);
8330 return false;
8332 return true;
8335 SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial)
8337 switch (pSkill->categoryId)
8339 case SKILL_CATEGORY_LANGUAGES:
8340 return SKILL_RANGE_LANGUAGE;
8341 case SKILL_CATEGORY_WEAPON:
8342 return SKILL_RANGE_LEVEL;
8343 case SKILL_CATEGORY_ARMOR:
8344 case SKILL_CATEGORY_CLASS:
8345 return SKILL_RANGE_MONO;
8346 case SKILL_CATEGORY_SECONDARY:
8347 case SKILL_CATEGORY_PROFESSION:
8348 // not set skills for professions and racial abilities
8349 if (IsProfessionSkill(pSkill->id))
8350 return SKILL_RANGE_RANK;
8351 else if (racial)
8352 return SKILL_RANGE_NONE;
8353 else
8354 return SKILL_RANGE_MONO;
8355 default:
8356 case SKILL_CATEGORY_ATTRIBUTES: // not found in dbc
8357 case SKILL_CATEGORY_GENERIC: // only GENERIC(DND)
8358 return SKILL_RANGE_NONE;
8362 void ObjectMgr::LoadGameTele()
8364 m_GameTeleMap.clear(); // for reload case
8366 uint32 count = 0;
8367 QueryResult* result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
8369 if (!result)
8371 BarGoLink bar(1);
8373 bar.step();
8375 sLog.outString();
8376 sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
8377 return;
8380 BarGoLink bar(result->GetRowCount());
8384 bar.step();
8386 Field* fields = result->Fetch();
8388 uint32 id = fields[0].GetUInt32();
8390 GameTele gt;
8392 gt.position_x = fields[1].GetFloat();
8393 gt.position_y = fields[2].GetFloat();
8394 gt.position_z = fields[3].GetFloat();
8395 gt.orientation = fields[4].GetFloat();
8396 gt.mapId = fields[5].GetUInt32();
8397 gt.name = fields[6].GetCppString();
8399 if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
8401 sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
8402 continue;
8405 if (!Utf8toWStr(gt.name, gt.wnameLow))
8407 sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
8408 continue;
8411 wstrToLower(gt.wnameLow);
8413 m_GameTeleMap[id] = gt;
8415 ++count;
8417 while (result->NextRow());
8418 delete result;
8420 sLog.outString();
8421 sLog.outString(">> Loaded %u GameTeleports", count);
8424 GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
8426 // explicit name case
8427 std::wstring wname;
8428 if (!Utf8toWStr(name, wname))
8429 return NULL;
8431 // converting string that we try to find to lower case
8432 wstrToLower(wname);
8434 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
8435 const GameTele* alt = NULL;
8436 for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
8437 if (itr->second.wnameLow == wname)
8438 return &itr->second;
8439 else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
8440 alt = &itr->second;
8442 return alt;
8445 bool ObjectMgr::AddGameTele(GameTele& tele)
8447 // find max id
8448 uint32 new_id = 0;
8449 for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
8450 if (itr->first > new_id)
8451 new_id = itr->first;
8453 // use next
8454 ++new_id;
8456 if (!Utf8toWStr(tele.name, tele.wnameLow))
8457 return false;
8459 wstrToLower(tele.wnameLow);
8461 m_GameTeleMap[new_id] = tele;
8462 std::string safeName(tele.name);
8463 WorldDatabase.escape_string(safeName);
8465 return WorldDatabase.PExecuteLog("INSERT INTO game_tele "
8466 "(id,position_x,position_y,position_z,orientation,map,name) "
8467 "VALUES (%u,%f,%f,%f,%f,%u,'%s')",
8468 new_id, tele.position_x, tele.position_y, tele.position_z,
8469 tele.orientation, tele.mapId, safeName.c_str());
8472 bool ObjectMgr::DeleteGameTele(const std::string& name)
8474 // explicit name case
8475 std::wstring wname;
8476 if (!Utf8toWStr(name, wname))
8477 return false;
8479 // converting string that we try to find to lower case
8480 wstrToLower(wname);
8482 for (GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
8484 if (itr->second.wnameLow == wname)
8486 WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'", itr->second.name.c_str());
8487 m_GameTeleMap.erase(itr);
8488 return true;
8492 return false;
8495 void ObjectMgr::LoadMailLevelRewards()
8497 m_mailLevelRewardMap.clear(); // for reload case
8499 uint32 count = 0;
8500 QueryResult* result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
8502 if (!result)
8504 BarGoLink bar(1);
8506 bar.step();
8508 sLog.outString();
8509 sLog.outErrorDb(">> Loaded `mail_level_reward`, table is empty!");
8510 return;
8513 BarGoLink bar(result->GetRowCount());
8517 bar.step();
8519 Field* fields = result->Fetch();
8521 uint8 level = fields[0].GetUInt8();
8522 uint32 raceMask = fields[1].GetUInt32();
8523 uint32 mailTemplateId = fields[2].GetUInt32();
8524 uint32 senderEntry = fields[3].GetUInt32();
8526 if (level > MAX_LEVEL)
8528 sLog.outErrorDb("Table `mail_level_reward` have data for level %u that more supported by client (%u), ignoring.", level, MAX_LEVEL);
8529 continue;
8532 if (!(raceMask & RACEMASK_ALL_PLAYABLE))
8534 sLog.outErrorDb("Table `mail_level_reward` have raceMask (%u) for level %u that not include any player races, ignoring.", raceMask, level);
8535 continue;
8538 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
8540 sLog.outErrorDb("Table `mail_level_reward` have invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.", mailTemplateId, level);
8541 continue;
8544 if (!GetCreatureTemplate(senderEntry))
8546 sLog.outErrorDb("Table `mail_level_reward` have nonexistent sender creature entry (%u) for level %u that invalid not include any player races, ignoring.", senderEntry, level);
8547 continue;
8550 m_mailLevelRewardMap[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
8552 ++count;
8554 while (result->NextRow());
8555 delete result;
8557 sLog.outString();
8558 sLog.outString(">> Loaded %u level dependent mail rewards,", count);
8561 void ObjectMgr::LoadTrainers(char const* tableName, bool isTemplates)
8563 CacheTrainerSpellMap& trainerList = isTemplates ? m_mCacheTrainerTemplateSpellMap : m_mCacheTrainerSpellMap;
8565 // For reload case
8566 for (CacheTrainerSpellMap::iterator itr = trainerList.begin(); itr != trainerList.end(); ++itr)
8567 itr->second.Clear();
8568 trainerList.clear();
8570 std::set<uint32> skip_trainers;
8572 QueryResult* result = WorldDatabase.PQuery("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM %s", tableName);
8574 if (!result)
8576 BarGoLink bar(1);
8578 bar.step();
8580 sLog.outString();
8581 sLog.outString(">> Loaded `%s`, table is empty!", tableName);
8582 return;
8585 BarGoLink bar(result->GetRowCount());
8587 std::set<uint32> talentIds;
8589 uint32 count = 0;
8592 bar.step();
8594 Field* fields = result->Fetch();
8596 uint32 entry = fields[0].GetUInt32();
8597 uint32 spell = fields[1].GetUInt32();
8599 SpellEntry const* spellinfo = sSpellStore.LookupEntry(spell);
8600 if (!spellinfo)
8602 sLog.outErrorDb("Table `%s` (Entry: %u ) has non existing spell %u, ignore", tableName, entry, spell);
8603 continue;
8606 if (!SpellMgr::IsSpellValid(spellinfo))
8608 sLog.outErrorDb("Table `%s` (Entry: %u) has broken learning spell %u, ignore", tableName, entry, spell);
8609 continue;
8612 if (GetTalentSpellCost(spell))
8614 if (talentIds.find(spell) == talentIds.end())
8616 sLog.outErrorDb("Table `%s` has talent as learning spell %u, ignore", tableName, spell);
8617 talentIds.insert(spell);
8619 continue;
8622 if (!isTemplates)
8624 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
8626 if (!cInfo)
8628 sLog.outErrorDb("Table `%s` have entry for nonexistent creature template (Entry: %u), ignore", tableName, entry);
8629 continue;
8632 if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
8634 if (skip_trainers.find(entry) == skip_trainers.end())
8636 sLog.outErrorDb("Table `%s` have data for creature (Entry: %u) without trainer flag, ignore", tableName, entry);
8637 skip_trainers.insert(entry);
8639 continue;
8642 if (TrainerSpellData const* tSpells = cInfo->trainerId ? GetNpcTrainerTemplateSpells(cInfo->trainerId) : NULL)
8644 if (tSpells->spellList.find(spell) != tSpells->spellList.end())
8646 sLog.outErrorDb("Table `%s` (Entry: %u) has spell %u listed in trainer template %u, ignore", tableName, entry, spell, cInfo->trainerId);
8647 continue;
8652 TrainerSpellData& data = trainerList[entry];
8654 TrainerSpell& trainerSpell = data.spellList[spell];
8655 trainerSpell.spell = spell;
8656 trainerSpell.spellCost = fields[2].GetUInt32();
8657 trainerSpell.reqSkill = fields[3].GetUInt32();
8658 trainerSpell.reqSkillValue = fields[4].GetUInt32();
8659 trainerSpell.reqLevel = fields[5].GetUInt32();
8661 trainerSpell.isProvidedReqLevel = trainerSpell.reqLevel > 0;
8663 // calculate learned spell for profession case when stored cast-spell
8664 trainerSpell.learnedSpell = spell;
8665 for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
8667 SpellEffectEntry const* spellEffect = spellinfo->GetSpellEffect(SpellEffectIndex(i));
8668 if (!spellEffect)
8669 continue;
8671 if (spellEffect->Effect == SPELL_EFFECT_LEARN_SPELL &&
8672 SpellMgr::IsProfessionOrRidingSpell(spellEffect->EffectTriggerSpell))
8674 // prof spells sometime only additions to main spell learn that have level data
8675 for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
8677 SpellEffectEntry const* spellEff = spellinfo->GetSpellEffect(SpellEffectIndex(j));
8678 if (!spellEff)
8679 continue;
8681 if (spellEff->Effect == SPELL_EFFECT_LEARN_SPELL)
8683 trainerSpell.learnedSpell = spellEff->EffectTriggerSpell;
8684 break;
8687 break;
8691 // already checked as valid spell so exist.
8692 SpellEntry const* learnSpellinfo = sSpellStore.LookupEntry(trainerSpell.learnedSpell);
8693 if (SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell))
8695 data.trainerType = 2;
8697 uint32 minLevel = sSpellMgr.GetProfessionSpellMinLevel(trainerSpell.learnedSpell);
8698 if (trainerSpell.reqLevel)
8700 if (minLevel == trainerSpell.reqLevel)
8701 ERROR_DB_STRICT_LOG("Table `%s` (Entry: %u) has redundant reqlevel %u (=prof reqlevel) for spell %u", tableName, entry, trainerSpell.reqLevel, spell);
8702 else
8703 sLog.outErrorDb("Table `%s` (Entry: %u) has wrong redundant reqlevel %u (<>prof reqlevel %u) for spell %u", tableName, entry, trainerSpell.reqLevel, minLevel, spell);
8705 else
8706 trainerSpell.reqLevel = minLevel;
8708 // for non-prof. spell use spellLevel if not provided any
8709 else
8711 if (trainerSpell.reqLevel)
8713 if (trainerSpell.reqLevel == learnSpellinfo->GetSpellLevel())
8714 ERROR_DB_STRICT_LOG("Table `%s` (Entry: %u) has redundant reqlevel %u (=spell level) for spell %u", tableName, entry, trainerSpell.reqLevel, spell);
8716 else
8717 trainerSpell.reqLevel = learnSpellinfo->GetSpellLevel();
8720 ++count;
8723 while (result->NextRow());
8724 delete result;
8726 sLog.outString();
8727 sLog.outString(">> Loaded %d trainer %sspells", count, isTemplates ? "template " : "");
8730 void ObjectMgr::LoadTrainerTemplates()
8732 LoadTrainers("npc_trainer_template", true);
8734 // post loading check
8735 std::set<uint32> trainer_ids;
8737 for (CacheTrainerSpellMap::const_iterator tItr = m_mCacheTrainerTemplateSpellMap.begin(); tItr != m_mCacheTrainerTemplateSpellMap.end(); ++tItr)
8738 trainer_ids.insert(tItr->first);
8740 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
8742 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
8744 if (cInfo->trainerId)
8746 if (m_mCacheTrainerTemplateSpellMap.find(cInfo->trainerId) != m_mCacheTrainerTemplateSpellMap.end())
8747 trainer_ids.erase(cInfo->trainerId);
8748 else
8749 sLog.outErrorDb("Creature (Entry: %u) has trainer_id = %u for nonexistent trainer template", cInfo->Entry, cInfo->trainerId);
8754 for (std::set<uint32>::const_iterator tItr = trainer_ids.begin(); tItr != trainer_ids.end(); ++tItr)
8755 sLog.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr);
8758 void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates)
8760 CacheVendorItemMap& vendorList = isTemplates ? m_mCacheVendorTemplateItemMap : m_mCacheVendorItemMap;
8762 // For reload case
8763 for (CacheVendorItemMap::iterator itr = vendorList.begin(); itr != vendorList.end(); ++itr)
8764 itr->second.Clear();
8765 vendorList.clear();
8767 std::set<uint32> skip_vendors;
8769 QueryResult* result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM %s", tableName);
8770 if (!result)
8772 BarGoLink bar(1);
8774 bar.step();
8776 sLog.outString();
8777 sLog.outString(">> Loaded `%s`, table is empty!", tableName);
8778 return;
8781 BarGoLink bar(result->GetRowCount());
8783 uint32 count = 0;
8786 bar.step();
8787 Field* fields = result->Fetch();
8789 uint32 entry = fields[0].GetUInt32();
8790 uint32 item_id = abs(fields[1].GetInt32());
8791 uint8 type = fields[1].GetInt32() > 0 ? VENDOR_ITEM_TYPE_ITEM : VENDOR_ITEM_TYPE_CURRENCY;
8792 uint32 maxcount = fields[2].GetUInt32();
8793 uint32 incrtime = fields[3].GetUInt32();
8794 uint32 ExtendedCost = fields[4].GetUInt32();
8796 if (!IsVendorItemValid(isTemplates, tableName, entry, item_id, type, maxcount, incrtime, ExtendedCost, NULL, &skip_vendors))
8797 continue;
8799 VendorItemData& vList = vendorList[entry];
8801 vList.AddItem(item_id, type, maxcount, incrtime, ExtendedCost);
8802 ++count;
8805 while (result->NextRow());
8806 delete result;
8808 sLog.outString();
8809 sLog.outString(">> Loaded %u vendor %sitems", count, isTemplates ? "template " : "");
8813 void ObjectMgr::LoadVendorTemplates()
8815 LoadVendors("npc_vendor_template", true);
8817 // post loading check
8818 std::set<uint32> vendor_ids;
8820 for (CacheVendorItemMap::const_iterator vItr = m_mCacheVendorTemplateItemMap.begin(); vItr != m_mCacheVendorTemplateItemMap.end(); ++vItr)
8821 vendor_ids.insert(vItr->first);
8823 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
8825 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
8827 if (cInfo->vendorId)
8829 if (m_mCacheVendorTemplateItemMap.find(cInfo->vendorId) != m_mCacheVendorTemplateItemMap.end())
8830 vendor_ids.erase(cInfo->vendorId);
8831 else
8832 sLog.outErrorDb("Creature (Entry: %u) has vendor_id = %u for nonexistent vendor template", cInfo->Entry, cInfo->vendorId);
8837 for (std::set<uint32>::const_iterator vItr = vendor_ids.begin(); vItr != vendor_ids.end(); ++vItr)
8838 sLog.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr);
8841 void ObjectMgr::LoadNpcGossips()
8844 m_mCacheNpcTextIdMap.clear();
8846 QueryResult* result = WorldDatabase.Query("SELECT npc_guid, textid FROM npc_gossip");
8847 if (!result)
8849 BarGoLink bar(1);
8851 bar.step();
8853 sLog.outString();
8854 sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
8855 return;
8858 BarGoLink bar(result->GetRowCount());
8860 uint32 count = 0;
8861 uint32 guid, textid;
8864 bar.step();
8866 Field* fields = result->Fetch();
8868 guid = fields[0].GetUInt32();
8869 textid = fields[1].GetUInt32();
8871 if (!GetCreatureData(guid))
8873 sLog.outErrorDb("Table `npc_gossip` have nonexistent creature (GUID: %u) entry, ignore. ", guid);
8874 continue;
8876 if (!GetGossipText(textid))
8878 sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
8879 continue;
8882 m_mCacheNpcTextIdMap[guid] = textid ;
8883 ++count;
8886 while (result->NextRow());
8887 delete result;
8889 sLog.outString();
8890 sLog.outString(">> Loaded %d NpcTextId ", count);
8893 void ObjectMgr::LoadGossipMenu(std::set<uint32>& gossipScriptSet)
8895 m_mGossipMenusMap.clear();
8896 // 0 1 2
8897 QueryResult* result = WorldDatabase.Query("SELECT entry, text_id, script_id, "
8898 // 3
8899 "condition_id FROM gossip_menu");
8901 if (!result)
8903 BarGoLink bar(1);
8905 bar.step();
8907 sLog.outString();
8908 sLog.outErrorDb(">> Loaded gossip_menu, table is empty!");
8909 return;
8912 BarGoLink bar(result->GetRowCount());
8914 uint32 count = 0;
8918 bar.step();
8920 Field* fields = result->Fetch();
8922 GossipMenus gMenu;
8924 gMenu.entry = fields[0].GetUInt32();
8925 gMenu.text_id = fields[1].GetUInt32();
8926 gMenu.script_id = fields[2].GetUInt32();
8928 gMenu.conditionId = fields[3].GetUInt16();
8930 if (!GetGossipText(gMenu.text_id))
8932 sLog.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
8933 continue;
8936 // Check script-id
8937 if (gMenu.script_id)
8939 if (sGossipScripts.second.find(gMenu.script_id) == sGossipScripts.second.end())
8941 sLog.outErrorDb("Table gossip_menu for menu %u, text-id %u have script_id %u that does not exist in `gossip_scripts`, ignoring", gMenu.entry, gMenu.text_id, gMenu.script_id);
8942 continue;
8945 // Remove used script id
8946 gossipScriptSet.erase(gMenu.script_id);
8949 if (gMenu.conditionId)
8951 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(gMenu.conditionId);
8952 if (!condition)
8954 sLog.outErrorDb("Table gossip_menu for menu %u, text-id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenu.entry, gMenu.text_id, gMenu.conditionId);
8955 gMenu.conditionId = 0;
8959 m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu));
8961 ++count;
8963 while (result->NextRow());
8965 delete result;
8967 sLog.outString();
8968 sLog.outString(">> Loaded %u gossip_menu entries", count);
8970 // post loading tests
8971 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
8972 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
8973 if (cInfo->GossipMenuId)
8974 if (m_mGossipMenusMap.find(cInfo->GossipMenuId) == m_mGossipMenusMap.end())
8975 sLog.outErrorDb("Creature (Entry: %u) has gossip_menu_id = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId);
8977 for (uint32 i = 1; i < sGOStorage.GetMaxEntry(); ++i)
8978 if (GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
8979 if (uint32 menuid = gInfo->GetGossipMenuId())
8980 if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end())
8981 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", gInfo->id, menuid);
8984 void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
8986 m_mGossipMenuItemsMap.clear();
8988 QueryResult* result = WorldDatabase.Query(
8989 "SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
8990 "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, "
8991 "condition_id "
8992 "FROM gossip_menu_option");
8994 if (!result)
8996 BarGoLink bar(1);
8998 bar.step();
9000 sLog.outString();
9001 sLog.outErrorDb(">> Loaded gossip_menu_option, table is empty!");
9002 return;
9005 // prepare data for unused menu ids
9006 std::set<uint32> menu_ids; // for later integrity check
9007 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)) // check unused menu ids only in strict mode
9009 for (GossipMenusMap::const_iterator itr = m_mGossipMenusMap.begin(); itr != m_mGossipMenusMap.end(); ++itr)
9010 if (itr->first)
9011 menu_ids.insert(itr->first);
9013 for (uint32 i = 1; i < sGOStorage.GetMaxEntry(); ++i)
9014 if (GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
9015 if (uint32 menuid = gInfo->GetGossipMenuId())
9016 menu_ids.erase(menuid);
9019 // loading
9020 BarGoLink bar(result->GetRowCount());
9022 uint32 count = 0;
9024 // prepare menuid -> CreatureInfo map for fast access
9025 typedef std::multimap<uint32, const CreatureInfo*> Menu2CInfoMap;
9026 Menu2CInfoMap menu2CInfoMap;
9027 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
9028 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
9029 if (cInfo->GossipMenuId)
9031 menu2CInfoMap.insert(Menu2CInfoMap::value_type(cInfo->GossipMenuId, cInfo));
9033 // unused check data preparing part
9034 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
9035 menu_ids.erase(cInfo->GossipMenuId);
9040 bar.step();
9042 Field* fields = result->Fetch();
9044 GossipMenuItems gMenuItem;
9046 gMenuItem.menu_id = fields[0].GetUInt32();
9047 gMenuItem.id = fields[1].GetUInt32();
9048 gMenuItem.option_icon = fields[2].GetUInt8();
9049 gMenuItem.option_text = fields[3].GetCppString();
9050 gMenuItem.option_id = fields[4].GetUInt32();
9051 gMenuItem.npc_option_npcflag = fields[5].GetUInt32();
9052 gMenuItem.action_menu_id = fields[6].GetInt32();
9053 gMenuItem.action_poi_id = fields[7].GetUInt32();
9054 gMenuItem.action_script_id = fields[8].GetUInt32();
9055 gMenuItem.box_coded = fields[9].GetUInt8() != 0;
9056 gMenuItem.box_money = fields[10].GetUInt32();
9057 gMenuItem.box_text = fields[11].GetCppString();
9059 gMenuItem.conditionId = fields[12].GetUInt16();
9061 if (gMenuItem.menu_id) // == 0 id is special and not have menu_id data
9063 if (m_mGossipMenusMap.find(gMenuItem.menu_id) == m_mGossipMenusMap.end())
9065 sLog.outErrorDb("Gossip menu option (MenuId: %u) for nonexistent menu", gMenuItem.menu_id);
9066 continue;
9070 if (gMenuItem.action_menu_id > 0)
9072 if (m_mGossipMenusMap.find(gMenuItem.action_menu_id) == m_mGossipMenusMap.end())
9073 sLog.outErrorDb("Gossip menu option (MenuId: %u Id: %u) have action_menu_id = %u for nonexistent menu", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_menu_id);
9074 else if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
9075 menu_ids.erase(gMenuItem.action_menu_id);
9078 if (gMenuItem.option_icon >= GOSSIP_ICON_MAX)
9080 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_icon);
9081 gMenuItem.option_icon = GOSSIP_ICON_CHAT;
9084 if (gMenuItem.option_id == GOSSIP_OPTION_NONE)
9085 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u use option id GOSSIP_OPTION_NONE. Option will never be used", gMenuItem.menu_id, gMenuItem.id);
9087 if (gMenuItem.option_id >= GOSSIP_OPTION_MAX)
9088 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_id);
9090 if (gMenuItem.menu_id && gMenuItem.npc_option_npcflag)
9092 bool found_menu_uses = false;
9093 bool found_flags_uses = false;
9095 std::pair<Menu2CInfoMap::const_iterator, Menu2CInfoMap::const_iterator> tm_bounds = menu2CInfoMap.equal_range(gMenuItem.menu_id);
9096 for (Menu2CInfoMap::const_iterator it2 = tm_bounds.first; !found_flags_uses && it2 != tm_bounds.second; ++it2)
9098 CreatureInfo const* cInfo = it2->second;
9100 found_menu_uses = true;
9102 // some from creatures with gossip menu can use gossip option base at npc_flags
9103 if (gMenuItem.npc_option_npcflag & cInfo->npcflag)
9104 found_flags_uses = true;
9107 if (found_menu_uses && !found_flags_uses)
9108 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_npcflag` = %u but creatures using this menu does not have corresponding`npcflag`. Option will not accessible in game.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.npc_option_npcflag);
9111 if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
9113 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_poi_id);
9114 gMenuItem.action_poi_id = 0;
9117 if (gMenuItem.action_script_id)
9119 if (sGossipScripts.second.find(gMenuItem.action_script_id) == sGossipScripts.second.end())
9121 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u that does not exist in `gossip_scripts`, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
9122 continue;
9125 // Remove used script id
9126 gossipScriptSet.erase(gMenuItem.action_script_id);
9129 if (gMenuItem.conditionId)
9131 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(gMenuItem.conditionId);
9132 if (!condition)
9134 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.conditionId);
9135 gMenuItem.conditionId = 0;
9139 m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));
9141 ++count;
9144 while (result->NextRow());
9146 delete result;
9148 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
9150 for (std::set<uint32>::const_iterator itr = menu_ids.begin(); itr != menu_ids.end(); ++itr)
9151 sLog.outErrorDb("Table `gossip_menu` contain unused (in creature or GO or menu options) menu id %u.", *itr);
9154 sLog.outString();
9155 sLog.outString(">> Loaded %u gossip_menu_option entries", count);
9158 void ObjectMgr::LoadGossipMenus()
9160 // Check which script-ids in gossip_scripts are not used
9161 std::set<uint32> gossipScriptSet;
9162 for (ScriptMapMap::const_iterator itr = sGossipScripts.second.begin(); itr != sGossipScripts.second.end(); ++itr)
9163 gossipScriptSet.insert(itr->first);
9165 // Load gossip_menu and gossip_menu_option data
9166 sLog.outString("(Re)Loading Gossip menus...");
9167 LoadGossipMenu(gossipScriptSet);
9168 sLog.outString("(Re)Loading Gossip menu options...");
9169 LoadGossipMenuItems(gossipScriptSet);
9171 for (std::set<uint32>::const_iterator itr = gossipScriptSet.begin(); itr != gossipScriptSet.end(); ++itr)
9172 sLog.outErrorDb("Table `gossip_scripts` contains unused script, id %u.", *itr);
9175 void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, uint8 type, uint32 maxcount, uint32 incrtime, uint32 extendedcost)
9177 VendorItemData& vList = m_mCacheVendorItemMap[entry];
9178 vList.AddItem(item, type, maxcount, incrtime, extendedcost);
9180 WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%i','%u','%u','%u')", entry, type == VENDOR_ITEM_TYPE_CURRENCY ? -int32(item) : item, maxcount, incrtime, extendedcost);
9183 bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type)
9185 CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
9186 if (iter == m_mCacheVendorItemMap.end())
9187 return false;
9189 if (!iter->second.RemoveItem(item, type))
9190 return false;
9192 WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%i'", entry, type == VENDOR_ITEM_TYPE_CURRENCY ? -int32(item) : item);
9193 return true;
9196 bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item_id, uint8 type, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors) const
9198 char const* idStr = isTemplate ? "vendor template" : "vendor";
9199 char const* nameStr = type == VENDOR_ITEM_TYPE_CURRENCY ? "Currency" : "Item";
9200 CreatureInfo const* cInfo = NULL;
9202 if (!isTemplate)
9204 cInfo = GetCreatureTemplate(vendor_entry);
9205 if (!cInfo)
9207 if (pl)
9208 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
9209 else
9210 sLog.outErrorDb("Table `%s` has data for nonexistent creature (Entry: %u), ignoring", tableName, vendor_entry);
9211 return false;
9214 if (!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
9216 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9218 if (pl)
9219 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
9220 else
9221 sLog.outErrorDb("Table `%s` has data for creature (Entry: %u) without vendor flag, ignoring", tableName, vendor_entry);
9223 if (skip_vendors)
9224 skip_vendors->insert(vendor_entry);
9226 return false;
9230 CurrencyTypesEntry const * currencyEntry;
9231 if (type == VENDOR_ITEM_TYPE_ITEM)
9233 if (!GetItemPrototype(item_id))
9235 if (pl)
9236 ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
9237 else
9238 sLog.outErrorDb("Table `%s` for %s %u contains nonexistent item (%u), ignoring",
9239 tableName, idStr, vendor_entry, item_id);
9240 return false;
9243 else if (type == VENDOR_ITEM_TYPE_CURRENCY)
9245 currencyEntry = sCurrencyTypesStore.LookupEntry(item_id);
9246 if (!currencyEntry)
9248 if (pl)
9249 ChatHandler(pl).PSendSysMessage(LANG_CURRENCY_NOT_FOUND, item_id);
9250 else
9251 sLog.outErrorDb("Table `%s` for %s %u contains nonexistent currency (%u), ignoring",
9252 tableName, idStr, vendor_entry, item_id);
9253 return false;
9255 else
9257 if (currencyEntry->ID == CURRENCY_CONQUEST_ARENA_META || currencyEntry->ID == CURRENCY_CONQUEST_BG_META)
9259 if (pl)
9260 ChatHandler(pl).PSendSysMessage(LANG_VENDOR_META_CURRENCY_NOT_ALLOWED, item_id);
9261 else
9262 sLog.outErrorDb("Table `%s` for %s %u contains not allowed meta currency (%u), ignoring",
9263 tableName, idStr, vendor_entry, item_id);
9264 return false;
9268 else
9270 if (pl)
9271 ChatHandler(pl).PSendSysMessage(LANG_VENDOR_WRONG_ITEM_TYPE, item_id, type);
9272 else
9273 sLog.outErrorDb("Table `%s` for %s %u contains nonexistent vendor item type %u (entry %u), ignoring",
9274 tableName, idStr, vendor_entry, type, item_id);
9277 if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
9279 if (pl)
9280 ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
9281 else
9282 sLog.outErrorDb("Table `%s` contains %s (Entry: %u) with wrong ExtendedCost (%u) for %s %u, ignoring",
9283 tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry);
9284 return false;
9287 if (type == VENDOR_ITEM_TYPE_ITEM)
9289 if (maxcount > 0 && incrtime == 0)
9291 if (pl)
9292 ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
9293 else
9294 sLog.outErrorDb("Table `%s` has `maxcount` (%u) for %s %u of %s %u but `incrtime`=0, ignoring",
9295 tableName, maxcount, nameStr, item_id, idStr, vendor_entry);
9296 return false;
9298 else if (maxcount == 0 && incrtime > 0)
9300 if (pl)
9301 ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
9302 else
9303 sLog.outErrorDb("Table `%s` has `maxcount`=0 for %s %u of %s %u but `incrtime`<>0, ignoring",
9304 tableName, nameStr, item_id, idStr, vendor_entry);
9305 return false;
9308 else if (type == VENDOR_ITEM_TYPE_CURRENCY)
9310 if (maxcount < uint32(currencyEntry->GetPrecision()))
9312 if (pl)
9313 ChatHandler(pl).PSendSysMessage(LANG_VENDOR_WRONG_CURRENCY_MAXCOUNT, item_id, uint32(currencyEntry->GetPrecision()));
9314 else
9315 sLog.outErrorDb("Table `%s` contains %s (Entry: %u) with too low maxcount. Maxcount for currencies is buycount, so it can't be 0 or less than that's currency precision (%u), ignoring",
9316 tableName, nameStr, item_id, uint32(currencyEntry->GetPrecision()));
9317 return false;
9321 VendorItemData const* vItems = isTemplate ? GetNpcVendorTemplateItemList(vendor_entry) : GetNpcVendorItemList(vendor_entry);
9322 VendorItemData const* tItems = isTemplate ? NULL : GetNpcVendorTemplateItemList(vendor_entry);
9324 if (!vItems && !tItems)
9325 return true; // later checks for non-empty lists
9327 if (vItems && vItems->FindItemCostPair(item_id, type, ExtendedCost))
9329 if (pl)
9330 ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, type == VENDOR_ITEM_TYPE_CURRENCY, ExtendedCost);
9331 else
9332 sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring",
9333 tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry);
9334 return false;
9337 if (!isTemplate)
9339 if (tItems && tItems->FindItemCostPair(item_id, type, ExtendedCost))
9341 if (pl)
9342 ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, type == VENDOR_ITEM_TYPE_CURRENCY, ExtendedCost);
9343 else
9345 if (!cInfo->vendorId)
9346 sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring",
9347 tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry);
9348 else
9349 sLog.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u (or possible in vendor template %u), ignoring",
9350 tableName, nameStr, item_id, ExtendedCost, idStr, vendor_entry, cInfo->vendorId);
9352 return false;
9356 uint32 countItems = vItems ? vItems->GetItemCount() : 0;
9357 countItems += tItems ? tItems->GetItemCount() : 0;
9359 if (countItems >= MAX_VENDOR_ITEMS)
9361 if (pl)
9362 ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
9363 else
9364 sLog.outErrorDb("Table `%s` has too many entries (%u >= %i) for %s %u, ignoring",
9365 tableName, countItems, MAX_VENDOR_ITEMS, idStr, vendor_entry);
9366 return false;
9369 return true;
9372 void ObjectMgr::AddGroup(Group* group)
9374 mGroupMap[group->GetId()] = group ;
9377 void ObjectMgr::RemoveGroup(Group* group)
9379 mGroupMap.erase(group->GetId());
9382 void ObjectMgr::AddArenaTeam(ArenaTeam* arenaTeam)
9384 mArenaTeamMap[arenaTeam->GetId()] = arenaTeam;
9387 void ObjectMgr::RemoveArenaTeam(uint32 Id)
9389 mArenaTeamMap.erase(Id);
9393 void ObjectMgr::GetCreatureLocaleStrings(uint32 entry, int32 loc_idx, char const** namePtr, char const** subnamePtr) const
9395 if (loc_idx >= 0)
9397 if (CreatureLocale const* il = GetCreatureLocale(entry))
9399 if (namePtr && il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
9400 *namePtr = il->Name[loc_idx].c_str();
9402 if (subnamePtr && il->SubName.size() > size_t(loc_idx) && !il->SubName[loc_idx].empty())
9403 *subnamePtr = il->SubName[loc_idx].c_str();
9408 void ObjectMgr::GetItemLocaleStrings(uint32 entry, int32 loc_idx, std::string* namePtr, std::string* descriptionPtr) const
9410 if (loc_idx >= 0)
9412 if (ItemLocale const* il = GetItemLocale(entry))
9414 if (namePtr && il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
9415 *namePtr = il->Name[loc_idx];
9417 if (descriptionPtr && il->Description.size() > size_t(loc_idx) && !il->Description[loc_idx].empty())
9418 *descriptionPtr = il->Description[loc_idx];
9423 void ObjectMgr::GetQuestLocaleStrings(uint32 entry, int32 loc_idx, std::string* titlePtr) const
9425 if (loc_idx >= 0)
9427 if (QuestLocale const* il = GetQuestLocale(entry))
9429 if (titlePtr && il->Title.size() > size_t(loc_idx) && !il->Title[loc_idx].empty())
9430 *titlePtr = il->Title[loc_idx];
9435 void ObjectMgr::GetNpcTextLocaleStringsAll(uint32 entry, int32 loc_idx, ObjectMgr::NpcTextArray* text0_Ptr, ObjectMgr::NpcTextArray* text1_Ptr) const
9437 if (loc_idx >= 0)
9439 if (NpcTextLocale const* nl = GetNpcTextLocale(entry))
9441 if (text0_Ptr)
9442 for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
9443 if (nl->Text_0[i].size() > (size_t)loc_idx && !nl->Text_0[i][loc_idx].empty())
9444 (*text0_Ptr)[i] = nl->Text_0[i][loc_idx];
9446 if (text1_Ptr)
9447 for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
9448 if (nl->Text_1[i].size() > (size_t)loc_idx && !nl->Text_1[i][loc_idx].empty())
9449 (*text1_Ptr)[i] = nl->Text_1[i][loc_idx];
9454 void ObjectMgr::GetNpcTextLocaleStrings0(uint32 entry, int32 loc_idx, std::string* text0_0_Ptr, std::string* text1_0_Ptr) const
9456 if (loc_idx >= 0)
9458 if (NpcTextLocale const* nl = GetNpcTextLocale(entry))
9460 if (text0_0_Ptr)
9461 if (nl->Text_0[0].size() > (size_t)loc_idx && !nl->Text_0[0][loc_idx].empty())
9462 *text0_0_Ptr = nl->Text_0[0][loc_idx];
9464 if (text1_0_Ptr)
9465 if (nl->Text_1[0].size() > (size_t)loc_idx && !nl->Text_1[0][loc_idx].empty())
9466 *text1_0_Ptr = nl->Text_1[0][loc_idx];
9471 // Functions for scripting access
9472 bool LoadMangosStrings(DatabaseType& db, char const* table, int32 start_value, int32 end_value)
9474 // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
9475 // start/end reversed for negative values
9476 if (start_value > MAX_DB_SCRIPT_STRING_ID || end_value >= start_value)
9478 sLog.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.", table, start_value, end_value + 1);
9479 return false;
9482 return sObjectMgr.LoadMangosStrings(db, table, start_value, end_value);
9485 void ObjectMgr::LoadCreatureTemplateSpells()
9487 sCreatureTemplateSpellsStorage.Load();
9489 sLog.outString(">> Loaded %u creature_template_spells definitions", sCreatureTemplateSpellsStorage.GetRecordCount());
9490 sLog.outString();
9492 for (SQLStorageBase::SQLSIterator<CreatureTemplateSpells> itr = sCreatureTemplateSpellsStorage.getDataBegin<CreatureTemplateSpells>(); itr < sCreatureTemplateSpellsStorage.getDataEnd<CreatureTemplateSpells>(); ++itr)
9494 if (!sCreatureStorage.LookupEntry<CreatureInfo>(itr->entry))
9496 sLog.outErrorDb("LoadCreatureTemplateSpells: Spells found for creature entry %u, but creature does not exist, skipping", itr->entry);
9497 sCreatureTemplateSpellsStorage.EraseEntry(itr->entry);
9499 for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
9501 if (itr->spells[i] && !sSpellStore.LookupEntry(itr->spells[i]))
9503 sLog.outErrorDb("LoadCreatureTemplateSpells: Spells found for creature entry %u, assigned spell %u does not exist, set to 0", itr->entry, itr->spells[i]);
9504 const_cast<CreatureTemplateSpells*>(*itr)->spells[i] = 0;
9510 CreatureInfo const* GetCreatureTemplateStore(uint32 entry)
9512 return sCreatureStorage.LookupEntry<CreatureInfo>(entry);
9515 Quest const* GetQuestTemplateStore(uint32 entry)
9517 return sObjectMgr.GetQuestTemplate(entry);
9520 bool FindCreatureData::operator()(CreatureDataPair const& dataPair)
9522 // skip wrong entry ids
9523 if (i_id && dataPair.second.id != i_id)
9524 return false;
9526 if (!i_anyData)
9527 i_anyData = &dataPair;
9529 // without player we can't find more stricted cases, so use fouded
9530 if (!i_player)
9531 return true;
9533 // skip diff. map cases
9534 if (dataPair.second.mapid != i_player->GetMapId())
9535 return false;
9537 float new_dist = i_player->GetDistance2d(dataPair.second.posX, dataPair.second.posY);
9539 if (!i_mapData || new_dist < i_mapDist)
9541 i_mapData = &dataPair;
9542 i_mapDist = new_dist;
9545 // skip not spawned (in any state),
9546 uint16 pool_id = sPoolMgr.IsPartOfAPool<Creature>(dataPair.first);
9547 if (pool_id && !i_player->GetMap()->GetPersistentState()->IsSpawnedPoolObject<Creature>(dataPair.first))
9548 return false;
9550 if (!i_spawnedData || new_dist < i_spawnedDist)
9552 i_spawnedData = &dataPair;
9553 i_spawnedDist = new_dist;
9556 return false;
9559 CreatureDataPair const* FindCreatureData::GetResult() const
9561 if (i_spawnedData)
9562 return i_spawnedData;
9564 if (i_mapData)
9565 return i_mapData;
9567 return i_anyData;
9570 bool FindGOData::operator()(GameObjectDataPair const& dataPair)
9572 // skip wrong entry ids
9573 if (i_id && dataPair.second.id != i_id)
9574 return false;
9576 if (!i_anyData)
9577 i_anyData = &dataPair;
9579 // without player we can't find more stricted cases, so use fouded
9580 if (!i_player)
9581 return true;
9583 // skip diff. map cases
9584 if (dataPair.second.mapid != i_player->GetMapId())
9585 return false;
9587 float new_dist = i_player->GetDistance2d(dataPair.second.posX, dataPair.second.posY);
9589 if (!i_mapData || new_dist < i_mapDist)
9591 i_mapData = &dataPair;
9592 i_mapDist = new_dist;
9595 // skip not spawned (in any state)
9596 uint16 pool_id = sPoolMgr.IsPartOfAPool<GameObject>(dataPair.first);
9597 if (pool_id && !i_player->GetMap()->GetPersistentState()->IsSpawnedPoolObject<GameObject>(dataPair.first))
9598 return false;
9600 if (!i_spawnedData || new_dist < i_spawnedDist)
9602 i_spawnedData = &dataPair;
9603 i_spawnedDist = new_dist;
9606 return false;
9609 GameObjectDataPair const* FindGOData::GetResult() const
9611 if (i_mapData)
9612 return i_mapData;
9614 if (i_spawnedData)
9615 return i_spawnedData;
9617 return i_anyData;