· 6 years ago · Jun 01, 2019, 09:28 PM
1/*
2 * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
3 * Copyright (C) 2009-2011 MaNGOSZero <https://github.com/mangos/zero>
4 * Copyright (C) 2011-2016 Nostalrius <https://nostalrius.org>
5 * Copyright (C) 2016-2017 Elysium Project <https://github.com/elysium-project>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "ObjectMgr.h"
23#include "Database/DatabaseEnv.h"
24#include "Database/DatabaseImpl.h"
25#include "Database/SQLStorageImpl.h"
26#include "Policies/SingletonImp.h"
27
28#include "SQLStorages.h"
29#include "Log.h"
30#include "MapManager.h"
31#include "ObjectGuid.h"
32#include "ScriptMgr.h"
33#include "SpellMgr.h"
34#include "UpdateMask.h"
35#include "World.h"
36#include "Group.h"
37#include "Transport.h"
38#include "ProgressBar.h"
39#include "Language.h"
40#include "PoolManager.h"
41#include "GameEventMgr.h"
42#include "Spell.h"
43#include "Chat.h"
44#include "AccountMgr.h"
45#include "MapPersistentStateMgr.h"
46#include "SpellAuras.h"
47#include "Util.h"
48#include "WaypointManager.h"
49#include "GossipDef.h"
50#include "Mail.h"
51#include "Formulas.h"
52#include "InstanceData.h"
53#include "CharacterDatabaseCache.h"
54#include "HardcodedEvents.h"
55
56#include <limits>
57
58INSTANTIATE_SINGLETON_1(ObjectMgr);
59
60bool normalizePlayerName(std::string& name)
61{
62 if (name.empty())
63 return false;
64
65 wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME + 1];
66 size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
67
68 if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
69 return false;
70
71 wstr_buf[0] = wcharToUpper(wstr_buf[0]);
72 for (size_t i = 1; i < wstr_len; ++i)
73 wstr_buf[i] = wcharToLower(wstr_buf[i]);
74
75 if (!WStrToUtf8(wstr_buf, wstr_len, name))
76 return false;
77
78 return true;
79}
80
81LanguageDesc lang_description[LANGUAGES_COUNT] =
82{
83 { LANG_ADDON, 0, 0 },
84 { LANG_UNIVERSAL, 0, 0 },
85 { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
86 { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
87 { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
88 { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
89 { LANG_COMMON, 668, SKILL_LANG_COMMON },
90 { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
91 { LANG_TITAN, 816, SKILL_LANG_TITAN },
92 { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
93 { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
94 { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
95 { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
96 { LANG_TROLL, 7341, SKILL_LANG_TROLL },
97 { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
98};
99
100LanguageDesc const* GetLanguageDescByID(uint32 lang)
101{
102 for (int i = 0; i < LANGUAGES_COUNT; ++i)
103 {
104 if (uint32(lang_description[i].lang_id) == lang)
105 return &lang_description[i];
106 }
107
108 return NULL;
109}
110
111template<typename T>
112T IdGenerator<T>::Generate()
113{
114 if (m_nextGuid >= std::numeric_limits<T>::max() - 1)
115 {
116 sLog.outError("%s guid overflow!! Can't continue, shutting down server. ", m_name);
117 World::StopNow(ERROR_EXIT_CODE);
118 }
119 return m_nextGuid++;
120}
121
122template uint32 IdGenerator<uint32>::Generate();
123template uint64 IdGenerator<uint64>::Generate();
124
125ObjectMgr::ObjectMgr() :
126 m_FirstTemporaryCreatureGuid(1),
127 m_FirstTemporaryGameObjectGuid(1),
128 m_GuildIds("Guild ids"),
129 m_MailIds("Mail ids"),
130 m_GroupIds("Group ids"),
131 m_PetitionIds("Petition ids"),
132 // Nostalrius
133 DBCLocaleIndex(0),
134 m_OldMailCounter(0)
135{
136 // Only zero condition left, others will be added while loading DB tables
137 mConditions.resize(1);
138}
139
140ObjectMgr::~ObjectMgr()
141{
142 for (QuestMap::iterator i = mQuestTemplates.begin(); i != mQuestTemplates.end(); ++i)
143 delete i->second;
144
145 for (PetLevelInfoMap::iterator i = petInfo.begin(); i != petInfo.end(); ++i)
146 delete[] i->second;
147
148 // free only if loaded
149 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
150 delete[] playerClassInfo[class_].levelInfo;
151
152 for (int race = 0; race < MAX_RACES; ++race)
153 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
154 delete[] playerInfo[race][class_].levelInfo;
155
156 // free objects
157 for (GroupMap::iterator itr = mGroupMap.begin(); itr != mGroupMap.end(); ++itr)
158 delete itr->second;
159
160 for (CacheVendorItemMap::iterator itr = m_mCacheVendorTemplateItemMap.begin(); itr != m_mCacheVendorTemplateItemMap.end(); ++itr)
161 itr->second.Clear();
162
163 for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
164 itr->second.Clear();
165
166 for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
167 itr->second.Clear();
168
169 for (PlayerCacheDataMap::iterator itr = m_playerCacheData.begin(); itr != m_playerCacheData.end(); ++itr)
170 delete itr->second;
171}
172
173char* const ObjectMgr::GetPatchName()
174{
175 switch(sWorld.GetWowPatch())
176 {
177 case 0:
178 return "Patch 1.2: Mysteries of Maraudon";
179 case 1:
180 return "Patch 1.3: Ruins of the Dire Maul";
181 case 2:
182 return "Patch 1.4: The Call to War";
183 case 3:
184 return "Patch 1.5: Battlegrounds";
185 case 4:
186 return "Patch 1.6: Assault on Blackwing Lair";
187 case 5:
188 return "Patch 1.7: Rise of the Blood God";
189 case 6:
190 return "Patch 1.8: Dragons of Nightmare";
191 case 7:
192 return "Patch 1.9: The Gates of Ahn'Qiraj";
193 case 8:
194 return "Patch 1.10: Storms of Azeroth";
195 case 9:
196 return "Patch 1.11: Shadow of the Necropolis";
197 case 10:
198 return "Patch 1.12: Drums of War";
199 }
200
201 return "Invalid Patch!";
202}
203
204// Nostalrius
205void ObjectMgr::LoadSpellDisabledEntrys()
206{
207 m_DisabledSpells.clear(); // need for reload case
208
209 QueryResult* result = WorldDatabase.Query("SELECT entry FROM spell_disabled");
210
211 uint32 total_count = 0;
212
213 if (!result)
214 {
215 sLog.outString();
216 sLog.outString(">> Loaded %u disabled spells", total_count);
217 return;
218 }
219
220 Field* fields;
221 do
222 {
223 fields = result->Fetch();
224 uint32 spellid = fields[0].GetUInt32();
225 if (!sSpellMgr.GetSpellEntry(spellid))
226 {
227 sLog.outError("Spell entry %u from `spell_disabled` doesn't exist in dbc, ignoring.", spellid);
228 continue;
229 }
230 m_DisabledSpells.insert(spellid);
231 ++total_count;
232 }
233 while (result->NextRow());
234
235 sLog.outString();
236 sLog.outString(">> Loaded %u disabled spells from `spell_disabled`", total_count);
237 delete result;
238}
239/*
240DROP TABLE IF EXISTS map_loot_disabled;
241CREATE TABLE `map_loot_disabled` (
242 `mapid` int(11) default 0,
243 `comment` varchar(255),
244 PRIMARY KEY (`mapid`)
245) ENGINE=MyISAM DEFAULT CHARSET=latin1;
246*/
247void ObjectMgr::LoadMapLootDisabled()
248{
249 m_DisabledMapLoots.clear(); // need for reload case
250
251 QueryResult* result = WorldDatabase.Query("SELECT mapid FROM map_loot_disabled");
252
253 uint32 total_count = 0;
254
255 if (!result)
256 {
257 sLog.outString();
258 sLog.outString(">> Loaded %u disabled maps loot", total_count);
259 return;
260 }
261
262 Field* fields;
263 do
264 {
265 fields = result->Fetch();
266 uint32 mapid = fields[0].GetUInt32();
267 m_DisabledMapLoots.insert(mapid);
268 ++total_count;
269 }
270 while (result->NextRow());
271
272 sLog.outString();
273 sLog.outString(">> Loaded %u disabled spells from `map_loot_disabled`", total_count);
274 delete result;
275}
276
277void ObjectMgr::LoadCinematicsWaypoints()
278{
279 m_CinematicWaypoints.clear();
280
281 QueryResult* result = WorldDatabase.Query("SELECT cinematic,timer,posx,posy,posz FROM cinematic_waypoints");
282
283 uint32 total_count = 0;
284
285 if (!result)
286 {
287 sLog.outString();
288 sLog.outString(">> Loaded %u disabled cinematic Waypoints", total_count);
289 return;
290 }
291
292 Field* fields;
293 do
294 {
295 fields = result->Fetch();
296 CinematicWaypointEntry tmp;
297
298 tmp.cinematic_id = fields[0].GetUInt32();
299 tmp.time = fields[1].GetUInt32();
300 tmp.position.x = fields[2].GetFloat();
301 tmp.position.y = fields[3].GetFloat();
302 tmp.position.z = fields[4].GetFloat();
303
304 m_CinematicWaypoints.push_back(tmp);
305 ++total_count;
306 }
307 while (result->NextRow());
308 delete result;
309
310 sLog.outString();
311 sLog.outString(">> Loaded %u cinematic Waypoints from `cinematic_waypoints`", total_count);
312}
313
314Position const* ObjectMgr::GetCinematicPosition(uint32 cinematicId, uint32 elapsed_time)
315{
316 // On cherche un waypoint :
317 // 1- Qui correspond a la cinematic_id
318 // 2- Qui soit le plus grand possible
319 // 3- Avec un time < elapsed_time (sinon on reste toujours au meme)
320 Position const* tmpBestPos = NULL;
321 uint32 tmpBestTimer = 0;
322 std::vector<CinematicWaypointEntry>::const_iterator it;
323 for (it = m_CinematicWaypoints.begin(); it != m_CinematicWaypoints.end(); ++it)
324 {
325 if (cinematicId == it->cinematic_id)
326 {
327 if (it->time < elapsed_time)
328 {
329 if (it->time > tmpBestTimer)
330 {
331 tmpBestTimer = it->time;
332 tmpBestPos = &(it->position);
333 }
334 }
335 }
336 }
337 return tmpBestPos;
338}
339
340Position const* ObjectMgr::GetCinematicInitialPosition(uint32 cinematicId)
341{
342 std::vector<CinematicWaypointEntry>::const_iterator it;
343 for (it = m_CinematicWaypoints.begin(); it != m_CinematicWaypoints.end(); ++it)
344 {
345 if (cinematicId == it->cinematic_id)
346 {
347 if (it->time == 0)
348 return &(it->position);
349 }
350 }
351 // Non trouve
352 sLog.outError("Impossible de trouver le point de depart de la cinematique %u", cinematicId);
353 return NULL;
354}
355
356// Systeme de phasing
357// ALTER TABLE characters ADD COLUMN world_phase_mask int(11) unsigned not null default 0;
358void ObjectMgr::LoadPlayerPhaseFromDb()
359{
360 m_PlayerPhases.clear();
361
362 QueryResult* result = CharacterDatabase.Query("SELECT guid, world_phase_mask FROM characters");
363
364 uint32 total_count = 0;
365
366 if (!result)
367 {
368 sLog.outString();
369 sLog.outString(">> Loaded %u characters phases", total_count);
370 return;
371 }
372
373 Field* fields;
374 do
375 {
376 fields = result->Fetch();
377
378 uint32 guid = fields[0].GetUInt32();
379 uint32 mask = fields[1].GetUInt32();
380 m_PlayerPhases[guid] = mask;
381 ++total_count;
382 }
383 while (result->NextRow());
384
385 sLog.outString();
386 sLog.outString(">> Loaded %u character phases", total_count);
387 delete result;
388}
389
390uint32 ObjectMgr::GetPlayerWorldMaskByGUID(const uint64 guid)
391{
392 return m_PlayerPhases[GUID_LOPART(guid)];
393}
394void ObjectMgr::SetPlayerWorldMask(const uint64 guid, uint32 newWorldMask)
395{
396 if (m_PlayerPhases[GUID_LOPART(guid)] == newWorldMask)
397 return;
398
399 m_PlayerPhases[GUID_LOPART(guid)] = newWorldMask;
400 //CharacterDatabase.PQuery("UPDATE characters SET world_phase_mask = %u WHERE guid = %u", newWorldMask, GUID_LOPART(guid));
401}
402/*
403# Pour le moment dans la DB World. Il serait plus judicieux de la deplacer dans characters ... ?
404CREATE TABLE `variables` (
405 `index` int(10) unsigned NOT NULL default '0',
406 `value` int(10) unsigned NOT NULL default '0',
407 `comment` varchar(255),
408 PRIMARY KEY (`index`)
409) ENGINE=MyISAM DEFAULT CHARSET=latin1;
410*/
411uint32 ObjectMgr::GetSavedVariable(uint32 index, uint32 defaultValue, bool *exist)
412{
413 SavedVariablesVector::iterator it;
414 for (it = m_SavedVariables.begin(); it != m_SavedVariables.end(); ++it)
415 {
416 if (it->uiIndex == index)
417 {
418 if (exist)
419 (*exist) = true;
420 return it->uiValue;
421 }
422 }
423 if (exist)
424 (*exist) = false;
425 return defaultValue;
426}
427
428SavedVariable& ObjectMgr::_InsertVariable(uint32 index, uint32 value, bool saved)
429{
430 SavedVariable tmp;
431 tmp.uiIndex = index;
432 tmp.uiValue = value;
433 tmp.bSavedInDb = saved;
434
435 m_SavedVariables.push_back(tmp);
436 return m_SavedVariables[m_SavedVariables.size()-1];
437}
438
439void ObjectMgr::_SaveVariable(const SavedVariable& toSave)
440{
441 // Must do this in a transaction, else if worker threads > 1 we could do one before the other
442 // when order is important...
443 WorldDatabase.BeginTransaction();
444 WorldDatabase.PExecute("DELETE FROM `variables` WHERE `index` = %u", toSave.uiIndex);
445 WorldDatabase.PExecute("INSERT INTO `variables` (`index`, `value`) VALUES (%u, %u)", toSave.uiIndex, toSave.uiValue);
446 WorldDatabase.CommitTransaction();
447}
448
449void ObjectMgr::InitSavedVariable(uint32 index, uint32 value)
450{
451 SavedVariablesVector::iterator it;
452 // Deja enregistree ?
453 for (it = m_SavedVariables.begin(); it != m_SavedVariables.end(); ++it)
454 if (it->uiIndex == index)
455 return;
456
457 // If we are there, it means that the variable does not exist.
458 SavedVariable& variable = _InsertVariable(index, value, true);
459 _SaveVariable(variable);
460}
461
462void ObjectMgr::SetSavedVariable(uint32 index, uint32 value, bool autoSave)
463{
464 for (auto it = m_SavedVariables.begin(); it != m_SavedVariables.end(); ++it)
465 {
466 if (it->uiIndex == index)
467 {
468 // Si la valeur n'a pas change
469 if (it->uiValue == value)
470 return;
471
472 it->uiValue = value;
473 if (autoSave)
474 _SaveVariable(*it);
475 else
476 it->bSavedInDb = false;
477 return;
478 }
479 }
480 // Si on est la, c'est que la variable n'existe pas.
481 SavedVariable& variable = _InsertVariable(index, value, autoSave);
482 if (autoSave)
483 _SaveVariable(variable);
484}
485
486void ObjectMgr::LoadVariable(uint32 index, uint32* variable, uint32 defaultValue, uint32 maxValue, uint32 minValue)
487{
488 bool inIndex = false;
489 (*variable) = GetSavedVariable(index, defaultValue, &inIndex);
490 uint32 originalValue = (*variable);
491 if (maxValue != 0 && (*variable) > maxValue)
492 (*variable) = defaultValue;
493 if ((*variable) < minValue)
494 (*variable) = defaultValue;
495 if (!inIndex)
496 _InsertVariable(index, (*variable), true);
497 if (originalValue != (*variable))
498 SetSavedVariable(index, (*variable), true);
499}
500void ObjectMgr::SaveVariables()
501{
502 SavedVariablesVector::iterator it;
503 for (it = m_SavedVariables.begin(); it != m_SavedVariables.end(); ++it)
504 {
505 if (!it->bSavedInDb)
506 _SaveVariable(*it);
507 }
508}
509
510void ObjectMgr::LoadSavedVariable()
511{
512 m_SavedVariables.clear();
513
514 QueryResult* result = WorldDatabase.Query("SELECT `index`,`value` FROM variables");
515
516 uint32 total_count = 0;
517
518 if (!result)
519 {
520 sLog.outString();
521 sLog.outString(">> Loaded %u saved variables", total_count);
522 return;
523 }
524
525 Field* fields;
526 do
527 {
528 fields = result->Fetch();
529 _InsertVariable(fields[0].GetUInt32(), fields[1].GetUInt32(), true);
530 ++total_count;
531 }
532 while (result->NextRow());
533
534 sLog.outString();
535 sLog.outString(">> Loaded %u saved variables", total_count);
536 delete result;
537}
538
539// Mise en cache de donnees des joueurs
540void ObjectMgr::LoadPlayerCacheData()
541{
542 m_playerCacheData.clear();
543 m_playerNameToGuid.clear();
544
545 QueryResult* result = CharacterDatabase.Query("SELECT guid, race, class, gender, account, name, level, zone FROM characters;");
546
547 uint32 total_count = 0;
548
549 if (!result)
550 {
551 sLog.outString();
552 sLog.outString(">> 0 cached player data ...");
553 return;
554 }
555
556 Field* fields;
557 do
558 {
559 fields = result->Fetch();
560 // guid, race, class, gender, account, name
561 std::string name = fields[5].GetCppString();
562 if (normalizePlayerName(name))
563 InsertPlayerInCache(fields[0].GetUInt32(), fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetUInt32(), fields[4].GetUInt32(), name, fields[6].GetUInt32(), fields[7].GetUInt32());
564 ++total_count;
565 }
566 while (result->NextRow());
567
568 sLog.outString();
569 sLog.outString(">> Loaded %u players in cache.", total_count);
570 delete result;
571}
572
573PlayerCacheData* ObjectMgr::GetPlayerDataByGUID(uint32 guidLow)
574{
575 auto itr = m_playerCacheData.find(guidLow);
576 if (itr != m_playerCacheData.end())
577 return itr->second;
578 return nullptr;
579}
580
581PlayerCacheData* ObjectMgr::GetPlayerDataByName(const std::string& name)
582{
583 if (ObjectGuid guid = GetPlayerGuidByName(name))
584 return GetPlayerDataByGUID(guid.GetCounter());
585 return nullptr;
586}
587
588ObjectGuid ObjectMgr::GetPlayerGuidByName(const std::string& name) const
589{
590 auto itr = m_playerNameToGuid.find(name);
591 if (itr != m_playerNameToGuid.end())
592 return ObjectGuid(HIGHGUID_PLAYER, itr->second);
593 return ObjectGuid();
594}
595
596bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string &name) const
597{
598 if (auto pData = sObjectMgr.GetPlayerDataByGUID(guid.GetCounter()))
599 {
600 name = pData->sName;
601 return true;
602 }
603 return false;
604}
605
606Team ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const
607{
608 if (auto player = GetPlayer(guid))
609 return Player::TeamForRace(player->getRace());
610
611 if (auto pData = sObjectMgr.GetPlayerDataByGUID(guid.GetCounter()))
612 return Player::TeamForRace(pData->uiRace);
613
614 return TEAM_NONE;
615}
616
617uint8 ObjectMgr::GetPlayerClassByGUID(ObjectGuid guid) const
618{
619 // prevent DB access for online player
620 if (Player* player = GetPlayer(guid))
621 {
622 return player->getClass();
623 }
624
625 uint32 lowguid = guid.GetCounter();
626
627 QueryResult* result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid = '%u'", lowguid);
628
629 if (result)
630 {
631 uint8 pClass = (*result)[0].GetUInt8();
632 delete result;
633 return pClass;
634 }
635
636 return 0;
637}
638
639uint32 ObjectMgr::GetPlayerAccountIdByGUID(ObjectGuid guid) const
640{
641 if (auto player = GetPlayer(guid))
642 return player->GetSession()->GetAccountId();
643
644 if (auto pData = sObjectMgr.GetPlayerDataByGUID(guid.GetCounter()))
645 return pData->uiAccount;
646
647 return 0;
648}
649
650uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
651{
652 if (auto pData = sObjectMgr.GetPlayerDataByName(name))
653 return pData->uiAccount;
654 return 0;
655}
656
657void ObjectMgr::InsertPlayerInCache(Player *pPlayer)
658{
659 auto pSession = pPlayer->GetSession();
660 if (!pSession)
661 return;
662 auto accountId = pSession->GetAccountId();
663 InsertPlayerInCache(pPlayer->GetGUIDLow(), pPlayer->getRace(), pPlayer->getClass(), pPlayer->getGender(), accountId, pPlayer->GetName(), pPlayer->getLevel(), pPlayer->GetCachedZoneId());
664}
665
666void ObjectMgr::InsertPlayerInCache(uint32 lowGuid, uint32 race, uint32 _class, uint32 gender, uint32 accountId, const std::string& name, uint32 level, uint32 zoneId)
667{
668 auto data = new PlayerCacheData;
669 data->uiGuid = lowGuid;
670 data->uiAccount = accountId;
671 data->uiRace = race;
672 data->uiClass = _class;
673 data->uiGender = gender;
674 data->uiLevel = level;
675 data->sName = name;
676 data->uiZoneId = zoneId;
677
678 m_playerCacheData[lowGuid] = data;
679 m_playerNameToGuid[name] = lowGuid;
680}
681
682void ObjectMgr::DeletePlayerFromCache(uint32 lowGuid)
683{
684 auto itr = m_playerCacheData.find(lowGuid);
685 if (itr != m_playerCacheData.end())
686 {
687 auto itr2 = m_playerNameToGuid.find(itr->second->sName);
688 if (itr2 != m_playerNameToGuid.end())
689 m_playerNameToGuid.erase(itr2);
690 m_playerCacheData.erase(itr);
691 }
692}
693
694void ObjectMgr::ChangePlayerNameInCache(uint32 guidLow, const std::string& oldName, const std::string& newName)
695{
696 auto itr = m_playerCacheData.find(guidLow);
697 if (itr != m_playerCacheData.end())
698 {
699 m_playerNameToGuid.erase(oldName);
700 m_playerNameToGuid[newName] = guidLow;
701 itr->second->sName = newName;
702 }
703}
704
705Group* ObjectMgr::GetGroupById(uint32 id) const
706{
707 GroupMap::const_iterator itr = mGroupMap.find(id);
708 if (itr != mGroupMap.end())
709 return itr->second;
710
711 return NULL;
712}
713
714Group* ObjectMgr::GetGroupByMember(ObjectGuid memberGuid)
715{
716 for (GroupMap::const_iterator itr = mGroupMap.begin(); itr != mGroupMap.end(); ++itr)
717 if (itr->second->IsMember(memberGuid))
718 return itr->second;
719 return NULL;
720}
721
722CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
723{
724 return sCreatureStorage.LookupEntry<CreatureInfo>(id);
725}
726
727void ObjectMgr::LoadCreatureLocales()
728{
729 mCreatureLocaleMap.clear(); // need for reload case
730
731 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");
732
733 if (!result)
734 {
735 BarGoLink bar(1);
736
737 bar.step();
738
739 sLog.outString();
740 sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
741 return;
742 }
743
744 BarGoLink bar(result->GetRowCount());
745
746 do
747 {
748 Field *fields = result->Fetch();
749 bar.step();
750
751 uint32 entry = fields[0].GetUInt32();
752
753 if (!GetCreatureTemplate(entry))
754 {
755 ERROR_DB_STRICT_LOG("Table `locales_creature` has data for not existed creature entry %u, skipped.", entry);
756 continue;
757 }
758
759 CreatureLocale& data = mCreatureLocaleMap[entry];
760
761 for (int i = 1; i < MAX_LOCALE; ++i)
762 {
763 std::string str = fields[1 + 2 * (i - 1)].GetCppString();
764 if (!str.empty())
765 {
766 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
767 if (idx >= 0)
768 {
769 if ((int32)data.Name.size() <= idx)
770 data.Name.resize(idx + 1);
771
772 data.Name[idx] = str;
773 }
774 }
775 str = fields[1 + 2 * (i - 1) + 1].GetCppString();
776 if (!str.empty())
777 {
778 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
779 if (idx >= 0)
780 {
781 if ((int32)data.SubName.size() <= idx)
782 data.SubName.resize(idx + 1);
783
784 data.SubName[idx] = str;
785 }
786 }
787 }
788 }
789 while (result->NextRow());
790
791 delete result;
792
793 sLog.outString();
794 sLog.outString(">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size());
795}
796
797void ObjectMgr::LoadGossipMenuItemsLocales()
798{
799 mGossipMenuItemsLocaleMap.clear(); // need for reload case
800
801 QueryResult *result = WorldDatabase.Query("SELECT menu_id,id,"
802 "option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
803 "option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
804 "option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
805 "option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
806 "FROM locales_gossip_menu_option");
807
808 if (!result)
809 {
810 BarGoLink bar(1);
811
812 bar.step();
813
814 sLog.outString();
815 sLog.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
816 return;
817 }
818
819 BarGoLink bar(result->GetRowCount());
820
821 do
822 {
823 Field *fields = result->Fetch();
824 bar.step();
825
826 uint16 menuId = fields[0].GetUInt16();
827 uint16 id = fields[1].GetUInt16();
828
829 GossipMenuItemsMapBounds bounds = GetGossipMenuItemsMapBounds(menuId);
830
831 bool found = false;
832 if (bounds.first != bounds.second)
833 {
834 for (GossipMenuItemsMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
835 {
836 if (itr->second.id == id)
837 {
838 found = true;
839 break;
840 }
841 }
842 }
843
844 if (!found)
845 {
846 ERROR_DB_STRICT_LOG("Table `locales_gossip_menu_option` has data for nonexistent gossip menu %u item %u, skipped.", menuId, id);
847 continue;
848 }
849
850 GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId, id)];
851
852 for (int i = 1; i < MAX_LOCALE; ++i)
853 {
854 std::string str = fields[2 + 2 * (i - 1)].GetCppString();
855 if (!str.empty())
856 {
857 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
858 if (idx >= 0)
859 {
860 if ((int32)data.OptionText.size() <= idx)
861 data.OptionText.resize(idx + 1);
862
863 data.OptionText[idx] = str;
864 }
865 }
866 str = fields[2 + 2 * (i - 1) + 1].GetCppString();
867 if (!str.empty())
868 {
869 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
870 if (idx >= 0)
871 {
872 if ((int32)data.BoxText.size() <= idx)
873 data.BoxText.resize(idx + 1);
874
875 data.BoxText[idx] = str;
876 }
877 }
878 }
879 }
880 while (result->NextRow());
881
882 delete result;
883
884 sLog.outString();
885 sLog.outString(">> Loaded %lu gossip_menu_option locale strings", (unsigned long)mGossipMenuItemsLocaleMap.size());
886}
887
888void ObjectMgr::LoadPointOfInterestLocales()
889{
890 mPointOfInterestLocaleMap.clear(); // need for reload case
891
892 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");
893
894 if (!result)
895 {
896 BarGoLink bar(1);
897
898 bar.step();
899
900 sLog.outString("");
901 sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
902 return;
903 }
904
905 BarGoLink bar(result->GetRowCount());
906
907 do
908 {
909 Field *fields = result->Fetch();
910 bar.step();
911
912 uint32 entry = fields[0].GetUInt32();
913
914 if (!GetPointOfInterest(entry))
915 {
916 ERROR_DB_STRICT_LOG("Table `locales_points_of_interest` has data for nonexistent POI entry %u, skipped.", entry);
917 continue;
918 }
919
920 PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];
921
922 for (int i = 1; i < MAX_LOCALE; ++i)
923 {
924 std::string str = fields[i].GetCppString();
925 if (str.empty())
926 continue;
927
928 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
929 if (idx >= 0)
930 {
931 if (data.IconName.size() <= size_t(idx))
932 data.IconName.resize(idx + 1);
933
934 data.IconName[idx] = str;
935 }
936 }
937 }
938 while (result->NextRow());
939
940 delete result;
941
942 sLog.outString();
943 sLog.outString(">> Loaded %u points_of_interest locale strings", mPointOfInterestLocaleMap.size());
944}
945
946struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader, SQLStorage>
947{
948 template<class D>
949 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
950 {
951 dst = D(sScriptMgr.GetScriptId(src));
952 }
953};
954
955void ObjectMgr::LoadCreatureTemplates()
956{
957 SQLCreatureLoader loader;
958 loader.LoadProgressive(sCreatureStorage, sWorld.GetWowPatch());
959
960 sLog.outString(">> Loaded %u creature definitions", sCreatureStorage.GetRecordCount());
961 sLog.outString();
962
963 CheckCreatureTemplates();
964}
965
966void ObjectMgr::CheckCreatureTemplates()
967{
968 // check data correctness
969 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
970 {
971 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
972 if (!cInfo)
973 continue;
974
975 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
976 if (!factionTemplate)
977 sLog.outErrorDb("Creature (Entry: %u) has nonexistent faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
978
979 factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
980 if (!factionTemplate)
981 sLog.outErrorDb("Creature (Entry: %u) has nonexistent faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
982
983 for (int k = 0; k < MAX_KILL_CREDIT; ++k)
984 {
985 if (cInfo->KillCredit[k])
986 {
987 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
988 {
989 sLog.outErrorDb("Creature (Entry: %u) has nonexistent creature entry in `KillCredit%d` (%u)", cInfo->Entry, k + 1, cInfo->KillCredit[k]);
990 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `KillCredit%d`=0 WHERE entry=%u;", k + 1, cInfo->Entry);
991 const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0;
992 }
993 }
994 }
995
996 // used later for scale
997 CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
998
999 for (int i = 0; i < MAX_CREATURE_MODEL; ++i)
1000 {
1001 if (cInfo->ModelId[i])
1002 {
1003 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->ModelId[i]);
1004 if (!displayEntry)
1005 {
1006 sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid_%d (%u), can crash client", cInfo->Entry, i + 1, cInfo->ModelId[i]);
1007 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `modelid_%d`=0 WHERE entry=%u;", i + 1, cInfo->Entry);
1008 const_cast<CreatureInfo*>(cInfo)->ModelId[i] = 0;
1009 }
1010 else if (!displayScaleEntry)
1011 displayScaleEntry = displayEntry;
1012
1013 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->ModelId[i]);
1014 if (!minfo)
1015 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]);
1016 }
1017 }
1018
1019 if (!displayScaleEntry)
1020 sLog.outErrorDb("Creature (Entry: %u) has nonexistent modelid in modelid_1/modelid_2", cInfo->Entry);
1021
1022 // use below code for 0-checks for unit_class
1023 if (!cInfo->unit_class)
1024 ERROR_DB_STRICT_LOG("Creature (Entry: %u) not has proper unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
1025 else if (((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
1026 sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
1027
1028 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
1029 {
1030 sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`", cInfo->Entry, cInfo->dmgschool);
1031 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `dmgschool`=%u WHERE entry=%u;", SPELL_SCHOOL_NORMAL, cInfo->Entry);
1032 const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
1033 }
1034
1035 if (cInfo->baseattacktime == 0)
1036 {
1037 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `baseattacktime`=%u WHERE entry=%u;", BASE_ATTACK_TIME, cInfo->Entry);
1038 const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
1039 }
1040
1041 if (cInfo->rangeattacktime == 0)
1042 {
1043 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `rangeattacktime`=%u WHERE entry=%u;", BASE_ATTACK_TIME, cInfo->Entry);
1044 const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
1045 }
1046
1047 if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
1048 sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u", cInfo->Entry, cInfo->trainer_type);
1049
1050 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
1051 {
1052 sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`", cInfo->Entry, cInfo->type);
1053 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `type`=%u WHERE entry=%u;", CREATURE_TYPE_HUMANOID, cInfo->Entry);
1054 const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
1055 }
1056
1057 // must exist or used hidden but used in data horse case
1058 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
1059 {
1060 sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`", cInfo->Entry, cInfo->family);
1061 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `family`=%u WHERE entry=%u;", 0, cInfo->Entry);
1062 const_cast<CreatureInfo*>(cInfo)->family = 0;
1063 }
1064
1065 if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
1066 {
1067 sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim", cInfo->Entry, cInfo->InhabitType);
1068 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `InhabitType`=%u WHERE entry=%u;", INHABIT_ANYWHERE, cInfo->Entry);
1069 const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
1070 }
1071
1072 if (cInfo->PetSpellDataId)
1073 {
1074 CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
1075 if (!spellDataId)
1076 sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
1077 }
1078
1079 for (int j = 0; j < CREATURE_MAX_SPELLS; ++j)
1080 {
1081 if (cInfo->spells[j] && !sSpellMgr.GetSpellEntry(cInfo->spells[j]))
1082 {
1083 sLog.outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0", cInfo->Entry, j + 1, cInfo->spells[j]);
1084 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `spell%u`=0 WHERE entry=%u;", j + 1, cInfo->Entry);
1085 const_cast<CreatureInfo*>(cInfo)->spells[j] = 0;
1086 }
1087 }
1088
1089 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
1090 {
1091 sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.", cInfo->Entry, cInfo->MovementType);
1092 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `MovementType`=%u WHERE entry=%u;", IDLE_MOTION_TYPE, cInfo->Entry);
1093 const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
1094 }
1095
1096 if (cInfo->equipmentId > 0) // 0 no equipment
1097 {
1098 if (!GetEquipmentInfo(cInfo->equipmentId) && !GetEquipmentInfoRaw(cInfo->equipmentId))
1099 {
1100 sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template` or `creature_equip_template_raw`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
1101 sLog.out(LOG_DBERRFIX, "UPDATE creature_template SET `equipment_id`=0 WHERE entry=%u;", cInfo->Entry);
1102 const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
1103 }
1104 }
1105
1106 if (cInfo->vendorId > 0)
1107 {
1108 if (!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
1109 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);
1110 }
1111
1112 /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
1113 if (cInfo->scale <= 0.0f)
1114 {
1115 if (displayScaleEntry)
1116 const_cast<CreatureInfo*>(cInfo)->scale = displayScaleEntry->scale;
1117 else
1118 const_cast<CreatureInfo*>(cInfo)->scale = DEFAULT_OBJECT_SCALE;
1119 }
1120 }
1121}
1122void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
1123{
1124 // Now add the auras, format "spell1 spell2 ..."
1125 char *p, *s;
1126 std::vector<int> val;
1127 s = p = (char*)reinterpret_cast<char const*>(addon->auras);
1128 if (p)
1129 {
1130 while (p[0] != 0)
1131 {
1132 ++p;
1133 if (p[0] == ' ')
1134 {
1135 val.push_back(atoi(s));
1136 s = ++p;
1137 }
1138 }
1139 if (p != s)
1140 val.push_back(atoi(s));
1141
1142 // free char* loaded memory
1143 delete[](char*)reinterpret_cast<char const*>(addon->auras);
1144 }
1145
1146 // empty list
1147 if (val.empty())
1148 {
1149 addon->auras = NULL;
1150 return;
1151 }
1152
1153 // replace by new structures array
1154 const_cast<uint32*&>(addon->auras) = new uint32[val.size() + 1];
1155
1156 uint32 i = 0;
1157 for (uint32 j = 0; j < val.size(); ++j)
1158 {
1159 uint32& cAura = const_cast<uint32&>(addon->auras[i]);
1160 cAura = uint32(val[j]);
1161
1162 SpellEntry const *AdditionalSpellInfo = sSpellMgr.GetSpellEntry(cAura);
1163 if (!AdditionalSpellInfo)
1164 {
1165 sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura, table);
1166 continue;
1167 }
1168
1169 if (std::find(&addon->auras[0], &addon->auras[i], cAura) != &addon->auras[i])
1170 {
1171 sLog.outErrorDb("Creature (%s: %u) has duplicate spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura, table);
1172 continue;
1173 }
1174
1175 ++i;
1176 }
1177
1178 // fill terminator element (after last added)
1179 const_cast<uint32&>(addon->auras[i]) = 0;
1180}
1181
1182void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment)
1183{
1184 creatureaddons.LoadProgressive(sWorld.GetWowPatch());
1185
1186 sLog.outString(">> Loaded %u %s", creatureaddons.GetRecordCount(), comment);
1187 sLog.outString();
1188
1189 // check data correctness and convert 'auras'
1190 for (uint32 i = 1; i < creatureaddons.GetMaxEntry(); ++i)
1191 {
1192 CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i);
1193 if (!addon)
1194 continue;
1195
1196 if (addon->mount)
1197 {
1198 if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount))
1199 {
1200 sLog.outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->mount, creatureaddons.GetTableName());
1201 const_cast<CreatureDataAddon*>(addon)->mount = 0;
1202 }
1203 }
1204
1205 if (addon->sheath_state > SHEATH_STATE_RANGED)
1206 sLog.outErrorDb("Creature (%s %u) has unknown sheath state (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->sheath_state, creatureaddons.GetTableName());
1207
1208 if (!sEmotesStore.LookupEntry(addon->emote))
1209 {
1210 sLog.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName());
1211 const_cast<CreatureDataAddon*>(addon)->emote = 0;
1212 }
1213
1214 ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName);
1215 }
1216}
1217
1218void ObjectMgr::LoadCreatureAddons()
1219{
1220 LoadCreatureAddons(sCreatureInfoAddonStorage, "Entry", "creature template addons");
1221
1222 // check entry ids
1223 for (uint32 i = 1; i < sCreatureInfoAddonStorage.GetMaxEntry(); ++i)
1224 if (CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i))
1225 if (!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
1226 sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`", addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());
1227
1228 LoadCreatureAddons(sCreatureDataAddonStorage, "GUID", "creature addons");
1229
1230 // check entry ids
1231 for (uint32 i = 1; i < sCreatureDataAddonStorage.GetMaxEntry(); ++i)
1232 if (CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i))
1233 if (mCreatureDataMap.find(addon->guidOrEntry) == mCreatureDataMap.end())
1234 sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`", addon->guidOrEntry);
1235}
1236
1237EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
1238{
1239 return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
1240}
1241
1242EquipmentInfoRaw const* ObjectMgr::GetEquipmentInfoRaw(uint32 entry)
1243{
1244 return sEquipmentStorageRaw.LookupEntry<EquipmentInfoRaw>(entry);
1245}
1246
1247void ObjectMgr::LoadEquipmentTemplates()
1248{
1249 sEquipmentStorage.LoadProgressive(sWorld.GetWowPatch(), true);
1250
1251 for (uint32 i = 0; i < sEquipmentStorage.GetMaxEntry(); ++i)
1252 {
1253 EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i);
1254
1255 if (!eqInfo)
1256 continue;
1257
1258 for (uint8 j = 0; j < 3; ++j)
1259 {
1260 if (!eqInfo->equipentry[j])
1261 continue;
1262
1263 ItemPrototype const *itemProto = GetItemPrototype(eqInfo->equipentry[j]);
1264 if (!itemProto)
1265 {
1266 sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j + 1, i);
1267 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
1268 continue;
1269 }
1270
1271 if (itemProto->InventoryType != INVTYPE_WEAPON &&
1272 itemProto->InventoryType != INVTYPE_SHIELD &&
1273 itemProto->InventoryType != INVTYPE_RANGED &&
1274 itemProto->InventoryType != INVTYPE_2HWEAPON &&
1275 itemProto->InventoryType != INVTYPE_WEAPONMAINHAND &&
1276 itemProto->InventoryType != INVTYPE_WEAPONOFFHAND &&
1277 itemProto->InventoryType != INVTYPE_HOLDABLE &&
1278 itemProto->InventoryType != INVTYPE_THROWN &&
1279 itemProto->InventoryType != INVTYPE_RANGEDRIGHT &&
1280 itemProto->InventoryType != INVTYPE_RELIC)
1281 {
1282 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);
1283 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
1284 }
1285 }
1286 }
1287
1288 sLog.outString(">> Loaded %u equipment template", sEquipmentStorage.GetRecordCount());
1289 sLog.outString();
1290
1291 sEquipmentStorageRaw.LoadProgressive(sWorld.GetWowPatch(), false);
1292 for (uint32 i = 1; i < sEquipmentStorageRaw.GetMaxEntry(); ++i)
1293 if (sEquipmentStorageRaw.LookupEntry<EquipmentInfoRaw>(i))
1294 if (sEquipmentStorage.LookupEntry<EquipmentInfo>(i))
1295 sLog.outErrorDb("Table 'creature_equip_template_raw` have redundant data for ID %u ('creature_equip_template` already have data)", i);
1296
1297 sLog.outString(">> Loaded %u equipment template (deprecated format)", sEquipmentStorageRaw.GetRecordCount());
1298 sLog.outString();
1299}
1300
1301CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
1302{
1303 return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
1304}
1305
1306// generally for models having another model for the other team (totems)
1307uint32 ObjectMgr::GetCreatureModelOtherTeamModel(uint32 modelId)
1308{
1309 if (const CreatureModelInfo *modelInfo = GetCreatureModelInfo(modelId))
1310 return modelInfo->modelid_other_team;
1311
1312 return 0;
1313}
1314
1315CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
1316{
1317 CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
1318 if (!minfo)
1319 return NULL;
1320
1321 // If a model for another gender exists, 50% chance to use it
1322 if (minfo->modelid_other_gender != 0 && urand(0, 1) == 0)
1323 {
1324 CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
1325 if (!minfo_tmp)
1326 {
1327 sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
1328 return minfo; // not fatal, just use the previous one
1329 }
1330 else
1331 return minfo_tmp;
1332 }
1333 else
1334 return minfo;
1335}
1336
1337void ObjectMgr::LoadCreatureModelInfo()
1338{
1339 sCreatureModelStorage.Load();
1340
1341 // post processing
1342 for (uint32 i = 1; i < sCreatureModelStorage.GetMaxEntry(); ++i)
1343 {
1344 CreatureModelInfo const *minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
1345 if (!minfo)
1346 continue;
1347
1348 if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
1349 sLog.outErrorDb("Table `creature_model_info` has model for nonexistent model id (%u).", minfo->modelid);
1350
1351 if (minfo->gender > GENDER_NONE)
1352 {
1353 sLog.outErrorDb("Table `creature_model_info` has invalid gender (%u) for model id (%u).", uint32(minfo->gender), minfo->modelid);
1354 const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
1355 }
1356
1357 if (minfo->modelid_other_gender)
1358 {
1359 if (minfo->modelid_other_gender == minfo->modelid)
1360 {
1361 sLog.outErrorDb("Table `creature_model_info` has redundant modelid_other_gender model (%u) defined for model id %u.", minfo->modelid_other_gender, minfo->modelid);
1362 const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
1363 }
1364 else if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
1365 {
1366 sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_gender model (%u) defined for model id %u.", minfo->modelid_other_gender, minfo->modelid);
1367 const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
1368 }
1369 }
1370
1371 if (minfo->modelid_other_team)
1372 {
1373 if (minfo->modelid_other_team == minfo->modelid)
1374 {
1375 sLog.outErrorDb("Table `creature_model_info` has redundant modelid_other_team model (%u) defined for model id %u.", minfo->modelid_other_team, minfo->modelid);
1376 const_cast<CreatureModelInfo*>(minfo)->modelid_other_team = 0;
1377 }
1378 else if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_team))
1379 {
1380 sLog.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_team model (%u) defined for model id %u.", minfo->modelid_other_team, minfo->modelid);
1381 const_cast<CreatureModelInfo*>(minfo)->modelid_other_team = 0;
1382 }
1383 }
1384 }
1385
1386 // character races expected have model info data in table
1387 for (uint32 race = 1; race < sChrRacesStore.GetNumRows(); ++race)
1388 {
1389 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
1390 if (!raceEntry)
1391 continue;
1392
1393 if (!((1 << (race - 1)) & RACEMASK_ALL_PLAYABLE))
1394 continue;
1395
1396 if (CreatureModelInfo const *minfo = GetCreatureModelInfo(raceEntry->model_f))
1397 {
1398 if (minfo->gender != GENDER_FEMALE)
1399 sLog.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u female model id %u", minfo->gender, race, raceEntry->model_f);
1400
1401 if (minfo->modelid_other_gender != raceEntry->model_m)
1402 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);
1403
1404 if (minfo->bounding_radius <= 0.0f)
1405 {
1406 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);
1407 const_cast<CreatureModelInfo*>(minfo)->bounding_radius = DEFAULT_WORLD_OBJECT_SIZE;
1408 }
1409
1410 if (minfo->combat_reach != 1.5f)
1411 {
1412 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);
1413 const_cast<CreatureModelInfo*>(minfo)->combat_reach = 1.5f;
1414 }
1415 }
1416 else
1417 sLog.outErrorDb("Table `creature_model_info` expect have data for character race %u female model id %u", race, raceEntry->model_f);
1418
1419 if (CreatureModelInfo const *minfo = GetCreatureModelInfo(raceEntry->model_m))
1420 {
1421 if (minfo->gender != GENDER_MALE)
1422 sLog.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u male model id %u", minfo->gender, race, raceEntry->model_m);
1423
1424 if (minfo->modelid_other_gender != raceEntry->model_f)
1425 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);
1426
1427 if (minfo->bounding_radius <= 0.0f)
1428 {
1429 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);
1430 const_cast<CreatureModelInfo*>(minfo)->bounding_radius = DEFAULT_WORLD_OBJECT_SIZE;
1431 }
1432
1433 if (minfo->combat_reach != 1.5f)
1434 {
1435 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);
1436 const_cast<CreatureModelInfo*>(minfo)->combat_reach = 1.5f;
1437 }
1438 }
1439 else
1440 sLog.outErrorDb("Table `creature_model_info` expect have data for character race %u male model id %u", race, raceEntry->model_m);
1441
1442 }
1443
1444 sLog.outString(">> Loaded %u creature model based info", sCreatureModelStorage.GetRecordCount());
1445 sLog.outString();
1446}
1447
1448void ObjectMgr::LoadCreatures(bool reload)
1449{
1450 uint32 count = 0;
1451 // 0 1 2 3
1452 QueryResult *result = WorldDatabase.Query("SELECT creature.guid, creature.id, map, modelid,"
1453 // 4 5 6 7 8 9 10 11
1454 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
1455 // 12 13 14 15 16
1456 "curhealth, curmana, DeathState, MovementType, event,"
1457 // 17 18 19 20 21 22
1458 "pool_creature.pool_entry, pool_creature_template.pool_entry, spawnFlags, visibilitymod, patch_min, patch_max "
1459 "FROM creature "
1460 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1461 "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid "
1462 "LEFT OUTER JOIN pool_creature_template ON creature.id = pool_creature_template.id");
1463
1464 if (!result)
1465 {
1466 BarGoLink bar(1);
1467
1468 bar.step();
1469
1470 sLog.outString();
1471 sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
1472 return;
1473 }
1474
1475 // build single time for check creature data
1476
1477 BarGoLink bar(result->GetRowCount());
1478
1479 do
1480 {
1481 Field *fields = result->Fetch();
1482 bar.step();
1483
1484 uint32 guid = fields[ 0].GetUInt32();
1485 uint32 entry = fields[ 1].GetUInt32();
1486 uint8 patch_min = fields[21].GetUInt8();
1487 uint8 patch_max = fields[22].GetUInt8();
1488 bool existsInPatch = true;
1489
1490 if ((patch_min > patch_max) || (patch_max > 10))
1491 {
1492 sLog.outErrorDb("Table `creature` GUID %u (entry %u) has invalid values min_patch=%u, max_patch=%u.", guid, entry, patch_min, patch_max);
1493 sLog.out(LOG_DBERRFIX, "UPDATE creature SET min_patch=0, max_patch=10 WHERE guid=%u AND id=%u;", guid, entry);
1494 patch_min = 0;
1495 patch_max = 10;
1496 }
1497
1498 if (!((sWorld.GetWowPatch() >= patch_min) && (sWorld.GetWowPatch() <= patch_max)))
1499 existsInPatch = false;
1500
1501 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
1502 if (!cInfo)
1503 {
1504 if (existsInPatch) // don't print error when it is not loaded for the current patch
1505 {
1506 sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
1507 sLog.out(LOG_DBERRFIX, "DELETE FROM creature WHERE guid=%u;", guid);
1508 }
1509 continue;
1510 }
1511
1512 bool alreadyPresent = reload && mCreatureDataMap.find(guid) != mCreatureDataMap.end();
1513 CreatureData& data = mCreatureDataMap[guid];
1514
1515 data.id = entry;
1516 data.mapid = fields[ 2].GetUInt32();
1517 data.modelid_override = fields[ 3].GetUInt32();
1518 data.equipmentId = fields[ 4].GetUInt32();
1519 data.posX = fields[ 5].GetFloat();
1520 data.posY = fields[ 6].GetFloat();
1521 data.posZ = fields[ 7].GetFloat();
1522 data.orientation = fields[ 8].GetFloat();
1523 data.spawntimesecs = fields[ 9].GetUInt32();
1524 data.spawndist = fields[10].GetFloat();
1525 data.currentwaypoint = fields[11].GetUInt32();
1526 data.curhealth = fields[12].GetUInt32();
1527 data.curmana = fields[13].GetUInt32();
1528 data.is_dead = fields[14].GetBool();
1529 data.movementType = fields[15].GetUInt8();
1530 data.spawnFlags = fields[19].GetUInt32();
1531 data.visibilityModifier = fields[20].GetFloat();
1532 data.instanciatedContinentInstanceId = sMapMgr.GetContinentInstanceId(data.mapid, data.posX, data.posY);
1533 int16 gameEvent = fields[16].GetInt16();
1534 int16 GuidPoolId = fields[17].GetInt16();
1535 int16 EntryPoolId = fields[18].GetInt16();
1536
1537 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(data.mapid);
1538 if (!mapEntry)
1539 {
1540 sLog.outErrorDb("Table `creature` have creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
1541 sLog.out(LOG_DBERRFIX, "DELETE FROM creature WHERE guid=%u AND id=%u;", guid, data.id);
1542 continue;
1543 }
1544
1545 if (!existsInPatch)
1546 data.spawnFlags |= SPAWN_FLAG_DISABLED;
1547
1548 if (data.modelid_override > 0 && !sCreatureDisplayInfoStore.LookupEntry(data.modelid_override))
1549 {
1550 sLog.outErrorDb("Table `creature` GUID %u (entry %u) has model for nonexistent model id (%u), set to 0.", guid, data.id, data.modelid_override);
1551 sLog.out(LOG_DBERRFIX, "UPDATE creature SET modelid=0 WHERE guid=%u AND id=%u;", guid, data.id);
1552 data.modelid_override = 0;
1553 }
1554
1555 if (data.equipmentId > 0) // -1 no equipment, 0 use default
1556 {
1557 if (!GetEquipmentInfo(data.equipmentId) && !GetEquipmentInfoRaw(data.equipmentId))
1558 {
1559 sLog.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template` or `creature_equip_template_raw`, set to no equipment.", data.id, data.equipmentId);
1560 data.equipmentId = -1;
1561 }
1562 }
1563
1564 if (cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
1565 {
1566 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);
1567 sLog.out(LOG_DBERRFIX, "UPDATE creature SET curhealth=%u WHERE guid=%u AND id=%u;", cInfo->minhealth, guid, data.id);
1568 data.curhealth = cInfo->minhealth;
1569 }
1570
1571 if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
1572 {
1573 if (!mapEntry || !mapEntry->IsDungeon())
1574 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.",
1575 guid, data.id, CREATURE_FLAG_EXTRA_INSTANCE_BIND);
1576 }
1577
1578 if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_AGGRO_ZONE)
1579 {
1580 if (!mapEntry || !mapEntry->IsDungeon())
1581 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.",
1582 guid, data.id, CREATURE_FLAG_EXTRA_AGGRO_ZONE);
1583 }
1584
1585 if (data.curmana < cInfo->minmana)
1586 {
1587 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);
1588 sLog.out(LOG_DBERRFIX, "UPDATE creature SET curmana=%u WHERE guid=%u AND id=%u;", cInfo->minmana, guid, data.id);
1589 data.curmana = cInfo->minmana;
1590 }
1591
1592 if (data.spawndist < 0.0f)
1593 {
1594 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
1595 sLog.out(LOG_DBERRFIX, "UPDATE creature SET spawndist=0 WHERE guid=%u AND id=%u;", guid, data.id);
1596 data.spawndist = 0.0f;
1597 }
1598 else if (data.movementType == RANDOM_MOTION_TYPE)
1599 {
1600 if (data.spawndist == 0.0f)
1601 {
1602 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);
1603 sLog.out(LOG_DBERRFIX, "UPDATE creature SET MovementType=%u WHERE guid=%u AND id=%u;", IDLE_MOTION_TYPE, guid, data.id);
1604 data.movementType = IDLE_MOTION_TYPE;
1605 }
1606 }
1607 else if (data.movementType == IDLE_MOTION_TYPE)
1608 {
1609 if (data.spawndist != 0.0f)
1610 {
1611 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
1612 sLog.out(LOG_DBERRFIX, "UPDATE creature SET spawndist=0 WHERE guid=%u AND id=%u;", guid, data.id);
1613 data.spawndist = 0.0f;
1614 }
1615 }
1616
1617 if (!alreadyPresent && existsInPatch && gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1618 AddCreatureToGrid(guid, &data);
1619 ++count;
1620
1621 }
1622 while (result->NextRow());
1623
1624 delete result;
1625
1626 sLog.outString();
1627 sLog.outString(">> Loaded %lu creatures", (unsigned long)mCreatureDataMap.size());
1628}
1629
1630void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
1631{
1632 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1633 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1634
1635 mMapObjectGuids_lock.acquire();
1636 CellObjectGuids& cell_guids = mMapObjectGuids[data->mapid][cell_id];
1637 cell_guids.creatures.insert(guid);
1638 mMapObjectGuids_lock.release();
1639}
1640
1641void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
1642{
1643 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1644 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1645
1646 mMapObjectGuids_lock.acquire();
1647 CellObjectGuids& cell_guids = mMapObjectGuids[data->mapid][cell_id];
1648 cell_guids.creatures.erase(guid);
1649 mMapObjectGuids_lock.release();
1650}
1651
1652void ObjectMgr::LoadGameobjects(bool reload)
1653{
1654 uint32 count = 0;
1655
1656 // 0 1 2 3 4 5 6
1657 QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, gameobject.id, map, position_x, position_y, position_z, orientation,"
1658 // 7 8 9 10 11 12 13 14
1659 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, event, "
1660 // 15 16 17 18 19 20
1661 "pool_gameobject.pool_entry, pool_gameobject_template.pool_entry, spawnFlags, visibilitymod, patch_min, patch_max "
1662 "FROM gameobject "
1663 "LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1664 "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid "
1665 "LEFT OUTER JOIN pool_gameobject_template ON gameobject.id = pool_gameobject_template.id");
1666
1667 if (!result)
1668 {
1669 BarGoLink bar(1);
1670
1671 bar.step();
1672
1673 sLog.outString();
1674 sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1675 return;
1676 }
1677
1678 BarGoLink bar(result->GetRowCount());
1679
1680 do
1681 {
1682 Field *fields = result->Fetch();
1683 bar.step();
1684
1685 uint32 guid = fields[ 0].GetUInt32();
1686 uint32 entry = fields[ 1].GetUInt32();
1687 uint8 patch_min = fields[19].GetUInt8();
1688 uint8 patch_max = fields[20].GetUInt8();
1689
1690 if ((patch_min > patch_max) || (patch_max > 10))
1691 {
1692 sLog.outErrorDb("Table `gameobject` GUID %u (entry %u) has invalid values min_patch=%u, max_patch=%u.", guid, entry, patch_min, patch_max);
1693 sLog.out(LOG_DBERRFIX, "UPDATE gameobject SET min_patch=0, max_patch=10 WHERE guid=%u AND id=%u;", guid, entry);
1694 patch_min = 0;
1695 patch_max = 10;
1696 }
1697
1698 if (!((sWorld.GetWowPatch() >= patch_min) && (sWorld.GetWowPatch() <= patch_max)))
1699 continue;
1700
1701 GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
1702 if (!gInfo)
1703 {
1704 sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
1705 sLog.out(LOG_DBERRFIX, "DELETE FROM gameobject WHERE guid=%u;", guid);
1706 continue;
1707 }
1708
1709 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
1710 {
1711 sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
1712 continue;
1713 }
1714
1715 bool alreadyPresent = reload && mGameObjectDataMap.find(guid) != mGameObjectDataMap.end();
1716 GameObjectData& data = mGameObjectDataMap[guid];
1717
1718 data.id = entry;
1719 data.mapid = fields[ 2].GetUInt32();
1720 data.posX = fields[ 3].GetFloat();
1721 data.posY = fields[ 4].GetFloat();
1722 data.posZ = fields[ 5].GetFloat();
1723 data.orientation = fields[ 6].GetFloat();
1724 data.rotation0 = fields[ 7].GetFloat();
1725 data.rotation1 = fields[ 8].GetFloat();
1726 data.rotation2 = fields[ 9].GetFloat();
1727 data.rotation3 = fields[10].GetFloat();
1728 data.spawntimesecs = fields[11].GetInt32();
1729 data.spawnFlags = fields[17].GetUInt32();
1730 data.visibilityModifier = fields[18].GetFloat();
1731 data.instanciatedContinentInstanceId = sMapMgr.GetContinentInstanceId(data.mapid, data.posX, data.posY);
1732
1733 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(data.mapid);
1734 if (!mapEntry)
1735 {
1736 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that spawned at nonexistent map (Id: %u), skip", guid, data.id, data.mapid);
1737 continue;
1738 }
1739
1740 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
1741 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.", guid, data.id);
1742
1743 data.animprogress = fields[12].GetUInt32();
1744
1745 uint32 go_state = fields[13].GetUInt32();
1746 if (go_state >= MAX_GO_STATE)
1747 {
1748 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
1749 continue;
1750 }
1751 data.go_state = GOState(go_state);
1752
1753 int16 gameEvent = fields[14].GetInt16();
1754 int16 GuidPoolId = fields[15].GetInt16();
1755 int16 EntryPoolId = fields[16].GetInt16();
1756
1757 if (data.rotation0 < -1.0f || data.rotation0 > 1.0f)
1758 {
1759 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation0 (%f) value, skip", guid, data.id, data.rotation0);
1760 continue;
1761 }
1762
1763 if (data.rotation1 < -1.0f || data.rotation1 > 1.0f)
1764 {
1765 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation1 (%f) value, skip", guid, data.id, data.rotation1);
1766 continue;
1767 }
1768
1769 if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
1770 {
1771 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
1772 continue;
1773 }
1774
1775 if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
1776 {
1777 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
1778 continue;
1779 }
1780
1781 if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
1782 {
1783 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
1784 sLog.out(LOG_DBERRFIX, "DELETE FROM gameobject WHERE guid=%u;", guid);
1785 continue;
1786 }
1787
1788 if (!alreadyPresent && gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1789 AddGameobjectToGrid(guid, &data);
1790 ++count;
1791
1792 }
1793 while (result->NextRow());
1794
1795 delete result;
1796
1797 sLog.outString();
1798 sLog.outString(">> Loaded %lu gameobjects", (unsigned long)mGameObjectDataMap.size());
1799}
1800
1801void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
1802{
1803 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1804 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1805
1806 mMapObjectGuids_lock.acquire();
1807 CellObjectGuids& cell_guids = mMapObjectGuids[data->mapid][cell_id];
1808 cell_guids.gameobjects.insert(guid);
1809 mMapObjectGuids_lock.release();
1810}
1811
1812void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
1813{
1814 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1815 uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1816
1817 mMapObjectGuids_lock.acquire();
1818 CellObjectGuids& cell_guids = mMapObjectGuids[data->mapid][cell_id];
1819 cell_guids.gameobjects.erase(guid);
1820 mMapObjectGuids_lock.release();
1821}
1822
1823// name must be checked to correctness (if received) before call this function
1824/*
1825ObjectGuid ObjectMgr::GetPlayerGuidByName(std::string name) const
1826{
1827 ObjectGuid guid;
1828
1829 CharacterDatabase.escape_string(name);
1830
1831 // Player name safe to sending to DB (checked at login) and this function using
1832 QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
1833 if (result)
1834 {
1835 guid = ObjectGuid(HIGHGUID_PLAYER, (*result)[0].GetUInt32());
1836
1837 delete result;
1838 }
1839
1840 return guid;
1841}
1842
1843bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string &name) const
1844{
1845 // prevent DB access for online player
1846 if(Player* player = GetPlayer(guid))
1847 {
1848 name = player->GetName();
1849 return true;
1850 }
1851
1852 uint32 lowguid = guid.GetCounter();
1853
1854 QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", lowguid);
1855
1856 if(result)
1857 {
1858 name = (*result)[0].GetCppString();
1859 delete result;
1860 return true;
1861 }
1862
1863 return false;
1864}
1865
1866Team ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const
1867{
1868 // prevent DB access for online player
1869 if (Player* player = GetPlayer(guid))
1870 return Player::TeamForRace(player->getRace());
1871
1872 uint32 lowguid = guid.GetCounter();
1873
1874 QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", lowguid);
1875
1876 if (result)
1877 {
1878 uint8 race = (*result)[0].GetUInt8();
1879 delete result;
1880 return Player::TeamForRace(race);
1881 }
1882
1883 return TEAM_NONE;
1884}
1885
1886uint32 ObjectMgr::GetPlayerAccountIdByGUID(ObjectGuid guid) const
1887{
1888 // prevent DB access for online player
1889 if(Player* player = GetPlayer(guid))
1890 return player->GetSession()->GetAccountId();
1891
1892 uint32 lowguid = guid.GetCounter();
1893
1894 QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", lowguid);
1895 if (result)
1896 {
1897 uint32 acc = (*result)[0].GetUInt32();
1898 delete result;
1899 return acc;
1900 }
1901
1902 return 0;
1903}
1904
1905uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
1906{
1907 QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
1908 if(result)
1909 {
1910 uint32 acc = (*result)[0].GetUInt32();
1911 delete result;
1912 return acc;
1913 }
1914
1915 return 0;
1916}*/
1917
1918void ObjectMgr::LoadItemLocales()
1919{
1920 mItemLocaleMap.clear(); // need for reload case
1921
1922 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");
1923
1924 if (!result)
1925 {
1926 BarGoLink bar(1);
1927
1928 bar.step();
1929
1930 sLog.outString();
1931 sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
1932 return;
1933 }
1934
1935 BarGoLink bar(result->GetRowCount());
1936
1937 do
1938 {
1939 Field *fields = result->Fetch();
1940 bar.step();
1941
1942 uint32 entry = fields[0].GetUInt32();
1943
1944 if (!GetItemPrototype(entry))
1945 {
1946 ERROR_DB_STRICT_LOG("Table `locales_item` has data for nonexistent item entry %u, skipped.", entry);
1947 continue;
1948 }
1949
1950 ItemLocale& data = mItemLocaleMap[entry];
1951
1952 for (int i = 1; i < MAX_LOCALE; ++i)
1953 {
1954 std::string str = fields[1 + 2 * (i - 1)].GetCppString();
1955 if (!str.empty())
1956 {
1957 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1958 if (idx >= 0)
1959 {
1960 if ((int32)data.Name.size() <= idx)
1961 data.Name.resize(idx + 1);
1962
1963 data.Name[idx] = str;
1964 }
1965 }
1966
1967 str = fields[1 + 2 * (i - 1) + 1].GetCppString();
1968 if (!str.empty())
1969 {
1970 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1971 if (idx >= 0)
1972 {
1973 if ((int32)data.Description.size() <= idx)
1974 data.Description.resize(idx + 1);
1975
1976 data.Description[idx] = str;
1977 }
1978 }
1979 }
1980 }
1981 while (result->NextRow());
1982
1983 delete result;
1984
1985 sLog.outString();
1986 sLog.outString(">> Loaded %lu Item locale strings", (unsigned long)mItemLocaleMap.size());
1987}
1988
1989struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader, SQLStorage>
1990{
1991 template<class D>
1992 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
1993 {
1994 dst = D(sScriptMgr.GetScriptId(src));
1995 }
1996};
1997
1998void ObjectMgr::LoadItemPrototypes()
1999{
2000 SQLItemLoader loader;
2001 loader.LoadProgressive(sItemStorage, sWorld.GetWowPatch());
2002 mQuestStartingItems.clear();
2003 sLog.outString(">> Loaded %u item prototypes", sItemStorage.GetRecordCount());
2004 sLog.outString();
2005
2006 // check data correctness
2007 for (uint32 i = 1; i < sItemStorage.GetMaxEntry(); ++i)
2008 {
2009 ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
2010 if (!proto)
2011 continue;
2012
2013 if (proto->Class >= MAX_ITEM_CLASS)
2014 {
2015 sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)", i, proto->Class);
2016 const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK;
2017 }
2018
2019 if (proto->SubClass >= MaxItemSubclassValues[proto->Class])
2020 {
2021 sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u", i, proto->SubClass, proto->Class);
2022 const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
2023 }
2024
2025 if (proto->Quality >= MAX_ITEM_QUALITY)
2026 {
2027 sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)", i, proto->Quality);
2028 const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
2029 }
2030
2031 if (proto->BuyCount <= 0)
2032 {
2033 sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", i, proto->BuyCount);
2034 const_cast<ItemPrototype*>(proto)->BuyCount = 1;
2035 }
2036
2037 if (proto->InventoryType >= MAX_INVTYPE)
2038 {
2039 sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)", i, proto->InventoryType);
2040 const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
2041 }
2042
2043 if (proto->InventoryType != INVTYPE_NON_EQUIP)
2044 {
2045 if (proto->Flags & ITEM_FLAG_LOOTABLE)
2046 {
2047 sLog.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_LOOTABLE (%u), flag removed.", i, ITEM_FLAG_LOOTABLE);
2048 const_cast<ItemPrototype*>(proto)->Flags &= ~ITEM_FLAG_LOOTABLE;
2049 }
2050 }
2051 else if (proto->InventoryType != INVTYPE_BAG)
2052 {
2053 if (proto->ContainerSlots > 0)
2054 {
2055 sLog.outErrorDb("Non-container item (Entry: %u) has ContainerSlots (%u), set to 0.", i, proto->ContainerSlots);
2056 const_cast<ItemPrototype*>(proto)->ContainerSlots = 0;
2057 }
2058 }
2059
2060 if (proto->RequiredSkill >= MAX_SKILL_TYPE)
2061 {
2062 sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)", i, proto->RequiredSkill);
2063 const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
2064 }
2065
2066 {
2067 // can be used in equip slot, as page read use in inventory, or spell casting at use
2068 bool req = proto->InventoryType != INVTYPE_NON_EQUIP || proto->PageText;
2069 if (!req)
2070 {
2071 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
2072 {
2073 if (proto->Spells[j].SpellId)
2074 {
2075 req = true;
2076 break;
2077 }
2078 }
2079 }
2080
2081 if (req)
2082 {
2083 if (!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
2084 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.", i, proto->AllowableClass);
2085
2086 if (!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
2087 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.", i, proto->AllowableRace);
2088 }
2089 }
2090
2091 if (proto->RequiredSpell && !sSpellMgr.GetSpellEntry(proto->RequiredSpell))
2092 {
2093 sLog.outErrorDb("Item (Entry: %u) have wrong (nonexistent) spell in RequiredSpell (%u)", i, proto->RequiredSpell);
2094 const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
2095 }
2096
2097 if (proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
2098 sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", i, proto->RequiredReputationRank);
2099
2100 if (proto->RequiredReputationFaction)
2101 {
2102 if (!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
2103 {
2104 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", i, proto->RequiredReputationFaction);
2105 const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
2106 }
2107
2108 if (proto->RequiredReputationRank == MIN_REPUTATION_RANK)
2109 sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", i);
2110 }
2111 // else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
2112 // sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
2113
2114 if (proto->Stackable == 0)
2115 {
2116 sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.", i, proto->Stackable);
2117 const_cast<ItemPrototype*>(proto)->Stackable = 1;
2118 }
2119 else if (proto->Stackable > 255)
2120 {
2121 sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).", i, proto->Stackable);
2122 const_cast<ItemPrototype*>(proto)->Stackable = 255;
2123 }
2124
2125 if (proto->ContainerSlots)
2126 {
2127 if (proto->ContainerSlots > MAX_BAG_SIZE)
2128 {
2129 sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", i, proto->ContainerSlots, MAX_BAG_SIZE);
2130 const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
2131 }
2132 }
2133
2134 for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j)
2135 {
2136 // for ItemStatValue != 0
2137 if (proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
2138 {
2139 sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
2140 const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
2141 }
2142 }
2143
2144 for (int j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
2145 {
2146 if (proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
2147 {
2148 sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)", i, j + 1, proto->Damage[j].DamageType);
2149 const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
2150 }
2151 }
2152
2153 // normal spell list
2154 {
2155 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
2156 {
2157 if (proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER)
2158 {
2159 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
2160 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2161 const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2162 }
2163 // on hit can be sued only at weapon
2164 else if (proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
2165 {
2166 if (proto->Class != ITEM_CLASS_WEAPON)
2167 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);
2168 }
2169
2170 if (proto->Spells[j].SpellId)
2171 {
2172 SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(proto->Spells[j].SpellId);
2173 if (!spellInfo)
2174 {
2175 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)", i, j + 1, proto->Spells[j].SpellId);
2176 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
2177 }
2178 }
2179 }
2180 }
2181
2182 if (proto->Bonding >= MAX_BIND_TYPE)
2183 sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)", i, proto->Bonding);
2184
2185 if (proto->PageText)
2186 {
2187 if (!sPageTextStore.LookupEntry<PageText>(proto->PageText))
2188 sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i, proto->PageText);
2189 }
2190
2191 if (proto->LockID && !sLockStore.LookupEntry(proto->LockID))
2192 sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)", i, proto->LockID);
2193
2194 if (proto->Sheath >= MAX_SHEATHETYPE)
2195 {
2196 sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)", i, proto->Sheath);
2197 const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
2198 }
2199
2200 if (proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
2201 {
2202 sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", i, proto->RandomProperty);
2203 const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
2204 }
2205
2206 if (proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
2207 {
2208 sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)", i, proto->ItemSet);
2209 const_cast<ItemPrototype*>(proto)->ItemSet = 0;
2210 }
2211
2212 if (proto->Area && !AreaEntry::GetById(proto->Area))
2213 sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)", i, proto->Area);
2214
2215 if (proto->Map && !sMapStorage.LookupEntry<MapEntry>(proto->Map))
2216 sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)", i, proto->Map);
2217
2218 if (proto->BagFamily)
2219 {
2220 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(proto->BagFamily);
2221 if (!bf)
2222 {
2223 sLog.outErrorDb("Item (Entry: %u) has bag family %u not listed in ItemBagFamily.dbc, setted it to 0", i, proto->BagFamily);
2224 const_cast<ItemPrototype*>(proto)->BagFamily = 0;
2225 }
2226 }
2227
2228 if (proto->DisenchantID)
2229 {
2230 if (proto->Quality > ITEM_QUALITY_EPIC || proto->Quality < ITEM_QUALITY_UNCOMMON)
2231 {
2232 sLog.outErrorDb("Item (Entry: %u) has wrong quality (%u) for disenchanting, remove disenchanting loot id.", i, proto->Quality);
2233 const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
2234 }
2235 else if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR)
2236 {
2237 sLog.outErrorDb("Item (Entry: %u) has wrong item class (%u) for disenchanting, remove disenchanting loot id.", i, proto->Class);
2238 const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
2239 }
2240 }
2241
2242 if (proto->FoodType >= MAX_PET_DIET)
2243 {
2244 sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)", i, proto->FoodType);
2245 const_cast<ItemPrototype*>(proto)->FoodType = 0;
2246 }
2247
2248 if (proto->ExtraFlags)
2249 {
2250 if (proto->ExtraFlags & ~ITEM_EXTRA_ALL)
2251 sLog.outErrorDb("Item (Entry: %u) has wrong ExtraFlags (%u) with unused bits set", i, proto->ExtraFlags);
2252
2253 if (proto->ExtraFlags & ITEM_EXTRA_REAL_TIME_DURATION)
2254 {
2255 if (proto->Duration == 0)
2256 {
2257 sLog.outErrorDb("Item (Entry: %u) has redundant real-time duration flag in ExtraFlags, item not have duration", i);
2258 const_cast<ItemPrototype*>(proto)->ExtraFlags &= ~ITEM_EXTRA_REAL_TIME_DURATION;
2259 }
2260 }
2261 }
2262
2263
2264 if (proto->StartQuest > 0)
2265 // Item starts a quest, insert it into the quest->startItem map
2266 {
2267 if (mQuestStartingItems.find(proto->StartQuest) == mQuestStartingItems.end())
2268 mQuestStartingItems.insert( std::pair<uint32, uint32>(proto->StartQuest, proto->ItemId) );
2269 else
2270 sLog.outErrorDb("Item #%u also starts quest #%u.", i, proto->StartQuest);
2271 }
2272 }
2273
2274 // check some dbc referenced items (avoid duplicate reports)
2275 std::set<uint32> notFoundOutfit;
2276 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
2277 {
2278 CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
2279 if (!entry)
2280 continue;
2281
2282 for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
2283 {
2284 if (entry->ItemId[j] <= 0)
2285 continue;
2286
2287 uint32 item_id = entry->ItemId[j];
2288
2289 if (!GetItemPrototype(item_id))
2290 notFoundOutfit.insert(item_id);
2291 }
2292 }
2293
2294 for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
2295 sLog.outErrorDb("Item (Entry: %u) not exist in `item_template` but referenced in `CharStartOutfit.dbc`", *itr);
2296}
2297
2298void ObjectMgr::LoadItemRequiredTarget()
2299{
2300 m_ItemRequiredTarget.clear(); // needed for reload case
2301
2302 uint32 count = 0;
2303
2304 QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target");
2305
2306 if (!result)
2307 {
2308 BarGoLink bar(1);
2309
2310 bar.step();
2311
2312 sLog.outString();
2313 sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty.");
2314 return;
2315 }
2316
2317 BarGoLink bar(result->GetRowCount());
2318
2319 do
2320 {
2321 Field *fields = result->Fetch();
2322 bar.step();
2323
2324 uint32 uiItemId = fields[0].GetUInt32();
2325 uint32 uiType = fields[1].GetUInt32();
2326 uint32 uiTargetEntry = fields[2].GetUInt32();
2327
2328 ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId);
2329
2330 if (!pItemProto)
2331 {
2332 sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.", uiItemId, uiTargetEntry);
2333 continue;
2334 }
2335
2336 bool bIsItemSpellValid = false;
2337
2338 for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
2339 {
2340 if (SpellEntry const* pSpellInfo = sSpellMgr.GetSpellEntry(pItemProto->Spells[i].SpellId))
2341 {
2342 if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE ||
2343 pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
2344 {
2345 SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(pSpellInfo->Id);
2346 if (bounds.first != bounds.second)
2347 break;
2348
2349 for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
2350 {
2351 if (pSpellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
2352 pSpellInfo->EffectImplicitTargetB[j] == TARGET_CHAIN_DAMAGE ||
2353 pSpellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ANY ||
2354 pSpellInfo->EffectImplicitTargetB[j] == TARGET_UNIT_TARGET_ANY)
2355 {
2356 bIsItemSpellValid = true;
2357 break;
2358 }
2359 }
2360 if (bIsItemSpellValid)
2361 break;
2362 }
2363 }
2364 }
2365
2366 if (!bIsItemSpellValid)
2367 {
2368 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);
2369 continue;
2370 }
2371
2372 if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE)
2373 {
2374 sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.", uiType, uiTargetEntry);
2375 continue;
2376 }
2377
2378 if (!uiTargetEntry)
2379 {
2380 sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).", uiType);
2381 continue;
2382 }
2383
2384 if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry))
2385 {
2386 sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.", uiTargetEntry);
2387 continue;
2388 }
2389
2390 m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId, ItemRequiredTarget(ItemRequiredTargetType(uiType), uiTargetEntry)));
2391
2392 ++count;
2393 }
2394 while (result->NextRow());
2395
2396 delete result;
2397
2398 sLog.outString();
2399 sLog.outString(">> Loaded %u Item required targets", count);
2400}
2401
2402void ObjectMgr::LoadPetLevelInfo()
2403{
2404 // Loading levels data
2405 {
2406 // 0 1 2 3 4 5 6 7 8 9
2407 QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2408
2409 uint32 count = 0;
2410
2411 if (!result)
2412 {
2413 BarGoLink bar(1);
2414 bar.step();
2415
2416 sLog.outString();
2417 sLog.outString(">> Loaded %u level pet stats definitions", count);
2418 sLog.outErrorDb("Error loading `pet_levelstats` table or empty table.");
2419 return;
2420 }
2421
2422 BarGoLink bar(result->GetRowCount());
2423
2424 do
2425 {
2426 Field* fields = result->Fetch();
2427
2428 uint32 creature_id = fields[0].GetUInt32();
2429 if (!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
2430 {
2431 sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
2432 continue;
2433 }
2434
2435 uint32 current_level = fields[1].GetUInt32();
2436 if (current_level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2437 {
2438 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2439 sLog.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
2440 else
2441 {
2442 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
2443 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2444 }
2445 continue;
2446 }
2447 else if (current_level < 1)
2448 {
2449 sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
2450 continue;
2451 }
2452
2453 PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
2454
2455 if (pInfoMapEntry == NULL)
2456 pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)];
2457
2458 // data for level 1 stored in [0] array element, ...
2459 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
2460
2461 pLevelInfo->health = fields[2].GetUInt16();
2462 pLevelInfo->mana = fields[3].GetUInt16();
2463 pLevelInfo->armor = fields[9].GetUInt16();
2464
2465 for (int i = 0; i < MAX_STATS; i++)
2466 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
2467
2468 bar.step();
2469 ++count;
2470 }
2471 while (result->NextRow());
2472
2473 delete result;
2474
2475 sLog.outString();
2476 sLog.outString(">> Loaded %u level pet stats definitions", count);
2477 }
2478
2479 // Fill gaps and check integrity
2480 for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
2481 {
2482 PetLevelInfo* pInfo = itr->second;
2483
2484 // fatal error if no level 1 data
2485 if (!pInfo || pInfo[0].health == 0)
2486 {
2487 sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!", itr->first);
2488 Log::WaitBeforeContinueIfNeed();
2489 exit(1);
2490 }
2491
2492 // fill level gaps
2493 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
2494 {
2495 if (pInfo[level].health == 0)
2496 {
2497 sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
2498 pInfo[level] = pInfo[level - 1];
2499 }
2500 }
2501 }
2502}
2503
2504PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
2505{
2506 if (level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2507 level = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL);
2508
2509 PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
2510 if (itr == petInfo.end())
2511 return NULL;
2512
2513 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
2514}
2515
2516void ObjectMgr::LoadPlayerInfo()
2517{
2518 // Load playercreate
2519 {
2520 // 0 1 2 3 4 5 6
2521 QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
2522
2523 uint32 count = 0;
2524
2525 if (!result)
2526 {
2527 BarGoLink bar(1);
2528
2529 sLog.outString();
2530 sLog.outString(">> Loaded %u player create definitions", count);
2531 sLog.outErrorDb("Error loading `playercreateinfo` table or empty table.");
2532 Log::WaitBeforeContinueIfNeed();
2533 exit(1);
2534 }
2535
2536 BarGoLink bar(result->GetRowCount());
2537
2538 do
2539 {
2540 Field* fields = result->Fetch();
2541
2542 uint32 current_race = fields[0].GetUInt32();
2543 uint32 current_class = fields[1].GetUInt32();
2544 uint32 mapId = fields[2].GetUInt32();
2545 uint32 areaId = fields[3].GetUInt32();
2546 float positionX = fields[4].GetFloat();
2547 float positionY = fields[5].GetFloat();
2548 float positionZ = fields[6].GetFloat();
2549 float orientation = fields[7].GetFloat();
2550
2551 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2552 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2553 {
2554 sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
2555 continue;
2556 }
2557
2558 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2559 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2560 {
2561 sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
2562 continue;
2563 }
2564
2565 // accept DB data only for valid position (and non instanceable)
2566 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
2567 {
2568 sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
2569 continue;
2570 }
2571
2572 if (sMapStorage.LookupEntry<MapEntry>(mapId)->Instanceable())
2573 {
2574 sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
2575 continue;
2576 }
2577
2578 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2579
2580 pInfo->mapId = mapId;
2581 pInfo->areaId = areaId;
2582 pInfo->positionX = positionX;
2583 pInfo->positionY = positionY;
2584 pInfo->positionZ = positionZ;
2585 pInfo->orientation = orientation;
2586
2587 pInfo->displayId_m = rEntry->model_m;
2588 pInfo->displayId_f = rEntry->model_f;
2589
2590 bar.step();
2591 ++count;
2592 }
2593 while (result->NextRow());
2594
2595 delete result;
2596
2597 sLog.outString();
2598 sLog.outString(">> Loaded %u player create definitions", count);
2599 }
2600
2601 // Load playercreate items
2602 {
2603 // 0 1 2 3
2604 QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2605
2606 uint32 count = 0;
2607
2608 if (!result)
2609 {
2610 BarGoLink bar(1);
2611
2612 bar.step();
2613
2614 sLog.outString();
2615 sLog.outString(">> Loaded %u custom player create items", count);
2616 }
2617 else
2618 {
2619 BarGoLink bar(result->GetRowCount());
2620
2621 do
2622 {
2623 Field* fields = result->Fetch();
2624
2625 uint32 current_race = fields[0].GetUInt32();
2626 uint32 current_class = fields[1].GetUInt32();
2627
2628 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2629 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2630 {
2631 sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
2632 continue;
2633 }
2634
2635 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2636 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2637 {
2638 sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
2639 continue;
2640 }
2641
2642 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2643
2644 uint32 item_id = fields[2].GetUInt32();
2645
2646 if (!GetItemPrototype(item_id))
2647 {
2648 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);
2649 continue;
2650 }
2651
2652 uint32 amount = fields[3].GetUInt32();
2653
2654 if (!amount)
2655 {
2656 sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
2657 continue;
2658 }
2659
2660 pInfo->item.push_back(PlayerCreateInfoItem(item_id, amount));
2661
2662 bar.step();
2663 ++count;
2664 }
2665 while (result->NextRow());
2666
2667 delete result;
2668
2669 sLog.outString();
2670 sLog.outString(">> Loaded %u custom player create items", count);
2671 }
2672 }
2673
2674 // Load playercreate spells
2675 {
2676 // 0 1 2
2677 QueryResult *result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell");
2678
2679 uint32 count = 0;
2680
2681 if (!result)
2682 {
2683 BarGoLink bar(1);
2684
2685 sLog.outString();
2686 sLog.outString(">> Loaded %u player create spells", count);
2687 sLog.outErrorDb("Error loading `playercreateinfo_spell` table or empty table.");
2688 }
2689 else
2690 {
2691 BarGoLink bar(result->GetRowCount());
2692
2693 do
2694 {
2695 Field* fields = result->Fetch();
2696
2697 uint32 current_race = fields[0].GetUInt32();
2698 uint32 current_class = fields[1].GetUInt32();
2699
2700 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2701 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2702 {
2703 sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.", current_race);
2704 continue;
2705 }
2706
2707 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2708 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2709 {
2710 sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.", current_class);
2711 continue;
2712 }
2713
2714 uint32 spell_id = fields[2].GetUInt32();
2715 if (!sSpellMgr.GetSpellEntry(spell_id))
2716 {
2717 sLog.outErrorDb("Non existing spell %u in `playercreateinfo_spell` table, ignoring.", spell_id);
2718 continue;
2719 }
2720
2721 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2722 pInfo->spell.push_back(spell_id);
2723
2724 bar.step();
2725 ++count;
2726 }
2727 while (result->NextRow());
2728
2729 delete result;
2730
2731 sLog.outString();
2732 sLog.outString(">> Loaded %u player create spells", count);
2733 }
2734 }
2735
2736 // Load playercreate actions
2737 {
2738 // 0 1 2 3 4
2739 QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
2740
2741 uint32 count = 0;
2742
2743 if (!result)
2744 {
2745 BarGoLink bar(1);
2746
2747 sLog.outString();
2748 sLog.outString(">> Loaded %u player create actions", count);
2749 sLog.outErrorDb("Error loading `playercreateinfo_action` table or empty table.");
2750 }
2751 else
2752 {
2753 BarGoLink bar(result->GetRowCount());
2754
2755 do
2756 {
2757 Field* fields = result->Fetch();
2758
2759 uint32 current_race = fields[0].GetUInt32();
2760 uint32 current_class = fields[1].GetUInt32();
2761
2762 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2763 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2764 {
2765 sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
2766 continue;
2767 }
2768
2769 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2770 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2771 {
2772 sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
2773 continue;
2774 }
2775
2776 uint8 action_button = fields[2].GetUInt8();
2777 uint32 action = fields[3].GetUInt32();
2778 uint8 action_type = fields[4].GetUInt8();
2779
2780 if (!Player::IsActionButtonDataValid(action_button, action, action_type, NULL))
2781 continue;
2782
2783 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2784 pInfo->action.push_back(PlayerCreateInfoAction(action_button, action, action_type));
2785
2786 bar.step();
2787 ++count;
2788 }
2789 while (result->NextRow());
2790
2791 delete result;
2792
2793 sLog.outString();
2794 sLog.outString(">> Loaded %u player create actions", count);
2795 }
2796 }
2797
2798 // Loading levels data (class only dependent)
2799 {
2800 // 0 1 2 3
2801 QueryResult *result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
2802
2803 uint32 count = 0;
2804
2805 if (!result)
2806 {
2807 BarGoLink bar(1);
2808
2809 sLog.outString();
2810 sLog.outString(">> Loaded %u level health/mana definitions", count);
2811 sLog.outErrorDb("Error loading `player_classlevelstats` table or empty table.");
2812 Log::WaitBeforeContinueIfNeed();
2813 exit(1);
2814 }
2815
2816 BarGoLink bar(result->GetRowCount());
2817
2818 do
2819 {
2820 Field* fields = result->Fetch();
2821
2822 uint32 current_class = fields[0].GetUInt32();
2823 if (current_class >= MAX_CLASSES)
2824 {
2825 sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.", current_class);
2826 continue;
2827 }
2828
2829 uint32 current_level = fields[1].GetUInt32();
2830 if (current_level == 0)
2831 {
2832 sLog.outErrorDb("Wrong level %u in `player_classlevelstats` table, ignoring.", current_level);
2833 continue;
2834 }
2835 else if (current_level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2836 {
2837 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2838 sLog.outErrorDb("Wrong (> %u) level %u in `player_classlevelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
2839 else
2840 {
2841 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_classlevelstats` table, ignoring.", current_level);
2842 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2843 }
2844 continue;
2845 }
2846
2847 PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
2848
2849 if (!pClassInfo->levelInfo)
2850 pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)];
2851
2852 PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level - 1];
2853
2854 pClassLevelInfo->basehealth = fields[2].GetUInt16();
2855 pClassLevelInfo->basemana = fields[3].GetUInt16();
2856
2857 bar.step();
2858 ++count;
2859 }
2860 while (result->NextRow());
2861
2862 delete result;
2863
2864 sLog.outString();
2865 sLog.outString(">> Loaded %u level health/mana definitions", count);
2866 }
2867
2868 // Fill gaps and check integrity
2869 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2870 {
2871 // skip nonexistent classes
2872 if (!sChrClassesStore.LookupEntry(class_))
2873 continue;
2874
2875 PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
2876
2877 // fatal error if no level 1 data
2878 if (!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
2879 {
2880 sLog.outErrorDb("Class %i Level 1 does not have health/mana data!", class_);
2881 Log::WaitBeforeContinueIfNeed();
2882 exit(1);
2883 }
2884
2885 // fill level gaps
2886 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
2887 {
2888 if (pClassInfo->levelInfo[level].basehealth == 0)
2889 {
2890 sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.", class_, level + 1, level);
2891 pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
2892 }
2893 }
2894 }
2895
2896 // Loading levels data (class/race dependent)
2897 {
2898 // 0 1 2 3 4 5 6 7
2899 QueryResult *result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
2900
2901 uint32 count = 0;
2902
2903 if (!result)
2904 {
2905 BarGoLink bar(1);
2906
2907 sLog.outString();
2908 sLog.outString(">> Loaded %u level stats definitions", count);
2909 sLog.outErrorDb("Error loading `player_levelstats` table or empty table.");
2910 Log::WaitBeforeContinueIfNeed();
2911 exit(1);
2912 }
2913
2914 BarGoLink bar(result->GetRowCount());
2915
2916 do
2917 {
2918 Field* fields = result->Fetch();
2919
2920 uint32 current_race = fields[0].GetUInt32();
2921 uint32 current_class = fields[1].GetUInt32();
2922
2923 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2924 if (!rEntry || !((1 << (current_race - 1)) & RACEMASK_ALL_PLAYABLE))
2925 {
2926 sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.", current_race);
2927 continue;
2928 }
2929
2930 ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(current_class);
2931 if (!cEntry || !((1 << (current_class - 1)) & CLASSMASK_ALL_PLAYABLE))
2932 {
2933 sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.", current_class);
2934 continue;
2935 }
2936
2937 uint32 current_level = fields[2].GetUInt32();
2938 if (current_level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
2939 {
2940 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2941 sLog.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
2942 else
2943 {
2944 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.", current_level);
2945 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2946 }
2947 continue;
2948 }
2949
2950 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2951
2952 if (!pInfo->levelInfo)
2953 pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)];
2954
2955 PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level - 1];
2956
2957 for (int i = 0; i < MAX_STATS; ++i)
2958 pLevelInfo->stats[i] = fields[i + 3].GetUInt8();
2959
2960 bar.step();
2961 ++count;
2962 }
2963 while (result->NextRow());
2964
2965 delete result;
2966
2967 sLog.outString();
2968 sLog.outString(">> Loaded %u level stats definitions", count);
2969 }
2970
2971 // Fill gaps and check integrity
2972 for (int race = 1; race < MAX_RACES; ++race)
2973 {
2974 // skip nonexistent races
2975 if (!((1 << (race - 1)) & RACEMASK_ALL_PLAYABLE) || !sChrRacesStore.LookupEntry(race))
2976 continue;
2977
2978 for (int class_ = 1; class_ < MAX_CLASSES; ++class_)
2979 {
2980 // skip nonexistent classes
2981 if (!((1 << (class_ - 1)) & CLASSMASK_ALL_PLAYABLE) || !sChrClassesStore.LookupEntry(class_))
2982 continue;
2983
2984 PlayerInfo* pInfo = &playerInfo[race][class_];
2985
2986 // skip non loaded combinations
2987 if (!pInfo->displayId_m || !pInfo->displayId_f)
2988 continue;
2989
2990 // fatal error if no level 1 data
2991 if (!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0)
2992 {
2993 sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!", race, class_);
2994 Log::WaitBeforeContinueIfNeed();
2995 exit(1);
2996 }
2997
2998 // fill level gaps
2999 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3000 {
3001 if (pInfo->levelInfo[level].stats[0] == 0)
3002 {
3003 sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
3004 pInfo->levelInfo[level] = pInfo->levelInfo[level - 1];
3005 }
3006 }
3007 }
3008 }
3009
3010 // Loading xp per level data
3011 {
3012 mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL));
3013 for (uint32 level = 0; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3014 mPlayerXPperLevel[level] = 0;
3015
3016 // 0 1
3017 QueryResult *result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
3018
3019 uint32 count = 0;
3020
3021 if (!result)
3022 {
3023 BarGoLink bar(1);
3024
3025 sLog.outString();
3026 sLog.outString(">> Loaded %u xp for level definitions", count);
3027 sLog.outErrorDb("Error loading `player_xp_for_level` table or empty table.");
3028 Log::WaitBeforeContinueIfNeed();
3029 exit(1);
3030 }
3031
3032 BarGoLink bar(result->GetRowCount());
3033
3034 do
3035 {
3036 Field* fields = result->Fetch();
3037
3038 uint32 current_level = fields[0].GetUInt32();
3039 uint32 current_xp = fields[1].GetUInt32();
3040
3041 if (current_level >= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3042 {
3043 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3044 sLog.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3045 else
3046 {
3047 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
3048 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3049 }
3050 continue;
3051 }
3052 //PlayerXPperLevel
3053 mPlayerXPperLevel[current_level] = current_xp;
3054 bar.step();
3055 ++count;
3056 }
3057 while (result->NextRow());
3058
3059 delete result;
3060
3061 sLog.outString();
3062 sLog.outString(">> Loaded %u xp for level definitions", count);
3063 }
3064
3065 // fill level gaps
3066 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL); ++level)
3067 {
3068 if (mPlayerXPperLevel[level] == 0)
3069 {
3070 sLog.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
3071 mPlayerXPperLevel[level] = mPlayerXPperLevel[level - 1] + 100;
3072 }
3073 }
3074}
3075
3076void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
3077{
3078 if (level < 1 || class_ >= MAX_CLASSES)
3079 return;
3080
3081 PlayerClassInfo const* pInfo = &playerClassInfo[class_];
3082
3083 if (level > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3084 level = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL);
3085
3086 *info = pInfo->levelInfo[level - 1];
3087}
3088
3089void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
3090{
3091 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
3092 return;
3093
3094 PlayerInfo const* pInfo = &playerInfo[race][class_];
3095 if (pInfo->displayId_m == 0 || pInfo->displayId_f == 0)
3096 return;
3097
3098 if (level <= sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
3099 *info = pInfo->levelInfo[level - 1];
3100 else
3101 BuildPlayerLevelInfo(race, class_, level, info);
3102}
3103
3104void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
3105{
3106 // base data (last known level)
3107 *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) - 1];
3108
3109 for (int lvl = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
3110 {
3111 switch (_class)
3112 {
3113 case CLASS_WARRIOR:
3114 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3115 info->stats[STAT_STAMINA] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3116 info->stats[STAT_AGILITY] += (lvl > 36 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3117 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3118 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3119 break;
3120 case CLASS_PALADIN:
3121 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1 : 0);
3122 info->stats[STAT_STAMINA] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3123 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 7 && !(lvl % 2) ? 1 : 0));
3124 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl % 2) ? 1 : 0);
3125 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1 : 0);
3126 break;
3127 case CLASS_HUNTER:
3128 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1 : 0);
3129 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3130 info->stats[STAT_AGILITY] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3131 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl % 2) ? 1 : 0);
3132 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3133 break;
3134 case CLASS_ROGUE:
3135 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1 : 0);
3136 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3137 info->stats[STAT_AGILITY] += (lvl > 16 ? 2 : (lvl > 1 ? 1 : 0));
3138 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl % 2) ? 1 : 0);
3139 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3140 break;
3141 case CLASS_PRIEST:
3142 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3143 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3144 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3145 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2 : (lvl > 1 ? 1 : 0));
3146 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1 : 0);
3147 break;
3148 case CLASS_SHAMAN:
3149 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3150 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3151 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl % 2) ? 1 : 0);
3152 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1 : 0);
3153 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1 : 0);
3154 break;
3155 case CLASS_MAGE:
3156 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3157 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3158 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3159 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2 : (lvl > 1 ? 1 : 0));
3160 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3161 break;
3162 case CLASS_WARLOCK:
3163 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3164 info->stats[STAT_STAMINA] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3165 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3166 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3167 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3168 break;
3169 case CLASS_DRUID:
3170 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3171 info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
3172 info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3173 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
3174 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
3175 }
3176 }
3177}
3178
3179void ObjectMgr::LoadGroups()
3180{
3181 // -- loading groups --
3182 uint32 count = 0;
3183 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3184 QueryResult *result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, leaderGuid, groupId FROM groups");
3185
3186 if (!result)
3187 {
3188 BarGoLink bar(1);
3189
3190 bar.step();
3191
3192 sLog.outString();
3193 sLog.outString(">> Loaded %u group definitions", count);
3194 return;
3195 }
3196
3197 BarGoLink bar(result->GetRowCount());
3198
3199 do
3200 {
3201 bar.step();
3202 Field *fields = result->Fetch();
3203 ++count;
3204 Group *group = new Group;
3205 if (!group->LoadGroupFromDB(fields))
3206 {
3207 group->Disband();
3208 delete group;
3209 continue;
3210 }
3211 AddGroup(group);
3212 }
3213 while (result->NextRow());
3214
3215 delete result;
3216
3217 sLog.outString();
3218 sLog.outString(">> Loaded %u group definitions", count);
3219
3220 // -- loading members --
3221 count = 0;
3222 // 0 1 2 3
3223 result = CharacterDatabase.Query("SELECT memberGuid, assistant, subgroup, groupId FROM group_member ORDER BY groupId");
3224 if (!result)
3225 {
3226 BarGoLink bar2(1);
3227 bar2.step();
3228 }
3229 else
3230 {
3231 Group* group = NULL; // used as cached pointer for avoid relookup group for each member
3232
3233 BarGoLink bar2(result->GetRowCount());
3234 do
3235 {
3236 bar2.step();
3237 Field *fields = result->Fetch();
3238 count++;
3239
3240 uint32 memberGuidlow = fields[0].GetUInt32();
3241 ObjectGuid memberGuid = ObjectGuid(HIGHGUID_PLAYER, memberGuidlow);
3242 bool assistent = fields[1].GetBool();
3243 uint8 subgroup = fields[2].GetUInt8();
3244 uint32 groupId = fields[3].GetUInt32();
3245 if (!group || group->GetId() != groupId)
3246 {
3247 group = GetGroupById(groupId);
3248 if (!group)
3249 {
3250 sLog.outErrorDb("Incorrect entry in group_member table : no group with Id %d for member %s!",
3251 groupId, memberGuid.GetString().c_str());
3252 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow);
3253 continue;
3254 }
3255 }
3256
3257 if (!group->LoadMemberFromDB(memberGuidlow, subgroup, assistent))
3258 {
3259 sLog.outErrorDb("Incorrect entry in group_member table : member %s cannot be added to group (Id: %u)!",
3260 memberGuid.GetString().c_str(), groupId);
3261 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow);
3262 }
3263 }
3264 while (result->NextRow());
3265 delete result;
3266 }
3267
3268 // clean groups
3269 // TODO: maybe delete from the DB before loading in this case
3270 for (GroupMap::iterator itr = mGroupMap.begin(); itr != mGroupMap.end();)
3271 {
3272 if (itr->second->GetMembersCount() < 2)
3273 {
3274 itr->second->Disband();
3275 delete itr->second;
3276 mGroupMap.erase(itr++);
3277 }
3278 else
3279 ++itr;
3280 }
3281
3282 // -- loading instances --
3283 count = 0;
3284 result = CharacterDatabase.Query(
3285 // 0 1 2 3 4
3286 "SELECT group_instance.leaderGuid, map, instance, permanent, resettime, "
3287 // 5
3288 "(SELECT COUNT(*) FROM character_instance WHERE guid = group_instance.leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1), "
3289 // 6
3290 " groups.groupId "
3291 "FROM group_instance LEFT JOIN instance ON instance = id LEFT JOIN groups ON groups.leaderGUID = group_instance.leaderGUID ORDER BY leaderGuid"
3292 );
3293
3294 if (!result)
3295 {
3296 BarGoLink bar2(1);
3297 bar2.step();
3298 }
3299 else
3300 {
3301 Group* group = NULL; // used as cached pointer for avoid relookup group for each member
3302
3303 BarGoLink bar2(result->GetRowCount());
3304 do
3305 {
3306 bar2.step();
3307 Field *fields = result->Fetch();
3308 count++;
3309
3310 uint32 leaderGuidLow = fields[0].GetUInt32();
3311 uint32 mapId = fields[1].GetUInt32();
3312 uint32 groupId = fields[6].GetUInt32();
3313
3314 if (!group || group->GetId() != groupId)
3315 {
3316 // find group id in map by leader low guid
3317 group = GetGroupById(groupId);
3318 if (!group)
3319 {
3320 sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", leaderGuidLow);
3321 continue;
3322 }
3323 }
3324
3325 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(mapId);
3326 if (!mapEntry || !mapEntry->IsDungeon())
3327 {
3328 sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", mapId);
3329 continue;
3330 }
3331
3332 DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, fields[2].GetUInt32(), (time_t)fields[4].GetUInt64(), (fields[5].GetUInt32() == 0), true);
3333 group->BindToInstance(state, fields[3].GetBool(), true);
3334 }
3335 while (result->NextRow());
3336 delete result;
3337 }
3338
3339 sLog.outString();
3340 sLog.outString(">> Loaded %u group-instance binds total", count);
3341
3342 sLog.outString();
3343 sLog.outString(">> Loaded %u group members total", count);
3344}
3345
3346void ObjectMgr::LoadQuests()
3347{
3348 // For reload case
3349 for (QuestMap::const_iterator itr = mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
3350 delete itr->second;
3351
3352 mQuestTemplates.clear();
3353
3354 m_ExclusiveQuestGroups.clear();
3355
3356 // 0 1 2 3 4 5 6 7 8 9
3357 QueryResult *result = WorldDatabase.PQuery("SELECT entry, Method, ZoneOrSort, MinLevel, QuestLevel, Type, RequiredClasses, RequiredRaces, RequiredSkill, RequiredSkillValue,"
3358 // 10 11 12 13 14 15 16 17
3359 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
3360 // 18 19 20 21 22 23 24 25 26
3361 "QuestFlags, SpecialFlags, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
3362 // 27 28 29 30 31 32 33 34 35 36
3363 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
3364 // 37 38 39 40 41 42 43 44
3365 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
3366 // 45 46 47 48 49 50 51 52
3367 "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4,"
3368 // 53 54 55 56 57 58 59 60
3369 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
3370 // 61 62 63 64
3371 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
3372 // 65 66 67 68 69 70
3373 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
3374 // 71 72 73 74 75 76
3375 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
3376 // 77 78 79 80 81 82 83 84
3377 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
3378 // 85 86 87 88 89 90 91 92 93 94
3379 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
3380 // 95 96 97 98 99 100 101 102 103 104
3381 "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
3382 // 105 106 107 108 109 110 111 112
3383 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4,"
3384 // 113 114 115 116 117 118
3385 "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
3386 // 119 120 121 122
3387 "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4,"
3388 // 123 124 125
3389 "StartScript, CompleteScript, MaxLevel"
3390 " FROM quest_template t1 WHERE patch=(SELECT max(patch) FROM quest_template t2 WHERE t1.entry=t2.entry && patch <= %u)", sWorld.GetWowPatch());
3391 if (!result)
3392 {
3393 BarGoLink bar(1);
3394 bar.step();
3395
3396 sLog.outString();
3397 sLog.outString(">> Loaded 0 quests definitions");
3398 sLog.outErrorDb("`quest_template` table is empty!");
3399 return;
3400 }
3401
3402 // create multimap previous quest for each existing quest
3403 // some quests can have many previous maps set by NextQuestId in previous quest
3404 // for example set of race quests can lead to single not race specific quest
3405 BarGoLink bar(result->GetRowCount());
3406 do
3407 {
3408 bar.step();
3409 Field *fields = result->Fetch();
3410
3411 Quest * newQuest = new Quest(fields);
3412 mQuestTemplates[newQuest->GetQuestId()] = newQuest;
3413 }
3414 while (result->NextRow());
3415
3416 delete result;
3417
3418 // Post processing
3419
3420 std::map<uint32, uint32> usedMailTemplates;
3421
3422 for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
3423 {
3424 Quest * qinfo = iter->second;
3425
3426 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3427
3428 if (qinfo->GetQuestMethod() >= QUEST_METHOD_LIMIT)
3429 sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
3430
3431 if (qinfo->m_SpecialFlags > QUEST_SPECIAL_FLAG_DB_ALLOWED)
3432 sLog.outErrorDb("Quest %u has `SpecialFlags` = %u, above max flags not allowed for database.", qinfo->GetQuestId(), qinfo->m_SpecialFlags);
3433
3434 if (qinfo->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED))
3435 {
3436 // at auto-reward can be rewarded only RewChoiceItemId[0]
3437 for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
3438 {
3439 if (uint32 id = qinfo->RewChoiceItemId[j])
3440 {
3441 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3442 qinfo->GetQuestId(), j + 1, id, j + 1);
3443 // no changes, quest ignore this data
3444 }
3445 }
3446 }
3447
3448 // client quest log visual (area case)
3449 if (qinfo->ZoneOrSort > 0)
3450 {
3451 if (!AreaEntry::GetById(qinfo->ZoneOrSort))
3452 {
3453 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
3454 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3455 // no changes, quest not dependent from this value but can have problems at client
3456 }
3457 }
3458 // client quest log visual (sort case)
3459 if (qinfo->ZoneOrSort < 0)
3460 {
3461 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
3462 if (!qSort)
3463 {
3464 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
3465 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3466 // 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)
3467 }
3468
3469 //check for proper RequiredSkill value (skill case)
3470 if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
3471 {
3472 if (qinfo->RequiredSkill != skill_id)
3473 {
3474 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i but `RequiredSkill` does not have a corresponding value (%u).",
3475 qinfo->GetQuestId(), qinfo->ZoneOrSort, skill_id);
3476 //override, and force proper value here?
3477 }
3478 }
3479 }
3480
3481 // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
3482 if (qinfo->RequiredClasses)
3483 {
3484 if (!(qinfo->RequiredClasses & CLASSMASK_ALL_PLAYABLE))
3485 {
3486 sLog.outErrorDb("Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->RequiredClasses);
3487 qinfo->RequiredClasses = 0;
3488 }
3489 }
3490
3491 // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
3492 if (qinfo->RequiredRaces)
3493 {
3494 if (!(qinfo->RequiredRaces & RACEMASK_ALL_PLAYABLE))
3495 {
3496 sLog.outErrorDb("Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->RequiredRaces);
3497 qinfo->RequiredRaces = 0;
3498 }
3499 }
3500
3501 // RequiredSkill, can be 0
3502 if (qinfo->RequiredSkill)
3503 {
3504 if (!sSkillLineStore.LookupEntry(qinfo->RequiredSkill))
3505 {
3506 sLog.outErrorDb("Quest %u has `RequiredSkill` = %u but this skill does not exist",
3507 qinfo->GetQuestId(), qinfo->RequiredSkill);
3508 }
3509 }
3510
3511 if (qinfo->RequiredSkillValue)
3512 {
3513 if (qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue())
3514 {
3515 sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
3516 qinfo->GetQuestId(), qinfo->RequiredSkillValue, sWorld.GetConfigMaxSkillValue());
3517 // no changes, quest can't be done for this requirement
3518 }
3519 }
3520 // else Skill quests can have 0 skill level, this is ok
3521
3522 if (qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
3523 {
3524 sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
3525 qinfo->GetQuestId(), qinfo->RepObjectiveFaction, qinfo->RepObjectiveFaction);
3526 // no changes, quest can't be done for this requirement
3527 }
3528
3529 if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
3530 {
3531 sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3532 qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
3533 // no changes, quest can't be done for this requirement
3534 }
3535
3536 if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
3537 {
3538 sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3539 qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
3540 // no changes, quest can't be done for this requirement
3541 }
3542
3543 if (qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
3544 {
3545 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
3546 qinfo->GetQuestId(), qinfo->RequiredMinRepValue, ReputationMgr::Reputation_Cap);
3547 // no changes, quest can't be done for this requirement
3548 }
3549
3550 if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
3551 {
3552 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
3553 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
3554 // no changes, quest can't be done for this requirement
3555 }
3556
3557 if (!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0)
3558 {
3559 sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
3560 qinfo->GetQuestId(), qinfo->RepObjectiveValue);
3561 // warning
3562 }
3563
3564 if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0)
3565 {
3566 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
3567 qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
3568 // warning
3569 }
3570
3571 if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0)
3572 {
3573 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3574 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
3575 // warning
3576 }
3577
3578 if (qinfo->SrcItemId)
3579 {
3580 if (!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
3581 {
3582 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
3583 qinfo->GetQuestId(), qinfo->SrcItemId, qinfo->SrcItemId);
3584 qinfo->SrcItemId = 0; // quest can't be done for this requirement
3585 }
3586 else if (qinfo->SrcItemCount == 0)
3587 {
3588 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
3589 qinfo->GetQuestId(), qinfo->SrcItemId);
3590 qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward compatibility with DB
3591 }
3592 }
3593 else if (qinfo->SrcItemCount > 0)
3594 {
3595 sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
3596 qinfo->GetQuestId(), qinfo->SrcItemCount);
3597 qinfo->SrcItemCount = 0; // no quest work changes in fact
3598 }
3599
3600 if (qinfo->SrcSpell)
3601 {
3602 SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(qinfo->SrcSpell);
3603 if (!spellInfo)
3604 {
3605 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
3606 qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3607 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3608 }
3609 else if (!SpellMgr::IsSpellValid(spellInfo))
3610 {
3611 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
3612 qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3613 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3614 }
3615 }
3616
3617 for (int j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
3618 {
3619 if (uint32 id = qinfo->ReqItemId[j])
3620 {
3621 if (qinfo->ReqItemCount[j] == 0)
3622 {
3623 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
3624 qinfo->GetQuestId(), j + 1, id, j + 1);
3625 // no changes, quest can't be done for this requirement
3626 }
3627
3628 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER);
3629
3630 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3631 {
3632 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3633 qinfo->GetQuestId(), j + 1, id, id);
3634 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3635 }
3636 }
3637 else if (qinfo->ReqItemCount[j] > 0)
3638 {
3639 sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
3640 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqItemCount[j]);
3641 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3642 }
3643 }
3644
3645 for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
3646 {
3647 if (uint32 id = qinfo->ReqSourceId[j])
3648 {
3649 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3650 {
3651 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
3652 qinfo->GetQuestId(), j + 1, id, id);
3653 // no changes, quest can't be done for this requirement
3654 }
3655 }
3656 else
3657 {
3658 if (qinfo->ReqSourceCount[j] > 0)
3659 {
3660 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
3661 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqSourceCount[j]);
3662 // no changes, quest ignore this data
3663 }
3664 }
3665 }
3666
3667 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3668 {
3669 if (uint32 id = qinfo->ReqSpell[j])
3670 {
3671 SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(id);
3672 if (!spellInfo)
3673 {
3674 sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3675 qinfo->GetQuestId(), j + 1, id, id);
3676 continue;
3677 }
3678
3679 if (!qinfo->ReqCreatureOrGOId[j])
3680 {
3681 bool found = false;
3682 for (int k = 0; k < MAX_EFFECT_INDEX; ++k)
3683 {
3684 if ((spellInfo->Effect[k] == SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k]) == qinfo->QuestId) ||
3685 spellInfo->Effect[k] == SPELL_EFFECT_SEND_EVENT)
3686 {
3687 found = true;
3688 break;
3689 }
3690 }
3691
3692 if (found)
3693 {
3694 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
3695 {
3696 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);
3697
3698 // this will prevent quest completing without objective
3699 const_cast<Quest*>(qinfo)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
3700 }
3701 }
3702 else
3703 {
3704 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.",
3705 qinfo->GetQuestId(), j + 1, id, j + 1, id);
3706 // no changes, quest can't be done for this requirement
3707 }
3708 }
3709 }
3710 }
3711
3712 for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3713 {
3714 int32 id = qinfo->ReqCreatureOrGOId[j];
3715 if (id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
3716 {
3717 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
3718 qinfo->GetQuestId(), j + 1, id, uint32(-id));
3719 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3720 }
3721
3722 if (id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
3723 {
3724 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
3725 qinfo->GetQuestId(), j + 1, id, uint32(id));
3726 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3727 }
3728
3729 if (id)
3730 {
3731 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
3732
3733 qinfo->SetSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO));
3734
3735 if (!qinfo->ReqCreatureOrGOCount[j])
3736 {
3737 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
3738 qinfo->GetQuestId(), j + 1, id, j + 1);
3739 // no changes, quest can be incorrectly done, but we already report this
3740 }
3741 }
3742 else if (qinfo->ReqCreatureOrGOCount[j] > 0)
3743 {
3744 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
3745 qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqCreatureOrGOCount[j]);
3746 // no changes, quest ignore this data
3747 }
3748 }
3749
3750 bool choice_found = false;
3751 for (int j = QUEST_REWARD_CHOICES_COUNT - 1; j >= 0; --j)
3752 {
3753 if (uint32 id = qinfo->RewChoiceItemId[j])
3754 {
3755 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3756 {
3757 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3758 qinfo->GetQuestId(), j + 1, id, id);
3759 qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
3760 }
3761 else
3762 choice_found = true;
3763
3764 if (!qinfo->RewChoiceItemCount[j])
3765 {
3766 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
3767 qinfo->GetQuestId(), j + 1, id, j + 1);
3768 // no changes, quest can't be done
3769 }
3770 }
3771 else if (choice_found) // 1.12.1 client (but not later) crash if have gap in item reward choices
3772 {
3773 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemId%d` = %u, client can crash at like data.",
3774 qinfo->GetQuestId(), j + 1, j + 2, qinfo->RewChoiceItemId[j + 1]);
3775 // fill gap by clone later filled choice
3776 qinfo->RewChoiceItemId[j] = qinfo->RewChoiceItemId[j + 1];
3777 qinfo->RewChoiceItemCount[j] = qinfo->RewChoiceItemCount[j + 1];
3778 }
3779 else if (qinfo->RewChoiceItemCount[j] > 0)
3780 {
3781 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
3782 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewChoiceItemCount[j]);
3783 // no changes, quest ignore this data
3784 }
3785 }
3786
3787 for (int j = 0; j < QUEST_REWARDS_COUNT; ++j)
3788 {
3789 if (uint32 id = qinfo->RewItemId[j])
3790 {
3791 if (!sItemStorage.LookupEntry<ItemPrototype>(id))
3792 {
3793 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3794 qinfo->GetQuestId(), j + 1, id, id);
3795 qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
3796 }
3797
3798 if (!qinfo->RewItemCount[j])
3799 {
3800 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
3801 qinfo->GetQuestId(), j + 1, id, j + 1);
3802 // no changes
3803 }
3804 }
3805 else if (qinfo->RewItemCount[j] > 0)
3806 {
3807 sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
3808 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewItemCount[j]);
3809 // no changes, quest ignore this data
3810 }
3811 }
3812
3813 for (int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
3814 {
3815 if (qinfo->RewRepFaction[j])
3816 {
3817 if (!qinfo->RewRepValue[j])
3818 {
3819 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
3820 qinfo->GetQuestId(), j + 1, qinfo->RewRepValue[j], j + 1);
3821 // no changes
3822 }
3823
3824 if (!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
3825 {
3826 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.",
3827 qinfo->GetQuestId(), j + 1, qinfo->RewRepFaction[j] , qinfo->RewRepFaction[j]);
3828 qinfo->RewRepFaction[j] = 0; // quest will not reward this
3829 }
3830 }
3831 else if (qinfo->RewRepValue[j] != 0)
3832 {
3833 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %i.",
3834 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewRepValue[j]);
3835 // no changes, quest ignore this data
3836 }
3837 }
3838
3839 if (qinfo->RewSpell)
3840 {
3841 SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(qinfo->RewSpell);
3842
3843 if (!spellInfo)
3844 {
3845 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
3846 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
3847 qinfo->RewSpell = 0; // no spell reward will display for this quest
3848 }
3849 else if (!SpellMgr::IsSpellValid(spellInfo))
3850 {
3851 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.",
3852 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
3853 qinfo->RewSpell = 0; // no spell reward will display for this quest
3854 }
3855 else if (GetTalentSpellCost(qinfo->RewSpell))
3856 {
3857 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
3858 qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
3859 qinfo->RewSpell = 0; // no spell reward will display for this quest
3860 }
3861 }
3862
3863 if (qinfo->RewSpellCast)
3864 {
3865 SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(qinfo->RewSpellCast);
3866
3867 if (!spellInfo)
3868 {
3869 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
3870 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
3871 qinfo->RewSpellCast = 0; // no spell will be casted on player
3872 }
3873 else if (!SpellMgr::IsSpellValid(spellInfo))
3874 {
3875 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
3876 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
3877 qinfo->RewSpellCast = 0; // no spell will be casted on player
3878 }
3879 else if (GetTalentSpellCost(qinfo->RewSpellCast))
3880 {
3881 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
3882 qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
3883 qinfo->RewSpellCast = 0; // no spell will be casted on player
3884 }
3885 }
3886
3887 if (qinfo->RewMailTemplateId)
3888 {
3889 if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
3890 {
3891 sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
3892 qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId);
3893 qinfo->RewMailTemplateId = 0; // no mail will send to player
3894 qinfo->RewMailDelaySecs = 0; // no mail will send to player
3895 }
3896 else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
3897 {
3898 std::map<uint32, uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
3899 sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
3900 qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId, used_mt_itr->second);
3901 qinfo->RewMailTemplateId = 0; // no mail will send to player
3902 qinfo->RewMailDelaySecs = 0; // no mail will send to player
3903 }
3904 else
3905 usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
3906 }
3907
3908 if (qinfo->NextQuestInChain)
3909 {
3910 QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
3911 if (qNextItr == mQuestTemplates.end())
3912 {
3913 sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
3914 qinfo->GetQuestId(), qinfo->NextQuestInChain , qinfo->NextQuestInChain);
3915 qinfo->NextQuestInChain = 0;
3916 }
3917 else
3918 qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
3919 }
3920
3921 // fill additional data stores
3922 if (qinfo->PrevQuestId)
3923 {
3924 if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
3925 sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
3926 else
3927 qinfo->prevQuests.push_back(qinfo->PrevQuestId);
3928 }
3929
3930 if (qinfo->NextQuestId)
3931 {
3932 QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
3933 if (qNextItr == mQuestTemplates.end())
3934 sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
3935 else
3936 {
3937 int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
3938 qNextItr->second->prevQuests.push_back(signedQuestId);
3939 }
3940 }
3941
3942 if (qinfo->ExclusiveGroup)
3943 m_ExclusiveQuestGroups.insert(ExclusiveQuestGroupsMap::value_type(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
3944
3945 if (qinfo->LimitTime)
3946 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_TIMED);
3947 }
3948
3949 // check QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
3950 for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
3951 {
3952 SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(i);
3953 if (!spellInfo)
3954 continue;
3955
3956 for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
3957 {
3958 if (spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
3959 continue;
3960
3961 uint32 quest_id = spellInfo->EffectMiscValue[j];
3962
3963 Quest const* quest = GetQuestTemplate(quest_id);
3964
3965 // some quest referenced in spells not exist (outdated spells)
3966 if (!quest)
3967 continue;
3968
3969 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
3970 {
3971 sLog.outDetail("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);
3972
3973 // The below forced alteration has been disabled because of spell 33824 / quest 10162.
3974 // 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.
3975
3976 // this will prevent quest completing without objective
3977 // const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
3978 }
3979 }
3980 }
3981
3982 sLog.outString();
3983 sLog.outString(">> Loaded %lu quests definitions", (unsigned long)mQuestTemplates.size());
3984}
3985
3986uint32 ObjectMgr::GetQuestStartingItemID(uint32 quest_id) const
3987{
3988 auto questItemPair = mQuestStartingItems.find(quest_id);
3989
3990 if (questItemPair != mQuestStartingItems.end())
3991 return questItemPair->second;
3992
3993 return 0;
3994}
3995
3996void ObjectMgr::LoadQuestLocales()
3997{
3998 mQuestLocaleMap.clear(); // need for reload case
3999
4000 QueryResult *result = WorldDatabase.Query("SELECT entry,"
4001 "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
4002 "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
4003 "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
4004 "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
4005 "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
4006 "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
4007 "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
4008 "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
4009 " FROM locales_quest"
4010 );
4011
4012 if (!result)
4013 {
4014 BarGoLink bar(1);
4015
4016 bar.step();
4017
4018 sLog.outString();
4019 sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
4020 return;
4021 }
4022
4023 BarGoLink bar(result->GetRowCount());
4024
4025 do
4026 {
4027 Field *fields = result->Fetch();
4028 bar.step();
4029
4030 uint32 entry = fields[0].GetUInt32();
4031
4032 if (!GetQuestTemplate(entry))
4033 {
4034 ERROR_DB_STRICT_LOG("Table `locales_quest` has data for nonexistent quest entry %u, skipped.", entry);
4035 continue;
4036 }
4037
4038 QuestLocale& data = mQuestLocaleMap[entry];
4039
4040 for (int i = 1; i < MAX_LOCALE; ++i)
4041 {
4042 std::string str = fields[1 + 10 * (i - 1)].GetCppString();
4043 if (!str.empty())
4044 {
4045 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4046 if (idx >= 0)
4047 {
4048 if ((int32)data.Title.size() <= idx)
4049 data.Title.resize(idx + 1);
4050
4051 data.Title[idx] = str;
4052 }
4053 }
4054 str = fields[1 + 10 * (i - 1) + 1].GetCppString();
4055 if (!str.empty())
4056 {
4057 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4058 if (idx >= 0)
4059 {
4060 if ((int32)data.Details.size() <= idx)
4061 data.Details.resize(idx + 1);
4062
4063 data.Details[idx] = str;
4064 }
4065 }
4066 str = fields[1 + 10 * (i - 1) + 2].GetCppString();
4067 if (!str.empty())
4068 {
4069 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4070 if (idx >= 0)
4071 {
4072 if ((int32)data.Objectives.size() <= idx)
4073 data.Objectives.resize(idx + 1);
4074
4075 data.Objectives[idx] = str;
4076 }
4077 }
4078 str = fields[1 + 10 * (i - 1) + 3].GetCppString();
4079 if (!str.empty())
4080 {
4081 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4082 if (idx >= 0)
4083 {
4084 if ((int32)data.OfferRewardText.size() <= idx)
4085 data.OfferRewardText.resize(idx + 1);
4086
4087 data.OfferRewardText[idx] = str;
4088 }
4089 }
4090 str = fields[1 + 10 * (i - 1) + 4].GetCppString();
4091 if (!str.empty())
4092 {
4093 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4094 if (idx >= 0)
4095 {
4096 if ((int32)data.RequestItemsText.size() <= idx)
4097 data.RequestItemsText.resize(idx + 1);
4098
4099 data.RequestItemsText[idx] = str;
4100 }
4101 }
4102 str = fields[1 + 10 * (i - 1) + 5].GetCppString();
4103 if (!str.empty())
4104 {
4105 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4106 if (idx >= 0)
4107 {
4108 if ((int32)data.EndText.size() <= idx)
4109 data.EndText.resize(idx + 1);
4110
4111 data.EndText[idx] = str;
4112 }
4113 }
4114 for (int k = 0; k < 4; ++k)
4115 {
4116 str = fields[1 + 10 * (i - 1) + 6 + k].GetCppString();
4117 if (!str.empty())
4118 {
4119 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4120 if (idx >= 0)
4121 {
4122 if ((int32)data.ObjectiveText[k].size() <= idx)
4123 data.ObjectiveText[k].resize(idx + 1);
4124
4125 data.ObjectiveText[k][idx] = str;
4126 }
4127 }
4128 }
4129 }
4130 }
4131 while (result->NextRow());
4132
4133 delete result;
4134
4135 sLog.outString();
4136 sLog.outString(">> Loaded %lu Quest locale strings", (unsigned long)mQuestLocaleMap.size());
4137}
4138
4139void ObjectMgr::LoadPetCreateSpells()
4140{
4141 QueryResult *result = WorldDatabase.Query("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
4142 if (!result)
4143 {
4144 BarGoLink bar(1);
4145 bar.step();
4146
4147 sLog.outString();
4148 sLog.outString(">> Loaded 0 pet create spells");
4149 //sLog.outErrorDb("`petcreateinfo_spell` table is empty!");
4150 return;
4151 }
4152
4153 uint32 count = 0;
4154
4155 BarGoLink bar(result->GetRowCount());
4156
4157 mPetCreateSpell.clear();
4158
4159 do
4160 {
4161 Field *fields = result->Fetch();
4162 bar.step();
4163
4164 uint32 creature_id = fields[0].GetUInt32();
4165
4166 if (!creature_id)
4167 {
4168 sLog.outErrorDb("Creature id %u listed in `petcreateinfo_spell` not exist.", creature_id);
4169 continue;
4170 }
4171
4172 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creature_id);
4173 if (!cInfo)
4174 {
4175 sLog.outErrorDb("Creature id %u listed in `petcreateinfo_spell` not exist.", creature_id);
4176 continue;
4177 }
4178
4179 if (CreatureSpellDataEntry const* petSpellEntry = cInfo->PetSpellDataId ? sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId) : NULL)
4180 {
4181 sLog.outErrorDb("Creature id %u listed in `petcreateinfo_spell` have set `PetSpellDataId` field and will use its instead, skip.", creature_id);
4182 continue;
4183 }
4184
4185 PetCreateSpellEntry PetCreateSpell;
4186
4187 bool have_spell = false;
4188 bool have_spell_db = false;
4189 for (int i = 0; i < 4; ++i)
4190 {
4191 PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
4192
4193 if (!PetCreateSpell.spellid[i])
4194 continue;
4195
4196 have_spell_db = true;
4197
4198 SpellEntry const* i_spell = sSpellMgr.GetSpellEntry(PetCreateSpell.spellid[i]);
4199 if (!i_spell)
4200 {
4201 sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist", PetCreateSpell.spellid[i]);
4202 PetCreateSpell.spellid[i] = 0;
4203 continue;
4204 }
4205
4206 have_spell = true;
4207 }
4208
4209 if (!have_spell_db)
4210 {
4211 sLog.outErrorDb("Creature %u listed in `petcreateinfo_spell` have only 0 spell data, why it listed?", creature_id);
4212 continue;
4213 }
4214
4215 if (!have_spell)
4216 continue;
4217
4218 mPetCreateSpell[creature_id] = PetCreateSpell;
4219 ++count;
4220 }
4221 while (result->NextRow());
4222
4223 delete result;
4224
4225 // cache spell->learn spell map for use in next loop
4226 std::map<uint32, uint32> learnCache;
4227 for (uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id)
4228 {
4229 SpellEntry const *spellproto = sSpellMgr.GetSpellEntry(spell_id);
4230 if (!spellproto)
4231 continue;
4232
4233 if (spellproto->Effect[0] != SPELL_EFFECT_LEARN_SPELL && spellproto->Effect[0] != SPELL_EFFECT_LEARN_PET_SPELL)
4234 continue;
4235
4236 if (!spellproto->EffectTriggerSpell[0])
4237 continue;
4238
4239 learnCache[spellproto->EffectTriggerSpell[0]] = spellproto->Id;
4240 }
4241
4242 // fill data from DBC as more correct source if available
4243 uint32 dcount = 0;
4244 for (uint32 cr_id = 1; cr_id < sCreatureStorage.GetMaxEntry(); ++cr_id)
4245 {
4246 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(cr_id);
4247 if (!cInfo)
4248 continue;
4249
4250 CreatureSpellDataEntry const* petSpellEntry = cInfo->PetSpellDataId ? sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId) : NULL;
4251 if (!petSpellEntry)
4252 continue;
4253
4254 PetCreateSpellEntry PetCreateSpell;
4255 for (int i = 0; i < MAX_CREATURE_SPELL_DATA_SLOT; ++i)
4256 {
4257 uint32 petspell_id = petSpellEntry->spellId[i];
4258 if (petspell_id)
4259 {
4260 // in dbc stored spell for pet use, but for teaching work we need learn spell ids
4261 std::map<uint32, uint32>::const_iterator cache_itr = learnCache.find(petspell_id);
4262 if (cache_itr != learnCache.end())
4263 petspell_id = cache_itr->second;
4264 }
4265
4266 PetCreateSpell.spellid[i] = petspell_id;
4267 }
4268
4269 mPetCreateSpell[cr_id] = PetCreateSpell;
4270 ++dcount;
4271 }
4272
4273 sLog.outString();
4274 sLog.outString(">> Loaded %u pet create spells from table and %u from DBC", count, dcount);
4275}
4276
4277void ObjectMgr::LoadItemTexts()
4278{
4279 QueryResult *result = CharacterDatabase.Query("SELECT id, text FROM item_text");
4280
4281 uint32 count = 0;
4282
4283 if (!result)
4284 {
4285 BarGoLink bar(1);
4286 bar.step();
4287
4288 sLog.outString();
4289 sLog.outString(">> Loaded %u item pages", count);
4290 return;
4291 }
4292
4293 BarGoLink bar(result->GetRowCount());
4294
4295 Field* fields;
4296 do
4297 {
4298 bar.step();
4299
4300 fields = result->Fetch();
4301
4302 mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
4303
4304 ++count;
4305
4306 }
4307 while (result->NextRow());
4308
4309 delete result;
4310
4311 sLog.outString();
4312 sLog.outString(">> Loaded %u item texts", count);
4313}
4314
4315void ObjectMgr::LoadPageTexts()
4316{
4317 sPageTextStore.Load();
4318 sLog.outString(">> Loaded %u page texts", sPageTextStore.GetRecordCount());
4319 sLog.outString();
4320
4321 for (uint32 i = 1; i < sPageTextStore.GetMaxEntry(); ++i)
4322 {
4323 // check data correctness
4324 PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
4325 if (!page)
4326 continue;
4327
4328 if (page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
4329 {
4330 sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i, page->Next_Page);
4331 continue;
4332 }
4333
4334 // detect circular reference
4335 std::set<uint32> checkedPages;
4336 for (PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
4337 {
4338 if (!pageItr->Next_Page)
4339 break;
4340 checkedPages.insert(pageItr->Page_ID);
4341 if (checkedPages.find(pageItr->Next_Page) != checkedPages.end())
4342 {
4343 std::ostringstream ss;
4344 ss << "The text page(s) ";
4345 for (std::set<uint32>::iterator itr = checkedPages.begin(); itr != checkedPages.end(); ++itr)
4346 ss << *itr << " ";
4347 ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4348 << pageItr->Page_ID << " to 0";
4349 sLog.outErrorDb("%s", ss.str().c_str());
4350 const_cast<PageText*>(pageItr)->Next_Page = 0;
4351 break;
4352 }
4353 }
4354 }
4355}
4356
4357void ObjectMgr::LoadPageTextLocales()
4358{
4359 mPageTextLocaleMap.clear(); // need for reload case
4360
4361 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");
4362
4363 if (!result)
4364 {
4365 BarGoLink bar(1);
4366
4367 bar.step();
4368
4369 sLog.outString();
4370 sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
4371 return;
4372 }
4373
4374 BarGoLink bar(result->GetRowCount());
4375
4376 do
4377 {
4378 Field *fields = result->Fetch();
4379 bar.step();
4380
4381 uint32 entry = fields[0].GetUInt32();
4382
4383 if (!sPageTextStore.LookupEntry<PageText>(entry))
4384 {
4385 ERROR_DB_STRICT_LOG("Table `locales_page_text` has data for nonexistent page text entry %u, skipped.", entry);
4386 continue;
4387 }
4388
4389 PageTextLocale& data = mPageTextLocaleMap[entry];
4390
4391 for (int i = 1; i < MAX_LOCALE; ++i)
4392 {
4393 std::string str = fields[i].GetCppString();
4394 if (str.empty())
4395 continue;
4396
4397 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4398 if (idx >= 0)
4399 {
4400 if ((int32)data.Text.size() <= idx)
4401 data.Text.resize(idx + 1);
4402
4403 data.Text[idx] = str;
4404 }
4405 }
4406
4407 }
4408 while (result->NextRow());
4409
4410 delete result;
4411
4412 sLog.outString();
4413 sLog.outString(">> Loaded %lu PageText locale strings", (unsigned long)mPageTextLocaleMap.size());
4414}
4415
4416struct SQLMapLoader : public SQLStorageLoaderBase<SQLMapLoader, SQLStorage>
4417{
4418 template<class D>
4419 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
4420 {
4421 dst = D(sScriptMgr.GetScriptId(src));
4422 }
4423};
4424
4425void ObjectMgr::LoadMapTemplate()
4426{
4427 SQLMapLoader loader;
4428 loader.LoadProgressive(sMapStorage, sWorld.GetWowPatch());
4429
4430 for (auto itr = sMapStorage.begin<MapEntry>(); itr < sMapStorage.end<MapEntry>(); ++itr)
4431 {
4432 if (itr->IsDungeon() && itr->parent > 0)
4433 {
4434 // check existence
4435 MapEntry const* parentEntry = sMapStorage.LookupEntry<MapEntry>(itr->parent);
4436 if (!parentEntry)
4437 {
4438 sLog.outErrorDb("ObjectMgr::LoadMapTemplate: bad parent map id %u for instance template %u template!",
4439 itr->parent, itr->id);
4440 const_cast<MapEntry*>(*itr)->parent = 0;
4441 continue;
4442 }
4443
4444 if (parentEntry->IsContinent())
4445 {
4446 sLog.outErrorDb("ObjectMgr::LoadMapTemplate: parent point to continent map id %u for instance template %u template, ignored, need be set only for non-continent parents!",
4447 parentEntry->id, itr->id);
4448 const_cast<MapEntry*>(*itr)->parent = 0;
4449 continue;
4450 }
4451 }
4452
4453 // if ghost entrance coordinates provided, can't be not exist for instance without ground entrance
4454 if (itr->ghostEntranceMap >= 0)
4455 {
4456 if (!MapManager::IsValidMapCoord(itr->ghostEntranceMap, itr->ghostEntranceX, itr->ghostEntranceY))
4457 {
4458 sLog.outErrorDb("ObjectMgr::LoadMapTemplate: ghost entrance coordinates invalid for instance template %u template, ignored, need be set only for non-continent parents!", itr->id);
4459 sMapStorage.EraseEntry(itr->id);
4460 continue;
4461 }
4462
4463 MapEntry const* ghostEntry = sMapStorage.LookupEntry<MapEntry>(itr->ghostEntranceMap);
4464 if (!ghostEntry)
4465 {
4466 sLog.outErrorDb("ObjectMgr::LoadMapTemplate: bad ghost entrance for instance template %u template!", itr->id);
4467 sMapStorage.EraseEntry(itr->id);
4468 continue;
4469 }
4470
4471 if (!ghostEntry->IsContinent())
4472 {
4473 sLog.outErrorDb("ObjectMgr::LoadMapTemplate: ghost entrance not at continent map id %u for instance template %u template, ignored, need be set only for non-continent parents!", ghostEntry->id, itr->id);
4474 sMapStorage.EraseEntry(itr->id);
4475 continue;
4476 }
4477 }
4478
4479 // the reset_delay must be at least one day
4480 if (itr->resetDelay)
4481 const_cast<MapEntry*>(*itr)->resetDelay = std::max((uint32)1, (uint32)(itr->resetDelay * sWorld.getConfig(CONFIG_FLOAT_RATE_INSTANCE_RESET_TIME)));
4482 }
4483
4484 sLog.outString(">> Loaded %u Map Template definitions", sMapStorage.GetRecordCount());
4485 sLog.outString();
4486}
4487
4488struct SQLWorldLoader : public SQLStorageLoaderBase<SQLWorldLoader, SQLStorage>
4489{
4490 template<class D>
4491 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
4492 {
4493 dst = D(sScriptMgr.GetScriptId(src));
4494 }
4495};
4496
4497void ObjectMgr::LoadNPCText()
4498{
4499 mNpcTextMap.clear(); // need for reload case
4500
4501 QueryResult *result = WorldDatabase.Query("SELECT ID, "
4502 "Probability0, Probability1, Probability2, Probability3, Probability4, Probability5, Probability6, Probability7, "
4503 "BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7"
4504 " FROM npc_text");
4505
4506 int count = 0;
4507 if (!result)
4508 {
4509 BarGoLink bar(1);
4510 bar.step();
4511
4512 sLog.outString();
4513 sLog.outString(">> Loaded %u npc texts", count);
4514 return;
4515 }
4516
4517 BarGoLink bar(result->GetRowCount());
4518
4519 do
4520 {
4521 ++count;
4522
4523 Field *fields = result->Fetch();
4524
4525 bar.step();
4526
4527 uint32 textID = fields[0].GetUInt32();
4528 if (!textID)
4529 {
4530 sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
4531 continue;
4532 }
4533
4534 NpcText& npcText = mNpcTextMap[textID];
4535
4536 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; ++i)
4537 {
4538 npcText.Options[i].Probability = fields[1 + i].GetFloat();
4539 npcText.Options[i].BroadcastTextID = fields[9 + i].GetUInt32();
4540 }
4541
4542 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
4543 {
4544 if (npcText.Options[i].BroadcastTextID)
4545 {
4546 if (!GetBroadcastTextLocale(npcText.Options[i].BroadcastTextID))
4547 {
4548 sLog.outErrorDb("NPCText (ID: %u) has a non-existing or incompatible BroadcastText (ID: %u, Index: %u)", textID, npcText.Options[i].BroadcastTextID, i);
4549 npcText.Options[i].BroadcastTextID = 0;
4550 }
4551 }
4552 }
4553
4554 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
4555 {
4556 if (npcText.Options[i].Probability > 0 && npcText.Options[i].BroadcastTextID == 0)
4557 {
4558 sLog.outErrorDb("NPCText (ID: %u) has a probability (Index: %u) set, but no BroadcastTextID to go with it", textID, i);
4559 npcText.Options[i].Probability = 0;
4560 }
4561 }
4562 }
4563 while (result->NextRow());
4564
4565 sLog.outString();
4566 sLog.outString(">> Loaded %u npc texts", count);
4567 delete result;
4568}
4569
4570class SingleMailReturner
4571{
4572public:
4573 SingleMailReturner() : returnToLowGuid(0) {}
4574 uint32 returnToLowGuid; // Items deleted if null
4575 time_t basetime;
4576 uint32 messageID;
4577 ObjectGuid receiverGuid;
4578 uint32 itemTextId;
4579
4580 void Callback(QueryResult* result)
4581 {
4582 uint32 item_guid = 0;
4583 if (result)
4584 {
4585 Field *fields2 = result->Fetch();
4586
4587 item_guid = fields2[0].GetUInt32();
4588 delete result;
4589 }
4590 if (!sObjectAccessor.FindPlayerNotInWorld(receiverGuid)) // Do not process online players!
4591 {
4592 if (!returnToLowGuid) // Delete mail and items
4593 {
4594 // mail open and then not returned
4595 if (item_guid)
4596 {
4597 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid);
4598 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", messageID);
4599 }
4600 if (itemTextId)
4601 CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", itemTextId);
4602
4603 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", messageID);
4604 }
4605 else // Return to sender
4606 {
4607 // mail will be returned:
4608 CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'",
4609 receiverGuid.GetCounter(), returnToLowGuid, (uint64)(basetime + 30 * DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, messageID);
4610 if (item_guid)
4611 {
4612 // update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
4613 CharacterDatabase.PExecute("UPDATE mail_items SET receiver = %u WHERE item_guid = '%u'", returnToLowGuid, item_guid);
4614 CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = %u WHERE guid = '%u'", returnToLowGuid, item_guid);
4615 }
4616 }
4617 }
4618 delete this;
4619 }
4620};
4621
4622class OldMailsReturner
4623{
4624public:
4625 OldMailsReturner() : serverUp(false), basetime(0) {}
4626 bool serverUp;
4627 time_t basetime;
4628 void Callback(QueryResult* result)
4629 {
4630 if (!result)
4631 {
4632 sObjectMgr.ResetOldMailCounter();
4633 if (!serverUp)
4634 {
4635 BarGoLink bar(1);
4636 bar.step();
4637 sLog.outString();
4638 sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
4639 }
4640 delete this;
4641 return; // any mails need to be returned or deleted
4642 }
4643
4644 BarGoLink bar(result->GetRowCount());
4645 uint32 skippedCount = 0;
4646 Field *fields;
4647
4648 do
4649 {
4650 bar.step();
4651
4652 fields = result->Fetch();
4653 Mail *m = new Mail;
4654 m->messageID = fields[0].GetUInt32();
4655 m->messageType = fields[1].GetUInt8();
4656 m->sender = fields[2].GetUInt32();
4657 m->receiverGuid = ObjectGuid(HIGHGUID_PLAYER, fields[3].GetUInt32());
4658 bool has_items = fields[5].GetBool();
4659 m->expire_time = (time_t)fields[6].GetUInt64();
4660 m->deliver_time = 0;
4661 m->COD = fields[7].GetUInt32();
4662 m->checked = fields[8].GetUInt32();
4663 m->mailTemplateId = fields[9].GetInt16();
4664
4665 if (serverUp && sObjectAccessor.FindPlayerNotInWorld(m->receiverGuid))
4666 {
4667 // Online player. We wait for him to logout to send the mail back (ie next call)
4668 ++skippedCount;
4669 delete m;
4670 continue;
4671 }
4672 //delete or return mail:
4673 if (has_items)
4674 {
4675 SingleMailReturner* returner = new SingleMailReturner();
4676 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
4677 if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
4678 returner->returnToLowGuid = 0;
4679 else
4680 {
4681 returner->basetime = basetime;
4682 returner->returnToLowGuid = m->sender;
4683 }
4684 returner->receiverGuid = m->receiverGuid;
4685 returner->itemTextId = m->itemTextId;
4686 returner->messageID = m->messageID;
4687 CharacterDatabase.AsyncPQueryUnsafe(returner, &SingleMailReturner::Callback, "SELECT item_guid FROM mail_items WHERE mail_id='%u'", m->messageID);
4688 delete m;
4689 continue;
4690 }
4691
4692 if (m->itemTextId)
4693 CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
4694
4695 // deletemail = true;
4696 // delmails << m->messageID << ", ";
4697 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
4698 delete m;
4699
4700 }
4701 while (result->NextRow());
4702 sObjectMgr.IncrementOldMailCounter(skippedCount);
4703 delete result;
4704 delete this;
4705 }
4706};
4707
4708//not very fast function but it is called only once a day, or on starting-up
4709void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
4710{
4711 time_t basetime = time(NULL);
4712 DEBUG_LOG("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
4713 //delete all old mails without item and without body immediately, if starting server
4714 if (!serverUp)
4715 CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
4716 OldMailsReturner* cb = new OldMailsReturner();
4717 cb->serverUp = serverUp;
4718 cb->basetime = basetime;
4719 uint32 limit = serverUp ? 5 : 1000;
4720 CharacterDatabase.AsyncPQueryUnsafe(cb, &OldMailsReturner::Callback, "SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD "' ORDER BY expire_time LIMIT %u,%u", (uint64)basetime, m_OldMailCounter, limit);
4721}
4722
4723void ObjectMgr::LoadQuestAreaTriggers()
4724{
4725 mQuestAreaTriggerMap.clear(); // need for reload case
4726
4727 QueryResult *result = WorldDatabase.Query("SELECT id,quest FROM areatrigger_involvedrelation");
4728
4729 uint32 count = 0;
4730
4731 if (!result)
4732 {
4733 BarGoLink bar(1);
4734 bar.step();
4735
4736 sLog.outString();
4737 sLog.outString(">> Loaded %u quest trigger points", count);
4738 return;
4739 }
4740
4741 BarGoLink bar(result->GetRowCount());
4742
4743 do
4744 {
4745 ++count;
4746 bar.step();
4747
4748 Field *fields = result->Fetch();
4749
4750 uint32 trigger_ID = fields[0].GetUInt32();
4751 uint32 quest_ID = fields[1].GetUInt32();
4752
4753 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
4754 if (!atEntry)
4755 {
4756 sLog.outErrorDb("Table `areatrigger_involvedrelation` has area trigger (ID: %u) not listed in `AreaTrigger.dbc`.", trigger_ID);
4757 continue;
4758 }
4759
4760 Quest const* quest = GetQuestTemplate(quest_ID);
4761 if (!quest)
4762 {
4763 sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
4764 continue;
4765 }
4766
4767 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
4768 {
4769 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);
4770
4771 // this will prevent quest completing without objective
4772 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
4773
4774 // continue; - quest modified to required objective and trigger can be allowed.
4775 }
4776
4777 mQuestAreaTriggerMap[trigger_ID] = quest_ID;
4778
4779 }
4780 while (result->NextRow());
4781
4782 delete result;
4783
4784 sLog.outString();
4785 sLog.outString(">> Loaded %u quest trigger points", count);
4786}
4787
4788void ObjectMgr::LoadTavernAreaTriggers()
4789{
4790 mTavernAreaTriggerSet.clear(); // need for reload case
4791
4792 QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
4793
4794 uint32 count = 0;
4795
4796 if (!result)
4797 {
4798 BarGoLink bar(1);
4799 bar.step();
4800
4801 sLog.outString();
4802 sLog.outString(">> Loaded %u tavern triggers", count);
4803 return;
4804 }
4805
4806 BarGoLink bar(result->GetRowCount());
4807
4808 do
4809 {
4810 ++count;
4811 bar.step();
4812
4813 Field *fields = result->Fetch();
4814
4815 uint32 Trigger_ID = fields[0].GetUInt32();
4816
4817 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4818 if (!atEntry)
4819 {
4820 sLog.outErrorDb("Table `areatrigger_tavern` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID);
4821 continue;
4822 }
4823
4824 mTavernAreaTriggerSet.insert(Trigger_ID);
4825 }
4826 while (result->NextRow());
4827
4828 delete result;
4829
4830 sLog.outString();
4831 sLog.outString(">> Loaded %u tavern triggers", count);
4832}
4833
4834void ObjectMgr::LoadBattlegroundEntranceTriggers()
4835{
4836 mBGEntranceTriggers.clear(); // need for reload case
4837
4838 uint32 count = 0;
4839
4840 // 0 1 2 3 4 5 6 7
4841 QueryResult *result = WorldDatabase.Query("SELECT id, team, bg_template, exit_map, exit_position_x, exit_position_y, exit_position_z, exit_orientation FROM areatrigger_bg_entrance");
4842 if (!result)
4843 {
4844
4845 BarGoLink bar(1);
4846
4847 bar.step();
4848
4849 sLog.outString();
4850 sLog.outString(">> Loaded %u Battleground entrance area triggers", count);
4851 return;
4852 }
4853
4854 BarGoLink bar(result->GetRowCount());
4855
4856 do
4857 {
4858 Field *fields = result->Fetch();
4859
4860 bar.step();
4861
4862 ++count;
4863
4864 uint32 Trigger_ID = fields[0].GetUInt32();
4865
4866 BattlegroundEntranceTrigger bget;
4867
4868
4869 uint32 team = fields[1].GetUInt32();
4870 uint8 bgTypeId = fields[2].GetUInt8();
4871
4872 bget.exit_mapId = fields[3].GetUInt32();
4873 bget.exit_X = fields[4].GetFloat();
4874 bget.exit_Y = fields[5].GetFloat();
4875 bget.exit_Z = fields[6].GetFloat();
4876 bget.exit_Orientation = fields[7].GetFloat();
4877
4878 AreaTriggerEntry const* bgetEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4879 if (!bgetEntry)
4880 {
4881 sLog.outErrorDb("Table `areatrigger_bg_entrance` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID);
4882 continue;
4883 }
4884
4885 if (team != ALLIANCE && team != HORDE)
4886 {
4887 sLog.outError("Table `areatrigger_bg_entrance` has team (ID:%u) that is not Horde or Alliance for area trigger (ID:%u).", team, Trigger_ID);
4888 continue;
4889 }
4890 bget.team = Team(team);
4891
4892 if (bgTypeId >= MAX_BATTLEGROUND_TYPE_ID)
4893 {
4894 sLog.outErrorDb("Table `areatrigger_bg_entrance` has nonexistent battleground type (ID:%u) for area trigger (ID:%u), ignored.", bgTypeId, Trigger_ID);
4895 continue;
4896 }
4897 bget.bgTypeId = BattleGroundTypeId(bgTypeId);
4898
4899 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(bget.exit_mapId);
4900 if (!mapEntry)
4901 {
4902 sLog.outErrorDb("Table `areatrigger_bg_entrance` has nonexistent exit map (ID: %u) for area trigger (ID:%u).", bget.exit_mapId, Trigger_ID);
4903 continue;
4904 }
4905
4906 if (bget.exit_X == 0 && bget.exit_Y == 0 && bget.exit_Z == 0)
4907 {
4908 sLog.outErrorDb("Table `areatrigger_bg_entrance` has area trigger (ID:%u) without battleground exit coordinates.", Trigger_ID);
4909 continue;
4910 }
4911
4912 mBGEntranceTriggers[Trigger_ID] = bget;
4913
4914 }
4915 while (result->NextRow());
4916
4917 delete result;
4918
4919 sLog.outString();
4920 sLog.outString(">> Loaded %u battleground entrance area trigger definitions", count);
4921}
4922
4923uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, Team team)
4924{
4925 bool found = false;
4926 float dist = 0.0f;
4927 uint32 id = 0;
4928
4929 for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
4930 {
4931 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
4932 if (!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0])
4933 continue;
4934
4935 uint8 field = (uint8)((i - 1) / 32);
4936 uint32 submask = 1 << ((i - 1) % 32);
4937
4938 // skip not taxi network nodes
4939 if ((sTaxiNodesMask[field] & submask) == 0)
4940 continue;
4941
4942 float dist2 = (node->x - x) * (node->x - x) + (node->y - y) * (node->y - y) + (node->z - z) * (node->z - z);
4943 if (found)
4944 {
4945 if (dist2 < dist)
4946 {
4947 dist = dist2;
4948 id = i;
4949 }
4950 }
4951 else
4952 {
4953 found = true;
4954 dist = dist2;
4955 id = i;
4956 }
4957 }
4958
4959 return id;
4960}
4961
4962void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
4963{
4964 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
4965 if (src_i == sTaxiPathSetBySource.end())
4966 {
4967 path = 0;
4968 cost = 0;
4969 return;
4970 }
4971
4972 TaxiPathSetForSource& pathSet = src_i->second;
4973
4974 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
4975 if (dest_i == pathSet.end())
4976 {
4977 path = 0;
4978 cost = 0;
4979 return;
4980 }
4981
4982 cost = dest_i->second.price;
4983 path = dest_i->second.ID;
4984}
4985
4986uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, Team team, bool allowed_alt_team /* = false */)
4987{
4988 uint16 mount_entry = 0;
4989
4990 // select mount creature id
4991 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
4992 if (node)
4993 {
4994 if (team == ALLIANCE)
4995 {
4996 mount_entry = node->MountCreatureID[1];
4997 if (!mount_entry && allowed_alt_team)
4998 mount_entry = node->MountCreatureID[0];
4999 }
5000 else if (team == HORDE)
5001 {
5002 mount_entry = node->MountCreatureID[0];
5003
5004 if (!mount_entry && allowed_alt_team)
5005 mount_entry = node->MountCreatureID[1];
5006 }
5007 }
5008
5009 CreatureInfo const *mount_info = GetCreatureTemplate(mount_entry);
5010 if (!mount_info)
5011 return 0;
5012
5013 uint16 mount_id = Creature::ChooseDisplayId(mount_info);
5014 if (!mount_id)
5015 return 0;
5016
5017 CreatureModelInfo const *minfo = GetCreatureModelRandomGender(mount_id);
5018 if (minfo)
5019 mount_id = minfo->modelid;
5020
5021 return mount_id;
5022}
5023
5024void ObjectMgr::LoadGraveyardZones()
5025{
5026 mGraveYardMap.clear(); // need for reload case
5027
5028 QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
5029
5030 uint32 count = 0;
5031
5032 if (!result)
5033 {
5034 BarGoLink bar(1);
5035 bar.step();
5036
5037 sLog.outString();
5038 sLog.outString(">> Loaded %u graveyard-zone links", count);
5039 return;
5040 }
5041
5042 BarGoLink bar(result->GetRowCount());
5043
5044 do
5045 {
5046 ++count;
5047 bar.step();
5048
5049 Field *fields = result->Fetch();
5050
5051 uint32 safeLocId = fields[0].GetUInt32();
5052 uint32 zoneId = fields[1].GetUInt32();
5053 uint32 team = fields[2].GetUInt32();
5054
5055 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
5056 if (!entry)
5057 {
5058 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
5059 continue;
5060 }
5061
5062 const auto *areaEntry = AreaEntry::GetById(zoneId);
5063 if (!areaEntry)
5064 {
5065 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.", zoneId);
5066 continue;
5067 }
5068
5069 if (!areaEntry->IsZone())
5070 {
5071 sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.", zoneId);
5072 continue;
5073 }
5074
5075 if (team != TEAM_NONE && team != HORDE && team != ALLIANCE)
5076 {
5077 sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.", team);
5078 continue;
5079 }
5080
5081 if (!AddGraveYardLink(safeLocId, zoneId, Team(team), false))
5082 sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
5083 }
5084 while (result->NextRow());
5085
5086 delete result;
5087
5088 sLog.outString();
5089 sLog.outString(">> Loaded %u graveyard-zone links", count);
5090}
5091
5092WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, Team team)
5093{
5094 // search for zone associated closest graveyard
5095 uint32 zoneId = sTerrainMgr.GetZoneId(MapId, x, y, z);
5096
5097 // Simulate std. algorithm:
5098 // found some graveyard associated to (ghost_zone,ghost_map)
5099 //
5100 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5101 // then check faction
5102 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5103 // then check faction
5104 GraveYardMapBounds bounds = mGraveYardMap.equal_range(zoneId);
5105
5106 if (bounds.first == bounds.second)
5107 return nullptr;
5108
5109 // at corpse map
5110 bool foundNear = false;
5111 float distNear = 0.0f;
5112 WorldSafeLocsEntry const* entryNear = nullptr;
5113
5114 // at entrance map for corpse map
5115 bool foundEntr = false;
5116 float distEntr = 0.0f;
5117 WorldSafeLocsEntry const* entryEntr = nullptr;
5118
5119 // some where other
5120 WorldSafeLocsEntry const* entryFar = nullptr;
5121
5122 MapEntry const* tempEntry = sMapStorage.LookupEntry<MapEntry>(MapId);
5123
5124 for (GraveYardMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
5125 {
5126 GraveYardData const& data = itr->second;
5127
5128 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
5129 if (!entry)
5130 {
5131 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId);
5132 continue;
5133 }
5134
5135 // skip enemy faction graveyard
5136 // team == 0 case can be at call from .neargrave
5137 if (data.team != TEAM_NONE && team != TEAM_NONE && data.team != team)
5138 continue;
5139
5140 // find now nearest graveyard at other (continent) map
5141 if (MapId != entry->map_id)
5142 {
5143 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5144 if (!tempEntry ||
5145 tempEntry->ghostEntranceMap < 0 ||
5146 uint32(tempEntry->ghostEntranceMap) != entry->map_id ||
5147 (tempEntry->ghostEntranceX == 0.0f && tempEntry->ghostEntranceY == 0.0f))
5148 {
5149 // not have any coordinates for check distance anyway
5150 entryFar = entry;
5151 continue;
5152 }
5153
5154 // at entrance map calculate distance (2D);
5155 float dist2 = (entry->x - tempEntry->ghostEntranceX) * (entry->x - tempEntry->ghostEntranceX)
5156 + (entry->y - tempEntry->ghostEntranceY) * (entry->y - tempEntry->ghostEntranceY);
5157 if (foundEntr)
5158 {
5159 if (dist2 < distEntr)
5160 {
5161 distEntr = dist2;
5162 entryEntr = entry;
5163 }
5164 }
5165 else
5166 {
5167 foundEntr = true;
5168 distEntr = dist2;
5169 entryEntr = entry;
5170 }
5171 }
5172 // find now nearest graveyard at same map
5173 else
5174 {
5175 float dist2 = (entry->x - x) * (entry->x - x) + (entry->y - y) * (entry->y - y) + (entry->z - z) * (entry->z - z);
5176 if (foundNear)
5177 {
5178 if (dist2 < distNear)
5179 {
5180 distNear = dist2;
5181 entryNear = entry;
5182 }
5183 }
5184 else
5185 {
5186 foundNear = true;
5187 distNear = dist2;
5188 entryNear = entry;
5189 }
5190 }
5191 }
5192
5193 if (entryNear)
5194 return entryNear;
5195
5196 if (entryEntr)
5197 return entryEntr;
5198
5199 return entryFar;
5200}
5201
5202GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId) const
5203{
5204 GraveYardMapBounds bounds = mGraveYardMap.equal_range(zoneId);
5205
5206 for (GraveYardMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
5207 {
5208 if (itr->second.safeLocId == id)
5209 return &itr->second;
5210 }
5211
5212 return NULL;
5213}
5214
5215bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, Team team, bool inDB)
5216{
5217 if (FindGraveYardData(id, zoneId))
5218 return false;
5219
5220 // add link to loaded data
5221 GraveYardData data;
5222 data.safeLocId = id;
5223 data.team = team;
5224
5225 mGraveYardMap.insert(GraveYardMap::value_type(zoneId, data));
5226
5227 // add link to DB
5228 if (inDB)
5229 {
5230 WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
5231 "VALUES ('%u', '%u','%u')", id, zoneId, uint32(team));
5232 }
5233
5234 return true;
5235}
5236
5237void ObjectMgr::LoadAreaTriggerTeleports()
5238{
5239 mAreaTriggers.clear(); // need for reload case
5240
5241 uint32 count = 0;
5242
5243 QueryResult *result = WorldDatabase.PQuery(
5244 // 0 1 2 3 4 5
5245 "SELECT id, required_level, required_item, required_item2, required_quest_done, required_failed_text, "
5246 // 6 7 8 9 10 11
5247 "target_map, target_position_x, target_position_y, target_position_z, target_orientation, required_event, "
5248 // 12 13
5249 "required_pvp_rank, required_team "
5250 "FROM areatrigger_teleport t1 WHERE patch=(SELECT max(patch) FROM areatrigger_teleport t2 WHERE t1.id=t2.id && patch <= %u)", sWorld.GetWowPatch());
5251 if (!result)
5252 {
5253
5254 BarGoLink bar(1);
5255
5256 bar.step();
5257
5258 sLog.outString();
5259 sLog.outString(">> Loaded %u area trigger teleport definitions", count);
5260 return;
5261 }
5262
5263 BarGoLink bar(result->GetRowCount());
5264
5265 do
5266 {
5267 Field *fields = result->Fetch();
5268
5269 bar.step();
5270
5271 ++count;
5272
5273 uint32 Trigger_ID = fields[0].GetUInt32();
5274
5275 AreaTrigger at;
5276
5277 at.requiredLevel = fields[1].GetUInt8();
5278 at.requiredItem = fields[2].GetUInt32();
5279 at.requiredItem2 = fields[3].GetUInt32();
5280 at.requiredQuest = fields[4].GetUInt32();
5281 at.requiredFailedText = fields[5].GetCppString();
5282 at.target_mapId = fields[6].GetUInt32();
5283 at.target_X = fields[7].GetFloat();
5284 at.target_Y = fields[8].GetFloat();
5285 at.target_Z = fields[9].GetFloat();
5286 at.target_Orientation = fields[10].GetFloat();
5287 at.required_event = fields[11].GetInt32();
5288 at.required_pvp_rank = fields[12].GetUInt8();
5289 at.required_team = fields[13].GetUInt16();
5290
5291 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5292 if (!atEntry)
5293 {
5294 sLog.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID);
5295 continue;
5296 }
5297
5298
5299 uint16 eventId = abs(at.required_event);
5300 if (eventId && !sGameEventMgr.IsValidEvent(eventId))
5301 {
5302 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent event %u defined for trigger %u, ignoring", eventId, Trigger_ID);
5303 at.required_event = 0;
5304 }
5305
5306 if (at.requiredItem)
5307 {
5308 ItemPrototype const *pProto = GetItemPrototype(at.requiredItem);
5309 if (!pProto)
5310 {
5311 sLog.outError("Table `areatrigger_teleport` has nonexistent key item %u for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
5312 at.requiredItem = 0;
5313 }
5314 }
5315
5316 if (at.requiredItem2)
5317 {
5318 ItemPrototype const *pProto = GetItemPrototype(at.requiredItem2);
5319 if (!pProto)
5320 {
5321 sLog.outError("Table `areatrigger_teleport` has nonexistent second key item %u for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
5322 at.requiredItem2 = 0;
5323 }
5324 }
5325
5326 if (at.requiredQuest)
5327 {
5328 QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuest);
5329 if (qReqItr == mQuestTemplates.end())
5330 {
5331 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent required quest %u for trigger %u, remove quest done requirement.", at.requiredQuest, Trigger_ID);
5332 at.requiredQuest = 0;
5333 }
5334 }
5335
5336 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(at.target_mapId);
5337 if (!mapEntry)
5338 {
5339 sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent target map (ID: %u) for Area trigger (ID:%u).", at.target_mapId, Trigger_ID);
5340 continue;
5341 }
5342
5343 if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
5344 {
5345 sLog.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) without target coordinates.", Trigger_ID);
5346 continue;
5347 }
5348
5349 mAreaTriggers[Trigger_ID] = at;
5350
5351 }
5352 while (result->NextRow());
5353
5354 delete result;
5355
5356 sLog.outString();
5357 sLog.outString(">> Loaded %u area trigger teleport definitions", count);
5358}
5359
5360/*
5361 * Searches for the areatrigger which teleports players out of the given map (only direct to continent)
5362 */
5363AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 map_id) const
5364{
5365 MapEntry const* mapEntry = sMapStorage.LookupEntry<MapEntry>(map_id);
5366 if (!mapEntry || !mapEntry->IsDungeon())
5367 return nullptr;
5368
5369 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5370 {
5371 if (itr->second.target_mapId == uint32(mapEntry->ghostEntranceMap))
5372 {
5373 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
5374 if (atEntry && atEntry->mapid == map_id)
5375 return &itr->second;
5376 }
5377 }
5378 return nullptr;
5379}
5380
5381/**
5382 * Searches for the areatrigger which teleports players to the given map
5383 */
5384AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
5385{
5386 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5387 {
5388 if (itr->second.target_mapId == Map)
5389 {
5390 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
5391 if (atEntry)
5392 return &itr->second;
5393 }
5394 }
5395 return NULL;
5396}
5397
5398void ObjectMgr::PackGroupIds()
5399{
5400 // this routine renumbers groups in such a way so they start from 1 and go up
5401
5402 // obtain set of all groups
5403 std::set<uint32> groupIds;
5404
5405 // all valid ids are in the instance table
5406 // any associations to ids not in this table are assumed to be
5407 // cleaned already in CleanupInstances
5408 QueryResult *result = CharacterDatabase.Query("SELECT groupId FROM groups ORDER BY groupId ASC");
5409 if (result)
5410 {
5411 do
5412 {
5413 Field *fields = result->Fetch();
5414
5415 uint32 id = fields[0].GetUInt32();
5416
5417 if (id == 0)
5418 {
5419 CharacterDatabase.BeginTransaction();
5420 CharacterDatabase.PExecute("DELETE FROM groups WHERE groupId = '%u'", id);
5421 CharacterDatabase.PExecute("DELETE FROM group_member WHERE groupId = '%u'", id);
5422 CharacterDatabase.CommitTransaction();
5423 continue;
5424 }
5425
5426 groupIds.insert(id);
5427 }
5428 while (result->NextRow());
5429 delete result;
5430 }
5431
5432 BarGoLink bar(groupIds.size() + 1);
5433 bar.step();
5434
5435 uint32 groupId = 1;
5436 // we do assume std::set is sorted properly on integer value
5437 for (std::set<uint32>::iterator i = groupIds.begin(); i != groupIds.end(); ++i)
5438 {
5439 if (*i != groupId)
5440 {
5441 // remap group id
5442 CharacterDatabase.BeginTransaction();
5443 CharacterDatabase.PExecute("UPDATE groups SET groupId = '%u' WHERE groupId = '%u'", groupId, *i);
5444 CharacterDatabase.PExecute("UPDATE group_member SET groupId = '%u' WHERE groupId = '%u'", groupId, *i);
5445 CharacterDatabase.CommitTransaction();
5446 }
5447
5448 ++groupId;
5449 bar.step();
5450 }
5451
5452 m_GroupIds.Set(groupId);
5453
5454 sLog.outString(">> Group Ids remapped, next group id is %u", groupId);
5455 sLog.outString();
5456}
5457
5458void ObjectMgr::SetHighestGuids()
5459{
5460 QueryResult *result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
5461 if (result)
5462 {
5463 m_CharGuids.Set((*result)[0].GetUInt32() + 1);
5464 delete result;
5465 }
5466
5467 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
5468 if (result)
5469 {
5470 m_FirstTemporaryCreatureGuid = (*result)[0].GetUInt32() + 1;
5471 delete result;
5472 }
5473
5474 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
5475 if (result)
5476 {
5477 m_ItemGuids.Set((*result)[0].GetUInt32() + 1);
5478 delete result;
5479 }
5480
5481 // Cleanup other tables from nonexistent guids (>=m_hiItemGuid)
5482 CharacterDatabase.BeginTransaction();
5483 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5484 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5485 CharacterDatabase.PExecute("DELETE FROM auction WHERE itemguid >= '%u'", m_ItemGuids.GetNextAfterMaxUsed());
5486 CharacterDatabase.CommitTransaction();
5487
5488 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
5489 if (result)
5490 {
5491 m_FirstTemporaryGameObjectGuid = (*result)[0].GetUInt32() + 1;
5492 delete result;
5493 }
5494
5495 result = CharacterDatabase.Query("SELECT id FROM auction");
5496 if (result)
5497 {
5498 do
5499 {
5500 Field* fields = result->Fetch();
5501 m_AuctionsIds.insert(fields[0].GetUInt32());
5502 } while (result->NextRow());
5503 delete result;
5504 }
5505 m_NextAuctionId = 1;
5506
5507 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
5508 if (result)
5509 {
5510 m_MailIds.Set((*result)[0].GetUInt32() + 1);
5511 delete result;
5512 }
5513
5514 result = CharacterDatabase.Query("SELECT MAX(id) FROM item_text");
5515 if (result)
5516 {
5517 m_ItemTextIds.Set((*result)[0].GetUInt32() + 1);
5518 delete result;
5519 }
5520
5521 result = CharacterDatabase.Query("SELECT MAX(guid) FROM corpse");
5522 if (result)
5523 {
5524 m_CorpseGuids.Set((*result)[0].GetUInt32() + 1);
5525 delete result;
5526 }
5527
5528 result = CharacterDatabase.Query("SELECT MAX(guildid) FROM guild");
5529 if (result)
5530 {
5531 m_GuildIds.Set((*result)[0].GetUInt32() + 1);
5532 delete result;
5533 }
5534
5535 result = CharacterDatabase.Query("SELECT MAX(groupId) FROM groups");
5536 if (result)
5537 {
5538 m_GroupIds.Set((*result)[0].GetUInt32() + 1);
5539 delete result;
5540 }
5541
5542 result = CharacterDatabase.Query("SELECT MAX(petitionguid) FROM petition");
5543 if (result)
5544 {
5545 m_PetitionIds.Set((*result)[0].GetUInt32() + 1);
5546 delete result;
5547 }
5548
5549 // setup reserved ranges for static guids spawn
5550 m_StaticCreatureGuids.Set(m_FirstTemporaryCreatureGuid);
5551 m_FirstTemporaryCreatureGuid += sWorld.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_CREATURE);
5552
5553 m_StaticGameObjectGuids.Set(m_FirstTemporaryGameObjectGuid);
5554 m_FirstTemporaryGameObjectGuid += sWorld.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_GAMEOBJECT);
5555}
5556
5557uint32 ObjectMgr::CreateItemText(std::string text)
5558{
5559 uint32 newItemTextId = GenerateItemTextID();
5560 //insert new itempage to container
5561 mItemTexts[ newItemTextId ] = text;
5562 //save new itempage
5563 CharacterDatabase.escape_string(text);
5564 //any Delete query needed, itemTextId is maximum of all ids
5565 std::ostringstream query;
5566 query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
5567 CharacterDatabase.Execute(query.str().c_str()); //needs to be run this way, because mail body may be more than 1024 characters
5568 return newItemTextId;
5569}
5570
5571void ObjectMgr::LoadGameObjectLocales()
5572{
5573 mGameObjectLocaleMap.clear(); // need for reload case
5574
5575 QueryResult *result = WorldDatabase.Query("SELECT entry,"
5576 "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8 FROM locales_gameobject");
5577
5578 if (!result)
5579 {
5580 BarGoLink bar(1);
5581
5582 bar.step();
5583
5584 sLog.outString();
5585 sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
5586 return;
5587 }
5588
5589 BarGoLink bar(result->GetRowCount());
5590
5591 do
5592 {
5593 Field *fields = result->Fetch();
5594 bar.step();
5595
5596 uint32 entry = fields[0].GetUInt32();
5597
5598 if (!GetGameObjectInfo(entry))
5599 {
5600 ERROR_DB_STRICT_LOG("Table `locales_gameobject` has data for nonexistent gameobject entry %u, skipped.", entry);
5601 continue;
5602 }
5603
5604 GameObjectLocale& data = mGameObjectLocaleMap[entry];
5605
5606 for (int i = 1; i < MAX_LOCALE; ++i)
5607 {
5608 std::string str = fields[i].GetCppString();
5609 if (!str.empty())
5610 {
5611 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5612 if (idx >= 0)
5613 {
5614 if ((int32)data.Name.size() <= idx)
5615 data.Name.resize(idx + 1);
5616
5617 data.Name[idx] = str;
5618 }
5619 }
5620 }
5621
5622 }
5623 while (result->NextRow());
5624
5625 delete result;
5626
5627 sLog.outString();
5628 sLog.outString(">> Loaded %lu gameobject locale strings", (unsigned long)mGameObjectLocaleMap.size());
5629}
5630
5631struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader, SQLHashStorage>
5632{
5633 template<class D>
5634 void convert_from_str(uint32 /*field_pos*/, char const* src, D& dst)
5635 {
5636 dst = D(sScriptMgr.GetScriptId(src));
5637 }
5638};
5639
5640inline void CheckGOLockId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
5641{
5642 if (sLockStore.LookupEntry(dataN))
5643 return;
5644
5645 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
5646 goInfo->id, goInfo->type, N, dataN, dataN);
5647}
5648
5649inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
5650{
5651 if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN))
5652 {
5653 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
5654 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
5655 goInfo->id, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
5656 }
5657 else
5658 // too many error reports about nonexistent trap templates
5659 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
5660 goInfo->id, goInfo->type, N, dataN, dataN);
5661}
5662
5663inline void CheckGOSpellId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
5664{
5665 if (sSpellMgr.GetSpellEntry(dataN))
5666 return;
5667
5668 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
5669 goInfo->id, goInfo->type, N, dataN, dataN);
5670}
5671
5672inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo, uint32 const& dataN, uint32 N)
5673{
5674 if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR))
5675 return;
5676
5677 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
5678 goInfo->id, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR);
5679
5680 // prevent client and server unexpected work
5681 const_cast<uint32&>(dataN) = 0;
5682}
5683
5684inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
5685{
5686 // 0/1 correct values
5687 if (dataN <= 1)
5688 return;
5689
5690 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.",
5691 goInfo->id, goInfo->type, N, dataN);
5692}
5693
5694inline void CheckGOConsumable(GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
5695{
5696 // 0/1 correct values
5697 if (dataN <= 1)
5698 return;
5699
5700 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
5701 goInfo->id, goInfo->type, N, dataN);
5702}
5703
5704void ObjectMgr::LoadGameobjectInfo()
5705{
5706 SQLGameObjectLoader loader;
5707 loader.Load(sGOStorage);
5708 CheckGameObjectInfos();
5709 sLog.outString(">> Loaded %u game object templates", sGOStorage.GetRecordCount());
5710 sLog.outString();
5711}
5712
5713void ObjectMgr::CheckGameObjectInfos()
5714{
5715 // some checks
5716 for (auto itr = sGOStorage.begin<GameObjectInfo>(); itr != sGOStorage.end<GameObjectInfo>(); ++itr)
5717 {
5718 if (itr->size <= 0.0f) // prevent use too small scales
5719 {
5720 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have too small size=%f",
5721 itr->id, itr->type, itr->size);
5722 const_cast<GameObjectInfo*>(*itr)->size = DEFAULT_OBJECT_SCALE;
5723 }
5724
5725 // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore
5726
5727 switch (itr->type)
5728 {
5729 case GAMEOBJECT_TYPE_DOOR: //0
5730 {
5731 if (itr->door.lockId)
5732 CheckGOLockId(*itr, itr->door.lockId, 1);
5733 CheckGONoDamageImmuneId(*itr, itr->door.noDamageImmune, 3);
5734 break;
5735 }
5736 case GAMEOBJECT_TYPE_BUTTON: //1
5737 {
5738 if (itr->button.lockId)
5739 CheckGOLockId(*itr, itr->button.lockId, 1);
5740 if (itr->button.linkedTrapId) // linked trap
5741 CheckGOLinkedTrapId(*itr, itr->button.linkedTrapId, 3);
5742 CheckGONoDamageImmuneId(*itr, itr->button.noDamageImmune, 4);
5743 break;
5744 }
5745 case GAMEOBJECT_TYPE_QUESTGIVER: //2
5746 {
5747 if (itr->questgiver.lockId)
5748 CheckGOLockId(*itr, itr->questgiver.lockId, 0);
5749 CheckGONoDamageImmuneId(*itr, itr->questgiver.noDamageImmune, 5);
5750 break;
5751 }
5752 case GAMEOBJECT_TYPE_CHEST: //3
5753 {
5754 if (itr->chest.lockId)
5755 CheckGOLockId(*itr, itr->chest.lockId, 0);
5756
5757 CheckGOConsumable(*itr, itr->chest.consumable, 3);
5758
5759 if (itr->chest.linkedTrapId) // linked trap
5760 CheckGOLinkedTrapId(*itr, itr->chest.linkedTrapId, 7);
5761 break;
5762 }
5763 case GAMEOBJECT_TYPE_TRAP: //6
5764 {
5765 if (itr->trap.lockId)
5766 CheckGOLockId(*itr, itr->trap.lockId, 0);
5767 /* disable check for while, too many nonexistent spells
5768 if (goInfo->trap.spellId) // spell
5769 CheckGOSpellId(goInfo,goInfo->trap.spellId,3);
5770 */
5771 break;
5772 }
5773 case GAMEOBJECT_TYPE_CHAIR: //7
5774 CheckAndFixGOChairHeightId(*itr, itr->chair.height, 1);
5775 break;
5776 case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
5777 {
5778 if (itr->spellFocus.focusId)
5779 {
5780 if (!sSpellFocusObjectStore.LookupEntry(itr->spellFocus.focusId))
5781 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
5782 itr->id, itr->type, itr->spellFocus.focusId, itr->spellFocus.focusId);
5783 }
5784
5785 if (itr->spellFocus.linkedTrapId) // linked trap
5786 CheckGOLinkedTrapId(*itr, itr->spellFocus.linkedTrapId, 2);
5787 break;
5788 }
5789 case GAMEOBJECT_TYPE_GOOBER: //10
5790 {
5791 if (itr->goober.lockId)
5792 CheckGOLockId(*itr, itr->goober.lockId, 0);
5793
5794 CheckGOConsumable(*itr, itr->goober.consumable, 3);
5795
5796 if (itr->goober.pageId) // pageId
5797 {
5798 if (!sPageTextStore.LookupEntry<PageText>(itr->goober.pageId))
5799 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
5800 itr->id, itr->type, itr->goober.pageId, itr->goober.pageId);
5801 }
5802 /* disable check for while, too many nonexistent spells
5803 if (goInfo->goober.spellId) // spell
5804 CheckGOSpellId(goInfo,goInfo->goober.spellId,10);
5805 */
5806 CheckGONoDamageImmuneId(*itr, itr->goober.noDamageImmune, 11);
5807 if (itr->goober.linkedTrapId) // linked trap
5808 CheckGOLinkedTrapId(*itr, itr->goober.linkedTrapId, 12);
5809 break;
5810 }
5811 case GAMEOBJECT_TYPE_AREADAMAGE: //12
5812 {
5813 if (itr->areadamage.lockId)
5814 CheckGOLockId(*itr, itr->areadamage.lockId, 0);
5815 break;
5816 }
5817 case GAMEOBJECT_TYPE_CAMERA: //13
5818 {
5819 if (itr->camera.lockId)
5820 CheckGOLockId(*itr, itr->camera.lockId, 0);
5821 break;
5822 }
5823 case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
5824 {
5825 if (itr->moTransport.taxiPathId)
5826 {
5827 if (itr->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[itr->moTransport.taxiPathId].empty())
5828 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
5829 itr->id, itr->type, itr->moTransport.taxiPathId, itr->moTransport.taxiPathId);
5830 }
5831 break;
5832 }
5833 case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
5834 {
5835 /* disable check for while, too many nonexistent spells
5836 // always must have spell
5837 CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1);
5838 */
5839 break;
5840 }
5841 case GAMEOBJECT_TYPE_SPELLCASTER: //22
5842 {
5843 // always must have spell
5844 CheckGOSpellId(*itr, itr->spellcaster.spellId, 0);
5845 break;
5846 }
5847 case GAMEOBJECT_TYPE_FLAGSTAND: //24
5848 {
5849 if (itr->flagstand.lockId)
5850 CheckGOLockId(*itr, itr->flagstand.lockId, 0);
5851 CheckGONoDamageImmuneId(*itr, itr->flagstand.noDamageImmune, 5);
5852 break;
5853 }
5854 case GAMEOBJECT_TYPE_FISHINGHOLE: //25
5855 {
5856 if (itr->fishinghole.lockId)
5857 CheckGOLockId(*itr, itr->fishinghole.lockId, 4);
5858 break;
5859 }
5860 case GAMEOBJECT_TYPE_FLAGDROP: //26
5861 {
5862 if (itr->flagdrop.lockId)
5863 CheckGOLockId(*itr, itr->flagdrop.lockId, 0);
5864 CheckGONoDamageImmuneId(*itr, itr->flagdrop.noDamageImmune, 3);
5865 break;
5866 }
5867 }
5868 }
5869}
5870
5871
5872void ObjectMgr::LoadGameobjectsRequirements()
5873{
5874 uint32 count = 0;
5875 QueryResult *result = WorldDatabase.Query("SELECT guid, reqType, reqGuid FROM gameobject_requirement");
5876 _gobjRequirements.clear();
5877
5878 if (!result)
5879 {
5880 BarGoLink bar(1);
5881
5882 bar.step();
5883
5884 sLog.outString();
5885 sLog.outString(">> Loaded %u gameobject requirements", count);
5886 return;
5887 }
5888
5889 BarGoLink bar(result->GetRowCount());
5890
5891 do
5892 {
5893 bar.step();
5894
5895 Field *fields = result->Fetch();
5896 uint32 guid = fields[0].GetUInt32();
5897 if (!GetGOData(guid))
5898 {
5899 sLog.outErrorDb("Table `gameobject_requirement` has data for invalid game object guid (%u)", guid);
5900 continue;
5901 }
5902
5903 GameObjectUseRequirement entry;
5904 entry.reqType = GameObjectUseRequirement::GameObjectUseRequireType(fields[1].GetUInt8());
5905 uint32 reqGuidLow = fields[2].GetUInt32();
5906 switch (entry.reqType)
5907 {
5908 case GameObjectUseRequirement::GOBJ_REQUIRE_DEAD_CREATURE:
5909 if (CreatureData const* data = GetCreatureData(reqGuidLow))
5910 entry.guid = ObjectGuid(HIGHGUID_UNIT, data->id, reqGuidLow);
5911 else
5912 {
5913 sLog.outErrorDb("Table `gameobject_requirement` has not found creature guid (%u)", reqGuidLow);
5914 continue;
5915 }
5916 break;
5917 case GameObjectUseRequirement::GOBJ_REQUIRE_ACTIVE_OBJECT:
5918 if (GameObjectData const* data = GetGOData(reqGuidLow))
5919 entry.guid = ObjectGuid(HIGHGUID_GAMEOBJECT, data->id, reqGuidLow);
5920 else
5921 {
5922 sLog.outErrorDb("Table `gameobject_requirement` has not found game object guid (%u)", reqGuidLow);
5923 continue;
5924 }
5925 break;
5926 default:
5927 sLog.outErrorDb("Table `gameobject_requirement` has invalid reqType (%u)", entry.reqType);
5928 break;
5929 }
5930
5931 _gobjRequirements[guid] = entry;
5932 ++count;
5933 }
5934 while (result->NextRow());
5935 delete result;
5936
5937 sLog.outString();
5938 sLog.outString(">> Loaded %u GameObjects requirements", count);
5939}
5940
5941GameObjectUseRequirement const* ObjectMgr::GetGameObjectUseRequirement(ObjectGuid guid) const
5942{
5943 std::map<uint32, GameObjectUseRequirement>::const_iterator it = _gobjRequirements.find(guid.GetCounter());
5944 if (it != _gobjRequirements.end())
5945 return &it->second;
5946 return NULL;
5947}
5948
5949void ObjectMgr::LoadExplorationBaseXP()
5950{
5951 uint32 count = 0;
5952 QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
5953
5954 if (!result)
5955 {
5956 BarGoLink bar(1);
5957
5958 bar.step();
5959
5960 sLog.outString();
5961 sLog.outString(">> Loaded %u BaseXP definitions", count);
5962 return;
5963 }
5964
5965 BarGoLink bar(result->GetRowCount());
5966
5967 do
5968 {
5969 bar.step();
5970
5971 Field *fields = result->Fetch();
5972 uint32 level = fields[0].GetUInt32();
5973 uint32 basexp = fields[1].GetUInt32();
5974 mBaseXPTable[level] = basexp;
5975 ++count;
5976 }
5977 while (result->NextRow());
5978
5979 delete result;
5980
5981 sLog.outString();
5982 sLog.outString(">> Loaded %u BaseXP definitions", count);
5983}
5984
5985uint32 ObjectMgr::GetBaseXP(uint32 level) const
5986{
5987 BaseXPMap::const_iterator itr = mBaseXPTable.find(level);
5988 return itr != mBaseXPTable.end() ? itr->second : 0;
5989}
5990
5991uint32 ObjectMgr::GetXPForLevel(uint32 level) const
5992{
5993 if (level < mPlayerXPperLevel.size())
5994 return mPlayerXPperLevel[level];
5995 return 0;
5996}
5997
5998void ObjectMgr::LoadPetNames()
5999{
6000 uint32 count = 0;
6001 QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
6002
6003 if (!result)
6004 {
6005 BarGoLink bar(1);
6006
6007 bar.step();
6008
6009 sLog.outString();
6010 sLog.outString(">> Loaded %u pet name parts", count);
6011 return;
6012 }
6013
6014 BarGoLink bar(result->GetRowCount());
6015
6016 do
6017 {
6018 bar.step();
6019
6020 Field *fields = result->Fetch();
6021 std::string word = fields[0].GetString();
6022 uint32 entry = fields[1].GetUInt32();
6023 bool half = fields[2].GetBool();
6024 if (half)
6025 PetHalfName1[entry].push_back(word);
6026 else
6027 PetHalfName0[entry].push_back(word);
6028 ++count;
6029 }
6030 while (result->NextRow());
6031 delete result;
6032
6033 sLog.outString();
6034 sLog.outString(">> Loaded %u pet name parts", count);
6035}
6036
6037void ObjectMgr::LoadPetNumber()
6038{
6039 m_NextPetNumber = 1;
6040}
6041
6042uint32 ObjectMgr::GeneratePetNumber()
6043{
6044 m_NextPetNumber = sCharacterDatabaseCache.GetNextAvailablePetNumber(m_NextPetNumber);
6045 return m_NextPetNumber++;
6046}
6047
6048std::string ObjectMgr::GeneratePetName(uint32 entry)
6049{
6050 std::vector<std::string> & list0 = PetHalfName0[entry];
6051 std::vector<std::string> & list1 = PetHalfName1[entry];
6052
6053 if (list0.empty() || list1.empty())
6054 {
6055 CreatureInfo const *cinfo = GetCreatureTemplate(entry);
6056 char const* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
6057 if (!petname)
6058 petname = cinfo->Name;
6059 return std::string(petname);
6060 }
6061
6062 return *(list0.begin() + urand(0, list0.size() - 1)) + *(list1.begin() + urand(0, list1.size() - 1));
6063}
6064
6065void ObjectMgr::LoadCorpses()
6066{
6067 uint32 count = 0;
6068 // 0 1 2 3 4 5 6
6069 QueryResult *result = CharacterDatabase.Query("SELECT corpse.guid, player, corpse.position_x, corpse.position_y, corpse.position_z, corpse.orientation, corpse.map, "
6070 // 7 8 9 10 11 12 13 14 15 16 17
6071 "time, corpse_type, instance, gender, race, class, playerBytes, playerBytes2, equipmentCache, guildId, playerFlags FROM corpse "
6072 "JOIN characters ON player = characters.guid "
6073 "LEFT JOIN guild_member ON player=guild_member.guid WHERE corpse_type <> 0");
6074
6075 if (!result)
6076 {
6077 BarGoLink bar(1);
6078
6079 bar.step();
6080
6081 sLog.outString();
6082 sLog.outString(">> Loaded %u corpses", count);
6083 return;
6084 }
6085
6086 BarGoLink bar(result->GetRowCount());
6087
6088 do
6089 {
6090 bar.step();
6091
6092 Field *fields = result->Fetch();
6093
6094 uint32 guid = fields[0].GetUInt32();
6095
6096 Corpse *corpse = new Corpse;
6097 if (!corpse->LoadFromDB(guid, fields))
6098 {
6099 delete corpse;
6100 continue;
6101 }
6102
6103 sObjectAccessor.AddCorpse(corpse);
6104
6105 ++count;
6106 }
6107 while (result->NextRow());
6108 delete result;
6109
6110 sLog.outString();
6111 sLog.outString(">> Loaded %u corpses", count);
6112}
6113
6114void ObjectMgr::LoadReputationRewardRate()
6115{
6116 m_RepRewardRateMap.clear(); // for reload case
6117
6118 uint32 count = 0;
6119 QueryResult *result = WorldDatabase.Query("SELECT faction, quest_rate, creature_rate, spell_rate FROM reputation_reward_rate");
6120
6121 if (!result)
6122 {
6123 BarGoLink bar(1);
6124
6125 bar.step();
6126
6127 sLog.outString();
6128 sLog.outErrorDb(">> Loaded `reputation_reward_rate`, table is empty!");
6129 return;
6130 }
6131
6132 BarGoLink bar(result->GetRowCount());
6133
6134 do
6135 {
6136 bar.step();
6137
6138 Field *fields = result->Fetch();
6139
6140 uint32 factionId = fields[0].GetUInt32();
6141
6142 RepRewardRate repRate;
6143
6144 repRate.quest_rate = fields[1].GetFloat();
6145 repRate.creature_rate = fields[2].GetFloat();
6146 repRate.spell_rate = fields[3].GetFloat();
6147
6148 FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
6149 if (!factionEntry)
6150 {
6151 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
6152 continue;
6153 }
6154
6155 if (repRate.quest_rate < 0.0f)
6156 {
6157 sLog.outErrorDb("Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.quest_rate, factionId);
6158 continue;
6159 }
6160
6161 if (repRate.creature_rate < 0.0f)
6162 {
6163 sLog.outErrorDb("Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creature_rate, factionId);
6164 continue;
6165 }
6166
6167 if (repRate.spell_rate < 0.0f)
6168 {
6169 sLog.outErrorDb("Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spell_rate, factionId);
6170 continue;
6171 }
6172
6173 m_RepRewardRateMap[factionId] = repRate;
6174
6175 ++count;
6176 }
6177 while (result->NextRow());
6178
6179 delete result;
6180
6181 sLog.outString();
6182 sLog.outString(">> Loaded %u reputation_reward_rate", count);
6183}
6184
6185void ObjectMgr::LoadReputationOnKill()
6186{
6187 uint32 count = 0;
6188
6189 // 0 1 2
6190 QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
6191 // 3 4 5 6 7 8 9
6192 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
6193 "FROM creature_onkill_reputation");
6194
6195 if (!result)
6196 {
6197 BarGoLink bar(1);
6198
6199 bar.step();
6200
6201 sLog.outString();
6202 sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
6203 return;
6204 }
6205
6206 BarGoLink bar(result->GetRowCount());
6207
6208 do
6209 {
6210 Field *fields = result->Fetch();
6211 bar.step();
6212
6213 uint32 creature_id = fields[0].GetUInt32();
6214
6215 ReputationOnKillEntry repOnKill;
6216 repOnKill.repfaction1 = fields[1].GetUInt32();
6217 repOnKill.repfaction2 = fields[2].GetUInt32();
6218 repOnKill.is_teamaward1 = fields[3].GetBool();
6219 repOnKill.reputation_max_cap1 = fields[4].GetUInt32();
6220 repOnKill.repvalue1 = fields[5].GetInt32();
6221 repOnKill.is_teamaward2 = fields[6].GetBool();
6222 repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
6223 repOnKill.repvalue2 = fields[8].GetInt32();
6224 repOnKill.team_dependent = fields[9].GetUInt8();
6225
6226 if (!GetCreatureTemplate(creature_id))
6227 {
6228 sLog.outErrorDb("Table `creature_onkill_reputation` have data for nonexistent creature entry (%u), skipped", creature_id);
6229 continue;
6230 }
6231
6232 if (repOnKill.repfaction1)
6233 {
6234 FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
6235 if (!factionEntry1)
6236 {
6237 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.repfaction1);
6238 continue;
6239 }
6240 }
6241
6242 if (repOnKill.repfaction2)
6243 {
6244 FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
6245 if (!factionEntry2)
6246 {
6247 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.repfaction2);
6248 continue;
6249 }
6250 }
6251
6252 mRepOnKill[creature_id] = repOnKill;
6253
6254 ++count;
6255 }
6256 while (result->NextRow());
6257
6258 delete result;
6259
6260 sLog.outString();
6261 sLog.outString(">> Loaded %u creature award reputation definitions", count);
6262}
6263
6264void ObjectMgr::LoadReputationSpilloverTemplate()
6265{
6266 m_RepSpilloverTemplateMap.clear(); // for reload case
6267
6268 uint32 count = 0;
6269 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");
6270
6271 if (!result)
6272 {
6273 BarGoLink bar(1);
6274
6275 bar.step();
6276
6277 sLog.outString();
6278 sLog.outString(">> Loaded `reputation_spillover_template`, table is empty.");
6279 return;
6280 }
6281
6282 BarGoLink bar(result->GetRowCount());
6283
6284 do
6285 {
6286 bar.step();
6287
6288 Field *fields = result->Fetch();
6289
6290 uint32 factionId = fields[0].GetUInt32();
6291
6292 RepSpilloverTemplate repTemplate;
6293
6294 repTemplate.faction[0] = fields[1].GetUInt32();
6295 repTemplate.faction_rate[0] = fields[2].GetFloat();
6296 repTemplate.faction_rank[0] = fields[3].GetUInt32();
6297 repTemplate.faction[1] = fields[4].GetUInt32();
6298 repTemplate.faction_rate[1] = fields[5].GetFloat();
6299 repTemplate.faction_rank[1] = fields[6].GetUInt32();
6300 repTemplate.faction[2] = fields[7].GetUInt32();
6301 repTemplate.faction_rate[2] = fields[8].GetFloat();
6302 repTemplate.faction_rank[2] = fields[9].GetUInt32();
6303 repTemplate.faction[3] = fields[10].GetUInt32();
6304 repTemplate.faction_rate[3] = fields[11].GetFloat();
6305 repTemplate.faction_rank[3] = fields[12].GetUInt32();
6306
6307 FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
6308
6309 if (!factionEntry)
6310 {
6311 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
6312 continue;
6313 }
6314
6315 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
6316 {
6317 if (repTemplate.faction[i])
6318 {
6319 FactionEntry const *factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
6320
6321 if (!factionSpillover)
6322 {
6323 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);
6324 continue;
6325 }
6326
6327 if (factionSpillover->reputationListID < 0)
6328 {
6329 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);
6330 continue;
6331 }
6332
6333 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
6334 {
6335 sLog.outErrorDb("Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
6336 continue;
6337 }
6338 }
6339 }
6340
6341 FactionEntry const *factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]);
6342 if (repTemplate.faction[0] && !factionEntry0)
6343 {
6344 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]);
6345 continue;
6346 }
6347 FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]);
6348 if (repTemplate.faction[1] && !factionEntry1)
6349 {
6350 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]);
6351 continue;
6352 }
6353 FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]);
6354 if (repTemplate.faction[2] && !factionEntry2)
6355 {
6356 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]);
6357 continue;
6358 }
6359 FactionEntry const *factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]);
6360 if (repTemplate.faction[3] && !factionEntry3)
6361 {
6362 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]);
6363 continue;
6364 }
6365
6366 m_RepSpilloverTemplateMap[factionId] = repTemplate;
6367
6368 ++count;
6369 }
6370 while (result->NextRow());
6371
6372 delete result;
6373
6374 sLog.outString();
6375 sLog.outString(">> Loaded %u reputation_spillover_template", count);
6376}
6377
6378void ObjectMgr::LoadPointsOfInterest()
6379{
6380 mPointsOfInterest.clear(); // need for reload case
6381
6382 uint32 count = 0;
6383
6384 // 0 1 2 3 4 5
6385 QueryResult *result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
6386
6387 if (!result)
6388 {
6389 BarGoLink bar(1);
6390
6391 bar.step();
6392
6393 sLog.outString();
6394 sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
6395 return;
6396 }
6397
6398 BarGoLink bar(result->GetRowCount());
6399
6400 do
6401 {
6402 Field *fields = result->Fetch();
6403 bar.step();
6404
6405 uint32 point_id = fields[0].GetUInt32();
6406
6407 PointOfInterest POI;
6408 POI.x = fields[1].GetFloat();
6409 POI.y = fields[2].GetFloat();
6410 POI.icon = fields[3].GetUInt32();
6411 POI.flags = fields[4].GetUInt32();
6412 POI.data = fields[5].GetUInt32();
6413 POI.icon_name = fields[6].GetCppString();
6414
6415 if (!MaNGOS::IsValidMapCoord(POI.x, POI.y))
6416 {
6417 sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id, POI.x, POI.y);
6418 continue;
6419 }
6420
6421 mPointsOfInterest[point_id] = POI;
6422
6423 ++count;
6424 }
6425 while (result->NextRow());
6426
6427 delete result;
6428
6429 sLog.outString();
6430 sLog.outString(">> Loaded %u Points of Interest definitions", count);
6431}
6432
6433void ObjectMgr::LoadWeatherZoneChances()
6434{
6435 uint32 count = 0;
6436
6437 // 0 1 2 3 4 5 6 7 8 9 10 11 12
6438 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");
6439
6440 if (!result)
6441 {
6442 BarGoLink bar(1);
6443
6444 bar.step();
6445
6446 sLog.outString();
6447 sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
6448 return;
6449 }
6450
6451 BarGoLink bar(result->GetRowCount());
6452
6453 do
6454 {
6455 Field *fields = result->Fetch();
6456 bar.step();
6457
6458 uint32 zone_id = fields[0].GetUInt32();
6459
6460 WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
6461
6462 for (int season = 0; season < WEATHER_SEASONS; ++season)
6463 {
6464 wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE - 1) + 1].GetUInt32();
6465 wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE - 1) + 2].GetUInt32();
6466 wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE - 1) + 3].GetUInt32();
6467
6468 if (wzc.data[season].rainChance > 100)
6469 {
6470 wzc.data[season].rainChance = 25;
6471 sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%", zone_id, season);
6472 }
6473
6474 if (wzc.data[season].snowChance > 100)
6475 {
6476 wzc.data[season].snowChance = 25;
6477 sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%", zone_id, season);
6478 }
6479
6480 if (wzc.data[season].stormChance > 100)
6481 {
6482 wzc.data[season].stormChance = 25;
6483 sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%", zone_id, season);
6484 }
6485 }
6486
6487 ++count;
6488 }
6489 while (result->NextRow());
6490
6491 delete result;
6492
6493 sLog.outString();
6494 sLog.outString(">> Loaded %u weather definitions", count);
6495}
6496
6497void ObjectMgr::DeleteCreatureData(uint32 guid)
6498{
6499 // remove mapid*cellid -> guid_set map
6500 CreatureData const* data = GetCreatureData(guid);
6501 if (data)
6502 RemoveCreatureFromGrid(guid, data);
6503
6504 mCreatureDataMap.erase(guid);
6505}
6506
6507void ObjectMgr::DeleteGOData(uint32 guid)
6508{
6509 // remove mapid*cellid -> guid_set map
6510 GameObjectData const* data = GetGOData(guid);
6511 if (data)
6512 RemoveGameobjectFromGrid(guid, data);
6513
6514 mGameObjectDataMap.erase(guid);
6515}
6516
6517void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
6518{
6519 // corpses are always added to spawn mode 0 and they are spawned by their instance id
6520 mMapObjectGuids_lock.acquire();
6521 CellObjectGuids& cell_guids = mMapObjectGuids[mapid][cellid];
6522 cell_guids.corpses[player_guid] = instance;
6523 mMapObjectGuids_lock.release();
6524}
6525
6526void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
6527{
6528 // corpses are always added to spawn mode 0 and they are spawned by their instance id
6529 CellObjectGuids& cell_guids = mMapObjectGuids[mapid][cellid];
6530 cell_guids.corpses.erase(player_guid);
6531}
6532
6533void ObjectMgr::LoadQuestRelationsHelper(QuestRelationsMap& map, char const* table)
6534{
6535 map.clear(); // need for reload case
6536
6537 uint32 count = 0;
6538
6539 QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s t1 WHERE patch=(SELECT max(patch) FROM %s t2 WHERE t1.id=t2.id && t1.quest=t2.quest && patch <= %u)", table, table, sWorld.GetWowPatch());
6540
6541 if (!result)
6542 {
6543 BarGoLink bar(1);
6544
6545 bar.step();
6546
6547 sLog.outString();
6548 sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.", table, table);
6549 return;
6550 }
6551
6552 BarGoLink bar(result->GetRowCount());
6553
6554 do
6555 {
6556 Field *fields = result->Fetch();
6557 bar.step();
6558
6559 uint32 id = fields[0].GetUInt32();
6560 uint32 quest = fields[1].GetUInt32();
6561
6562 if (mQuestTemplates.find(quest) == mQuestTemplates.end())
6563 {
6564 sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.", table, quest, id);
6565 continue;
6566 }
6567
6568 map.insert(QuestRelationsMap::value_type(id, quest));
6569
6570 ++count;
6571 }
6572 while (result->NextRow());
6573
6574 delete result;
6575
6576 sLog.outString();
6577 sLog.outString(">> Loaded %u quest relations from %s", count, table);
6578}
6579
6580void ObjectMgr::LoadGameobjectQuestRelations()
6581{
6582 LoadQuestRelationsHelper(m_GOQuestRelations, "gameobject_questrelation");
6583
6584 for (QuestRelationsMap::iterator itr = m_GOQuestRelations.begin(); itr != m_GOQuestRelations.end(); ++itr)
6585 {
6586 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
6587 if (!goInfo)
6588 sLog.outErrorDb("Table `gameobject_questrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr->first, itr->second);
6589 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
6590 sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
6591 }
6592}
6593
6594void ObjectMgr::LoadGameobjectInvolvedRelations()
6595{
6596 LoadQuestRelationsHelper(m_GOQuestInvolvedRelations, "gameobject_involvedrelation");
6597
6598 for (QuestRelationsMap::iterator itr = m_GOQuestInvolvedRelations.begin(); itr != m_GOQuestInvolvedRelations.end(); ++itr)
6599 {
6600 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
6601 if (!goInfo)
6602 sLog.outErrorDb("Table `gameobject_involvedrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr->first, itr->second);
6603 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
6604 sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
6605 }
6606}
6607
6608void ObjectMgr::LoadCreatureQuestRelations()
6609{
6610 LoadQuestRelationsHelper(m_CreatureQuestRelations, "creature_questrelation");
6611
6612 for (QuestRelationsMap::iterator itr = m_CreatureQuestRelations.begin(); itr != m_CreatureQuestRelations.end(); ++itr)
6613 {
6614 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
6615 if (!cInfo)
6616 sLog.outErrorDb("Table `creature_questrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
6617 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
6618 sLog.outDetail("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
6619 }
6620}
6621
6622void ObjectMgr::LoadCreatureInvolvedRelations()
6623{
6624 LoadQuestRelationsHelper(m_CreatureQuestInvolvedRelations, "creature_involvedrelation");
6625
6626 for (QuestRelationsMap::iterator itr = m_CreatureQuestInvolvedRelations.begin(); itr != m_CreatureQuestInvolvedRelations.end(); ++itr)
6627 {
6628 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
6629 if (!cInfo)
6630 sLog.outErrorDb("Table `creature_involvedrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
6631 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
6632 sLog.outDetail("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
6633 }
6634}
6635
6636void ObjectMgr::LoadReservedPlayersNames()
6637{
6638 m_ReservedNames.clear(); // need for reload case
6639
6640 QueryResult *result = WorldDatabase.Query("SELECT name FROM reserved_name");
6641
6642 uint32 count = 0;
6643
6644 if (!result)
6645 {
6646 BarGoLink bar(1);
6647 bar.step();
6648
6649 sLog.outString();
6650 sLog.outString(">> Loaded %u reserved player names", count);
6651 return;
6652 }
6653
6654 BarGoLink bar(result->GetRowCount());
6655
6656 Field* fields;
6657 do
6658 {
6659 bar.step();
6660 fields = result->Fetch();
6661 std::string name = fields[0].GetCppString();
6662
6663 std::wstring wstr;
6664 if (!Utf8toWStr(name, wstr))
6665 {
6666 sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str());
6667 continue;
6668 }
6669
6670 wstrToLower(wstr);
6671
6672 m_ReservedNames.insert(wstr);
6673 ++count;
6674 }
6675 while (result->NextRow());
6676
6677 delete result;
6678
6679 sLog.outString();
6680 sLog.outString(">> Loaded %u reserved player names", count);
6681}
6682
6683bool ObjectMgr::IsReservedName(const std::string& name) const
6684{
6685 std::wstring wstr;
6686 if (!Utf8toWStr(name, wstr))
6687 return false;
6688
6689 wstrToLower(wstr);
6690
6691 return m_ReservedNames.find(wstr) != m_ReservedNames.end();
6692}
6693
6694enum LanguageType
6695{
6696 LT_BASIC_LATIN = 0x0000,
6697 LT_EXTENDEN_LATIN = 0x0001,
6698 LT_CYRILLIC = 0x0002,
6699 LT_EAST_ASIA = 0x0004,
6700 LT_ANY = 0xFFFF
6701};
6702
6703static LanguageType GetRealmLanguageType(bool create)
6704{
6705 switch (sWorld.getConfig(CONFIG_UINT32_REALM_ZONE))
6706 {
6707 case REALM_ZONE_UNKNOWN: // any language
6708 case REALM_ZONE_DEVELOPMENT:
6709 case REALM_ZONE_TEST_SERVER:
6710 case REALM_ZONE_QA_SERVER:
6711 return LT_ANY;
6712 case REALM_ZONE_UNITED_STATES: // extended-Latin
6713 case REALM_ZONE_OCEANIC:
6714 case REALM_ZONE_LATIN_AMERICA:
6715 case REALM_ZONE_ENGLISH:
6716 case REALM_ZONE_GERMAN:
6717 case REALM_ZONE_FRENCH:
6718 case REALM_ZONE_SPANISH:
6719 return LT_EXTENDEN_LATIN;
6720 case REALM_ZONE_KOREA: // East-Asian
6721 case REALM_ZONE_TAIWAN:
6722 case REALM_ZONE_CHINA:
6723 return LT_EAST_ASIA;
6724 case REALM_ZONE_RUSSIAN: // Cyrillic
6725 return LT_CYRILLIC;
6726 default:
6727 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
6728 }
6729}
6730
6731bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
6732{
6733 if (strictMask == 0) // any language, ignore realm
6734 {
6735 if (isExtendedLatinString(wstr, numericOrSpace))
6736 return true;
6737 if (isCyrillicString(wstr, numericOrSpace))
6738 return true;
6739 if (isEastAsianString(wstr, numericOrSpace))
6740 return true;
6741 return false;
6742 }
6743
6744 if (strictMask & 0x2) // realm zone specific
6745 {
6746 LanguageType lt = GetRealmLanguageType(create);
6747 if (lt & LT_EXTENDEN_LATIN)
6748 if (isExtendedLatinString(wstr, numericOrSpace))
6749 return true;
6750 if (lt & LT_CYRILLIC)
6751 if (isCyrillicString(wstr, numericOrSpace))
6752 return true;
6753 if (lt & LT_EAST_ASIA)
6754 if (isEastAsianString(wstr, numericOrSpace))
6755 return true;
6756 }
6757
6758 if (strictMask & 0x1) // basic Latin
6759 {
6760 if (isBasicLatinString(wstr, numericOrSpace))
6761 return true;
6762 }
6763
6764 return false;
6765}
6766
6767uint8 ObjectMgr::CheckPlayerName(const std::string& name, bool create)
6768{
6769 std::wstring wname;
6770 if (!Utf8toWStr(name, wname))
6771 return CHAR_NAME_INVALID_CHARACTER;
6772
6773 if (wname.size() > MAX_PLAYER_NAME)
6774 return CHAR_NAME_TOO_LONG;
6775
6776 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_PLAYER_NAME);
6777 if (wname.size() < minName)
6778 return CHAR_NAME_TOO_SHORT;
6779
6780 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_PLAYER_NAMES);
6781 if (!isValidString(wname, strictMask, false, create))
6782 return CHAR_NAME_MIXED_LANGUAGES;
6783
6784 return CHAR_NAME_SUCCESS;
6785}
6786
6787bool ObjectMgr::IsValidCharterName(const std::string& name)
6788{
6789 std::wstring wname;
6790 if (!Utf8toWStr(name, wname))
6791 return false;
6792
6793 if (wname.size() > MAX_CHARTER_NAME)
6794 return false;
6795
6796 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_CHARTER_NAME);
6797 if (wname.size() < minName)
6798 return false;
6799
6800 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_CHARTER_NAMES);
6801
6802 return isValidString(wname, strictMask, true);
6803}
6804
6805PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name)
6806{
6807 std::wstring wname;
6808 if (!Utf8toWStr(name, wname))
6809 return PET_NAME_INVALID;
6810
6811 if (wname.size() > MAX_PET_NAME)
6812 return PET_NAME_TOO_LONG;
6813
6814 uint32 minName = sWorld.getConfig(CONFIG_UINT32_MIN_PET_NAME);
6815 if (wname.size() < minName)
6816 return PET_NAME_TOO_SHORT;
6817
6818 uint32 strictMask = sWorld.getConfig(CONFIG_UINT32_STRICT_PET_NAMES);
6819 if (!isValidString(wname, strictMask, false))
6820 return PET_NAME_MIXED_LANGUAGES;
6821
6822 return PET_NAME_SUCCESS;
6823}
6824
6825int ObjectMgr::GetIndexForLocale(LocaleConstant loc)
6826{
6827 if (loc == LOCALE_enUS)
6828 return -1;
6829
6830 for (size_t i = 0; i < m_LocalForIndex.size(); ++i)
6831 if (m_LocalForIndex[i] == loc)
6832 return i;
6833
6834 return -1;
6835}
6836
6837LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
6838{
6839 if (i < 0 || i >= (int32)m_LocalForIndex.size())
6840 return LOCALE_enUS;
6841
6842 return m_LocalForIndex[i];
6843}
6844
6845int ObjectMgr::GetOrNewIndexForLocale(LocaleConstant loc)
6846{
6847 if (loc == LOCALE_enUS)
6848 return -1;
6849
6850 for (size_t i = 0; i < m_LocalForIndex.size(); ++i)
6851 if (m_LocalForIndex[i] == loc)
6852 return i;
6853
6854 m_LocalForIndex.push_back(loc);
6855 return m_LocalForIndex.size() - 1;
6856}
6857
6858void ObjectMgr::LoadGameObjectForQuests()
6859{
6860 mGameObjectForQuestSet.clear(); // need for reload case
6861
6862 if (!sGOStorage.GetMaxEntry())
6863 {
6864 BarGoLink bar(1);
6865 bar.step();
6866 sLog.outString();
6867 sLog.outString(">> Loaded 0 GameObjects for quests");
6868 return;
6869 }
6870
6871 BarGoLink bar(sGOStorage.GetRecordCount());
6872 uint32 count = 0;
6873
6874 // collect GO entries for GO that must activated
6875 for (auto itr = sGOStorage.begin<GameObjectInfo>(); itr < sGOStorage.end<GameObjectInfo>(); ++itr)
6876 {
6877 bar.step();
6878
6879 switch (itr->type)
6880 {
6881 case GAMEOBJECT_TYPE_QUESTGIVER:
6882 {
6883 if (m_GOQuestRelations.find(itr->id) != m_GOQuestRelations.end() ||
6884 m_GOQuestInvolvedRelations.find(itr->id) != m_GOQuestInvolvedRelations.end())
6885 {
6886 mGameObjectForQuestSet.insert(itr->id);
6887 ++count;
6888 }
6889
6890 break;
6891 }
6892 case GAMEOBJECT_TYPE_CHEST:
6893 {
6894 // scan GO chest with loot including quest items
6895 uint32 loot_id = itr->GetLootId();
6896
6897 // always activate to quest, GO may not have loot, OR find if GO has loot for quest.
6898 if (itr->chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
6899 {
6900 mGameObjectForQuestSet.insert(itr->id);
6901 ++count;
6902 }
6903 break;
6904 }
6905 case GAMEOBJECT_TYPE_GENERIC:
6906 {
6907 if (itr->_generic.questID) // quest related objects, has visual effects
6908 {
6909 mGameObjectForQuestSet.insert(itr->id);
6910 count++;
6911 }
6912 break;
6913 }
6914 case GAMEOBJECT_TYPE_SPELL_FOCUS:
6915 {
6916 if (itr->spellFocus.questID) // quest related objects, has visual effect
6917 {
6918 mGameObjectForQuestSet.insert(itr->id);
6919 count++;
6920 }
6921 break;
6922 }
6923 case GAMEOBJECT_TYPE_GOOBER:
6924 {
6925 if (itr->goober.questId) //quests objects
6926 {
6927 mGameObjectForQuestSet.insert(itr->id);
6928 count++;
6929 }
6930 break;
6931 }
6932 default:
6933 break;
6934 }
6935 }
6936
6937 sLog.outString();
6938 sLog.outString(">> Loaded %u GameObjects for quests", count);
6939}
6940
6941void ObjectMgr::LoadBroadcastTexts()
6942{
6943 mBroadcastTextLocaleMap.clear(); // for reload case
6944
6945 // 0 1 2 3 4 5 6 7 8 9 10 11
6946 QueryResult *result = WorldDatabase.Query("SELECT ID, MaleText, FemaleText, Sound, Type, Language, EmoteId0, EmoteId1, EmoteId2, EmoteDelay0, EmoteDelay1, EmoteDelay2 FROM broadcast_text");
6947 if (!result)
6948 {
6949 sLog.outString(">> Loaded 0 broadcast texts. DB table `broadcast_text` is empty.");
6950 return;
6951 }
6952
6953 mBroadcastTextLocaleMap.rehash(result->GetRowCount());
6954
6955 do
6956 {
6957 Field* fields = result->Fetch();
6958
6959 BroadcastText bct;
6960
6961 bct.Id = fields[0].GetUInt32();
6962 bct.MaleText[LOCALE_enUS] = fields[1].GetString() ? fields[1].GetString() : std::string();
6963 bct.FemaleText[LOCALE_enUS] = fields[2].GetString() ? fields[2].GetString() : std::string();
6964 bct.SoundId = fields[3].GetUInt32();
6965 bct.Type = fields[4].GetUInt32();
6966 bct.Language = fields[5].GetUInt32();
6967 bct.EmoteId0 = fields[6].GetUInt32();
6968 bct.EmoteId1 = fields[7].GetUInt32();
6969 bct.EmoteId2 = fields[8].GetUInt32();
6970 bct.EmoteDelay0 = fields[9].GetUInt32();
6971 bct.EmoteDelay1 = fields[10].GetUInt32();
6972 bct.EmoteDelay2 = fields[11].GetUInt32();
6973
6974
6975 if (bct.SoundId)
6976 {
6977 if (!sSoundEntriesStore.LookupEntry(bct.SoundId))
6978 {
6979 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` has SoundId %u but sound does not exist.", bct.Id, bct.SoundId);
6980 bct.SoundId = 0;
6981 }
6982 }
6983
6984 if (!GetLanguageDescByID(bct.Language))
6985 {
6986 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` using Language %u but Language does not exist.", bct.Id, bct.Language);
6987 bct.Language = LANG_UNIVERSAL;
6988 }
6989
6990 if (bct.Type > CHAT_TYPE_ZONE_YELL)
6991 {
6992 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` has Type %u but this Chat Type does not exist.", bct.Id, bct.Type);
6993 bct.Type = CHAT_TYPE_SAY;
6994 }
6995
6996 if (bct.EmoteId0)
6997 {
6998 if (!sEmotesStore.LookupEntry(bct.EmoteId0))
6999 {
7000 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` has EmoteId0 %u but emote does not exist.", bct.Id, bct.EmoteId0);
7001 bct.EmoteId0 = 0;
7002 }
7003 }
7004
7005 if (bct.EmoteId1)
7006 {
7007 if (!sEmotesStore.LookupEntry(bct.EmoteId1))
7008 {
7009 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` has EmoteId1 %u but emote does not exist.", bct.Id, bct.EmoteId1);
7010 bct.EmoteId1 = 0;
7011 }
7012 }
7013
7014 if (bct.EmoteId2)
7015 {
7016 if (!sEmotesStore.LookupEntry(bct.EmoteId2))
7017 {
7018 sLog.outErrorDb("BroadcastText (Id: %u) in table `broadcast_text` has EmoteId2 %u but emote does not exist.", bct.Id, bct.EmoteId2);
7019 bct.EmoteId2 = 0;
7020 }
7021 }
7022
7023 mBroadcastTextLocaleMap[bct.Id] = bct;
7024 } while (result->NextRow());
7025
7026 sLog.outString(">> Loaded %lu broadcast texts.", (unsigned long)mBroadcastTextLocaleMap.size());
7027 sLog.outString();
7028}
7029
7030void ObjectMgr::LoadBroadcastTextLocales()
7031{
7032 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
7033 QueryResult *result = WorldDatabase.Query("SELECT Id, MaleText_loc1, MaleText_loc2, MaleText_loc3, MaleText_loc4, MaleText_loc5, MaleText_loc6, MaleText_loc7, MaleText_loc8, FemaleText_loc1, FemaleText_loc2, FemaleText_loc3, FemaleText_loc4, FemaleText_loc5, FemaleText_loc6, FemaleText_loc7, FemaleText_loc8 FROM locales_broadcast_text");
7034
7035 if (!result)
7036 {
7037 sLog.outString(">> Loaded 0 broadcast text locales. DB table `locales_broadcast_text` is empty.");
7038 return;
7039 }
7040
7041 uint32 count = 0;
7042
7043 do
7044 {
7045 Field* fields = result->Fetch();
7046
7047 uint32 id = fields[0].GetUInt32();
7048 BroadcastTextLocaleMap::iterator bct = mBroadcastTextLocaleMap.find(id);
7049
7050 if (bct == mBroadcastTextLocaleMap.end())
7051 {
7052 sLog.outErrorDb("BroadcastText (Id: %u) in table `locales_broadcast_text` does not exist. Skipped!", id);
7053 continue;
7054 }
7055
7056 BroadcastText& data = mBroadcastTextLocaleMap[id];
7057
7058 // Load MaleText
7059 for (int i = 1; i < MAX_LOCALE; ++i)
7060 {
7061 std::string str = fields[i].GetCppString();
7062 if (!str.empty())
7063 {
7064 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7065 if (idx >= 0)
7066 {
7067 // 0 -> default, idx in to idx+1
7068 if ((int32)data.MaleText.size() <= idx + 1)
7069 data.MaleText.resize(idx + 2);
7070
7071 data.MaleText[idx + 1] = str;
7072 }
7073 }
7074 }
7075
7076 // Load FemaleText
7077 for (int i = 1; i < MAX_LOCALE; ++i)
7078 {
7079 std::string str = fields[8 + i].GetCppString();
7080 if (!str.empty())
7081 {
7082 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7083 if (idx >= 0)
7084 {
7085 // 0 -> default, idx in to idx+1
7086 if ((int32)data.FemaleText.size() <= idx + 1)
7087 data.FemaleText.resize(idx + 2);
7088
7089 data.FemaleText[idx + 1] = str;
7090 }
7091 }
7092 }
7093
7094 ++count;
7095 } while (result->NextRow());
7096
7097 sLog.outString();
7098 sLog.outString(">> Loaded %u broadcast text locales.", count);
7099}
7100
7101const char *ObjectMgr::GetBroadcastText(uint32 id, int locale_index, uint8 gender, bool forceGender) const
7102{
7103 if (BroadcastText const* bct = GetBroadcastTextLocale(id))
7104 {
7105 if ((gender == GENDER_FEMALE || gender == GENDER_NONE) && (forceGender || !bct->FemaleText[LOCALE_enUS].empty()))
7106 {
7107 if ((int32)bct->FemaleText.size() > locale_index + 1 && !bct->FemaleText[locale_index + 1].empty())
7108 return bct->FemaleText[locale_index + 1].c_str();
7109 else
7110 return bct->FemaleText[0].c_str();
7111 }
7112 // else if (gender == GENDER_MALE)
7113 {
7114 if ((int32)bct->MaleText.size() > locale_index + 1 && !bct->MaleText[locale_index + 1].empty())
7115 return bct->MaleText[locale_index + 1].c_str();
7116 else
7117 return bct->MaleText[0].c_str();
7118 }
7119 }
7120
7121 sLog.outErrorDb("Broadcast text id %i not found in DB.", id);
7122 return "<error>";
7123}
7124
7125bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value, bool extra_content)
7126{
7127 int32 start_value = min_value;
7128 int32 end_value = max_value;
7129 // some string can have negative indexes range
7130 if (start_value < 0)
7131 {
7132 if (end_value >= start_value)
7133 {
7134 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7135 return false;
7136 }
7137
7138 // real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1
7139 std::swap(start_value, end_value);
7140 ++start_value;
7141 ++end_value;
7142 }
7143 else
7144 {
7145 if (start_value >= end_value)
7146 {
7147 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7148 return false;
7149 }
7150 }
7151
7152 // cleanup affected map part for reloading case
7153 for (MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
7154 {
7155 if (itr->first >= start_value && itr->first < end_value)
7156 mMangosStringLocaleMap.erase(itr++);
7157 else
7158 ++itr;
7159 }
7160
7161 QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 %s FROM %s", extra_content ? ",sound,type,language,emote" : "", table);
7162
7163 if (!result)
7164 {
7165 BarGoLink bar(1);
7166
7167 bar.step();
7168
7169 sLog.outString();
7170 if (min_value == MIN_MANGOS_STRING_ID) // error only in case internal strings
7171 sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.", table);
7172 else
7173 sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.", table);
7174 return false;
7175 }
7176
7177 uint32 count = 0;
7178
7179 BarGoLink bar(result->GetRowCount());
7180
7181 do
7182 {
7183 Field *fields = result->Fetch();
7184 bar.step();
7185
7186 int32 entry = fields[0].GetInt32();
7187
7188 if (entry == 0)
7189 {
7190 sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.", table);
7191 continue;
7192 }
7193 else if (entry < start_value || entry >= end_value)
7194 {
7195 sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.", table, entry, min_value, max_value);
7196 continue;
7197 }
7198
7199 MangosStringLocale& data = mMangosStringLocaleMap[entry];
7200
7201 if (data.Content.size() > 0)
7202 {
7203 sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.", table, entry);
7204 continue;
7205 }
7206
7207 data.Content.resize(1);
7208 ++count;
7209
7210 // 0 -> default, idx in to idx+1
7211 data.Content[0] = fields[1].GetCppString();
7212
7213 for (int i = 1; i < MAX_LOCALE; ++i)
7214 {
7215 std::string str = fields[i + 1].GetCppString();
7216 if (!str.empty())
7217 {
7218 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7219 if (idx >= 0)
7220 {
7221 // 0 -> default, idx in to idx+1
7222 if ((int32)data.Content.size() <= idx + 1)
7223 data.Content.resize(idx + 2);
7224
7225 data.Content[idx + 1] = str;
7226 }
7227 }
7228 }
7229
7230
7231 // Load additional string content if necessary
7232 if (extra_content)
7233 {
7234 data.SoundId = fields[10].GetUInt32();
7235 data.Type = fields[11].GetUInt32();
7236 data.LanguageId = Language(fields[12].GetUInt32());
7237 data.Emote = fields[13].GetUInt32();
7238
7239 if (data.SoundId && !sSoundEntriesStore.LookupEntry(data.SoundId))
7240 {
7241 sLog.outErrorDb("Entry %i in table `%s` has soundId %u but sound does not exist.", entry, table, data.SoundId);
7242 data.SoundId = 0;
7243 }
7244
7245 if (!GetLanguageDescByID(data.LanguageId))
7246 {
7247 sLog.outErrorDb("Entry %i in table `%s` using Language %u but Language does not exist.", entry, table, uint32(data.LanguageId));
7248 data.LanguageId = LANG_UNIVERSAL;
7249 }
7250
7251 if (data.Type > CHAT_TYPE_ZONE_YELL)
7252 {
7253 sLog.outErrorDb("Entry %i in table `%s` has Type %u but this Chat Type does not exist.", entry, table, data.Type);
7254 data.Type = CHAT_TYPE_SAY;
7255 }
7256
7257 if (data.Emote && !sEmotesStore.LookupEntry(data.Emote))
7258 {
7259 sLog.outErrorDb("Entry %i in table `%s` has Emote %u but emote does not exist.", entry, table, data.Emote);
7260 data.Emote = EMOTE_ONESHOT_NONE;
7261 }
7262 }
7263 }
7264 while (result->NextRow());
7265
7266 delete result;
7267
7268 sLog.outString();
7269 if (min_value == MIN_MANGOS_STRING_ID)
7270 sLog.outString(">> Loaded %u MaNGOS strings from table %s", count, table);
7271 else
7272 sLog.outString(">> Loaded %u string templates from %s", count, table);
7273
7274 return true;
7275}
7276
7277
7278bool ObjectMgr::LoadNostalriusStrings()
7279{
7280 int32 start_value = MIN_NOSTALRIUS_STRING_ID;
7281 int32 end_value = MAX_NOSTALRIUS_STRING_ID;
7282
7283 // cleanup affected map part for reloading case
7284 for (MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
7285 {
7286 if (itr->first >= start_value && itr->first < end_value)
7287 mMangosStringLocaleMap.erase(itr++);
7288 else
7289 ++itr;
7290 }
7291
7292 QueryResult *result = WorldDatabase.Query("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM nostalrius_string");
7293
7294 if (!result)
7295 {
7296 sLog.outString(">> Chargement d'aucun text Nostalrius. La table 'nostalrius_string' est vide.");
7297 return false;
7298 }
7299
7300 uint32 count = 0;
7301
7302 do
7303 {
7304 Field *fields = result->Fetch();
7305 int32 entry = fields[0].GetInt32() + MIN_NOSTALRIUS_STRING_ID;
7306 if (entry < start_value || entry >= end_value)
7307 {
7308 sLog.outErrorDb("Table `nostalrius_string` contain entry %i out of allowed range (%u - %u), ignored.", entry, start_value, end_value);
7309 continue;
7310 }
7311
7312 MangosStringLocale& data = mMangosStringLocaleMap[entry];
7313
7314 if (data.Content.size() > 0)
7315 {
7316 sLog.outErrorDb("Table `nostalrius_string` contain data for already loaded entry %i (from another table?), ignored.", entry);
7317 continue;
7318 }
7319
7320 data.Content.resize(1);
7321 ++count;
7322
7323 // 0 -> default, idx in to idx+1
7324 data.Content[0] = fields[1].GetCppString();
7325
7326 for (int i = 1; i < MAX_LOCALE; ++i)
7327 {
7328 std::string str = fields[i + 1].GetCppString();
7329 if (!str.empty())
7330 {
7331 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7332 if (idx >= 0)
7333 {
7334 // 0 -> default, idx in to idx+1
7335 if ((int32)data.Content.size() <= idx + 1)
7336 data.Content.resize(idx + 2);
7337
7338 data.Content[idx + 1] = str;
7339 }
7340 }
7341 }
7342 }
7343 while (result->NextRow());
7344
7345 delete result;
7346
7347 sLog.outString();
7348 sLog.outString(">> Loaded %u texts from 'nostalrius_string'.", count);
7349
7350 return true;
7351}
7352
7353const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
7354{
7355 // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
7356 // Content[0] always exist if exist MangosStringLocale
7357 if (MangosStringLocale const *msl = GetMangosStringLocale(entry))
7358 {
7359 if ((int32)msl->Content.size() > locale_idx + 1 && !msl->Content[locale_idx + 1].empty())
7360 return msl->Content[locale_idx + 1].c_str();
7361 else
7362 return msl->Content[0].c_str();
7363 }
7364
7365 if (entry > 0)
7366 sLog.outErrorDb("Entry %i not found in `mangos_string` table.", entry);
7367 else if (entry > MAX_CREATURE_AI_TEXT_STRING_ID)
7368 sLog.outErrorDb("Entry %i not found in `creature_ai_texts` table.", entry);
7369 else
7370 sLog.outErrorDb("Mangos string entry %i not found in DB.", entry);
7371 return "<error>";
7372}
7373
7374bool ObjectMgr::LoadQuestGreetings()
7375{
7376 for (uint32 i = 0; i < QUESTGIVER_TYPE_MAX; i++)
7377 mQuestGreetingLocaleMap[i].clear(); // need for reload case
7378
7379 QueryResult *result = WorldDatabase.Query("SELECT entry,type,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8,Emote,EmoteDelay FROM quest_greeting");
7380
7381 if (!result)
7382 {
7383 sLog.outString(">> Loaded 0 quest greetings. DB table `quest_greeting` is empty.");
7384 return false;
7385 }
7386
7387 uint32 count = 0;
7388
7389 do
7390 {
7391 Field *fields = result->Fetch();
7392 uint32 entry = fields[0].GetUInt32();
7393 uint8 type = fields[1].GetUInt8();
7394
7395 switch (type)
7396 {
7397 case QUESTGIVER_CREATURE:
7398 {
7399 if (!ObjectMgr::GetCreatureTemplate(entry))
7400 {
7401 sLog.outErrorDb("Table `quest_greeting` have entry for nonexistent creature template (Entry: %u), ignore", entry);
7402 continue;
7403 }
7404 break;
7405 }
7406 case QUESTGIVER_GAMEOBJECT:
7407 {
7408 if (!ObjectMgr::GetGameObjectInfo(entry))
7409 {
7410 sLog.outErrorDb("Table `quest_greeting` have entry for nonexistent gameobject template (Entry: %u), ignore", entry);
7411 continue;
7412 }
7413 break;
7414 }
7415 default:
7416 {
7417 sLog.outErrorDb("Table `quest_greeting` have entry with invalid type (Type: %u), ignore", type);
7418 continue;
7419 }
7420 }
7421
7422 QuestGreetingLocale& data = mQuestGreetingLocaleMap[type][entry];
7423
7424 data.Content.resize(1);
7425 ++count;
7426
7427 // 0 -> default, idx in to idx+1
7428 data.Content[0] = fields[2].GetCppString();
7429
7430 for (int i = 1; i < MAX_LOCALE; ++i)
7431 {
7432 std::string str = fields[i + 2].GetCppString();
7433 if (!str.empty())
7434 {
7435 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
7436 if (idx >= 0)
7437 {
7438 // 0 -> default, idx in to idx+1
7439 if ((int32)data.Content.size() <= idx + 1)
7440 data.Content.resize(idx + 2);
7441
7442 data.Content[idx + 1] = str;
7443 }
7444 }
7445 }
7446
7447 data.Emote = fields[11].GetUInt16();
7448 data.EmoteDelay = fields[12].GetUInt32();
7449
7450 if (data.Emote && !sEmotesStore.LookupEntry(data.Emote))
7451 {
7452 sLog.outErrorDb("Entry %i in table `quest_greeting` has Emote %u but emote does not exist.", entry, data.Emote);
7453 data.Emote = EMOTE_ONESHOT_NONE;
7454 }
7455 } while (result->NextRow());
7456
7457 delete result;
7458
7459 sLog.outString();
7460 sLog.outString(">> Loaded %u quest greetings.", count);
7461
7462 return true;
7463}
7464
7465void ObjectMgr::LoadFishingBaseSkillLevel()
7466{
7467 mFishingBaseForArea.clear(); // for reload case
7468
7469 uint32 count = 0;
7470 QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
7471
7472 if (!result)
7473 {
7474 BarGoLink bar(1);
7475
7476 bar.step();
7477
7478 sLog.outString();
7479 sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
7480 return;
7481 }
7482
7483 BarGoLink bar(result->GetRowCount());
7484
7485 do
7486 {
7487 bar.step();
7488
7489 Field *fields = result->Fetch();
7490 uint32 entry = fields[0].GetUInt32();
7491 int32 skill = fields[1].GetInt32();
7492
7493 const auto *fArea = AreaEntry::GetById(entry);
7494 if (!fArea)
7495 {
7496 sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
7497 continue;
7498 }
7499
7500 mFishingBaseForArea[entry] = skill;
7501 ++count;
7502 }
7503 while (result->NextRow());
7504
7505 delete result;
7506
7507 sLog.outString();
7508 sLog.outString(">> Loaded %u areas for fishing base skill level", count);
7509}
7510
7511SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
7512{
7513 switch (pSkill->categoryId)
7514 {
7515 case SKILL_CATEGORY_LANGUAGES:
7516 return SKILL_RANGE_LANGUAGE;
7517 case SKILL_CATEGORY_WEAPON:
7518 if (pSkill->id != SKILL_FIST_WEAPONS)
7519 return SKILL_RANGE_LEVEL;
7520 else
7521 return SKILL_RANGE_MONO;
7522 case SKILL_CATEGORY_ARMOR:
7523 case SKILL_CATEGORY_CLASS:
7524 if (pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING)
7525 return SKILL_RANGE_MONO;
7526 else
7527 return SKILL_RANGE_LEVEL;
7528 case SKILL_CATEGORY_SECONDARY:
7529 case SKILL_CATEGORY_PROFESSION:
7530 // not set skills for professions and racial abilities
7531 if (IsProfessionSkill(pSkill->id))
7532 return SKILL_RANGE_RANK;
7533 else if (racial)
7534 return SKILL_RANGE_NONE;
7535 else
7536 return SKILL_RANGE_MONO;
7537 default:
7538 case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
7539 case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
7540 return SKILL_RANGE_NONE;
7541 }
7542}
7543
7544void ObjectMgr::LoadGameTele()
7545{
7546 m_GameTeleMap.clear(); // for reload case
7547
7548 uint32 count = 0;
7549 QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
7550
7551 if (!result)
7552 {
7553 BarGoLink bar(1);
7554
7555 bar.step();
7556
7557 sLog.outString();
7558 sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
7559 return;
7560 }
7561
7562 BarGoLink bar(result->GetRowCount());
7563
7564 do
7565 {
7566 bar.step();
7567
7568 Field *fields = result->Fetch();
7569
7570 uint32 id = fields[0].GetUInt32();
7571
7572 GameTele gt;
7573
7574 gt.position_x = fields[1].GetFloat();
7575 gt.position_y = fields[2].GetFloat();
7576 gt.position_z = fields[3].GetFloat();
7577 gt.orientation = fields[4].GetFloat();
7578 gt.mapId = fields[5].GetUInt32();
7579 gt.name = fields[6].GetCppString();
7580
7581 if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
7582 {
7583 sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
7584 continue;
7585 }
7586
7587 if (!Utf8toWStr(gt.name, gt.wnameLow))
7588 {
7589 sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
7590 continue;
7591 }
7592
7593 wstrToLower(gt.wnameLow);
7594
7595 m_GameTeleMap[id] = gt;
7596
7597 ++count;
7598 }
7599 while (result->NextRow());
7600 delete result;
7601
7602 sLog.outString();
7603 sLog.outString(">> Loaded %u GameTeleports", count);
7604}
7605
7606GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
7607{
7608 // explicit name case
7609 std::wstring wname;
7610 if (!Utf8toWStr(name, wname))
7611 return NULL;
7612
7613 // converting string that we try to find to lower case
7614 wstrToLower(wname);
7615
7616 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
7617 const GameTele* alt = NULL;
7618 for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7619 if (itr->second.wnameLow == wname)
7620 return &itr->second;
7621 else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
7622 alt = &itr->second;
7623
7624 return alt;
7625}
7626
7627bool ObjectMgr::AddGameTele(GameTele& tele)
7628{
7629 // find max id
7630 uint32 new_id = 0;
7631 for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7632 if (itr->first > new_id)
7633 new_id = itr->first;
7634
7635 // use next
7636 ++new_id;
7637
7638 if (!Utf8toWStr(tele.name, tele.wnameLow))
7639 return false;
7640
7641 wstrToLower(tele.wnameLow);
7642
7643 m_GameTeleMap[new_id] = tele;
7644
7645 return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%u,'%s')",
7646 new_id, tele.position_x, tele.position_y, tele.position_z, tele.orientation, tele.mapId, tele.name.c_str());
7647}
7648
7649bool ObjectMgr::DeleteGameTele(const std::string& name)
7650{
7651 // explicit name case
7652 std::wstring wname;
7653 if (!Utf8toWStr(name, wname))
7654 return false;
7655
7656 // converting string that we try to find to lower case
7657 wstrToLower(wname);
7658
7659 for (GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7660 {
7661 if (itr->second.wnameLow == wname)
7662 {
7663 WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'", itr->second.name.c_str());
7664 m_GameTeleMap.erase(itr);
7665 return true;
7666 }
7667 }
7668
7669 return false;
7670}
7671
7672void ObjectMgr::LoadTrainers(char const* tableName, bool isTemplates)
7673{
7674 CacheTrainerSpellMap& trainerList = isTemplates ? m_mCacheTrainerTemplateSpellMap : m_mCacheTrainerSpellMap;
7675
7676 // For reload case
7677 for (CacheTrainerSpellMap::iterator itr = trainerList.begin(); itr != trainerList.end(); ++itr)
7678 itr->second.Clear();
7679 trainerList.clear();
7680
7681 std::set<uint32> skip_trainers;
7682
7683 QueryResult *result = WorldDatabase.PQuery("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM %s", tableName);
7684
7685 if (!result)
7686 {
7687 BarGoLink bar(1);
7688
7689 bar.step();
7690
7691 sLog.outString();
7692 sLog.outString(">> Loaded `%s`, table is empty!", tableName);
7693 return;
7694 }
7695
7696 BarGoLink bar(result->GetRowCount());
7697
7698 std::set<uint32> talentIds;
7699
7700 uint32 count = 0;
7701 do
7702 {
7703 bar.step();
7704
7705 Field* fields = result->Fetch();
7706
7707 uint32 entry = fields[0].GetUInt32();
7708 uint32 spell = fields[1].GetUInt32();
7709
7710 SpellEntry const *spellinfo = sSpellMgr.GetSpellEntry(spell);
7711 if (!spellinfo)
7712 {
7713 sLog.outErrorDb("Table `%s` (Entry: %u ) has non existing spell %u, ignore", tableName, entry, spell);
7714 continue;
7715 }
7716
7717 if (spellinfo->Effect[0] != SPELL_EFFECT_LEARN_SPELL)
7718 {
7719 sLog.outErrorDb("Table `%s` for trainer (Entry: %u) has non-learning spell %u, ignore", tableName, entry, spell);
7720 for (uint32 spell2 = 1; spell2 < sSpellStore.GetNumRows(); ++spell2)
7721 {
7722 if (SpellEntry const* spellEntry2 = sSpellMgr.GetSpellEntry(spell2))
7723 {
7724 if (spellEntry2->Effect[0] == SPELL_EFFECT_LEARN_SPELL && spellEntry2->EffectTriggerSpell[0] == spell)
7725 {
7726 sLog.outErrorDb(" ... possible must be used spell %u instead", spell2);
7727 break;
7728 }
7729 }
7730 }
7731 continue;
7732 }
7733
7734 if (!SpellMgr::IsSpellValid(spellinfo))
7735 {
7736 sLog.outErrorDb("Table `%s` (Entry: %u) has broken learning spell %u, ignore", tableName, entry, spell);
7737 continue;
7738 }
7739
7740 if (GetTalentSpellCost(spell))
7741 {
7742 if (talentIds.find(spell) == talentIds.end())
7743 {
7744 sLog.outErrorDb("Table `%s` has talent as learning spell %u, ignore", tableName, spell);
7745 talentIds.insert(spell);
7746 }
7747 continue;
7748 }
7749
7750 if (!isTemplates)
7751 {
7752 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
7753
7754 if (!cInfo)
7755 {
7756 sLog.outErrorDb("Table `%s` have entry for nonexistent creature template (Entry: %u), ignore", tableName, entry);
7757 continue;
7758 }
7759
7760 if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
7761 {
7762 if (skip_trainers.find(entry) == skip_trainers.end())
7763 {
7764 sLog.outErrorDb("Table `%s` have data for creature (Entry: %u) without trainer flag, ignore", tableName, entry);
7765 skip_trainers.insert(entry);
7766 }
7767 continue;
7768 }
7769
7770 if (TrainerSpellData const* tSpells = cInfo->trainerId ? GetNpcTrainerTemplateSpells(cInfo->trainerId) : NULL)
7771 {
7772 if (tSpells->spellList.find(spell) != tSpells->spellList.end())
7773 {
7774 sLog.outErrorDb("Table `%s` (Entry: %u) has spell %u listed in trainer template %u, ignore", tableName, entry, spell, cInfo->trainerId);
7775 continue;
7776 }
7777 }
7778 }
7779
7780 TrainerSpellData& data = trainerList[entry];
7781
7782 TrainerSpell& trainerSpell = data.spellList[spell];
7783 trainerSpell.spell = spell;
7784 trainerSpell.spellCost = fields[2].GetUInt32();
7785 trainerSpell.reqSkill = fields[3].GetUInt32();
7786 trainerSpell.reqSkillValue = fields[4].GetUInt32();
7787 trainerSpell.reqLevel = fields[5].GetUInt32();
7788
7789 if (trainerSpell.reqLevel)
7790 {
7791 if (trainerSpell.reqLevel == spellinfo->spellLevel)
7792 ERROR_DB_STRICT_LOG("Table `%s` (Entry: %u) has redundant reqlevel %u (=spell level) for spell %u", tableName, entry, trainerSpell.reqLevel, spell);
7793 }
7794 else
7795 trainerSpell.reqLevel = spellinfo->spellLevel;
7796
7797 if (SpellMgr::IsProfessionSpell(spellinfo->EffectTriggerSpell[0]))
7798 data.trainerType = 2;
7799
7800 ++count;
7801
7802 }
7803 while (result->NextRow());
7804 delete result;
7805
7806 sLog.outString();
7807 sLog.outString(">> Loaded %d trainer %sspells", count, isTemplates ? "template " : "");
7808}
7809
7810void ObjectMgr::LoadTrainerTemplates()
7811{
7812 LoadTrainers("npc_trainer_template", true);
7813
7814 // post loading check
7815 std::set<uint32> trainer_ids;
7816
7817 for (CacheTrainerSpellMap::const_iterator tItr = m_mCacheTrainerTemplateSpellMap.begin(); tItr != m_mCacheTrainerTemplateSpellMap.end(); ++tItr)
7818 trainer_ids.insert(tItr->first);
7819
7820 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
7821 {
7822 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
7823 {
7824 if (cInfo->trainerId)
7825 {
7826 if (m_mCacheTrainerTemplateSpellMap.find(cInfo->trainerId) != m_mCacheTrainerTemplateSpellMap.end())
7827 trainer_ids.erase(cInfo->trainerId);
7828 else
7829 sLog.outErrorDb("Creature (Entry: %u) has trainer_id = %u for nonexistent trainer template", cInfo->Entry, cInfo->trainerId);
7830 }
7831 }
7832 }
7833
7834 for (std::set<uint32>::const_iterator tItr = trainer_ids.begin(); tItr != trainer_ids.end(); ++tItr)
7835 sLog.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr);
7836}
7837
7838void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates)
7839{
7840 CacheVendorItemMap& vendorList = isTemplates ? m_mCacheVendorTemplateItemMap : m_mCacheVendorItemMap;
7841
7842 // For reload case
7843 for (CacheVendorItemMap::iterator itr = vendorList.begin(); itr != vendorList.end(); ++itr)
7844 itr->second.Clear();
7845 vendorList.clear();
7846
7847 std::set<uint32> skip_vendors;
7848
7849 //QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime FROM %s WHERE (item NOT IN (SELECT entry FROM forbidden_items WHERE (AfterOrBefore = 0 && patch <= %u) || (AfterOrBefore = 1 && patch >= %u)))", tableName, sWorld.GetWowPatch(), sWorld.GetWowPatch());
7850 //QueryResult* result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, itemflags FROM %s WHERE (item NOT IN (SELECT entry FROM forbidden_items WHERE (AfterOrBefore = 0 && patch <= %u) || (AfterOrBefore = 1 && patch >= %u))) ORDER BY entry, slot ASC", tableName, sWorld.GetWowPatch(), sWorld.GetWowPatch());
7851 QueryResult* result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, itemflags FROM %s WHERE (item NOT IN (SELECT entry FROM forbidden_items WHERE (AfterOrBefore = 0 && patch <= %u) || (AfterOrBefore = 1 && patch >= %u))) ORDER BY entry, slot ASC", tableName, sWorld.GetWowPatch(), sWorld.GetWowPatch());
7852 if (!result)
7853 {
7854 BarGoLink bar(1);
7855
7856 bar.step();
7857
7858 sLog.outString();
7859 sLog.outString(">> Loaded `%s`, table is empty!", tableName);
7860 return;
7861 }
7862
7863 BarGoLink bar(result->GetRowCount());
7864
7865 uint32 count = 0;
7866 do
7867 {
7868 bar.step();
7869 Field* fields = result->Fetch();
7870
7871 uint32 entry = fields[0].GetUInt32();
7872 uint32 item_id = fields[1].GetUInt32();
7873 uint32 maxcount = fields[2].GetUInt32();
7874 uint32 incrtime = fields[3].GetUInt32();
7875
7876 if (!IsVendorItemValid(isTemplates, tableName, entry, item_id, maxcount, incrtime, NULL, &skip_vendors))
7877 continue;
7878
7879 VendorItemData& vList = vendorList[entry];
7880
7881 vList.AddItem(item_id, maxcount, incrtime);
7882 ++count;
7883
7884 }
7885 while (result->NextRow());
7886 delete result;
7887
7888 sLog.outString();
7889 sLog.outString(">> Loaded %u vendor %sitems", count, isTemplates ? "template " : "");
7890}
7891
7892
7893void ObjectMgr::LoadVendorTemplates()
7894{
7895 LoadVendors("npc_vendor_template", true);
7896
7897 // post loading check
7898 std::set<uint32> vendor_ids;
7899
7900 for (CacheVendorItemMap::const_iterator vItr = m_mCacheVendorTemplateItemMap.begin(); vItr != m_mCacheVendorTemplateItemMap.end(); ++vItr)
7901 vendor_ids.insert(vItr->first);
7902
7903 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
7904 {
7905 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
7906 {
7907 if (cInfo->vendorId)
7908 {
7909 if (m_mCacheVendorTemplateItemMap.find(cInfo->vendorId) != m_mCacheVendorTemplateItemMap.end())
7910 vendor_ids.erase(cInfo->vendorId);
7911 else
7912 sLog.outErrorDb("Creature (Entry: %u) has vendor_id = %u for nonexistent vendor template", cInfo->Entry, cInfo->vendorId);
7913 }
7914 }
7915 }
7916
7917 // We need to use a query to get all used vendor ids because of progression.
7918 // It might be used by a creature that is not loaded in this patch.
7919 QueryResult* result = WorldDatabase.Query("SELECT vendor_id FROM creature_template WHERE vendor_id > 0");
7920
7921 Field* fields;
7922
7923 if (result)
7924 {
7925 do
7926 {
7927 fields = result->Fetch();
7928 uint32 vendorId = fields[0].GetUInt32();
7929 if (vendor_ids.find(vendorId) != vendor_ids.end())
7930 vendor_ids.erase(vendorId);
7931 } while (result->NextRow());
7932 delete result;
7933 }
7934
7935 for (std::set<uint32>::const_iterator vItr = vendor_ids.begin(); vItr != vendor_ids.end(); ++vItr)
7936 sLog.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr);
7937}
7938
7939void ObjectMgr::LoadNpcGossips()
7940{
7941
7942 m_mCacheNpcTextIdMap.clear();
7943
7944 QueryResult* result = WorldDatabase.Query("SELECT npc_guid, textid FROM npc_gossip");
7945 if (!result)
7946 {
7947 BarGoLink bar(1);
7948
7949 bar.step();
7950
7951 sLog.outString();
7952 sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
7953 return;
7954 }
7955
7956 BarGoLink bar(result->GetRowCount());
7957
7958 uint32 count = 0;
7959 uint32 guid, textid;
7960 do
7961 {
7962 bar.step();
7963
7964 Field* fields = result->Fetch();
7965
7966 guid = fields[0].GetUInt32();
7967 textid = fields[1].GetUInt32();
7968
7969 if (!GetCreatureData(guid))
7970 {
7971 sLog.outErrorDb("Table `npc_gossip` have nonexistent creature (GUID: %u) entry, ignore. ", guid);
7972 continue;
7973 }
7974 if (!GetNpcText(textid))
7975 {
7976 sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
7977 continue;
7978 }
7979
7980 m_mCacheNpcTextIdMap[guid] = textid ;
7981 ++count;
7982
7983 }
7984 while (result->NextRow());
7985 delete result;
7986
7987 sLog.outString();
7988 sLog.outString(">> Loaded %d NpcTextId ", count);
7989}
7990
7991void ObjectMgr::LoadGossipMenu()
7992{
7993 m_mGossipMenusMap.clear();
7994
7995 // 0 1 2
7996 QueryResult* result = WorldDatabase.Query("SELECT entry, text_id, condition_id FROM gossip_menu");
7997
7998 if (!result)
7999 {
8000 BarGoLink bar(1);
8001
8002 bar.step();
8003
8004 sLog.outString();
8005 sLog.outErrorDb(">> Loaded gossip_menu, table is empty!");
8006 return;
8007 }
8008
8009 BarGoLink bar(result->GetRowCount());
8010
8011 uint32 count = 0;
8012
8013 do
8014 {
8015 bar.step();
8016
8017 Field* fields = result->Fetch();
8018
8019 GossipMenus gMenu;
8020
8021 gMenu.entry = fields[0].GetUInt32();
8022 gMenu.text_id = fields[1].GetUInt32();
8023 gMenu.conditionId = fields[2].GetUInt16();
8024
8025 if (!GetNpcText(gMenu.text_id))
8026 {
8027 sLog.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
8028 continue;
8029 }
8030
8031 if (gMenu.conditionId)
8032 {
8033 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(gMenu.conditionId);
8034 if (!condition)
8035 {
8036 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);
8037 gMenu.conditionId = 0;
8038 }
8039 }
8040
8041 m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu));
8042
8043 ++count;
8044 }
8045 while (result->NextRow());
8046
8047 delete result;
8048
8049 sLog.outString();
8050 sLog.outString(">> Loaded %u gossip_menu entries", count);
8051
8052 // post loading tests
8053 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
8054 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
8055 if (cInfo->GossipMenuId)
8056 if (m_mGossipMenusMap.find(cInfo->GossipMenuId) == m_mGossipMenusMap.end())
8057 sLog.outErrorDb("Creature (Entry: %u) has gossip_menu_id = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId);
8058
8059 for (auto itr = sGOStorage.begin<GameObjectInfo>(); itr < sGOStorage.end<GameObjectInfo>(); ++itr)
8060 if (uint32 menuid = itr->GetGossipMenuId())
8061 if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end())
8062 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", itr->id, menuid);
8063}
8064
8065void ObjectMgr::LoadGossipMenuItems()
8066{
8067 m_mGossipMenuItemsMap.clear();
8068
8069 QueryResult* result = WorldDatabase.Query(
8070 "SELECT menu_id, id, option_icon, option_text, OptionBroadcastTextID, option_id, npc_option_npcflag, "
8071 "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, BoxBroadcastTextID, "
8072 "condition_id "
8073 "FROM gossip_menu_option ORDER BY menu_id, id");
8074
8075 if (!result)
8076 {
8077 BarGoLink bar(1);
8078
8079 bar.step();
8080
8081 sLog.outString();
8082 sLog.outErrorDb(">> Loaded gossip_menu_option, table is empty!");
8083 return;
8084 }
8085
8086 // prepare data for unused menu ids
8087 std::set<uint32> menu_ids; // for later integrity check
8088 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)) // check unused menu ids only in strict mode
8089 {
8090 for (GossipMenusMap::const_iterator itr = m_mGossipMenusMap.begin(); itr != m_mGossipMenusMap.end(); ++itr)
8091 if (itr->first)
8092 menu_ids.insert(itr->first);
8093
8094 for (auto itr = sGOStorage.begin<GameObjectInfo>(); itr < sGOStorage.end<GameObjectInfo>(); ++itr)
8095 if (uint32 menuid = itr->GetGossipMenuId())
8096 menu_ids.erase(menuid);
8097 }
8098
8099 // loading
8100 BarGoLink bar(result->GetRowCount());
8101
8102 uint32 count = 0;
8103
8104 std::set<uint32> gossipScriptSet;
8105
8106 for (ScriptMapMap::const_iterator itr = sGossipScripts.begin(); itr != sGossipScripts.end(); ++itr)
8107 gossipScriptSet.insert(itr->first);
8108
8109 // prepare menuid -> CreatureInfo map for fast access
8110 typedef std::multimap<uint32, const CreatureInfo*> Menu2CInfoMap;
8111 Menu2CInfoMap menu2CInfoMap;
8112 for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
8113 if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
8114 if (cInfo->GossipMenuId)
8115 menu2CInfoMap.insert(Menu2CInfoMap::value_type(cInfo->GossipMenuId, cInfo));
8116
8117 do
8118 {
8119 bar.step();
8120
8121 Field* fields = result->Fetch();
8122
8123 GossipMenuItems gMenuItem;
8124
8125 gMenuItem.menu_id = fields[0].GetUInt32();
8126 gMenuItem.id = fields[1].GetUInt32();
8127 gMenuItem.option_icon = fields[2].GetUInt8();
8128 gMenuItem.option_text = fields[3].GetCppString();
8129 gMenuItem.OptionBroadcastTextID = fields[4].GetUInt32();
8130
8131 gMenuItem.option_id = fields[5].GetUInt32();
8132 gMenuItem.npc_option_npcflag = fields[6].GetUInt32();
8133 gMenuItem.action_menu_id = fields[7].GetInt32();
8134 gMenuItem.action_poi_id = fields[8].GetUInt32();
8135 gMenuItem.action_script_id = fields[9].GetUInt32();
8136 gMenuItem.box_coded = fields[10].GetUInt8() != 0;
8137 //gMenuItem.box_money = fields[11].GetUInt32();
8138 gMenuItem.box_text = fields[12].GetCppString();
8139 gMenuItem.BoxBroadcastTextID = fields[13].GetUInt32();
8140
8141 gMenuItem.conditionId = fields[14].GetUInt16();
8142
8143 if (gMenuItem.menu_id) // == 0 id is special and not have menu_id data
8144 {
8145 if (m_mGossipMenusMap.find(gMenuItem.menu_id) == m_mGossipMenusMap.end())
8146 {
8147 sLog.outErrorDb("Gossip menu option (MenuId: %u) for nonexistent menu", gMenuItem.menu_id);
8148 continue;
8149 }
8150 }
8151
8152 if (gMenuItem.action_menu_id > 0)
8153 {
8154 if (m_mGossipMenusMap.find(gMenuItem.action_menu_id) == m_mGossipMenusMap.end())
8155 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);
8156 else if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
8157 menu_ids.erase(gMenuItem.action_menu_id);
8158 }
8159
8160 if (gMenuItem.option_icon >= GOSSIP_ICON_MAX)
8161 {
8162 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);
8163 gMenuItem.option_icon = GOSSIP_ICON_CHAT;
8164 }
8165
8166 if (gMenuItem.OptionBroadcastTextID)
8167 {
8168 if (!GetBroadcastTextLocale(gMenuItem.OptionBroadcastTextID))
8169 {
8170 sLog.outErrorDb("Table `gossip_menu_option` for MenuId %u, OptionID %u has non-existing or incompatible OptionBroadcastTextID %u, ignoring.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.OptionBroadcastTextID);
8171 gMenuItem.OptionBroadcastTextID = 0;
8172 }
8173 }
8174
8175 if (gMenuItem.BoxBroadcastTextID)
8176 {
8177 if (!GetBroadcastTextLocale(gMenuItem.BoxBroadcastTextID))
8178 {
8179 sLog.outErrorDb("Table `gossip_menu_option` for MenuId %u, OptionID %u has non-existing or incompatible BoxBroadcastTextId %u, ignoring.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.BoxBroadcastTextID);
8180 gMenuItem.BoxBroadcastTextID = 0;
8181 }
8182 }
8183
8184 if (gMenuItem.option_id == GOSSIP_OPTION_NONE)
8185 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);
8186
8187 if (gMenuItem.option_id >= GOSSIP_OPTION_MAX)
8188 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);
8189
8190 if (gMenuItem.menu_id && (gMenuItem.npc_option_npcflag || !sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)))
8191 {
8192 bool found_menu_uses = false;
8193 bool found_flags_uses = false;
8194
8195 std::pair<Menu2CInfoMap::const_iterator, Menu2CInfoMap::const_iterator> tm_bounds = menu2CInfoMap.equal_range(gMenuItem.menu_id);
8196 for (Menu2CInfoMap::const_iterator it2 = tm_bounds.first; !found_flags_uses && it2 != tm_bounds.second; ++it2)
8197 {
8198 CreatureInfo const* cInfo = it2->second;
8199
8200 found_menu_uses = true;
8201
8202 // some from creatures with gossip menu can use gossip option base at npc_flags
8203 if (gMenuItem.npc_option_npcflag & cInfo->npcflag)
8204 found_flags_uses = true;
8205
8206 // unused check data preparing part
8207 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
8208 menu_ids.erase(gMenuItem.menu_id);
8209 }
8210
8211 if (found_menu_uses && !found_flags_uses)
8212 sLog.outDetail("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);
8213 }
8214
8215 if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
8216 {
8217 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);
8218 gMenuItem.action_poi_id = 0;
8219 }
8220
8221 if (gMenuItem.action_script_id)
8222 {
8223 if (gMenuItem.option_id != GOSSIP_OPTION_GOSSIP)
8224 {
8225 sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u but option_id is not GOSSIP_OPTION_GOSSIP, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
8226 continue;
8227 }
8228
8229 if (sGossipScripts.find(gMenuItem.action_script_id) == sGossipScripts.end())
8230 {
8231 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);
8232 continue;
8233 }
8234
8235 gossipScriptSet.erase(gMenuItem.action_script_id);
8236 }
8237
8238 if (gMenuItem.conditionId)
8239 {
8240 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(gMenuItem.conditionId);
8241 if (!condition)
8242 {
8243 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);
8244 gMenuItem.conditionId = 0;
8245 }
8246 }
8247
8248 m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));
8249
8250 ++count;
8251
8252 }
8253 while (result->NextRow());
8254
8255 delete result;
8256
8257 for (std::set<uint32>::const_iterator itr = gossipScriptSet.begin(); itr != gossipScriptSet.end(); ++itr)
8258 sLog.outErrorDb("Table `gossip_scripts` contain unused script, id %u.", *itr);
8259
8260 if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
8261 {
8262 for (std::set<uint32>::const_iterator itr = menu_ids.begin(); itr != menu_ids.end(); ++itr)
8263 sLog.outErrorDb("Table `gossip_menu` contain unused (in creature or GO or menu options) menu id %u.", *itr);
8264 }
8265
8266 sLog.outString();
8267 sLog.outString(">> Loaded %u gossip_menu_option entries", count);
8268}
8269
8270void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, uint32 maxcount, uint32 incrtime)
8271{
8272 VendorItemData& vList = m_mCacheVendorItemMap[entry];
8273 vList.AddItem(item, maxcount, incrtime);
8274
8275 WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime) VALUES('%u','%u','%u','%u')", entry, item, maxcount, incrtime);
8276}
8277
8278bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item)
8279{
8280 CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
8281 if (iter == m_mCacheVendorItemMap.end())
8282 return false;
8283
8284 if (!iter->second.FindItem(item))
8285 return false;
8286
8287 iter->second.RemoveItem(item);
8288 WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'", entry, item);
8289 return true;
8290}
8291
8292bool ObjectMgr::IsVendorItemValid(bool isTemplate, char const* tableName, uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, Player* pl, std::set<uint32>* skip_vendors) const
8293{
8294 char const* idStr = isTemplate ? "vendor template" : "vendor";
8295 CreatureInfo const* cInfo = NULL;
8296
8297 if (!isTemplate)
8298 {
8299 cInfo = GetCreatureTemplate(vendor_entry);
8300 if (!cInfo)
8301 {
8302 if (pl)
8303 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
8304 else
8305 sLog.outErrorDb("Table `%s` has data for nonexistent creature (Entry: %u), ignoring", tableName, vendor_entry);
8306 return false;
8307 }
8308
8309 if (!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
8310 {
8311 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
8312 {
8313 if (pl)
8314 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
8315 else
8316 sLog.outErrorDb("Table `%s` has data for creature (Entry: %u) without vendor flag, ignoring", tableName, vendor_entry);
8317
8318 if (skip_vendors)
8319 skip_vendors->insert(vendor_entry);
8320 }
8321 return false;
8322 }
8323 }
8324
8325 if (!GetItemPrototype(item_id))
8326 {
8327 if (pl)
8328 ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
8329 else
8330 sLog.outErrorDb("Table `%s` for %s %u contain nonexistent item (%u), ignoring",
8331 tableName, idStr, vendor_entry, item_id);
8332 return false;
8333 }
8334
8335 if (maxcount > 0 && incrtime == 0)
8336 {
8337 if (pl)
8338 ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
8339 else
8340 sLog.outErrorDb("Table `%s` has `maxcount` (%u) for item %u of %s %u but `incrtime`=0, ignoring",
8341 tableName, maxcount, item_id, idStr, vendor_entry);
8342 return false;
8343 }
8344 else if (maxcount == 0 && incrtime > 0)
8345 {
8346 if (pl)
8347 ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
8348 else
8349 sLog.outErrorDb("Table `%s` has `maxcount`=0 for item %u of %s %u but `incrtime`<>0, ignoring",
8350 tableName, item_id, idStr, vendor_entry);
8351 return false;
8352 }
8353
8354 VendorItemData const* vItems = isTemplate ? GetNpcVendorTemplateItemList(vendor_entry) : GetNpcVendorItemList(vendor_entry);
8355 VendorItemData const* tItems = isTemplate ? NULL : GetNpcVendorTemplateItemList(vendor_entry);
8356
8357 if (!vItems && !tItems)
8358 return true; // later checks for non-empty lists
8359
8360 if (vItems && vItems->FindItem(item_id))
8361 {
8362 if (pl)
8363 ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id);
8364 else
8365 sLog.outErrorDb("Table `%s` has duplicate items %u for %s %u, ignoring",
8366 tableName, item_id, idStr, vendor_entry);
8367 sLog.outErrorDb("Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignoring",
8368 item_id, vendor_entry);
8369 return false;
8370 }
8371
8372 if (!isTemplate)
8373 {
8374 if (tItems && tItems->GetItem(item_id))
8375 {
8376 if (pl)
8377 ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id);
8378 else
8379 {
8380 if (!cInfo->vendorId)
8381 sLog.outErrorDb("Table `%s` has duplicate items %u for %s %u, ignoring",
8382 tableName, item_id, idStr, vendor_entry);
8383 else
8384 sLog.outErrorDb("Table `%s` has duplicate items %u for %s %u (or possible in vendor template %u), ignoring",
8385 tableName, item_id, idStr, vendor_entry, cInfo->vendorId);
8386 }
8387 return false;
8388 }
8389 }
8390
8391 /* uint32 countItems = vItems ? vItems->GetItemCount() : 0;
8392 countItems += tItems ? tItems->GetItemCount() : 0;
8393
8394 if (countItems >= MAX_VENDOR_ITEMS)
8395 {
8396 if (pl)
8397 ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
8398 else
8399 sLog.outErrorDb("Table `%s` has too many items (%u >= %i) for %s %u, ignoring",
8400 tableName, countItems, MAX_VENDOR_ITEMS, idStr, vendor_entry);
8401 return false;
8402 }*/
8403
8404 return true;
8405}
8406
8407void ObjectMgr::AddGroup(Group* group)
8408{
8409 mGroupMap[group->GetId()] = group ;
8410}
8411
8412void ObjectMgr::RemoveGroup(Group* group)
8413{
8414 mGroupMap.erase(group->GetId());
8415}
8416
8417// Functions for scripting access
8418bool LoadMangosStrings(DatabaseType& db, char const* table, int32 start_value, int32 end_value, bool extra_content)
8419{
8420 // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
8421 // start/end reversed for negative values
8422 if (start_value > MAX_NOSTALRIUS_STRING_ID || end_value >= start_value)
8423 {
8424 sLog.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.", table, start_value, end_value + 1);
8425 return false;
8426 }
8427
8428 return sObjectMgr.LoadMangosStrings(db, table, start_value, end_value, extra_content);
8429}
8430
8431CreatureInfo const* GetCreatureTemplateStore(uint32 entry)
8432{
8433 return sCreatureStorage.LookupEntry<CreatureInfo>(entry);
8434}
8435
8436Quest const* GetQuestTemplateStore(uint32 entry)
8437{
8438 return sObjectMgr.GetQuestTemplate(entry);
8439}
8440
8441bool FindCreatureData::operator()(CreatureDataPair const& dataPair)
8442{
8443 // skip wrong entry ids
8444 if (i_id && dataPair.second.id != i_id)
8445 return false;
8446
8447 if (!i_anyData)
8448 i_anyData = &dataPair;
8449
8450 // without player we can't find more stricted cases, so use fouded
8451 if (!i_player)
8452 return true;
8453
8454 // skip diff. map cases
8455 if (dataPair.second.mapid != i_player->GetMapId())
8456 return false;
8457
8458 float new_dist = i_player->GetDistance2d(dataPair.second.posX, dataPair.second.posY);
8459
8460 if (!i_mapData || new_dist < i_mapDist)
8461 {
8462 i_mapData = &dataPair;
8463 i_mapDist = new_dist;
8464 }
8465
8466 // skip not spawned (in any state),
8467 uint16 pool_id = sPoolMgr.IsPartOfAPool<Creature>(dataPair.first);
8468 if (pool_id && !i_player->GetMap()->GetPersistentState()->IsSpawnedPoolObject<Creature>(dataPair.first))
8469 return false;
8470
8471 if (!i_spawnedData || new_dist < i_spawnedDist)
8472 {
8473 i_spawnedData = &dataPair;
8474 i_spawnedDist = new_dist;
8475 }
8476
8477 return false;
8478}
8479
8480CreatureDataPair const* FindCreatureData::GetResult() const
8481{
8482 if (i_spawnedData)
8483 return i_spawnedData;
8484
8485 if (i_mapData)
8486 return i_mapData;
8487
8488 return i_anyData;
8489}
8490
8491bool FindGOData::operator()(GameObjectDataPair const& dataPair)
8492{
8493 // skip wrong entry ids
8494 if (i_id && dataPair.second.id != i_id)
8495 return false;
8496
8497 if (!i_anyData)
8498 i_anyData = &dataPair;
8499
8500 // without player we can't find more stricted cases, so use fouded
8501 if (!i_player)
8502 return true;
8503
8504 // skip diff. map cases
8505 if (dataPair.second.mapid != i_player->GetMapId())
8506 return false;
8507
8508 float new_dist = i_player->GetDistance2d(dataPair.second.posX, dataPair.second.posY);
8509
8510 if (!i_mapData || new_dist < i_mapDist)
8511 {
8512 i_mapData = &dataPair;
8513 i_mapDist = new_dist;
8514 }
8515
8516 // skip not spawned (in any state)
8517 uint16 pool_id = sPoolMgr.IsPartOfAPool<GameObject>(dataPair.first);
8518 if (pool_id && !i_player->GetMap()->GetPersistentState()->IsSpawnedPoolObject<GameObject>(dataPair.first))
8519 return false;
8520
8521 if (!i_spawnedData || new_dist < i_spawnedDist)
8522 {
8523 i_spawnedData = &dataPair;
8524 i_spawnedDist = new_dist;
8525 }
8526
8527 return false;
8528}
8529
8530GameObjectDataPair const* FindGOData::GetResult() const
8531{
8532 if (i_mapData)
8533 return i_mapData;
8534
8535 if (i_spawnedData)
8536 return i_spawnedData;
8537
8538 return i_anyData;
8539}
8540
8541uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
8542{
8543 GameObjectInfo const* goinfo = GetGameObjectInfo(entry);
8544 if (!goinfo)
8545 return 0;
8546
8547 Map* map = const_cast<Map*>(sMapMgr.FindMap(mapId));
8548 if (!map)
8549 return 0;
8550
8551 uint32 guid = map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT);
8552 GameObjectData& data = NewGOData(guid);
8553 data.id = entry;
8554 data.mapid = mapId;
8555 data.posX = x;
8556 data.posY = y;
8557 data.posZ = z;
8558 data.orientation = o;
8559 data.rotation0 = rotation0;
8560 data.rotation1 = rotation1;
8561 data.rotation2 = rotation2;
8562 data.rotation3 = rotation3;
8563 data.spawntimesecs = spawntimedelay;
8564 data.animprogress = 100;
8565 data.go_state = GO_STATE_READY;
8566 data.spawnFlags = 0;
8567
8568 AddGameobjectToGrid(guid, &data);
8569
8570 // Spawn if necessary (loaded grids only)
8571 // We use spawn coords to spawn
8572 if (!map->Instanceable() && map->IsLoaded(x, y))
8573 {
8574 GameObject *go = new GameObject;
8575 if (!go->LoadFromDB(guid, map))
8576 {
8577 sLog.outError("AddGOData: cannot add gameobject entry %u to map", entry);
8578 delete go;
8579 return 0;
8580 }
8581 map->Add(go);
8582 }
8583
8584 DEBUG_LOG("AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, x, y, z, o);
8585
8586 return guid;
8587}
8588
8589bool ObjectMgr::MoveCreData(uint32 guid, uint32 mapId, const Position& pos)
8590{
8591 CreatureData& data = NewOrExistCreatureData(guid);
8592 if (!data.id)
8593 return false;
8594
8595 RemoveCreatureFromGrid(guid, &data);
8596 if (data.posX == pos.x && data.posY == pos.y && data.posZ == pos.z)
8597 return true;
8598 data.posX = pos.x;
8599 data.posY = pos.y;
8600 data.posZ = pos.z;
8601 data.orientation = pos.o;
8602 AddCreatureToGrid(guid, &data);
8603
8604 // Spawn if necessary (loaded grids only)
8605 if (Map* map = const_cast<Map*>(sMapMgr.FindMap(mapId)))
8606 {
8607 // We use spawn coords to spawn
8608 if (!map->Instanceable() && map->IsLoaded(data.posX, data.posY))
8609 {
8610 Creature *creature = new Creature;
8611 if (!creature->LoadFromDB(guid, map))
8612 {
8613 sLog.outError("AddCreature: cannot add creature entry %u to map", guid);
8614 delete creature;
8615 return false;
8616 }
8617 map->Add(creature);
8618 }
8619 }
8620 return true;
8621}
8622
8623uint32 ObjectMgr::AddCreData(uint32 entry, uint32 /*team*/, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay)
8624{
8625 CreatureInfo const *cInfo = GetCreatureTemplate(entry);
8626 if (!cInfo)
8627 return 0;
8628 Map* map = const_cast<Map*>(sMapMgr.FindMap(mapId));
8629 if (!map)
8630 return 0;
8631
8632 uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
8633
8634 uint32 guid = map->GenerateLocalLowGuid(HIGHGUID_UNIT);
8635 CreatureData& data = NewOrExistCreatureData(guid);
8636 data.id = entry;
8637 data.mapid = mapId;
8638 data.equipmentId = cInfo->equipmentId;
8639 data.posX = x;
8640 data.posY = y;
8641 data.posZ = z;
8642 data.orientation = o;
8643 data.spawntimesecs = spawntimedelay;
8644 data.spawndist = 0;
8645 data.currentwaypoint = 0;
8646 data.curhealth = cInfo->maxhealth;
8647 data.curmana = cInfo->maxmana;
8648 data.is_dead = false;
8649 data.movementType = cInfo->MovementType;
8650
8651
8652 AddCreatureToGrid(guid, &data);
8653
8654 // Spawn if necessary (loaded grids only)
8655 // We use spawn coords to spawn
8656 if (!map->Instanceable() && !map->IsRemovalGrid(x, y))
8657 {
8658 Creature* creature = new Creature;
8659 if (!creature->LoadFromDB(guid, map))
8660 {
8661 sLog.outError("AddCreature: cannot add creature entry %u to map", entry);
8662 delete creature;
8663 return 0;
8664 }
8665 map->Add(creature);
8666 }
8667
8668 return guid;
8669}
8670
8671
8672void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, Team team, bool inDB)
8673{
8674 GraveYardMap::iterator graveLow = mGraveYardMap.lower_bound(zoneId);
8675 GraveYardMap::iterator graveUp = mGraveYardMap.upper_bound(zoneId);
8676 if (graveLow == graveUp)
8677 {
8678 //sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
8679 return;
8680 }
8681
8682 bool found = false;
8683
8684 GraveYardMap::iterator itr;
8685
8686 for (itr = graveLow; itr != graveUp; ++itr)
8687 {
8688 GraveYardData & data = itr->second;
8689
8690 // skip not matching safezone id
8691 if (data.safeLocId != id)
8692 continue;
8693
8694 // skip enemy faction graveyard at same map (normal area, city, or battleground)
8695 // team == 0 case can be at call from .neargrave
8696 if (data.team != 0 && team != 0 && data.team != team)
8697 continue;
8698
8699 found = true;
8700 break;
8701 }
8702
8703 // no match, return
8704 if (!found)
8705 return;
8706
8707 // remove from links
8708 mGraveYardMap.erase(itr);
8709
8710 // remove link from DB
8711 if (inDB)
8712 WorldDatabase.PExecute("DELETE FROM game_graveyard_zone WHERE id = '%u' AND ghost_zone = '%u' AND faction = '%u'", id, zoneId, team);
8713
8714 return;
8715}
8716
8717void ObjectMgr::LoadFactionChangeReputations()
8718{
8719 factionchange_reputations.clear();
8720 QueryResult* result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
8721
8722 if (!result)
8723 {
8724 sLog.outString(">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
8725 return;
8726 }
8727
8728 uint32 count = 0;
8729
8730 do
8731 {
8732 Field *fields = result->Fetch();
8733
8734 uint32 alliance = fields[0].GetUInt32();
8735 uint32 horde = fields[1].GetUInt32();
8736
8737 if (!sFactionStore.LookupEntry(alliance) || !sFactionStore.LookupEntry(horde))
8738 {
8739 sLog.outErrorDb("Couple %u/%u erreur. Sort inexistant. Supprime de la DB", alliance, horde);
8740 WorldDatabase.PExecute("DELETE FROM player_factionchange_reputations WHERE alliance_id = %u AND horde_id = %u", alliance, horde);
8741 }
8742 else
8743 factionchange_reputations[alliance] = horde;
8744
8745 ++count;
8746 }
8747 while (result->NextRow());
8748
8749 delete result;
8750 sLog.outString(">> Loaded %u reputations changes.", count);
8751}
8752
8753
8754void ObjectMgr::LoadFactionChangeSpells()
8755{
8756 factionchange_spells.clear();
8757 QueryResult* result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
8758
8759 if (!result)
8760 {
8761 sLog.outErrorDb(">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
8762 return;
8763 }
8764
8765 uint32 count = 0;
8766
8767 do
8768 {
8769 Field *fields = result->Fetch();
8770
8771 uint32 alliance = fields[0].GetUInt32();
8772 uint32 horde = fields[1].GetUInt32();
8773
8774 if (!sSpellMgr.GetSpellEntry(alliance) || !sSpellMgr.GetSpellEntry(horde))
8775 {
8776 sLog.outErrorDb("Couple %u/%u erreur. Sort inexistant. Supprime de la DB", alliance, horde);
8777 WorldDatabase.PExecute("DELETE FROM player_factionchange_spells WHERE alliance_id = %u AND horde_id = %u", alliance, horde);
8778 }
8779 else
8780 factionchange_spells[alliance] = horde;
8781
8782 ++count;
8783 }
8784 while (result->NextRow());
8785
8786 delete result;
8787 sLog.outString(">> %u equivalences de sorts Alliance/Horde chargees", count);
8788}
8789
8790
8791void ObjectMgr::LoadFactionChangeItems()
8792{
8793 factionchange_items.clear();
8794 QueryResult* result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
8795
8796 if (!result)
8797 {
8798 sLog.outErrorDb(">> Loaded 0 faction change item pairs. DB table `player_factionchange_spells` is empty.");
8799 return;
8800 }
8801
8802 uint32 count = 0;
8803
8804 do
8805 {
8806 Field *fields = result->Fetch();
8807
8808 uint32 alliance = fields[0].GetUInt32();
8809 uint32 horde = fields[1].GetUInt32();
8810
8811 if (!GetItemPrototype(alliance) || !GetItemPrototype(horde))
8812 {
8813 sLog.outErrorDb("Couple %u/%u erreur. Item inexistant. Supprime de la DB", alliance, horde);
8814 WorldDatabase.PExecute("DELETE FROM player_factionchange_items WHERE alliance_id = %u AND horde_id = %u", alliance, horde);
8815 }
8816 else
8817 factionchange_items[alliance] = horde;
8818
8819 ++count;
8820 }
8821 while (result->NextRow());
8822
8823 delete result;
8824 sLog.outString(">> %u equivalences d'items Alliance/Horde chargees", count);
8825}
8826
8827void ObjectMgr::LoadFactionChangeQuests()
8828{
8829 factionchange_quests.clear();
8830 QueryResult* result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
8831
8832 if (!result)
8833 {
8834 sLog.outErrorDb(">> Loaded 0 change quests pairs. DB table `player_factionchange_quests` is empty.");
8835 return;
8836 }
8837
8838 uint32 count = 0;
8839
8840 do
8841 {
8842 Field *fields = result->Fetch();
8843
8844 uint32 alliance = fields[0].GetUInt32();
8845 uint32 horde = fields[1].GetUInt32();
8846
8847 if (!GetQuestTemplate(alliance) || !GetQuestTemplate(horde))
8848 {
8849 sLog.outErrorDb("Couple %u/%u erreur. Quete inexistante. Supprime de la DB", alliance, horde);
8850 WorldDatabase.PExecute("DELETE FROM player_factionchange_quests WHERE alliance_id = %u AND horde_id = %u", alliance, horde);
8851 }
8852 else
8853 factionchange_quests[alliance] = horde;
8854
8855 ++count;
8856 }
8857 while (result->NextRow());
8858
8859 delete result;
8860 sLog.outString(">> %u equivalences de quetes Alliance/Horde chargees", count);
8861}
8862
8863void ObjectMgr::LoadFactionChangeMounts()
8864{
8865 factionchange_mounts.clear();
8866 QueryResult* result = WorldDatabase.Query("SELECT RaceId, MountNum, ItemEntry FROM player_factionchange_mounts");
8867
8868 if (!result)
8869 {
8870 sLog.outErrorDb(">> Loaded 0 entries. The table `player_factionchange_mounts` is empty.");
8871 return;
8872 }
8873
8874 uint32 count = 0;
8875
8876 do
8877 {
8878 Field *fields = result->Fetch();
8879
8880 uint8 RaceId = fields[0].GetUInt8();
8881 uint8 MountNum = fields[1].GetUInt8();
8882 uint32 ItemEntry = fields[2].GetUInt32();
8883
8884 if (!GetItemPrototype(ItemEntry))
8885 {
8886 sLog.outErrorDb("Couple %u/%u/%u erreur. Quete inexistante. Supprime de la DB", RaceId, MountNum, ItemEntry);
8887 //WorldDatabase.PExecute("DELETE FROM player_factionchange_mounts WHERE RaceId = %u AND MountNum = %u", RaceId, MountNum);
8888 }
8889 else
8890 {
8891 FactionChangeMountData data;
8892 data.RaceId = Races(RaceId);
8893 data.MountNum = MountNum;
8894 data.ItemEntry = ItemEntry;
8895 factionchange_mounts.push_back(data);
8896 ++count;
8897 }
8898 }
8899 while (result->NextRow());
8900
8901 delete result;
8902 sLog.outString(">> %u montures de races chargees", count);
8903}
8904void ObjectMgr::RestoreDeletedItems()
8905{
8906 QueryResult* result = CharacterDatabase.Query("SELECT id, player_guid, item_entry, stack_count FROM character_deleted_items");
8907
8908 if (!result)
8909 {
8910 sLog.outString();
8911 sLog.outString(">> Restored 0 prevously deleted items.");
8912 return;
8913 }
8914
8915 uint32 count = 0;
8916
8917 do
8918 {
8919 Field *fields = result->Fetch();
8920
8921 uint32 id = fields[0].GetUInt32();
8922 uint32 memberGuidlow = fields[1].GetUInt32();
8923 uint32 itemEntry = fields[2].GetUInt32();
8924 uint32 stackCount = fields[3].GetUInt32();
8925
8926 if (ItemPrototype const* itemProto = GetItemPrototype(itemEntry))
8927 {
8928 ObjectGuid memberGuid = ObjectGuid(HIGHGUID_PLAYER, memberGuidlow);
8929 Player* pPlayer = ObjectAccessor::FindPlayerNotInWorld(memberGuid);
8930
8931 if (Item* restoredItem = Item::CreateItem(itemEntry, stackCount ? stackCount : 1, pPlayer ? pPlayer : (const Player *) 0))
8932 {
8933 // save new item before send
8934 restoredItem->SaveToDB();
8935
8936 // subject
8937 std::string subject = itemProto->Name1;
8938
8939 // text
8940 std::string textFormat = GetMangosString(LANG_RESTORED_ITEM, LOCALE_enUS);
8941
8942 MailDraft(subject, textFormat)
8943 .AddItem(restoredItem)
8944 .SendMailTo(MailReceiver(memberGuid), MailSender(MAIL_NORMAL, memberGuid.GetCounter(), MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED, 0, 30 * DAY);
8945
8946 CharacterDatabase.PExecute("DELETE FROM character_deleted_items WHERE id = %u", id);
8947
8948 count++;
8949 }
8950 }
8951 } while (result->NextRow());
8952
8953 delete result;
8954 sLog.outString();
8955 sLog.outString(">> Restored %u previously deleted items to players.", count);
8956}
8957uint32 GetRealMountEntry(uint32 entry)
8958{
8959 switch (entry)
8960 {
8961 case 50101:
8962 return 1132;
8963 case 50102:
8964 return 8591;
8965 case 50103:
8966 return 15277;
8967 case 50104:
8968 return 13332;
8969 case 50105:
8970 return 5655;
8971 case 50106:
8972 return 5873;
8973 case 50107:
8974 return 8631;
8975 case 50108:
8976 return 8595;
8977 }
8978 return entry;
8979}
8980
8981bool ObjectMgr::GetMountDataByEntry(uint32 itemEntry, Races& race, uint8& mountNum) const
8982{
8983 // Mount custom Nostalrius encore dans la DB.
8984 itemEntry = GetRealMountEntry(itemEntry);
8985 for (FactionChangeMountsData::const_iterator it = factionchange_mounts.begin(); it != factionchange_mounts.end(); ++it)
8986 {
8987 if (it->ItemEntry == itemEntry)
8988 {
8989 race = it->RaceId;
8990 mountNum = it->MountNum;
8991 return true;
8992 }
8993 }
8994 return false;
8995}
8996
8997uint32 ObjectMgr::GetMountItemEntry(Races race, uint8 num) const
8998{
8999 for (FactionChangeMountsData::const_iterator it = factionchange_mounts.begin(); it != factionchange_mounts.end(); ++it)
9000 if (it->RaceId == race && it->MountNum == num)
9001 return it->ItemEntry;
9002
9003 return false;
9004}
9005
9006uint32 ObjectMgr::GetRandomMountForRace(Races race) const
9007{
9008 // 1- Compter le nombre total de montures de cette race
9009 uint32 count = 0;
9010 for (FactionChangeMountsData::const_iterator it = factionchange_mounts.begin(); it != factionchange_mounts.end(); ++it)
9011 if (it->RaceId == race)
9012 ++count;
9013 // 2- Quelques verifs / Generer index aleatoire
9014 if (!count)
9015 return 0;
9016 count -= urand(0, count - 1);
9017 // 3- Reboucler
9018 for (FactionChangeMountsData::const_iterator it = factionchange_mounts.begin(); it != factionchange_mounts.end(); ++it)
9019 {
9020 if (it->RaceId == race)
9021 {
9022 if (count == 0)
9023 return it->ItemEntry;
9024 --count;
9025 }
9026 }
9027 // 4- Un soucis ... ?
9028 return 0;
9029}
9030
9031Races ObjectMgr::GetOppositeRace(Races origRace) const
9032{
9033 switch (origRace)
9034 {
9035 case RACE_HUMAN:
9036 return RACE_ORC;
9037 case RACE_ORC:
9038 return RACE_HUMAN;
9039 case RACE_DWARF:
9040 return RACE_TROLL;
9041 case RACE_NIGHTELF:
9042 return RACE_UNDEAD;
9043 case RACE_UNDEAD:
9044 return RACE_NIGHTELF;
9045 case RACE_TAUREN:
9046 return RACE_TAUREN;
9047 case RACE_GNOME:
9048 return RACE_TAUREN;
9049 case RACE_TROLL:
9050 return RACE_DWARF;
9051 default:
9052 return RACE_GOBLIN;
9053 }
9054}
9055
9056
9057void ObjectMgr::LoadConditions()
9058{
9059 SQLWorldLoader loader;
9060 loader.Load(sConditionStorage);
9061
9062 for (uint32 i = 0; i < sConditionStorage.GetMaxEntry(); ++i)
9063 {
9064 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(i);
9065 if (!condition)
9066 continue;
9067
9068 if (!condition->IsValid())
9069 {
9070 sLog.outErrorDb("ObjectMgr::LoadConditions: invalid condition_entry %u, skip", i);
9071 sConditionStorage.EraseEntry(i);
9072 continue;
9073 }
9074 }
9075
9076 sLog.outString(">> Loaded %u Condition definitions", sConditionStorage.GetRecordCount());
9077 sLog.outString();
9078}
9079
9080
9081// Check if a player meets condition conditionId
9082bool ObjectMgr::IsPlayerMeetToCondition(uint16 conditionId, Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const
9083{
9084 if (const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(conditionId))
9085 return condition->Meets(pPlayer, map, source, conditionSourceType);
9086
9087 return false;
9088}
9089
9090char const* conditionSourceToStr[] =
9091{
9092 "loot system",
9093 "referencing loot",
9094 "gossip menu",
9095 "gossip menu option",
9096 "event AI",
9097 "hardcoded",
9098 "vendor's item check",
9099 "spell_area check",
9100 "DBScript engine"
9101};
9102
9103// Checks if player meets the condition
9104bool PlayerCondition::Meets(Player const* player, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const
9105{
9106 DEBUG_LOG("Condition-System: Check condition %u, type %i - called from %s with params plr: %s, map %i, src %s",
9107 m_entry, m_condition, conditionSourceToStr[conditionSourceType], player ? player->GetGuidStr().c_str() : "<NULL>", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "<NULL>");
9108
9109 if (!CheckParamRequirements(player, map, source, conditionSourceType))
9110 return false;
9111
9112 switch (m_condition)
9113 {
9114 case CONDITION_NOT:
9115 // Checked on load
9116 return !sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player, map, source, conditionSourceType);
9117 case CONDITION_OR:
9118 // Checked on load
9119 return sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player, map, source, conditionSourceType) || sConditionStorage.LookupEntry<PlayerCondition>(m_value2)->Meets(player, map, source, conditionSourceType);
9120 case CONDITION_AND:
9121 // Checked on load
9122 return sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player, map, source, conditionSourceType) && sConditionStorage.LookupEntry<PlayerCondition>(m_value2)->Meets(player, map, source, conditionSourceType);
9123 case CONDITION_NONE:
9124 return true; // empty condition, always met
9125 case CONDITION_AURA:
9126 return player->HasAura(m_value1, SpellEffectIndex(m_value2));
9127 case CONDITION_ITEM:
9128 return player->HasItemCount(m_value1, m_value2);
9129 case CONDITION_ITEM_EQUIPPED:
9130 return player->HasItemWithIdEquipped(m_value1, 1);
9131 case CONDITION_AREAID:
9132 {
9133 uint32 zone, area;
9134 WorldObject const* searcher = source ? source : player;
9135 searcher->GetZoneAndAreaId(zone, area);
9136 return (zone == m_value1 || area == m_value1) == (m_value2 == 0);
9137 }
9138 case CONDITION_REPUTATION_RANK_MIN:
9139 {
9140 FactionEntry const* faction = sFactionStore.LookupEntry(m_value1);
9141 return faction && player->GetReputationMgr().GetRank(faction) >= ReputationRank(m_value2);
9142 }
9143 case CONDITION_TEAM:
9144 return uint32(player->GetTeam()) == m_value1;
9145 case CONDITION_SKILL:
9146 return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) >= m_value2;
9147 case CONDITION_QUESTREWARDED:
9148 return player->GetQuestRewardStatus(m_value1);
9149 case CONDITION_QUESTTAKEN:
9150 return player->IsCurrentQuest(m_value1, m_value2);
9151 case CONDITION_AD_COMMISSION_AURA:
9152 {
9153 Unit::SpellAuraHolderMap const& auras = player->GetSpellAuraHolderMap();
9154 for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
9155 if ((itr->second->GetSpellProto()->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED || itr->second->GetSpellProto()->Attributes & SPELL_ATTR_IS_ABILITY) && itr->second->GetSpellProto()->SpellVisual == 3580)
9156 return true;
9157 return false;
9158 }
9159 case CONDITION_NO_AURA:
9160 return !player->HasAura(m_value1, SpellEffectIndex(m_value2));
9161 case CONDITION_ACTIVE_GAME_EVENT:
9162 return sGameEventMgr.IsActiveEvent(m_value1);
9163 case CONDITION_AREA_FLAG:
9164 {
9165 WorldObject const* searcher = source ? source : player;
9166 if (const auto *pAreaEntry = AreaEntry::GetById(searcher->GetAreaId()))
9167 if ((!m_value1 || (pAreaEntry->Flags & m_value1)) && (!m_value2 || !(pAreaEntry->Flags & m_value2)))
9168 return true;
9169
9170 return false;
9171 }
9172 case CONDITION_RACE_CLASS:
9173 if ((!m_value1 || (player->getRaceMask() & m_value1)) && (!m_value2 || (player->getClassMask() & m_value2)))
9174 return true;
9175 return false;
9176 case CONDITION_LEVEL:
9177 {
9178 switch (m_value2)
9179 {
9180 case 0:
9181 return player->getLevel() == m_value1;
9182 case 1:
9183 return player->getLevel() >= m_value1;
9184 case 2:
9185 return player->getLevel() <= m_value1;
9186 }
9187 return false;
9188 }
9189 case CONDITION_NOITEM:
9190 return !player->HasItemCount(m_value1, m_value2);
9191 case CONDITION_SPELL:
9192 {
9193 switch (m_value2)
9194 {
9195 case 0:
9196 return player->HasSpell(m_value1);
9197 case 1:
9198 return !player->HasSpell(m_value1);
9199 }
9200 return false;
9201 }
9202 case CONDITION_INSTANCE_SCRIPT:
9203 {
9204 if (!map)
9205 map = player ? player->GetMap() : source->GetMap();
9206
9207 if (InstanceData const* data = map->GetInstanceData())
9208 return data->CheckConditionCriteriaMeet(player, m_value1, source, m_value2);
9209 return false;
9210 }
9211 case CONDITION_QUESTAVAILABLE:
9212 {
9213 return player->CanTakeQuest(sObjectMgr.GetQuestTemplate(m_value1), false);
9214 }
9215 case CONDITION_RESERVED_1:
9216 case CONDITION_RESERVED_2:
9217 case CONDITION_RESERVED_3:
9218 case CONDITION_RESERVED_4:
9219 return false;
9220 case CONDITION_QUEST_NONE:
9221 {
9222 if (!player->IsCurrentQuest(m_value1) && !player->GetQuestRewardStatus(m_value1))
9223 return true;
9224 return false;
9225 }
9226 case CONDITION_ITEM_WITH_BANK:
9227 return player->HasItemCount(m_value1, m_value2, true);
9228 case CONDITION_NOITEM_WITH_BANK:
9229 return !player->HasItemCount(m_value1, m_value2, true);
9230 case CONDITION_NOT_ACTIVE_GAME_EVENT:
9231 return !sGameEventMgr.IsActiveEvent(m_value1);
9232 case CONDITION_ACTIVE_HOLIDAY:
9233 return sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1));
9234 case CONDITION_NOT_ACTIVE_HOLIDAY:
9235 return !sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1));
9236 case CONDITION_LEARNABLE_ABILITY:
9237 {
9238 // Already know the spell
9239 if (player->HasSpell(m_value1))
9240 return false;
9241
9242 // If item defined, check if player has the item already.
9243 if (m_value2)
9244 {
9245 // Hard coded item count. This should be ok, since the intention with this condition is to have
9246 // a all-in-one check regarding items that learn some ability (primary/secondary tradeskills).
9247 // Commonly, items like this is unique and/or are not expected to be obtained more than once.
9248 if (player->HasItemCount(m_value2, 1, true))
9249 return false;
9250 }
9251
9252 bool isSkillOk = false;
9253
9254 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(m_value1);
9255
9256 for (SkillLineAbilityMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
9257 {
9258 const SkillLineAbilityEntry* skillInfo = itr->second;
9259
9260 if (!skillInfo)
9261 continue;
9262
9263 // doesn't have skill
9264 if (!player->HasSkill(skillInfo->skillId))
9265 return false;
9266
9267 // doesn't match class
9268 if (skillInfo->classmask && (skillInfo->classmask & player->getClassMask()) == 0)
9269 return false;
9270
9271 // doesn't match race
9272 if (skillInfo->racemask && (skillInfo->racemask & player->getRaceMask()) == 0)
9273 return false;
9274
9275 // skill level too low
9276 if (skillInfo->min_value > player->GetSkillValue(skillInfo->skillId))
9277 return false;
9278
9279 isSkillOk = true;
9280 break;
9281 }
9282
9283 if (isSkillOk)
9284 return true;
9285
9286 return false;
9287 }
9288 case CONDITION_SKILL_BELOW:
9289 {
9290 if (m_value2 == 1)
9291 return !player->HasSkill(m_value1);
9292 else
9293 return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) < m_value2;
9294 }
9295 case CONDITION_REPUTATION_RANK_MAX:
9296 {
9297 FactionEntry const* faction = sFactionStore.LookupEntry(m_value1);
9298 return faction && player->GetReputationMgr().GetRank(faction) <= ReputationRank(m_value2);
9299 }
9300 case CONDITION_SOURCE_AURA:
9301 {
9302 if (!source->isType(TYPEMASK_UNIT))
9303 {
9304 sLog.outErrorDb("CONDITION_SOURCE_AURA (entry %u) is used for non unit source (source %s) by %s", m_entry, source->GetGuidStr().c_str(), player->GetGuidStr().c_str());
9305 return false;
9306 }
9307 return ((Unit*)source)->HasAura(m_value1, SpellEffectIndex(m_value2));
9308 }
9309 case CONDITION_LAST_WAYPOINT:
9310 {
9311 if (source->GetTypeId() != TYPEID_UNIT)
9312 {
9313 sLog.outErrorDb("CONDITION_LAST_WAYPOINT (entry %u) is used for non creature source (source %s) by %s", m_entry, source->GetGuidStr().c_str(), player->GetGuidStr().c_str());
9314 return false;
9315 }
9316 uint32 lastReachedWp = ((Creature*)source)->GetMotionMaster()->getLastReachedWaypoint();
9317 switch (m_value2)
9318 {
9319 case 0:
9320 return m_value1 == lastReachedWp;
9321 case 1:
9322 return m_value1 <= lastReachedWp;
9323 case 2:
9324 return m_value1 > lastReachedWp;
9325 }
9326 return false;
9327 }
9328 case CONDITION_GENDER:
9329 return player->getGender() == m_value1;
9330 case CONDITION_DEAD_OR_AWAY:
9331 switch (m_value1)
9332 {
9333 case 0: // Player dead or out of range
9334 return !player || !player->isAlive() || (m_value2 && source && !source->IsWithinDistInMap(player, m_value2));
9335 case 1: // All players in Group dead or out of range
9336 if (!player)
9337 return true;
9338 if (Group* grp = ((Player*)player)->GetGroup())
9339 {
9340 for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
9341 {
9342 Player* pl = itr->getSource();
9343 if (pl && pl->isAlive() && !pl->isGameMaster() && (!m_value2 || !source || source->IsWithinDistInMap(pl, m_value2)))
9344 return false;
9345 }
9346 return true;
9347 }
9348 else
9349 return !player->isAlive() || (m_value2 && source && !source->IsWithinDistInMap(player, m_value2));
9350 case 2: // All players in instance dead or out of range
9351 for (Map::PlayerList::const_iterator itr = map->GetPlayers().begin(); itr != map->GetPlayers().end(); ++itr)
9352 {
9353 Player const* plr = itr->getSource();
9354 if (plr && plr->isAlive() && !plr->isGameMaster() && (!m_value2 || !source || source->IsWithinDistInMap(plr, m_value2)))
9355 return false;
9356 }
9357 return true;
9358 case 3: // Creature source is dead
9359 return !source || source->GetTypeId() != TYPEID_UNIT || !((Unit*)source)->isAlive();
9360 }
9361 case CONDITION_WOW_PATCH:
9362 {
9363 switch (m_value2)
9364 {
9365 case 0:
9366 return sWorld.GetWowPatch() == m_value1;
9367 case 1:
9368 return sWorld.GetWowPatch() >= m_value1;
9369 case 2:
9370 return sWorld.GetWowPatch() <= m_value1;
9371 }
9372 return false;
9373 }
9374 case CONDITION_NPC_ENTRY:
9375 {
9376 switch (m_value2)
9377 {
9378 case 0:
9379 return source->GetEntry() != m_value1;
9380 case 1:
9381 return source->GetEntry() == m_value1;
9382 }
9383
9384 return false;
9385 }
9386 case CONDITION_WAR_EFFORT_STAGE:
9387 {
9388 uint32 stage = sObjectMgr.GetSavedVariable(VAR_WE_STAGE, 0);
9389 switch (m_value2)
9390 {
9391 case 0:
9392 return stage >= m_value1;
9393 case 1:
9394 return stage == m_value1;
9395 case 2:
9396 return stage <= m_value1;
9397 }
9398
9399 return false;
9400 }
9401 default:
9402 return false;
9403 }
9404}
9405
9406// Checks if the patch is valid
9407bool PlayerCondition::CheckPatch() const
9408{
9409 if (m_condition == CONDITION_WOW_PATCH)
9410 {
9411 switch (m_value2)
9412 {
9413 case 0:
9414 return sWorld.GetWowPatch() == m_value1;
9415 case 1:
9416 return sWorld.GetWowPatch() >= m_value1;
9417 case 2:
9418 return sWorld.GetWowPatch() <= m_value1;
9419 }
9420 return false;
9421 }
9422 return true;
9423}
9424
9425// Which params must be provided to a Condition
9426bool PlayerCondition::CheckParamRequirements(Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const
9427{
9428 switch (m_condition)
9429 {
9430 case CONDITION_NOT:
9431 case CONDITION_AND:
9432 case CONDITION_OR:
9433 case CONDITION_NONE:
9434 case CONDITION_ACTIVE_GAME_EVENT:
9435 case CONDITION_NOT_ACTIVE_GAME_EVENT:
9436 case CONDITION_ACTIVE_HOLIDAY:
9437 case CONDITION_NOT_ACTIVE_HOLIDAY:
9438 break;
9439 case CONDITION_AREAID:
9440 case CONDITION_AREA_FLAG:
9441 if (!pPlayer && !source)
9442 {
9443 sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with plr: %s, map %i, src %s",
9444 m_entry, m_condition, conditionSourceToStr[conditionSourceType], pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL");
9445 return false;
9446 }
9447 break;
9448 case CONDITION_INSTANCE_SCRIPT:
9449 if (!pPlayer && !source && !map)
9450 {
9451 sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with plr: %s, map %i, src %s",
9452 m_entry, m_condition, conditionSourceToStr[conditionSourceType], pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL");
9453 return false;
9454 }
9455 break;
9456 case CONDITION_SOURCE_AURA:
9457 case CONDITION_LAST_WAYPOINT:
9458 if (!source)
9459 {
9460 sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with plr: %s, map %i, src %s",
9461 m_entry, m_condition, conditionSourceToStr[conditionSourceType], pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL");
9462 return false;
9463 }
9464 break;
9465 case CONDITION_DEAD_OR_AWAY:
9466 switch (m_value1)
9467 {
9468 case 0: // Player dead or out of range
9469 case 1: // All players in Group dead or out of range
9470 case 2: // All players in instance dead or out of range
9471 if (m_value2 && !source)
9472 {
9473 sLog.outErrorDb("CONDITION_DEAD_OR_AWAY %u - called from %s without source, but source expected for range check", m_entry, conditionSourceToStr[conditionSourceType]);
9474 return false;
9475 }
9476 if (m_value1 != 2)
9477 return true;
9478 // Case 2 (Instance map only)
9479 if (!map && (pPlayer || source))
9480 map = source ? source->GetMap() : pPlayer->GetMap();
9481 if (!map || !map->Instanceable())
9482 {
9483 sLog.outErrorDb("CONDITION_DEAD_OR_AWAY %u (Player in instance case) - called from %s without map param or from non-instanceable map %i", m_entry, conditionSourceToStr[conditionSourceType], map ? map->GetId() : -1);
9484 return false;
9485 }
9486 return true;
9487 case 3: // Creature source is dead
9488 return true;
9489 }
9490 break;
9491 default:
9492 if (!pPlayer)
9493 {
9494 sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with plr: %s, map %i, src %s",
9495 m_entry, m_condition, conditionSourceToStr[conditionSourceType], pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL");
9496 return false;
9497 }
9498 break;
9499 }
9500 return true;
9501}
9502
9503// Verification of condition values validity
9504bool PlayerCondition::IsValid(uint16 entry, ConditionType condition, uint32 value1, uint32 value2)
9505{
9506 switch (condition)
9507 {
9508 case CONDITION_NOT:
9509 {
9510 if (value1 >= entry)
9511 {
9512 sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry, condition, value1);
9513 return false;
9514 }
9515 const PlayerCondition* condition1 = sConditionStorage.LookupEntry<PlayerCondition>(value1);
9516 if (!condition1)
9517 {
9518 sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has value1 %u without proper condition, skipped", entry, condition, value1);
9519 return false;
9520 }
9521 break;
9522 }
9523 case CONDITION_OR:
9524 case CONDITION_AND:
9525 {
9526 if (value1 >= entry)
9527 {
9528 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry, condition, value1);
9529 return false;
9530 }
9531 if (value2 >= entry)
9532 {
9533 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value2 %u, must be lower than entry, skipped", entry, condition, value2);
9534 return false;
9535 }
9536 const PlayerCondition* condition1 = sConditionStorage.LookupEntry<PlayerCondition>(value1);
9537 if (!condition1)
9538 {
9539 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value1 %u without proper condition, skipped", entry, condition, value1);
9540 return false;
9541 }
9542 const PlayerCondition* condition2 = sConditionStorage.LookupEntry<PlayerCondition>(value2);
9543 if (!condition2)
9544 {
9545 sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value2 %u without proper condition, skipped", entry, condition, value2);
9546 return false;
9547 }
9548 break;
9549 }
9550 case CONDITION_AURA:
9551 case CONDITION_SOURCE_AURA:
9552 {
9553 if (!sSpellStore.LookupEntry(value1))
9554 {
9555 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
9556 return false;
9557 }
9558 if (value2 >= MAX_EFFECT_INDEX)
9559 {
9560 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);
9561 return false;
9562 }
9563 break;
9564 }
9565 case CONDITION_ITEM:
9566 case CONDITION_NOITEM:
9567 case CONDITION_ITEM_WITH_BANK:
9568 case CONDITION_NOITEM_WITH_BANK:
9569 {
9570 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value1);
9571 if (!proto)
9572 {
9573 sLog.outErrorDb("Item condition (entry %u, type %u) requires to have non existing item (%u), skipped", entry, condition, value1);
9574 return false;
9575 }
9576
9577 if (value2 < 1)
9578 {
9579 sLog.outErrorDb("Item condition (entry %u, type %u) useless with count < 1, skipped", entry, condition);
9580 return false;
9581 }
9582 break;
9583 }
9584 case CONDITION_ITEM_EQUIPPED:
9585 {
9586 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value1);
9587 if (!proto)
9588 {
9589 sLog.outErrorDb("ItemEquipped condition (entry %u, type %u) requires to have non existing item (%u) equipped, skipped", entry, condition, value1);
9590 return false;
9591 }
9592 break;
9593 }
9594 case CONDITION_AREAID:
9595 {
9596 const auto *areaEntry = AreaEntry::GetById(value1);
9597 if (!areaEntry)
9598 {
9599 sLog.outErrorDb("Zone condition (entry %u, type %u) requires to be in non existing area (%u), skipped", entry, condition, value1);
9600 return false;
9601 }
9602
9603 if (value2 > 1)
9604 {
9605 sLog.outErrorDb("Zone condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2);
9606 return false;
9607 }
9608 break;
9609 }
9610 case CONDITION_REPUTATION_RANK_MIN:
9611 case CONDITION_REPUTATION_RANK_MAX:
9612 {
9613 FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
9614 if (!factionEntry)
9615 {
9616 sLog.outErrorDb("Reputation condition (entry %u, type %u) requires to have reputation non existing faction (%u), skipped", entry, condition, value1);
9617 return false;
9618 }
9619
9620 if (value2 >= MAX_REPUTATION_RANK)
9621 {
9622 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);
9623 return false;
9624 }
9625 break;
9626 }
9627 case CONDITION_TEAM:
9628 {
9629 if (value1 != ALLIANCE && value1 != HORDE)
9630 {
9631 sLog.outErrorDb("Team condition (entry %u, type %u) specifies unknown team (%u), skipped", entry, condition, value1);
9632 return false;
9633 }
9634 break;
9635 }
9636 case CONDITION_SKILL:
9637 case CONDITION_SKILL_BELOW:
9638 {
9639 SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(value1);
9640 if (!pSkill)
9641 {
9642 sLog.outErrorDb("Skill condition (entry %u, type %u) specifies non-existing skill (%u), skipped", entry, condition, value1);
9643 return false;
9644 }
9645 if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue())
9646 {
9647 sLog.outErrorDb("Skill condition (entry %u, type %u) specifies invalid skill value (%u), skipped", entry, condition, value2);
9648 return false;
9649 }
9650 break;
9651 }
9652 case CONDITION_QUESTREWARDED:
9653 case CONDITION_QUESTTAKEN:
9654 case CONDITION_QUESTAVAILABLE:
9655 case CONDITION_QUEST_NONE:
9656 {
9657 Quest const* Quest = sObjectMgr.GetQuestTemplate(value1);
9658 if (!Quest)
9659 {
9660 sLog.outErrorDb("Quest condition (entry %u, type %u) specifies non-existing quest (%u), skipped", entry, condition, value1);
9661 return false;
9662 }
9663
9664 if (value2 && condition != CONDITION_QUESTTAKEN)
9665 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2);
9666 break;
9667 }
9668 case CONDITION_AD_COMMISSION_AURA:
9669 {
9670 if (value1)
9671 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value1 (%u)!", entry, condition, value1);
9672 if (value2)
9673 sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2);
9674 break;
9675 }
9676 case CONDITION_NO_AURA:
9677 {
9678 if (!sSpellStore.LookupEntry(value1))
9679 {
9680 sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
9681 return false;
9682 }
9683 if (value2 > MAX_EFFECT_INDEX)
9684 {
9685 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);
9686 return false;
9687 }
9688 break;
9689 }
9690 case CONDITION_ACTIVE_GAME_EVENT:
9691 case CONDITION_NOT_ACTIVE_GAME_EVENT:
9692 {
9693 if (!sGameEventMgr.IsValidEvent(value1))
9694 {
9695 sLog.outErrorDb("(Not)Active event condition (entry %u, type %u) requires existing event id (%u), skipped", entry, condition, value1);
9696 return false;
9697 }
9698 break;
9699 }
9700 case CONDITION_AREA_FLAG:
9701 {
9702 if (!value1 && !value2)
9703 {
9704 sLog.outErrorDb("Area flag condition (entry %u, type %u) has both values like 0, skipped", entry, condition);
9705 return false;
9706 }
9707 break;
9708 }
9709 case CONDITION_RACE_CLASS:
9710 {
9711 if (!value1 && !value2)
9712 {
9713 sLog.outErrorDb("Race_class condition (entry %u, type %u) has both values like 0, skipped", entry, condition);
9714 return false;
9715 }
9716
9717 if (value1 && !(value1 & RACEMASK_ALL_PLAYABLE))
9718 {
9719 sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid player class %u, skipped", entry, condition, value1);
9720 return false;
9721 }
9722
9723 if (value2 && !(value2 & CLASSMASK_ALL_PLAYABLE))
9724 {
9725 sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid race mask %u, skipped", entry, condition, value2);
9726 return false;
9727 }
9728 break;
9729 }
9730 case CONDITION_LEVEL:
9731 {
9732 if (!value1 || value1 > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
9733 {
9734 sLog.outErrorDb("Level condition (entry %u, type %u)has invalid level %u, skipped", entry, condition, value1);
9735 return false;
9736 }
9737
9738 if (value2 > 2)
9739 {
9740 sLog.outErrorDb("Level condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", entry, condition, value2);
9741 return false;
9742 }
9743
9744 break;
9745 }
9746 case CONDITION_SPELL:
9747 {
9748 if (!sSpellStore.LookupEntry(value1))
9749 {
9750 sLog.outErrorDb("Spell condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1);
9751 return false;
9752 }
9753
9754 if (value2 > 1)
9755 {
9756 sLog.outErrorDb("Spell condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2);
9757 return false;
9758 }
9759
9760 break;
9761 }
9762 case CONDITION_INSTANCE_SCRIPT:
9763 break;
9764 case CONDITION_RESERVED_1:
9765 case CONDITION_RESERVED_2:
9766 case CONDITION_RESERVED_3:
9767 case CONDITION_RESERVED_4:
9768 {
9769 sLog.outErrorDb("Condition (%u) reserved for later versions, skipped", condition);
9770 return false;
9771 }
9772 case CONDITION_ACTIVE_HOLIDAY:
9773 case CONDITION_NOT_ACTIVE_HOLIDAY:
9774 // no way check holidays in pre-3.x
9775 break;
9776 case CONDITION_LEARNABLE_ABILITY:
9777 {
9778 SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(value1);
9779
9780 if (bounds.first == bounds.second)
9781 {
9782 sLog.outErrorDb("Learnable ability conditon (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);
9783 return false;
9784 }
9785
9786 if (value2)
9787 {
9788 ItemPrototype const* proto = ObjectMgr::GetItemPrototype(value2);
9789 if (!proto)
9790 {
9791 sLog.outErrorDb("Learnable ability conditon (entry %u, type %u) has item entry %u defined but item does not exist, skipping.", entry, condition, value2);
9792 return false;
9793 }
9794 }
9795
9796 break;
9797 }
9798 case CONDITION_LAST_WAYPOINT:
9799 {
9800 if (value2 > 2)
9801 {
9802 sLog.outErrorDb("Last Waypoint condition (entry %u, type %u) has an invalid value in value2. (Has %u, supported 0, 1, or 2), skipping.", entry, condition, value2);
9803 return false;
9804 }
9805 break;
9806 }
9807 case CONDITION_GENDER:
9808 {
9809 if (value1 >= GENDER_NONE)
9810 {
9811 sLog.outErrorDb("Gender condition (entry %u, type %u) has an invalid value in value1. (Has %u, must be smaller than %u), skipping.", entry, condition, value1, GENDER_NONE);
9812 return false;
9813 }
9814 break;
9815 }
9816 case CONDITION_DEAD_OR_AWAY:
9817 {
9818 if (value1 >= 4)
9819 {
9820 sLog.outErrorDb("Dead condition (entry %u, type %u) has an invalid value in value1. (Has %u, must be smaller than 4), skipping.", entry, condition, value1);
9821 return false;
9822 }
9823 break;
9824 }
9825 case CONDITION_WOW_PATCH:
9826 {
9827 if (value1 > 10)
9828 {
9829 sLog.outErrorDb("Patch condition (entry %u, type %u) has an invalid value in value1 (must be 0..10), skipping.", entry, condition, value1);
9830 return false;
9831 }
9832 if (value2 > 2)
9833 {
9834 sLog.outErrorDb("Patch condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped.", entry, condition, value2);
9835 return false;
9836 }
9837 break;
9838 }
9839 case CONDITION_NPC_ENTRY:
9840 {
9841 if (!sObjectMgr.GetCreatureTemplate(value1))
9842 {
9843 sLog.outErrorDb("NPC Entry condition (entry %u, type %u) has invalid nonexistent NPC entry %u", entry, condition, value2);
9844 return false;
9845 }
9846 if (value2 < 0 || value2 > 1)
9847 {
9848 sLog.outErrorDb("NPC Entry condition (entry %u, type %u) has invalid criteria %u (must be 0 or 1)", entry, condition, value1);
9849 return false;
9850 }
9851 break;
9852 }
9853 case CONDITION_WAR_EFFORT_STAGE:
9854 {
9855 if (value1 < 0 || value1 > WAR_EFFORT_STAGE_COMPLETE)
9856 {
9857 sLog.outErrorDb("War Effort stage condition condition (entry %u, type %u) has invalid stage %u", entry, condition, value1);
9858 return false;
9859 }
9860 if (value2 < 0 || value2 > 2)
9861 {
9862 sLog.outErrorDb("War Effort stage condition condition (entry %u, type %u) has invalid equality %u", entry, condition, value2);
9863 return false;
9864 }
9865 break;
9866 }
9867 case CONDITION_NONE:
9868 break;
9869 default:
9870 sLog.outErrorDb("Condition entry %u has bad type of %d, skipped ", entry, condition);
9871 return false;
9872 }
9873 return true;
9874}
9875
9876// Check if a condition can be used without providing a player param
9877bool PlayerCondition::CanBeUsedWithoutPlayer(uint16 entry)
9878{
9879 PlayerCondition const* condition = sConditionStorage.LookupEntry<PlayerCondition>(entry);
9880 if (!condition)
9881 return false;
9882
9883 switch (condition->m_condition)
9884 {
9885 case CONDITION_NOT:
9886 return CanBeUsedWithoutPlayer(condition->m_value1);
9887 case CONDITION_AND:
9888 case CONDITION_OR:
9889 return CanBeUsedWithoutPlayer(condition->m_value1) && CanBeUsedWithoutPlayer(condition->m_value2);
9890 case CONDITION_NONE:
9891 case CONDITION_ACTIVE_GAME_EVENT:
9892 case CONDITION_NOT_ACTIVE_GAME_EVENT:
9893 case CONDITION_ACTIVE_HOLIDAY:
9894 case CONDITION_NOT_ACTIVE_HOLIDAY:
9895 case CONDITION_AREAID:
9896 case CONDITION_AREA_FLAG:
9897 case CONDITION_INSTANCE_SCRIPT:
9898 case CONDITION_SOURCE_AURA:
9899 case CONDITION_LAST_WAYPOINT:
9900 case CONDITION_WOW_PATCH:
9901 case CONDITION_NPC_ENTRY:
9902 case CONDITION_WAR_EFFORT_STAGE:
9903 return true;
9904 default:
9905 return false;
9906 }
9907}
9908
9909uint32 ObjectMgr::GenerateAuctionID()
9910{
9911 std::set<uint32>::const_iterator it = m_AuctionsIds.lower_bound(m_NextAuctionId);
9912 while (it != m_AuctionsIds.end() && *it == m_NextAuctionId)
9913 {
9914 ++m_NextAuctionId;
9915 ++it;
9916 }
9917 m_AuctionsIds.insert(m_NextAuctionId);
9918 return m_NextAuctionId++;
9919}
9920
9921void ObjectMgr::FreeAuctionID(uint32 id)
9922{
9923 m_AuctionsIds.erase(id);
9924}
9925
9926void ObjectMgr::GeneratePetNumberRange(uint32& first, uint32& last)
9927{
9928 first = GeneratePetNumber();
9929 uint32 prev = first;
9930 for (int i = 0; i < 1000; ++i)
9931 {
9932 uint32 nextGuid = GeneratePetNumber();
9933 // End of this range
9934 if (nextGuid != (prev + 1))
9935 {
9936 last = prev + 1;
9937 return;
9938 }
9939 prev = nextGuid;
9940 }
9941 last = first + 1000;
9942}
9943
9944void ObjectMgr::LoadAreaTemplate()
9945{
9946 sAreaStorage.Load();
9947
9948 for (auto itr = sAreaStorage.begin<AreaEntry>(); itr != sAreaStorage.end<AreaEntry>() ; ++itr)
9949 if (itr->IsZone() && itr->MapId != 0 && itr->MapId != 1)
9950 sAreaFlagByMapId.insert(AreaFlagByMapId::value_type(itr->MapId, itr->ExploreFlag));
9951}
9952
9953void ObjectMgr::LoadAreaLocales()
9954{
9955 mAreaLocaleMap.clear();
9956
9957 QueryResult* result = WorldDatabase.Query("SELECT Entry, NameLoc1, NameLoc2, NameLoc3, NameLoc4, NameLoc5, NameLoc6, NameLoc7, NameLoc8 FROM locales_area");
9958
9959 if (!result)
9960 {
9961 BarGoLink bar(1);
9962 bar.step();
9963 sLog.outString(">> Loaded 0 area locale strings. DB table `locales_area` is empty.");
9964 return;
9965 }
9966
9967 BarGoLink bar(result->GetRowCount());
9968
9969 do
9970 {
9971 auto fields = result->Fetch();
9972 bar.step();
9973
9974 auto entry = fields[0].GetUInt32();
9975
9976 if (!AreaEntry::GetById(entry))
9977 {
9978 ERROR_DB_STRICT_LOG("Table `locales_area` has data for nonexistent area entry %u, skipped.", entry);
9979 continue;
9980 }
9981
9982 AreaLocale& data = mAreaLocaleMap[entry];
9983
9984 for (uint8 i = 1; i < MAX_LOCALE; ++i)
9985 {
9986 auto str = fields[i].GetCppString();
9987 if (!str.empty())
9988 {
9989 int8 idx = GetOrNewIndexForLocale(LocaleConstant(i));
9990 if (idx >= 0)
9991 {
9992 if ((int32)data.Name.size() <= idx)
9993 data.Name.resize(idx + 1);
9994
9995 data.Name[idx] = str;
9996 }
9997 }
9998 }
9999 }
10000 while (result->NextRow());
10001
10002 delete result;
10003
10004 sLog.outString(">> Loaded " SIZEFMTD " area locale strings", mAreaLocaleMap.size());
10005 sLog.outString();
10006}
10007
10008void ObjectMgr::GetAreaLocaleString(uint32 entry, int32 loc_idx, std::string* namePtr) const
10009{
10010 if (loc_idx >= 0)
10011 {
10012 if (const auto *al = GetAreaLocale(entry))
10013 if (namePtr && al->Name.size() > size_t(loc_idx) && !al->Name[loc_idx].empty())
10014 *namePtr = al->Name[loc_idx].c_str();
10015 }
10016}