· 8 years ago · Aug 24, 2018, 12:04 AM
1/*
2* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
3* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4*
5* This program is free software; you can redistribute it and/or modify it
6* under the terms of the GNU General Public License as published by the
7* Free Software Foundation; either version 2 of the License, or (at your
8* option) any later version.
9*
10* This program is distributed in the hope that it will be useful, but WITHOUT
11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13* more details.
14*
15* You should have received a copy of the GNU General Public License along
16* with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "Common.h"
20#include "DatabaseEnv.h"
21#include "Log.h"
22#include "MapManager.h"
23#include "ObjectMgr.h"
24#include "GuildMgr.h"
25#include "GroupMgr.h"
26#include "SpellMgr.h"
27#include "UpdateMask.h"
28#include "World.h"
29#include "Arena.h"
30#include "Transport.h"
31#include "Language.h"
32#include "GameEventMgr.h"
33#include "Spell.h"
34#include "Chat.h"
35#include "AccountMgr.h"
36#include "InstanceSaveMgr.h"
37#include "SpellAuras.h"
38#include "Util.h"
39#include "WaypointManager.h"
40#include "GossipDef.h"
41#include "Vehicle.h"
42#include "AchievementMgr.h"
43#include "DisableMgr.h"
44#include "ScriptMgr.h"
45#include "SpellScript.h"
46#include "PoolMgr.h"
47#include "DB2Structure.h"
48#include "DB2Stores.h"
49#include "Configuration/Config.h"
50
51ScriptMapMap sQuestEndScripts;
52ScriptMapMap sQuestStartScripts;
53ScriptMapMap sSpellScripts;
54ScriptMapMap sGameObjectScripts;
55ScriptMapMap sEventScripts;
56ScriptMapMap sWaypointScripts;
57
58std::string GetScriptsTableNameByType(ScriptsType type)
59{
60 std::string res = "";
61 switch (type)
62 {
63 case SCRIPTS_QUEST_END: res = "quest_end_scripts"; break;
64 case SCRIPTS_QUEST_START: res = "quest_start_scripts"; break;
65 case SCRIPTS_SPELL: res = "spell_scripts"; break;
66 case SCRIPTS_GAMEOBJECT: res = "gameobject_scripts"; break;
67 case SCRIPTS_EVENT: res = "event_scripts"; break;
68 case SCRIPTS_WAYPOINT: res = "waypoint_scripts"; break;
69 default: break;
70 }
71 return res;
72}
73
74ScriptMapMap* GetScriptsMapByType(ScriptsType type)
75{
76 ScriptMapMap* res = NULL;
77 switch (type)
78 {
79 case SCRIPTS_QUEST_END: res = &sQuestEndScripts; break;
80 case SCRIPTS_QUEST_START: res = &sQuestStartScripts; break;
81 case SCRIPTS_SPELL: res = &sSpellScripts; break;
82 case SCRIPTS_GAMEOBJECT: res = &sGameObjectScripts; break;
83 case SCRIPTS_EVENT: res = &sEventScripts; break;
84 case SCRIPTS_WAYPOINT: res = &sWaypointScripts; break;
85 default: break;
86 }
87 return res;
88}
89
90std::string GetScriptCommandName(ScriptCommands command)
91{
92 std::string res = "";
93 switch (command)
94 {
95 case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
96 case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
97 case SCRIPT_COMMAND_FIELD_SET: res = "SCRIPT_COMMAND_FIELD_SET"; break;
98 case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
99 case SCRIPT_COMMAND_FLAG_SET: res = "SCRIPT_COMMAND_FLAG_SET"; break;
100 case SCRIPT_COMMAND_FLAG_REMOVE: res = "SCRIPT_COMMAND_FLAG_REMOVE"; break;
101 case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
102 case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
103 case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
104 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
105 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
106 case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
107 case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
108 case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
109 case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
110 case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
111 case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
112 case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
113 case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
114 case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
115 case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
116 case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
117 case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
118 case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
119 case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
120 case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
121 case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
122 default:
123 {
124 char sz[32];
125 sprintf(sz, "Unknown command: %u", command);
126 res = sz;
127 break;
128 }
129 }
130 return res;
131}
132
133std::string ScriptInfo::GetDebugInfo() const
134{
135 char sz[256];
136 sprintf(sz, "%s ('%s' script id: %u)", GetScriptCommandName(command).c_str(), GetScriptsTableNameByType(type).c_str(), id);
137 return std::string(sz);
138}
139
140bool normalizePlayerName(std::string& name)
141{
142 if (name.empty())
143 return false;
144
145 if (name[0] == -61 && name[1] == -97) // Interdiction d'utiliser ce caractere au debut, il fait planter l'affichage cote client
146 return false;
147
148 wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME + 1];
149 size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
150
151 if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
152 return false;
153
154 wstr_buf[0] = wcharToUpper(wstr_buf[0]);
155 for (size_t i = 1; i < wstr_len; ++i)
156 wstr_buf[i] = wcharToLower(wstr_buf[i]);
157
158 if (!WStrToUtf8(wstr_buf, wstr_len, name))
159 return false;
160
161 return true;
162}
163
164bool checkMailText(std::string _text)
165{
166 std::string temp_str(_text);
167 std::transform(temp_str.begin(), temp_str.end(), temp_str.begin(), tolower);
168
169 bool bOk = true;
170
171 // Check for special symbols
172 bOk = (temp_str.find("|tinterface") == std::string::npos);
173
174 return bOk;
175}
176
177LanguageDesc lang_description[LANGUAGES_COUNT] =
178{
179 { LANG_ADDON, 0, 0 },
180 { LANG_UNIVERSAL, 0, 0 },
181 { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
182 { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
183 { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
184 { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
185 { LANG_COMMON, 668, SKILL_LANG_COMMON },
186 { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
187 { LANG_TITAN, 816, SKILL_LANG_TITAN },
188 { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
189 { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
190 { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
191 { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
192 { LANG_TROLL, 7341, SKILL_LANG_TROLL },
193 { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
194 { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
195 { LANG_ZOMBIE, 0, 0 },
196 { LANG_GNOMISH_BINARY, 0, 0 },
197 { LANG_GOBLIN_BINARY, 0, 0 },
198 { LANG_WORGEN, 69270, SKILL_LANG_WORGEN },
199 { LANG_GOBLIN, 69269, SKILL_LANG_GOBLIN },
200 { LANG_PANDAREN_N, 108127, SKILL_LANG_PANDAREN_N },
201 { LANG_PANDAREN_H, 108130, SKILL_LANG_PANDAREN_A },
202 { LANG_PANDAREN_A, 108131, SKILL_LANG_PANDAREN_H }
203
204};
205
206LanguageDesc const* GetLanguageDescByID(uint32 lang)
207{
208 for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
209 {
210 if (uint32(lang_description[i].lang_id) == lang)
211 return &lang_description[i];
212 }
213
214 return NULL;
215}
216
217bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
218{
219 Player const* playerClicker = clicker->ToPlayer();
220 if (!playerClicker)
221 return true;
222
223 Unit const* summoner = NULL;
224 // Check summoners for party
225 if (clickee->isSummon())
226 summoner = clickee->ToTempSummon()->GetSummoner();
227 if (!summoner)
228 summoner = clickee;
229
230 // This only applies to players
231 switch (userType)
232 {
233 case SPELL_CLICK_USER_FRIEND:
234 if (!playerClicker->IsFriendlyTo(summoner))
235 return false;
236 break;
237 case SPELL_CLICK_USER_RAID:
238 if (!playerClicker->IsInRaidWith(summoner))
239 return false;
240 break;
241 case SPELL_CLICK_USER_PARTY:
242 if (!playerClicker->IsInPartyWith(summoner))
243 return false;
244 break;
245 default:
246 break;
247 }
248
249 return true;
250}
251
252ObjectMgr::ObjectMgr() : _auctionId(1), _equipmentSetGuid(1),
253_itemTextId(1), _mailId(1), _hiPetNumber(1), _voidItemId(1), _hiCharGuid(1),
254_hiCreatureGuid(1), _hiPetGuid(1), _hiVehicleGuid(1), _hiItemGuid(1),
255_hiGoGuid(1), _hiDoGuid(1), _hiCorpseGuid(1), _hiMoTransGuid(1), _hiAreaTriggerGuid(1), _skipUpdateCount(1)
256{}
257
258ObjectMgr::~ObjectMgr()
259{
260 for (QuestMap::iterator i = _questTemplates.begin(); i != _questTemplates.end(); ++i)
261 delete i->second;
262
263 for (PetLevelInfoContainer::iterator i = _petInfoStore.begin(); i != _petInfoStore.end(); ++i)
264 delete[] i->second;
265
266 for (int race = 0; race < MAX_RACES; ++race)
267 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
268 delete[] _playerInfo[race][class_].levelInfo;
269
270 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
271 itr->second.Clear();
272
273 _cacheTrainerSpellStore.clear();
274
275 for (DungeonEncounterContainer::iterator itr = _dungeonEncounterStore.begin(); itr != _dungeonEncounterStore.end(); ++itr)
276 for (DungeonEncounterList::iterator encounterItr = itr->second.begin(); encounterItr != itr->second.end(); ++encounterItr)
277 delete *encounterItr;
278}
279
280void ObjectMgr::AddLocaleString(std::string const& s, LocaleConstant locale, StringVector& data)
281{
282 if (!s.empty())
283 {
284 if (data.size() <= size_t(locale))
285 data.resize(locale + 1);
286
287 data[locale] = s;
288 }
289}
290
291void ObjectMgr::LoadCreatureLocales()
292{
293 uint32 oldMSTime = getMSTime();
294
295 _creatureLocaleStore.clear(); // need for reload case
296
297 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, name_loc9, subname_loc9, name_loc10, subname_loc10 FROM locales_creature");
298
299 if (!result)
300 return;
301
302 do
303 {
304 Field* fields = result->Fetch();
305
306 uint32 entry = fields[0].GetUInt32();
307
308 CreatureLocale& data = _creatureLocaleStore[entry];
309
310 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
311 {
312 LocaleConstant locale = (LocaleConstant)i;
313 AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.Name);
314 AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.SubName);
315 }
316 } while (result->NextRow());
317
318 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu creature locale strings in %u ms", (unsigned long)_creatureLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
319}
320
321void ObjectMgr::LoadGossipMenuItemsLocales()
322{
323 uint32 oldMSTime = getMSTime();
324
325 _gossipMenuItemsLocaleStore.clear(); // need for reload case
326
327 QueryResult result = WorldDatabase.Query("SELECT menu_id, id, "
328 "option_text_loc1, box_text_loc1, option_text_loc2, box_text_loc2, "
329 "option_text_loc3, box_text_loc3, option_text_loc4, box_text_loc4, "
330 "option_text_loc5, box_text_loc5, option_text_loc6, box_text_loc6, "
331 "option_text_loc7, box_text_loc7, option_text_loc8, box_text_loc8, "
332 "option_text_loc9, box_text_loc9, option_text_loc10, box_text_loc10 "
333 "FROM locales_gossip_menu_option");
334
335 if (!result)
336 return;
337
338 do
339 {
340 Field* fields = result->Fetch();
341
342 uint16 menuId = fields[0].GetUInt16();
343 uint16 id = fields[1].GetUInt16();
344
345 GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[MAKE_PAIR32(menuId, id)];
346
347 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
348 {
349 LocaleConstant locale = (LocaleConstant)i;
350 AddLocaleString(fields[2 + 2 * (i - 1)].GetString(), locale, data.OptionText);
351 AddLocaleString(fields[2 + 2 * (i - 1) + 1].GetString(), locale, data.BoxText);
352 }
353 } while (result->NextRow());
354
355 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu gossip_menu_option locale strings in %u ms", (unsigned long)_gossipMenuItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
356}
357
358void ObjectMgr::LoadPointOfInterestLocales()
359{
360 uint32 oldMSTime = getMSTime();
361
362 _pointOfInterestLocaleStore.clear(); // need for reload case
363
364 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, icon_name_loc9, icon_name_loc10 FROM locales_points_of_interest");
365
366 if (!result)
367 return;
368
369 do
370 {
371 Field* fields = result->Fetch();
372
373 uint32 entry = fields[0].GetUInt32();
374
375 PointOfInterestLocale& data = _pointOfInterestLocaleStore[entry];
376
377 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
378 AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.IconName);
379 } while (result->NextRow());
380
381 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu points_of_interest locale strings in %u ms", (unsigned long)_pointOfInterestLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
382}
383
384void ObjectMgr::LoadCreatureTemplates()
385{
386 uint32 oldMSTime = getMSTime();
387
388 // 0 1 2 3 4 5 6 7 8 9 10
389 QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, difficulty_entry_4, difficulty_entry_5, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, "
390 // 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
391 "modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction_A, faction_H, npcflag, npcflag2, speed_walk, speed_run, "
392 // 26 27 28 29 30 31 32 33 34 35 36 37 38
393 "speed_fly, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, "
394 // 39 40 41 42 43 44 45 46 47 48
395 "dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, "
396 // 49 50 51 52 53 54 55 56 57 58 59
397 "type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, "
398 // 60 61 62 63 64 65 66 67 68 69 70 71 72 73
399 "spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
400 // 74 75 76 77 78 79 80 81 82 83 84 85
401 "InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, "
402 // 86 87 88 89 90 91 92
403 " questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName "
404 "FROM creature_template;");
405
406 if (!result)
407 {
408 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
409 return;
410 }
411
412 //_creatureTemplateStore.rehash(result->GetRowCount());
413 uint32 count = 0;
414 do
415 {
416 uint8 index = 0;
417 Field* fields = result->Fetch();
418
419 uint32 entry = fields[index++].GetUInt32();
420
421
422 CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
423
424 creatureTemplate.Entry = entry;
425
426 for (uint8 i = 0; i < MAX_TEMPLATE_DIFFICULTY - 1; ++i)
427 creatureTemplate.DifficultyEntry[i] = fields[index++].GetUInt32();
428
429 for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
430 creatureTemplate.KillCredit[i] = fields[index++].GetUInt32();
431
432 creatureTemplate.Modelid1 = fields[index++].GetUInt32();
433 creatureTemplate.Modelid2 = fields[index++].GetUInt32();
434 creatureTemplate.Modelid3 = fields[index++].GetUInt32();
435 creatureTemplate.Modelid4 = fields[index++].GetUInt32();
436 creatureTemplate.Name = fields[index++].GetString();
437 creatureTemplate.SubName = fields[index++].GetString();
438 creatureTemplate.IconName = fields[index++].GetString();
439 creatureTemplate.GossipMenuId = fields[index++].GetUInt32();
440 creatureTemplate.minlevel = fields[index++].GetUInt8();
441 creatureTemplate.maxlevel = fields[index++].GetUInt8();
442 creatureTemplate.expansion = uint32(fields[index++].GetInt16());
443 creatureTemplate.expansionUnknown = uint32(fields[index++].GetUInt16());
444 creatureTemplate.faction_A = uint32(fields[index++].GetUInt16());
445 creatureTemplate.faction_H = uint32(fields[index++].GetUInt16());
446 creatureTemplate.npcflag = fields[index++].GetUInt32();
447 creatureTemplate.npcflag2 = fields[index++].GetUInt32();
448 creatureTemplate.speed_walk = fields[index++].GetFloat();
449 creatureTemplate.speed_run = fields[index++].GetFloat();
450 creatureTemplate.speed_fly = fields[index++].GetFloat();
451 creatureTemplate.scale = fields[index++].GetFloat();
452 creatureTemplate.rank = uint32(fields[index++].GetUInt8());
453 creatureTemplate.mindmg = fields[index++].GetFloat();
454 creatureTemplate.maxdmg = fields[index++].GetFloat();
455 creatureTemplate.dmgschool = uint32(fields[index++].GetInt8());
456 creatureTemplate.attackpower = fields[index++].GetUInt32();
457 creatureTemplate.dmg_multiplier = fields[index++].GetFloat();
458 creatureTemplate.baseattacktime = fields[index++].GetUInt32();
459 creatureTemplate.rangeattacktime = fields[index++].GetUInt32();
460 creatureTemplate.unit_class = uint32(fields[index++].GetUInt8());
461 creatureTemplate.unit_flags = fields[index++].GetUInt32();
462 creatureTemplate.unit_flags2 = fields[index++].GetUInt32();
463 creatureTemplate.dynamicflags = fields[index++].GetUInt32();
464 creatureTemplate.family = uint32(fields[index++].GetUInt32());
465 creatureTemplate.trainer_type = uint32(fields[index++].GetUInt8());
466 creatureTemplate.trainer_spell = fields[index++].GetUInt32();
467 creatureTemplate.trainer_class = uint32(fields[index++].GetUInt8());
468 creatureTemplate.trainer_race = uint32(fields[index++].GetUInt8());
469 creatureTemplate.minrangedmg = fields[index++].GetFloat();
470 creatureTemplate.maxrangedmg = fields[index++].GetFloat();
471 creatureTemplate.rangedattackpower = uint32(fields[index++].GetUInt16());
472 creatureTemplate.type = uint32(fields[index++].GetUInt8());
473 creatureTemplate.type_flags = fields[index++].GetUInt32();
474 creatureTemplate.type_flags2 = fields[index++].GetUInt32();
475 creatureTemplate.lootid = fields[index++].GetUInt32();
476 creatureTemplate.pickpocketLootId = fields[index++].GetUInt32();
477 creatureTemplate.SkinLootId = fields[index++].GetUInt32();
478
479 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
480 creatureTemplate.resistance[i] = fields[index++].GetInt16();
481
482 for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
483 creatureTemplate.spells[i] = fields[index++].GetUInt32();
484
485 creatureTemplate.PetSpellDataId = fields[index++].GetUInt32();
486 creatureTemplate.VehicleId = fields[index++].GetUInt32();
487 creatureTemplate.mingold = fields[index++].GetUInt32();
488 creatureTemplate.maxgold = fields[index++].GetUInt32();
489 creatureTemplate.AIName = fields[index++].GetString();
490 creatureTemplate.MovementType = uint32(fields[index++].GetUInt8());
491 creatureTemplate.InhabitType = uint32(fields[index++].GetUInt8());
492 creatureTemplate.HoverHeight = fields[index++].GetFloat();
493 creatureTemplate.ModHealth = fields[index++].GetFloat();
494 creatureTemplate.ModMana = fields[index++].GetFloat();
495 creatureTemplate.ModManaExtra = fields[index++].GetFloat();
496 creatureTemplate.ModArmor = fields[index++].GetFloat();
497 creatureTemplate.RacialLeader = fields[index++].GetBool();
498
499 for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
500 creatureTemplate.questItems[i] = fields[index++].GetUInt32();
501
502 creatureTemplate.movementId = fields[index++].GetUInt32();
503 creatureTemplate.RegenHealth = fields[index++].GetBool();
504 creatureTemplate.equipmentId = fields[index++].GetUInt32();
505 creatureTemplate.MechanicImmuneMask = fields[index++].GetUInt32();
506 creatureTemplate.flags_extra = fields[index++].GetUInt32();
507 creatureTemplate.ScriptID = GetScriptId(fields[index++].GetCString());
508
509 ++count;
510 } while (result->NextRow());
511
512 // Checking needs to be done after loading because of the difficulty self referencing
513 for (CreatureTemplateContainer::const_iterator itr = _creatureTemplateStore.begin(); itr != _creatureTemplateStore.end(); ++itr)
514 CheckCreatureTemplate(&itr->second);
515
516 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
517}
518
519void ObjectMgr::LoadCreatureTemplateAddons()
520{
521 uint32 oldMSTime = getMSTime();
522
523 // 0 1 2 3 4 5 6
524 QueryResult result = WorldDatabase.Query("SELECT entry, path_id, mount, bytes1, bytes2, emote, auras FROM creature_template_addon");
525
526 if (!result)
527 {
528 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
529 return;
530 }
531
532 uint32 count = 0;
533 do
534 {
535 Field* fields = result->Fetch();
536
537 uint32 entry = fields[0].GetUInt32();
538
539 if (!sObjectMgr->GetCreatureTemplate(entry))
540 {
541 sLog->outError(LOG_FILTER_SQL, "Creature template (Entry: %u) does not exist but has a record in `creature_template_addon`", entry);
542 continue;
543 }
544
545 CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
546
547 creatureAddon.path_id = fields[1].GetUInt32();
548 creatureAddon.mount = fields[2].GetUInt32();
549 creatureAddon.bytes1 = fields[3].GetUInt32();
550 creatureAddon.bytes2 = fields[4].GetUInt32();
551 creatureAddon.emote = fields[5].GetUInt32();
552
553 Tokenizer tokens(fields[6].GetString(), ' ');
554 uint8 i = 0;
555 creatureAddon.auras.resize(tokens.size());
556 for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
557 {
558 SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr)));
559 if (!AdditionalSpellInfo)
560 {
561 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong spell %u defined in `auras` field in `creature_template_addon`.", entry, uint32(atol(*itr)));
562 continue;
563 }
564 creatureAddon.auras[i++] = uint32(atol(*itr));
565 }
566
567 if (creatureAddon.mount)
568 {
569 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
570 {
571 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid displayInfoId (%u) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
572 creatureAddon.mount = 0;
573 }
574 }
575
576 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
577 {
578 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid emote (%u) defined in `creature_addon`.", entry, creatureAddon.emote);
579 creatureAddon.emote = 0;
580 }
581
582 ++count;
583 } while (result->NextRow());
584
585 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
586}
587
588void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
589{
590 if (!cInfo)
591 return;
592
593 bool ok = true; // bool to allow continue outside this loop
594 for (uint32 diff = 0; diff < MAX_TEMPLATE_DIFFICULTY - 1 && ok; ++diff)
595 {
596 if (!cInfo->DifficultyEntry[diff])
597 continue;
598
599 ok = false; // will be set to true at the end of this loop again
600
601 CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
602 if (!difficultyInfo)
603 {
604 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u does not exist.",
605 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
606 continue;
607 }
608
609 bool ok2 = true;
610 for (uint32 diff2 = 0; diff2 < MAX_TEMPLATE_DIFFICULTY - 1 && ok2; ++diff2)
611 {
612 ok2 = false;
613
614 if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end())
615 {
616 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) is listed as `difficulty_entry_%u` of another creature, but itself lists %u in `difficulty_entry_%u`.",
617 cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
618 continue;
619 }
620
621 if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end())
622 {
623 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) already listed as `difficulty_entry_%u` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
624 continue;
625 }
626
627 if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end())
628 {
629 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u has itself a value in `difficulty_entry_%u`.",
630 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
631 continue;
632 }
633 ok2 = true;
634 }
635 if (!ok2)
636 continue;
637
638 if (cInfo->unit_class != difficultyInfo->unit_class)
639 {
640 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u, class %u) has different `unit_class` in difficulty %u mode (Entry: %u, class %u).",
641 cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
642 continue;
643 }
644
645 if (cInfo->npcflag != difficultyInfo->npcflag)
646 {
647 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `npcflag` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
648 continue;
649 }
650
651 if (cInfo->trainer_class != difficultyInfo->trainer_class)
652 {
653 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_class` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
654 continue;
655 }
656
657 if (cInfo->trainer_race != difficultyInfo->trainer_race)
658 {
659 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_race` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
660 continue;
661 }
662
663 if (cInfo->trainer_type != difficultyInfo->trainer_type)
664 {
665 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_type` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
666 continue;
667 }
668
669 if (cInfo->trainer_spell != difficultyInfo->trainer_spell)
670 {
671 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
672 continue;
673 }
674
675 if (!difficultyInfo->AIName.empty())
676 {
677 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists difficulty %u mode entry %u with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
678 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
679 continue;
680 }
681
682 if (difficultyInfo->ScriptID)
683 {
684 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists difficulty %u mode entry %u with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
685 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
686 continue;
687 }
688
689 _hasDifficultyEntries[diff].insert(cInfo->Entry);
690 _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
691 ok = true;
692 }
693
694 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
695 if (!factionTemplate)
696 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has non-existing faction_A template (%u).", cInfo->Entry, cInfo->faction_A);
697
698 factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
699 if (!factionTemplate)
700 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has non-existing faction_H template (%u).", cInfo->Entry, cInfo->faction_H);
701
702 // used later for scale
703 CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
704
705 if (cInfo->Modelid1)
706 {
707 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
708 if (!displayEntry)
709 {
710 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
711 const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
712 }
713 else if (!displayScaleEntry)
714 displayScaleEntry = displayEntry;
715
716 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
717 if (!modelInfo)
718 sLog->outError(LOG_FILTER_SQL, "No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry);
719 }
720
721 if (cInfo->Modelid2)
722 {
723 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
724 if (!displayEntry)
725 {
726 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
727 const_cast<CreatureTemplate*>(cInfo)->Modelid2 = 0;
728 }
729 else if (!displayScaleEntry)
730 displayScaleEntry = displayEntry;
731
732 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);
733 if (!modelInfo)
734 sLog->outError(LOG_FILTER_SQL, "No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry);
735 }
736
737 if (cInfo->Modelid3)
738 {
739 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
740 if (!displayEntry)
741 {
742 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
743 const_cast<CreatureTemplate*>(cInfo)->Modelid3 = 0;
744 }
745 else if (!displayScaleEntry)
746 displayScaleEntry = displayEntry;
747
748 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
749 if (!modelInfo)
750 sLog->outError(LOG_FILTER_SQL, "No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry);
751 }
752
753 if (cInfo->Modelid4)
754 {
755 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
756 if (!displayEntry)
757 {
758 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
759 const_cast<CreatureTemplate*>(cInfo)->Modelid4 = 0;
760 }
761 else if (!displayScaleEntry)
762 displayScaleEntry = displayEntry;
763
764 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);
765 if (!modelInfo)
766 sLog->outError(LOG_FILTER_SQL, "No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry);
767 }
768
769 if (!displayScaleEntry)
770 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
771
772 for (int k = 0; k < MAX_KILL_CREDIT; ++k)
773 {
774 if (cInfo->KillCredit[k])
775 {
776 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
777 {
778 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) lists non-existing creature entry %u in `KillCredit%d`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
779 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
780 }
781 }
782 }
783
784 if (!cInfo->unit_class || ((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
785 {
786 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid unit_class (%u) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
787 const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
788 }
789
790 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
791 {
792 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
793 const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
794 }
795
796 if (cInfo->baseattacktime == 0)
797 const_cast<CreatureTemplate*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
798
799 if (cInfo->rangeattacktime == 0)
800 const_cast<CreatureTemplate*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
801
802 if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
803 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong trainer type %u.", cInfo->Entry, cInfo->trainer_type);
804
805 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
806 {
807 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid creature type (%u) in `type`.", cInfo->Entry, cInfo->type);
808 const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
809 }
810
811 // must exist or used hidden but used in data horse case
812 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
813 {
814 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family);
815 const_cast<CreatureTemplate*>(cInfo)->family = 0;
816 }
817
818 if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
819 {
820 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly.", cInfo->Entry, cInfo->InhabitType);
821 const_cast<CreatureTemplate*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
822 }
823
824 if (cInfo->HoverHeight < 0.0f)
825 {
826 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong value (%f) in `HoverHeight`", cInfo->Entry, cInfo->HoverHeight);
827 const_cast<CreatureTemplate*>(cInfo)->HoverHeight = 1.0f;
828 }
829
830 if (cInfo->VehicleId)
831 {
832 VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
833 if (!vehId)
834 {
835 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has a non-existing VehicleId (%u). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
836 const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
837 }
838 }
839
840 if (cInfo->PetSpellDataId)
841 {
842 CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
843 if (!spellDataId)
844 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId);
845 }
846
847 for (uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j)
848 {
849 if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j]))
850 {
851 WorldDatabase.PExecute("UPDATE creature_template SET spell%d = 0 WHERE entry = %u", j + 1, cInfo->Entry);
852 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has non-existing Spell%d (%u), set to 0.", cInfo->Entry, j + 1, cInfo->spells[j]);
853 const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
854 }
855 }
856
857 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
858 {
859 sLog->outError(LOG_FILTER_SQL, "Creature (Entry: %u) has wrong movement generator type (%u), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
860 const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
861 }
862
863 if (cInfo->equipmentId > 0) // 0 no equipment
864 {
865 if (!GetEquipmentInfo(cInfo->equipmentId))
866 {
867 sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `equipment_id` %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
868 const_cast<CreatureTemplate*>(cInfo)->equipmentId = 0;
869 }
870 }
871
872 /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
873 if (cInfo->scale <= 0.0f)
874 {
875 if (displayScaleEntry)
876 const_cast<CreatureTemplate*>(cInfo)->scale = displayScaleEntry->scale;
877 else
878 const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f;
879 }
880
881 if (cInfo->expansion > MAX_CREATURE_BASE_HP)
882 {
883 sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `exp` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
884 const_cast<CreatureTemplate*>(cInfo)->expansion = 0;
885 }
886
887 if (cInfo->expansionUnknown > MAX_CREATURE_BASE_HP)
888 {
889 sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `exp_unk` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansionUnknown);
890 const_cast<CreatureTemplate*>(cInfo)->expansionUnknown = 0;
891 }
892
893 if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
894 {
895 sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with disallowed `flags_extra` %u, removing incorrect flag.", cInfo->Entry, badFlags);
896 const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
897 }
898
899 const_cast<CreatureTemplate*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank);
900}
901
902void ObjectMgr::LoadCreatureAddons()
903{
904 uint32 oldMSTime = getMSTime();
905
906 // 0 1 2 3 4 5 6
907 QueryResult result = WorldDatabase.Query("SELECT guid, path_id, mount, bytes1, bytes2, emote, auras FROM creature_addon");
908
909 if (!result)
910 {
911 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
912 return;
913 }
914
915 uint32 count = 0;
916 do
917 {
918 Field* fields = result->Fetch();
919
920 uint32 guid = fields[0].GetUInt32();
921
922 CreatureData const* creData = GetCreatureData(guid);
923 if (!creData)
924 {
925 sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u) does not exist but has a record in `creature_addon`", guid);
926 continue;
927 }
928
929 CreatureAddon& creatureAddon = _creatureAddonStore[guid];
930
931 creatureAddon.path_id = fields[1].GetUInt32();
932 if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.path_id)
933 {
934 const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
935 sLog->outError(LOG_FILTER_SQL, "Creature (GUID %u) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
936 }
937
938 creatureAddon.mount = fields[2].GetUInt32();
939 creatureAddon.bytes1 = fields[3].GetUInt32();
940 creatureAddon.bytes2 = fields[4].GetUInt32();
941 creatureAddon.emote = fields[5].GetUInt32();
942
943 Tokenizer tokens(fields[6].GetString(), ' ');
944 uint8 i = 0;
945 creatureAddon.auras.resize(tokens.size());
946 for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
947 {
948 SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr)));
949 if (!AdditionalSpellInfo)
950 {
951 sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u) has wrong spell %u defined in `auras` field in `creature_addon`.", guid, uint32(atol(*itr)));
952 continue;
953 }
954 creatureAddon.auras[i++] = uint32(atol(*itr));
955 }
956
957 if (creatureAddon.mount)
958 {
959 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
960 {
961 sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u) has invalid displayInfoId (%u) for mount defined in `creature_addon`", guid, creatureAddon.mount);
962 creatureAddon.mount = 0;
963 }
964 }
965
966 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
967 {
968 sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);
969 creatureAddon.emote = 0;
970 }
971
972 ++count;
973 } while (result->NextRow());
974
975 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
976}
977
978CreatureAddon const* ObjectMgr::GetCreatureAddon(uint32 lowguid)
979{
980 CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
981 if (itr != _creatureAddonStore.end())
982 return &(itr->second);
983
984 return NULL;
985}
986
987CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry)
988{
989 CreatureAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
990 if (itr != _creatureTemplateAddonStore.end())
991 return &(itr->second);
992
993 return NULL;
994}
995
996EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
997{
998 EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
999 if (itr != _equipmentInfoStore.end())
1000 return &(itr->second);
1001
1002 return NULL;
1003}
1004
1005void ObjectMgr::LoadEquipmentTemplates()
1006{
1007 uint32 oldMSTime = getMSTime();
1008
1009 QueryResult result = WorldDatabase.Query("SELECT entry, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template");
1010
1011 if (!result)
1012 {
1013 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
1014 return;
1015 }
1016
1017 uint32 count = 0;
1018 do
1019 {
1020 Field* fields = result->Fetch();
1021
1022 uint32 entry = fields[0].GetUInt32();
1023
1024 EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry];
1025
1026 equipmentInfo.ItemEntry[0] = fields[1].GetUInt32();
1027 equipmentInfo.ItemEntry[1] = fields[2].GetUInt32();
1028 equipmentInfo.ItemEntry[2] = fields[3].GetUInt32();
1029
1030 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1031 {
1032 if (!equipmentInfo.ItemEntry[i])
1033 continue;
1034
1035 ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
1036
1037 if (!dbcItem)
1038 {
1039 sLog->outError(LOG_FILTER_SQL, "Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u, forced to 0.",
1040 equipmentInfo.ItemEntry[i], i + 1, entry);
1041 equipmentInfo.ItemEntry[i] = 0;
1042 continue;
1043 }
1044
1045 if (dbcItem->InventoryType != INVTYPE_WEAPON &&
1046 dbcItem->InventoryType != INVTYPE_SHIELD &&
1047 dbcItem->InventoryType != INVTYPE_RANGED &&
1048 dbcItem->InventoryType != INVTYPE_2HWEAPON &&
1049 dbcItem->InventoryType != INVTYPE_WEAPONMAINHAND &&
1050 dbcItem->InventoryType != INVTYPE_WEAPONOFFHAND &&
1051 dbcItem->InventoryType != INVTYPE_HOLDABLE &&
1052 dbcItem->InventoryType != INVTYPE_THROWN &&
1053 dbcItem->InventoryType != INVTYPE_RANGEDRIGHT)
1054 {
1055 sLog->outError(LOG_FILTER_SQL, "Item (entry = %u) in creature_equip_template. itemEntry %u for entry = %u is not equipable in a hand, forced to 0.",
1056 equipmentInfo.ItemEntry[i], i + 1, entry);
1057 equipmentInfo.ItemEntry[i] = 0;
1058 }
1059 }
1060
1061 ++count;
1062 } while (result->NextRow());
1063
1064 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u equipment templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
1065}
1066
1067CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId)
1068{
1069 CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
1070 if (itr != _creatureModelStore.end())
1071 return &(itr->second);
1072
1073 return NULL;
1074}
1075
1076uint32 ObjectMgr::ChooseDisplayId(uint32 /*team*/, const CreatureTemplate* cinfo, const CreatureData* data /*= NULL*/)
1077{
1078 // Load creature model (display id)
1079 if (data && data->displayid)
1080 return data->displayid;
1081
1082 return cinfo->GetRandomValidModelId();
1083}
1084
1085void ObjectMgr::ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData* data /*= NULL*/)
1086{
1087 npcflag = cinfo->npcflag;
1088 unit_flags = cinfo->unit_flags;
1089 dynamicflags = cinfo->dynamicflags;
1090
1091 if (data)
1092 {
1093 if (data->npcflag)
1094 npcflag = data->npcflag;
1095
1096 if (data->unit_flags)
1097 unit_flags = data->unit_flags;
1098
1099 if (data->dynamicflags)
1100 dynamicflags = data->dynamicflags;
1101 }
1102}
1103
1104CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* displayID)
1105{
1106 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID);
1107 if (!modelInfo)
1108 return NULL;
1109
1110 // If a model for another gender exists, 50% chance to use it
1111 if (modelInfo->modelid_other_gender != 0 && urand(0, 1) == 0)
1112 {
1113 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->modelid_other_gender);
1114 if (!minfo_tmp)
1115 sLog->outError(LOG_FILTER_SQL, "Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", *displayID, modelInfo->modelid_other_gender);
1116 else
1117 {
1118 // Model ID changed
1119 *displayID = modelInfo->modelid_other_gender;
1120 return minfo_tmp;
1121 }
1122 }
1123
1124 return modelInfo;
1125}
1126
1127void ObjectMgr::LoadCreatureModelInfo()
1128{
1129 uint32 oldMSTime = getMSTime();
1130
1131 QueryResult result = WorldDatabase.Query("SELECT modelid, bounding_radius, combat_reach, gender, modelid_other_gender FROM creature_model_info");
1132
1133 if (!result)
1134 {
1135 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
1136 return;
1137 }
1138
1139 _creatureModelStore.rehash(result->GetRowCount());
1140 uint32 count = 0;
1141
1142 do
1143 {
1144 Field* fields = result->Fetch();
1145
1146 uint32 modelId = fields[0].GetUInt32();
1147
1148 CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
1149
1150 modelInfo.bounding_radius = fields[1].GetFloat();
1151 modelInfo.combat_reach = fields[2].GetFloat();
1152 modelInfo.gender = fields[3].GetUInt8();
1153 modelInfo.modelid_other_gender = fields[4].GetUInt32();
1154
1155 // Checks
1156
1157 if (!sCreatureDisplayInfoStore.LookupEntry(modelId))
1158 sLog->outError(LOG_FILTER_SQL, "Table `creature_model_info` has model for not existed display id (%u).", modelId);
1159
1160 if (modelInfo.gender > GENDER_NONE)
1161 {
1162 sLog->outError(LOG_FILTER_SQL, "Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(modelInfo.gender), modelId);
1163 modelInfo.gender = GENDER_MALE;
1164 }
1165
1166 if (modelInfo.modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.modelid_other_gender))
1167 {
1168 sLog->outError(LOG_FILTER_SQL, "Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", modelInfo.modelid_other_gender, modelId);
1169 modelInfo.modelid_other_gender = 0;
1170 }
1171
1172 if (modelInfo.combat_reach < 0.1f)
1173 modelInfo.combat_reach = DEFAULT_COMBAT_REACH;
1174
1175 ++count;
1176 } while (result->NextRow());
1177
1178 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature model based info in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
1179}
1180
1181void ObjectMgr::LoadLinkedRespawn()
1182{
1183 uint32 oldMSTime = getMSTime();
1184
1185 _linkedRespawnStore.clear();
1186 // 0 1 2
1187 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1188
1189 if (!result)
1190 {
1191 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1192
1193 return;
1194 }
1195
1196 do
1197 {
1198 Field* fields = result->Fetch();
1199
1200 uint32 guidLow = fields[0].GetUInt32();
1201 uint32 linkedGuidLow = fields[1].GetUInt32();
1202 uint8 linkType = fields[2].GetUInt8();
1203
1204 uint64 guid = 0, linkedGuid = 0;
1205 bool error = false;
1206 switch (linkType)
1207 {
1208 case CREATURE_TO_CREATURE:
1209 {
1210 const CreatureData* slave = GetCreatureData(guidLow);
1211 if (!slave)
1212 {
1213 sLog->outError(LOG_FILTER_SQL, "Couldn't get creature data for GUIDLow %u", guidLow);
1214 error = true;
1215 break;
1216 }
1217
1218 const CreatureData* master = GetCreatureData(linkedGuidLow);
1219 if (!master)
1220 {
1221 sLog->outError(LOG_FILTER_SQL, "Couldn't get creature data for GUIDLow %u", linkedGuidLow);
1222 error = true;
1223 break;
1224 }
1225
1226 const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1227 if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
1228 {
1229 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
1230 error = true;
1231 break;
1232 }
1233
1234 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1235 {
1236 sLog->outError(LOG_FILTER_SQL, "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
1237 error = true;
1238 break;
1239 }
1240
1241 guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_UNIT);
1242 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_UNIT);
1243 break;
1244 }
1245 case CREATURE_TO_GO:
1246 {
1247 const CreatureData* slave = GetCreatureData(guidLow);
1248 if (!slave)
1249 {
1250 sLog->outError(LOG_FILTER_SQL, "Couldn't get creature data for GUIDLow %u", guidLow);
1251 error = true;
1252 break;
1253 }
1254
1255 const GameObjectData* master = GetGOData(linkedGuidLow);
1256 if (!master)
1257 {
1258 sLog->outError(LOG_FILTER_SQL, "Couldn't get gameobject data for GUIDLow %u", linkedGuidLow);
1259 error = true;
1260 break;
1261 }
1262
1263 const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1264 if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
1265 {
1266 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
1267 error = true;
1268 break;
1269 }
1270
1271 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1272 {
1273 sLog->outError(LOG_FILTER_SQL, "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
1274 error = true;
1275 break;
1276 }
1277
1278 guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_UNIT);
1279 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_GAMEOBJECT);
1280 break;
1281 }
1282 case GO_TO_GO:
1283 {
1284 const GameObjectData* slave = GetGOData(guidLow);
1285 if (!slave)
1286 {
1287 sLog->outError(LOG_FILTER_SQL, "Couldn't get gameobject data for GUIDLow %u", guidLow);
1288 error = true;
1289 break;
1290 }
1291
1292 const GameObjectData* master = GetGOData(linkedGuidLow);
1293 if (!master)
1294 {
1295 sLog->outError(LOG_FILTER_SQL, "Couldn't get gameobject data for GUIDLow %u", linkedGuidLow);
1296 error = true;
1297 break;
1298 }
1299
1300 const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1301 if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
1302 {
1303 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
1304 error = true;
1305 break;
1306 }
1307
1308 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1309 {
1310 sLog->outError(LOG_FILTER_SQL, "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
1311 error = true;
1312 break;
1313 }
1314
1315 guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_GAMEOBJECT);
1316 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_GAMEOBJECT);
1317 break;
1318 }
1319 case GO_TO_CREATURE:
1320 {
1321 const GameObjectData* slave = GetGOData(guidLow);
1322 if (!slave)
1323 {
1324 sLog->outError(LOG_FILTER_SQL, "Couldn't get gameobject data for GUIDLow %u", guidLow);
1325 error = true;
1326 break;
1327 }
1328
1329 const CreatureData* master = GetCreatureData(linkedGuidLow);
1330 if (!master)
1331 {
1332 sLog->outError(LOG_FILTER_SQL, "Couldn't get creature data for GUIDLow %u", linkedGuidLow);
1333 error = true;
1334 break;
1335 }
1336
1337 const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1338 if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
1339 {
1340 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
1341 error = true;
1342 break;
1343 }
1344
1345 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1346 {
1347 sLog->outError(LOG_FILTER_SQL, "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
1348 error = true;
1349 break;
1350 }
1351
1352 guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_GAMEOBJECT);
1353 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_UNIT);
1354 break;
1355 }
1356 }
1357
1358 if (!error)
1359 _linkedRespawnStore[guid] = linkedGuid;
1360 } while (result->NextRow());
1361
1362 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded " UI64FMTD " linked respawns in %u ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
1363}
1364
1365bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guidLow, uint32 linkedGuidLow)
1366{
1367 if (!guidLow)
1368 return false;
1369
1370 const CreatureData* master = GetCreatureData(guidLow);
1371 uint64 guid = MAKE_NEW_GUID(guidLow, master->id, HIGHGUID_UNIT);
1372
1373 if (!linkedGuidLow) // we're removing the linking
1374 {
1375 _linkedRespawnStore.erase(guid);
1376 PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN);
1377 stmt->setUInt32(0, guidLow);
1378 WorldDatabase.Execute(stmt);
1379 return true;
1380 }
1381
1382 const CreatureData* slave = GetCreatureData(linkedGuidLow);
1383 if (!slave)
1384 {
1385 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to non-existent creature '%u'.", guidLow, linkedGuidLow);
1386 return false;
1387 }
1388
1389 const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1390 if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
1391 {
1392 sLog->outError(LOG_FILTER_SQL, "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
1393 return false;
1394 }
1395
1396 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1397 {
1398 sLog->outError(LOG_FILTER_SQL, "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
1399 return false;
1400 }
1401
1402 uint64 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, slave->id, HIGHGUID_UNIT);
1403
1404 _linkedRespawnStore[guid] = linkedGuid;
1405 PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
1406 stmt->setUInt32(0, guidLow);
1407 stmt->setUInt32(1, linkedGuidLow);
1408 WorldDatabase.Execute(stmt);
1409 return true;
1410}
1411
1412void ObjectMgr::LoadTempSummons()
1413{
1414 uint32 oldMSTime = getMSTime();
1415
1416 // 0 1 2 3 4 5 6 7 8 9
1417 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
1418
1419 if (!result)
1420 {
1421 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
1422 return;
1423 }
1424
1425 uint32 count = 0;
1426 do
1427 {
1428 Field* fields = result->Fetch();
1429
1430 uint32 summonerId = fields[0].GetUInt32();
1431 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
1432 uint8 group = fields[2].GetUInt8();
1433
1434 switch (summonerType)
1435 {
1436 case SUMMONER_TYPE_CREATURE:
1437 if (!GetCreatureTemplate(summonerId))
1438 {
1439 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has summoner with non existing entry %u for creature summoner type, skipped.", summonerId);
1440 continue;
1441 }
1442 break;
1443 case SUMMONER_TYPE_GAMEOBJECT:
1444 if (!GetGameObjectTemplate(summonerId))
1445 {
1446 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has summoner with non existing entry %u for gameobject summoner type, skipped.", summonerId);
1447 continue;
1448 }
1449 break;
1450 case SUMMONER_TYPE_MAP:
1451 if (!sMapStore.LookupEntry(summonerId))
1452 {
1453 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has summoner with non existing entry %u for map summoner type, skipped.", summonerId);
1454 continue;
1455 }
1456 break;
1457 default:
1458 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has unhandled summoner type %u for summoner %u, skipped.", summonerType, summonerId);
1459 continue;
1460 }
1461
1462 TempSummonData data;
1463 data.entry = fields[3].GetUInt32();
1464
1465 if (!GetCreatureTemplate(data.entry))
1466 {
1467 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has creature in group [Summoner ID: %u, Summoner Type: %u, Group ID: %u] with non existing creature entry %u, skipped.", summonerId, summonerType, group, data.entry);
1468 continue;
1469 }
1470
1471 float posX = fields[4].GetFloat();
1472 float posY = fields[5].GetFloat();
1473 float posZ = fields[6].GetFloat();
1474 float orientation = fields[7].GetFloat();
1475
1476 data.pos.Relocate(posX, posY, posZ, orientation);
1477
1478 data.type = TempSummonType(fields[8].GetUInt8());
1479
1480 if (data.type > TEMPSUMMON_MANUAL_DESPAWN)
1481 {
1482 sLog->outError(LOG_FILTER_SQL, "Table `creature_summon_groups` has unhandled temp summon type %u in group [Summoner ID: %u, Summoner Type: %u, Group ID: %u] for creature entry %u, skipped.", data.type, summonerId, summonerType, group, data.entry);
1483 continue;
1484 }
1485
1486 data.time = fields[9].GetUInt32();
1487
1488 TempSummonGroupKey key(summonerId, summonerType, group);
1489 _tempSummonDataStore[key].push_back(data);
1490
1491 ++count;
1492
1493 } while (result->NextRow());
1494
1495 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u temp summons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
1496}
1497
1498void ObjectMgr::LoadCreatures()
1499{
1500 uint32 oldMSTime = getMSTime();
1501
1502 // 0 1 2 3 4 5 6 7 8 9 10 11 12
1503 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, zoneId, areaId, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, "
1504 // 13 14 15 16 17 18 19 20 21 22 23 24
1505 "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.isActive "
1506 "FROM creature "
1507 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1508 "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
1509
1510 if (!result)
1511 {
1512 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creatures. DB table `creature` is empty.");
1513
1514 return;
1515 }
1516
1517 // Build single time for check spawnmask
1518 std::map<uint32, uint32> spawnMasks;
1519 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
1520 if (sMapStore.LookupEntry(i))
1521 for (int k = 0; k < MAX_DIFFICULTY; ++k)
1522 if (GetMapDifficultyData(i, Difficulty(k)))
1523 spawnMasks[i] |= (1 << k);
1524
1525 //_creatureDataStore.rehash(result->GetRowCount());
1526 uint32 count = 0;
1527 do
1528 {
1529 Field* fields = result->Fetch();
1530
1531 uint8 index = 0;
1532
1533 uint32 guid = fields[index++].GetUInt32();
1534 uint32 entry = fields[index++].GetUInt32();
1535
1536 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
1537 if (!cInfo)
1538 {
1539 sLog->outError(LOG_FILTER_SQL, "Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
1540 continue;
1541 }
1542
1543 CreatureData& data = _creatureDataStore[guid];
1544 data.id = entry;
1545 data.mapid = fields[index++].GetUInt16();
1546 data.zoneId = fields[index++].GetUInt16();
1547 data.areaId = fields[index++].GetUInt16();
1548 data.displayid = fields[index++].GetUInt32();
1549 data.equipmentId = fields[index++].GetInt32();
1550 data.posX = fields[index++].GetFloat();
1551 data.posY = fields[index++].GetFloat();
1552 data.posZ = fields[index++].GetFloat();
1553 data.orientation = fields[index++].GetFloat();
1554 data.spawntimesecs = fields[index++].GetUInt32();
1555 data.spawndist = fields[index++].GetFloat();
1556 data.currentwaypoint = fields[index++].GetUInt32();
1557 data.curhealth = fields[index++].GetUInt32();
1558 data.curmana = fields[index++].GetUInt32();
1559 data.movementType = fields[index++].GetUInt8();
1560 data.spawnMask = fields[index++].GetUInt32();
1561 data.phaseMask = fields[index++].GetUInt32();
1562 int16 gameEvent = fields[index++].GetInt8();
1563 uint32 PoolId = fields[index++].GetUInt32();
1564 data.npcflag = fields[index++].GetUInt32();
1565 data.unit_flags = fields[index++].GetUInt32();
1566 data.dynamicflags = fields[index++].GetUInt32();
1567 data.isActive = fields[index++].GetBool();
1568
1569 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
1570 if (!mapEntry)
1571 {
1572 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u) that spawned at not existed map (Id: %u), skipped.", guid, data.mapid);
1573 continue;
1574 }
1575
1576 if (data.spawnMask & ~spawnMasks[data.mapid])
1577 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u) spawnMasks[data.mapid]: %u.", guid, data.spawnMask, data.mapid, spawnMasks[data.mapid]);
1578
1579 bool ok = true;
1580 for (uint32 diff = 0; diff < MAX_TEMPLATE_DIFFICULTY - 1 && ok; ++diff)
1581 {
1582 if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
1583 {
1584 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u) that listed as difficulty %u template (entry: %u) in `creature_template`, skipped.",
1585 guid, diff + 1, data.id);
1586 ok = false;
1587 }
1588 }
1589 if (!ok)
1590 continue;
1591
1592 // -1 no equipment, 0 use default
1593 if (data.equipmentId > 0)
1594 {
1595 if (!GetEquipmentInfo(data.equipmentId))
1596 {
1597 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
1598 data.equipmentId = -1;
1599 }
1600 }
1601
1602 if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
1603 {
1604 if (!mapEntry || !mapEntry->IsDungeon())
1605 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.", guid, data.id);
1606 }
1607
1608 if (data.spawndist < 0.0f)
1609 {
1610 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
1611 data.spawndist = 0.0f;
1612 }
1613 else if (data.movementType == RANDOM_MOTION_TYPE)
1614 {
1615 if (data.spawndist == 0.0f)
1616 {
1617 sLog->outError(LOG_FILTER_SQL, "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);
1618 data.movementType = IDLE_MOTION_TYPE;
1619 }
1620 }
1621 else if (data.movementType == IDLE_MOTION_TYPE)
1622 {
1623 if (data.spawndist != 0.0f)
1624 {
1625 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
1626 data.spawndist = 0.0f;
1627 }
1628 }
1629
1630 if (data.phaseMask == 0)
1631 {
1632 sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
1633 data.phaseMask = 1;
1634 }
1635
1636 // Add to grid if not managed by the game event or pool system
1637 if (gameEvent == 0 && PoolId == 0)
1638 AddCreatureToGrid(guid, &data);
1639
1640 if (!data.zoneId || !data.areaId)
1641 {
1642 uint32 zoneId = 0;
1643 uint32 areaId = 0;
1644
1645 //sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
1646 //WorldDatabase.PExecute("UPDATE creature SET zoneId = %u, areaId = %u WHERE guid = %u", zoneId, areaId, guid);
1647 }
1648
1649 ++count;
1650
1651 } while (result->NextRow());
1652
1653 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creatures in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
1654}
1655
1656void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
1657{
1658 uint32 mask = data->spawnMask;
1659 for (uint32 i = 0; mask != 0; i++, mask >>= 1)
1660 {
1661 if (mask & 1)
1662 {
1663 CellCoord cellCoord = MoPCore::ComputeCellCoord(data->posX, data->posY);
1664 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1665 cell_guids.creatures.insert(guid);
1666 }
1667 }
1668}
1669
1670void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
1671{
1672 uint32 mask = data->spawnMask;
1673 for (uint32 i = 0; mask != 0; i++, mask >>= 1)
1674 {
1675 if (mask & 1)
1676 {
1677 CellCoord cellCoord = MoPCore::ComputeCellCoord(data->posX, data->posY);
1678 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1679 cell_guids.creatures.erase(guid);
1680 }
1681 }
1682}
1683
1684uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
1685{
1686 GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
1687 if (!goinfo)
1688 return 0;
1689
1690 Map* map = sMapMgr->CreateBaseMap(mapId);
1691 if (!map)
1692 return 0;
1693
1694 uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT);
1695 GameObjectData& data = NewGOData(guid);
1696 data.id = entry;
1697 data.mapid = mapId;
1698 data.posX = x;
1699 data.posY = y;
1700 data.posZ = z;
1701 data.orientation = o;
1702 data.rotation0 = rotation0;
1703 data.rotation1 = rotation1;
1704 data.rotation2 = rotation2;
1705 data.rotation3 = rotation3;
1706 data.spawntimesecs = spawntimedelay;
1707 data.animprogress = 100;
1708 data.spawnMask = 1;
1709 data.go_state = GO_STATE_READY;
1710 data.phaseMask = PHASEMASK_NORMAL;
1711 data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
1712 data.dbData = false;
1713
1714 AddGameobjectToGrid(guid, &data);
1715
1716 // Spawn if necessary (loaded grids only)
1717 // We use spawn coords to spawn
1718 if (!map->Instanceable() && map->IsGridLoaded(x, y))
1719 {
1720 GameObject* go = new GameObject;
1721 if (!go->LoadGameObjectFromDB(guid, map))
1722 {
1723 sLog->outError(LOG_FILTER_GENERAL, "AddGOData: cannot add gameobject entry %u to map", entry);
1724 delete go;
1725 return 0;
1726 }
1727 }
1728
1729 sLog->outDebug(LOG_FILTER_MAPS, "AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, x, y, z, o);
1730
1731 return guid;
1732}
1733
1734bool ObjectMgr::MoveCreData(uint32 guid, uint32 mapId, Position pos)
1735{
1736 CreatureData& data = NewOrExistCreatureData(guid);
1737 if (!data.id)
1738 return false;
1739
1740 RemoveCreatureFromGrid(guid, &data);
1741 if (data.posX == pos.GetPositionX() && data.posY == pos.GetPositionY() && data.posZ == pos.GetPositionZ())
1742 return true;
1743 data.posX = pos.GetPositionX();
1744 data.posY = pos.GetPositionY();
1745 data.posZ = pos.GetPositionZ();
1746 data.orientation = pos.GetOrientation();
1747 AddCreatureToGrid(guid, &data);
1748
1749 // Spawn if necessary (loaded grids only)
1750 if (Map* map = sMapMgr->CreateBaseMap(mapId))
1751 {
1752 // We use spawn coords to spawn
1753 if (!map->Instanceable() && map->IsGridLoaded(data.posX, data.posY))
1754 {
1755 Creature* creature = new Creature;
1756 if (!creature->LoadCreatureFromDB(guid, map))
1757 {
1758 sLog->outError(LOG_FILTER_GENERAL, "AddCreature: cannot add creature entry %u to map", guid);
1759 delete creature;
1760 return false;
1761 }
1762 }
1763 }
1764 return true;
1765}
1766
1767uint32 ObjectMgr::AddCreData(uint32 entry, uint32 /*team*/, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay)
1768{
1769 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
1770 if (!cInfo)
1771 return 0;
1772
1773 uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
1774 CreatureBaseStats const* stats = GetCreatureBaseStats(level, cInfo->unit_class);
1775
1776 uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
1777 CreatureData& data = NewOrExistCreatureData(guid);
1778 data.id = entry;
1779 data.mapid = mapId;
1780 data.displayid = 0;
1781 data.equipmentId = cInfo->equipmentId;
1782 data.posX = x;
1783 data.posY = y;
1784 data.posZ = z;
1785 data.orientation = o;
1786 data.spawntimesecs = spawntimedelay;
1787 data.spawndist = 0;
1788 data.currentwaypoint = 0;
1789 data.curhealth = stats->GenerateHealth(cInfo);
1790 data.curmana = stats->GenerateMana(cInfo);
1791 data.movementType = cInfo->MovementType;
1792 data.spawnMask = 1;
1793 data.phaseMask = PHASEMASK_NORMAL;
1794 data.dbData = false;
1795 data.npcflag = cInfo->npcflag;
1796 data.unit_flags = cInfo->unit_flags;
1797 data.dynamicflags = cInfo->dynamicflags;
1798
1799 AddCreatureToGrid(guid, &data);
1800
1801 // Spawn if necessary (loaded grids only)
1802 if (Map* map = sMapMgr->CreateBaseMap(mapId))
1803 {
1804 // We use spawn coords to spawn
1805 if (!map->Instanceable() && !map->IsRemovalGrid(x, y))
1806 {
1807 Creature* creature = new Creature;
1808 if (!creature->LoadCreatureFromDB(guid, map))
1809 {
1810 sLog->outError(LOG_FILTER_GENERAL, "AddCreature: cannot add creature entry %u to map", entry);
1811 delete creature;
1812 return 0;
1813 }
1814 }
1815 }
1816
1817 return guid;
1818}
1819
1820void ObjectMgr::LoadGameobjects()
1821{
1822 uint32 oldMSTime = getMSTime();
1823
1824 uint32 count = 0;
1825
1826 // 0 1 2 3 4 5 6 7 8
1827 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, zoneId, areaId, position_x, position_y, position_z, orientation, "
1828 // 9 10 11 12 13 14 15 16 17 18 19 20
1829 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, isActive, spawnMask, phaseMask, eventEntry, pool_entry "
1830 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1831 "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");
1832
1833 if (!result)
1834 {
1835 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1836
1837 return;
1838 }
1839
1840 // build single time for check spawnmask
1841 std::map<uint32, uint32> spawnMasks;
1842 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
1843 if (sMapStore.LookupEntry(i))
1844 for (int k = 0; k < MAX_DIFFICULTY; ++k)
1845 if (GetMapDifficultyData(i, Difficulty(k)))
1846 spawnMasks[i] |= (1 << k);
1847
1848 //_gameObjectDataStore.rehash(result->GetRowCount());
1849 do
1850 {
1851 Field* fields = result->Fetch();
1852
1853 uint32 guid = fields[0].GetUInt32();
1854 uint32 entry = fields[1].GetUInt32();
1855
1856 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
1857 if (!gInfo)
1858 {
1859 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
1860 continue;
1861 }
1862
1863 if (!gInfo->displayId)
1864 {
1865 switch (gInfo->type)
1866 {
1867 case GAMEOBJECT_TYPE_TRAP:
1868 case GAMEOBJECT_TYPE_SPELL_FOCUS:
1869 break;
1870 default:
1871 sLog->outError(LOG_FILTER_SQL, "Gameobject (GUID: %u Entry %u GoType: %u) doesn't have a displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
1872 break;
1873 }
1874 }
1875
1876 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
1877 {
1878 sLog->outError(LOG_FILTER_SQL, "Gameobject (GUID: %u Entry %u GoType: %u) has an invalid displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
1879 continue;
1880 }
1881
1882 GameObjectData& data = _gameObjectDataStore[guid];
1883
1884 data.id = entry;
1885 data.mapid = fields[2].GetUInt16();
1886 data.zoneId = fields[3].GetUInt16();
1887 data.areaId = fields[4].GetUInt16();
1888 data.posX = fields[5].GetFloat();
1889 data.posY = fields[6].GetFloat();
1890 data.posZ = fields[7].GetFloat();
1891 data.orientation = fields[8].GetFloat();
1892 data.rotation0 = fields[9].GetFloat();
1893 data.rotation1 = fields[10].GetFloat();
1894 data.rotation2 = fields[11].GetFloat();
1895 data.rotation3 = fields[12].GetFloat();
1896 data.spawntimesecs = fields[13].GetInt32();
1897
1898 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
1899 if (!mapEntry)
1900 {
1901 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.mapid);
1902 continue;
1903 }
1904
1905 if (!data.zoneId || !data.areaId)
1906 {
1907 uint32 zoneId = 0;
1908 uint32 areaId = 0;
1909
1910 //sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
1911 //WorldDatabase.PExecute("UPDATE gameobject SET zoneId = %u, areaId = %u WHERE guid = %u", zoneId, areaId, guid);
1912 }
1913
1914 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
1915 {
1916 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but the gameobejct is marked as despawnable at action.", guid, data.id);
1917 }
1918
1919 data.animprogress = fields[14].GetUInt8();
1920 data.artKit = 0;
1921
1922 uint32 go_state = fields[15].GetUInt8();
1923 if (go_state >= MAX_GO_STATE)
1924 {
1925 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
1926 continue;
1927 }
1928 data.go_state = GOState(go_state);
1929
1930 data.isActive = fields[16].GetBool();
1931
1932 data.spawnMask = fields[17].GetUInt32();
1933
1934 if (data.spawnMask & ~spawnMasks[data.mapid])
1935 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including not supported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid);
1936
1937 data.phaseMask = fields[18].GetUInt16();
1938 int16 gameEvent = fields[19].GetInt8();
1939 uint32 PoolId = fields[20].GetUInt32();
1940
1941 if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
1942 {
1943 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
1944 continue;
1945 }
1946
1947 if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
1948 {
1949 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
1950 continue;
1951 }
1952
1953 if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
1954 {
1955 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
1956 continue;
1957 }
1958
1959 if (data.phaseMask == 0)
1960 {
1961 sLog->outError(LOG_FILTER_SQL, "Table `gameobject` has gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
1962 data.phaseMask = 1;
1963 }
1964
1965 if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1966 AddGameobjectToGrid(guid, &data);
1967 ++count;
1968 } while (result->NextRow());
1969
1970 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu gameobjects in %u ms", (unsigned long)_gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
1971}
1972
1973void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
1974{
1975 uint32 mask = data->spawnMask;
1976 for (uint32 i = 0; mask != 0; i++, mask >>= 1)
1977 {
1978 if (mask & 1)
1979 {
1980 CellCoord cellCoord = MoPCore::ComputeCellCoord(data->posX, data->posY);
1981 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1982 cell_guids.gameobjects.insert(guid);
1983 }
1984 }
1985}
1986
1987void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
1988{
1989 uint32 mask = data->spawnMask;
1990 for (uint32 i = 0; mask != 0; i++, mask >>= 1)
1991 {
1992 if (mask & 1)
1993 {
1994 CellCoord cellCoord = MoPCore::ComputeCellCoord(data->posX, data->posY);
1995 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1996 cell_guids.gameobjects.erase(guid);
1997 }
1998 }
1999}
2000
2001Player* ObjectMgr::GetPlayerByLowGUID(uint32 lowguid) const
2002{
2003 uint64 guid = MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER);
2004 return ObjectAccessor::FindPlayer(guid);
2005}
2006
2007// name must be checked to correctness (if received) before call this function
2008uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
2009{
2010 uint64 guid = 0;
2011
2012 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME);
2013
2014 stmt->setString(0, name);
2015
2016 PreparedQueryResult result = CharacterDatabase.Query(stmt);
2017
2018 if (result)
2019 guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
2020
2021 return guid;
2022}
2023
2024bool ObjectMgr::GetPlayerNameByGUID(uint64 guid, std::string &name) const
2025{
2026 // prevent DB access for online player
2027 if (Player* player = ObjectAccessor::FindPlayer(guid))
2028 {
2029 name = player->GetName();
2030 return true;
2031 }
2032
2033 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME);
2034
2035 stmt->setUInt32(0, GUID_LOPART(guid));
2036
2037 PreparedQueryResult result = CharacterDatabase.Query(stmt);
2038
2039 if (result)
2040 {
2041 name = (*result)[0].GetString();
2042 return true;
2043 }
2044
2045 return false;
2046}
2047
2048uint32 ObjectMgr::GetPlayerTeamByGUID(uint64 guid) const
2049{
2050 // prevent DB access for online player
2051 if (Player* player = ObjectAccessor::FindPlayer(guid))
2052 {
2053 return Player::TeamForRace(player->getRace());
2054 }
2055
2056 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_RACE);
2057
2058 stmt->setUInt32(0, GUID_LOPART(guid));
2059
2060 PreparedQueryResult result = CharacterDatabase.Query(stmt);
2061
2062 if (result)
2063 {
2064 uint8 race = (*result)[0].GetUInt8();
2065 return Player::TeamForRace(race);
2066 }
2067
2068 return 0;
2069}
2070
2071uint32 ObjectMgr::GetPlayerAccountIdByGUID(uint64 guid) const
2072{
2073 // prevent DB access for online player
2074 if (Player* player = ObjectAccessor::FindPlayer(guid))
2075 {
2076 return player->GetSession()->GetAccountId();
2077 }
2078
2079 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BY_GUID);
2080
2081 stmt->setUInt32(0, GUID_LOPART(guid));
2082
2083 PreparedQueryResult result = CharacterDatabase.Query(stmt);
2084
2085 if (result)
2086 {
2087 uint32 acc = (*result)[0].GetUInt32();
2088 return acc;
2089 }
2090
2091 return 0;
2092}
2093
2094uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
2095{
2096 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BY_NAME);
2097
2098 stmt->setString(0, name);
2099
2100 PreparedQueryResult result = CharacterDatabase.Query(stmt);
2101
2102 if (result)
2103 {
2104 uint32 acc = (*result)[0].GetUInt32();
2105 return acc;
2106 }
2107
2108 return 0;
2109}
2110
2111void ObjectMgr::LoadItemLocales()
2112{
2113 uint32 oldMSTime = getMSTime();
2114
2115 _itemLocaleStore.clear(); // need for reload case
2116
2117 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, name_loc9, description_loc9, name_loc10, description_loc10 FROM locales_item");
2118
2119 if (!result)
2120 return;
2121
2122 do
2123 {
2124 Field* fields = result->Fetch();
2125
2126 uint32 entry = fields[0].GetUInt32();
2127
2128 ItemLocale& data = _itemLocaleStore[entry];
2129
2130 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
2131 {
2132 LocaleConstant locale = (LocaleConstant)i;
2133 AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.Name);
2134 AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.Description);
2135 }
2136 } while (result->NextRow());
2137
2138 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu Item locale strings in %u ms", (unsigned long)_itemLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
2139}
2140
2141void FillItemDamageFields(float* minDamage, float* maxDamage, float* dps, uint32 itemLevel, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32 delay, float statScalingFactor, uint32 inventoryType, uint32 flags2)
2142{
2143 *minDamage = *maxDamage = *dps = 0.0f;
2144 if (itemClass != ITEM_CLASS_WEAPON || quality > ITEM_QUALITY_ARTIFACT)
2145 return;
2146
2147 DBCStorage<ItemDamageEntry>* store = NULL;
2148 // get the right store here
2149 if (inventoryType > 0xD + 13)
2150 return;
2151
2152 switch (inventoryType)
2153 {
2154 case INVTYPE_AMMO:
2155 store = &sItemDamageAmmoStore;
2156 break;
2157 case INVTYPE_2HWEAPON:
2158 if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON)
2159 store = &sItemDamageTwoHandCasterStore;
2160 else
2161 store = &sItemDamageTwoHandStore;
2162 break;
2163 case INVTYPE_RANGED:
2164 case INVTYPE_THROWN:
2165 case INVTYPE_RANGEDRIGHT:
2166 switch (itemSubClass)
2167 {
2168 case ITEM_SUBCLASS_WEAPON_WAND:
2169 store = &sItemDamageWandStore;
2170 break;
2171 case ITEM_SUBCLASS_WEAPON_THROWN:
2172 store = &sItemDamageThrownStore;
2173 break;
2174 case ITEM_SUBCLASS_WEAPON_BOW:
2175 case ITEM_SUBCLASS_WEAPON_GUN:
2176 case ITEM_SUBCLASS_WEAPON_CROSSBOW:
2177 store = &sItemDamageRangedStore;
2178 break;
2179 default:
2180 return;
2181 }
2182 break;
2183 case INVTYPE_WEAPON:
2184 case INVTYPE_WEAPONMAINHAND:
2185 case INVTYPE_WEAPONOFFHAND:
2186 if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON)
2187 store = &sItemDamageOneHandCasterStore;
2188 else
2189 store = &sItemDamageOneHandStore;
2190 break;
2191 default:
2192 return;
2193 }
2194
2195 if (!store)
2196 return;
2197
2198 ItemDamageEntry const* damageInfo = store->LookupEntry(itemLevel);
2199 if (!damageInfo)
2200 return;
2201
2202 *dps = damageInfo->DPS[quality];
2203 float avgDamage = *dps * delay * 0.001f;
2204 *minDamage = (statScalingFactor * -0.5f + 1.0f) * avgDamage;
2205 *maxDamage = floor(float(avgDamage * (statScalingFactor * 0.5f + 1.0f) + 0.5f));
2206}
2207
2208uint32 FillItemArmor(uint32 itemlevel, uint32 itemClass, uint32 itemSubclass, uint32 quality, uint32 inventoryType)
2209{
2210 if (quality > ITEM_QUALITY_ARTIFACT)
2211 return 0;
2212
2213 if (itemClass != ITEM_CLASS_ARMOR)
2214 return 0;
2215
2216 // all items but shields
2217 if (itemSubclass != ITEM_SUBCLASS_ARMOR_SHIELD)
2218 {
2219 ItemArmorQualityEntry const* armorQuality = sItemArmorQualityStore.LookupEntry(itemlevel);
2220 ItemArmorTotalEntry const* armorTotal = sItemArmorTotalStore.LookupEntry(itemlevel);
2221 if (!armorQuality || !armorTotal)
2222 return 0;
2223
2224 if (inventoryType == INVTYPE_ROBE)
2225 inventoryType = INVTYPE_CHEST;
2226
2227 ArmorLocationEntry const* location = sArmorLocationStore.LookupEntry(inventoryType);
2228 if (!location)
2229 return 0;
2230
2231 if (itemSubclass < ITEM_SUBCLASS_ARMOR_CLOTH || itemSubclass > ITEM_SUBCLASS_ARMOR_PLATE)
2232 return 0;
2233
2234 return uint32(armorQuality->Value[quality] * armorTotal->Value[itemSubclass - 1] * location->Value[itemSubclass - 1] + 0.5f);
2235 }
2236
2237 // shields
2238 ItemArmorShieldEntry const* shield = sItemArmorShieldStore.LookupEntry(itemlevel);
2239 if (!shield)
2240 return 0;
2241
2242 return uint32(shield->Value[quality] + 0.5f);
2243}
2244
2245uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
2246{
2247 if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON)
2248 return 0;
2249
2250 static float const qualityMultipliers[MAX_ITEM_QUALITY] =
2251 {
2252 1.0f, 1.0f, 1.0f, 1.17f, 1.37f, 1.68f, 0.0f, 0.0f
2253 };
2254
2255 static float const armorMultipliers[MAX_INVTYPE] =
2256 {
2257 0.00f, // INVTYPE_NON_EQUIP
2258 0.59f, // INVTYPE_HEAD
2259 0.00f, // INVTYPE_NECK
2260 0.59f, // INVTYPE_SHOULDERS
2261 0.00f, // INVTYPE_BODY
2262 1.00f, // INVTYPE_CHEST
2263 0.35f, // INVTYPE_WAIST
2264 0.75f, // INVTYPE_LEGS
2265 0.49f, // INVTYPE_FEET
2266 0.35f, // INVTYPE_WRISTS
2267 0.35f, // INVTYPE_HANDS
2268 0.00f, // INVTYPE_FINGER
2269 0.00f, // INVTYPE_TRINKET
2270 0.00f, // INVTYPE_WEAPON
2271 1.00f, // INVTYPE_SHIELD
2272 0.00f, // INVTYPE_RANGED
2273 0.00f, // INVTYPE_CLOAK
2274 0.00f, // INVTYPE_2HWEAPON
2275 0.00f, // INVTYPE_BAG
2276 0.00f, // INVTYPE_TABARD
2277 1.00f, // INVTYPE_ROBE
2278 0.00f, // INVTYPE_WEAPONMAINHAND
2279 0.00f, // INVTYPE_WEAPONOFFHAND
2280 0.00f, // INVTYPE_HOLDABLE
2281 0.00f, // INVTYPE_AMMO
2282 0.00f, // INVTYPE_THROWN
2283 0.00f, // INVTYPE_RANGEDRIGHT
2284 0.00f, // INVTYPE_QUIVER
2285 0.00f, // INVTYPE_RELIC
2286 };
2287
2288 static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] =
2289 {
2290 0.89f, // ITEM_SUBCLASS_WEAPON_AXE
2291 1.03f, // ITEM_SUBCLASS_WEAPON_AXE2
2292 0.77f, // ITEM_SUBCLASS_WEAPON_BOW
2293 0.77f, // ITEM_SUBCLASS_WEAPON_GUN
2294 0.89f, // ITEM_SUBCLASS_WEAPON_MACE
2295 1.03f, // ITEM_SUBCLASS_WEAPON_MACE2
2296 1.03f, // ITEM_SUBCLASS_WEAPON_POLEARM
2297 0.89f, // ITEM_SUBCLASS_WEAPON_SWORD
2298 1.03f, // ITEM_SUBCLASS_WEAPON_SWORD2
2299 0.00f, // ITEM_SUBCLASS_WEAPON_Obsolete
2300 1.03f, // ITEM_SUBCLASS_WEAPON_STAFF
2301 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC
2302 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2
2303 0.64f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON
2304 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
2305 0.64f, // ITEM_SUBCLASS_WEAPON_DAGGER
2306 0.64f, // ITEM_SUBCLASS_WEAPON_THROWN
2307 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR
2308 0.77f, // ITEM_SUBCLASS_WEAPON_CROSSBOW
2309 0.64f, // ITEM_SUBCLASS_WEAPON_WAND
2310 0.64f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE
2311 };
2312
2313 float levelPenalty = 1.0f;
2314 if (itemLevel <= 28)
2315 levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f;
2316
2317 if (itemClass == ITEM_CLASS_ARMOR)
2318 {
2319 if (inventoryType > INVTYPE_ROBE)
2320 return 0;
2321
2322 return 5 * uint32(23.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty + 0.5f);
2323 }
2324
2325 return 5 * uint32(17.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty + 0.5f);
2326};
2327
2328void FillDisenchantFields(uint32* disenchantID, uint32* requiredDisenchantSkill, ItemTemplate const& itemTemplate)
2329{
2330 *disenchantID = 0;
2331 *(int32*)requiredDisenchantSkill = -1;
2332 if ((itemTemplate.Flags & (ITEM_PROTO_FLAG_CONJURED | ITEM_PROTO_FLAG_UNK6)) ||
2333 itemTemplate.Bonding == BIND_QUEST_ITEM || itemTemplate.Area || itemTemplate.Map ||
2334 itemTemplate.Stackable > 1 ||
2335 itemTemplate.Quality < ITEM_QUALITY_UNCOMMON || itemTemplate.Quality > ITEM_QUALITY_EPIC ||
2336 !(itemTemplate.Class == ITEM_CLASS_ARMOR || itemTemplate.Class == ITEM_CLASS_WEAPON) ||
2337 !(Item::GetSpecialPrice(&itemTemplate) || sItemCurrencyCostStore.LookupEntry(itemTemplate.ItemId)))
2338 return;
2339
2340 for (uint32 i = 0; i < sItemDisenchantLootStore.GetNumRows(); ++i)
2341 {
2342 ItemDisenchantLootEntry const* disenchant = sItemDisenchantLootStore.LookupEntry(i);
2343 if (!disenchant)
2344 continue;
2345
2346 if (disenchant->ItemClass == itemTemplate.Class &&
2347 disenchant->ItemQuality == itemTemplate.Quality &&
2348 disenchant->MinItemLevel <= itemTemplate.ItemLevel &&
2349 disenchant->MaxItemLevel >= itemTemplate.ItemLevel)
2350 {
2351 if (disenchant->Id == 60 || disenchant->Id == 61) // epic item disenchant ilvl range 66-99 (classic)
2352 {
2353 if (itemTemplate.RequiredLevel > 60 || itemTemplate.RequiredSkillRank > 300)
2354 continue; // skip to epic item disenchant ilvl range 90-199 (TBC)
2355 }
2356 else if (disenchant->Id == 66 || disenchant->Id == 67) // epic item disenchant ilvl range 90-199 (TBC)
2357 {
2358 if (itemTemplate.RequiredLevel <= 60 || (itemTemplate.RequiredSkill && itemTemplate.RequiredSkillRank <= 300))
2359 continue;
2360 }
2361
2362 *disenchantID = disenchant->Id;
2363 *requiredDisenchantSkill = disenchant->RequiredDisenchantSkill;
2364 return;
2365 }
2366 }
2367}
2368
2369void ObjectMgr::LoadItemTemplates()
2370{
2371 uint32 oldMSTime = getMSTime();
2372 uint32 sparseCount = 0;
2373 uint32 dbCount = 0;
2374
2375 for (uint32 itemId = 0; itemId < sItemSparseStore.GetNumRows(); ++itemId)
2376 {
2377 ItemSparseEntry const* sparse = sItemSparseStore.LookupEntry(itemId);
2378 ItemEntry const* db2Data = sItemStore.LookupEntry(itemId);
2379 if (!sparse || !db2Data)
2380 continue;
2381
2382 ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
2383
2384 itemTemplate.ItemId = itemId;
2385 itemTemplate.Class = db2Data->Class;
2386 itemTemplate.SubClass = db2Data->SubClass;
2387 itemTemplate.SoundOverrideSubclass = db2Data->SoundOverrideSubclass;
2388 itemTemplate.Name1 = sparse->Name;
2389 itemTemplate.DisplayInfoID = db2Data->DisplayId;
2390 itemTemplate.Quality = sparse->Quality;
2391 itemTemplate.Flags = sparse->Flags;
2392 itemTemplate.Flags2 = sparse->Flags2;
2393 itemTemplate.Flags3 = sparse->Unk540_1;
2394 itemTemplate.Unk430_1 = sparse->Unk430_1;
2395 itemTemplate.Unk430_2 = sparse->Unk430_2;
2396 itemTemplate.BuyCount = std::max(sparse->BuyCount, 1u);
2397 itemTemplate.BuyPrice = sparse->BuyPrice;
2398 itemTemplate.SellPrice = sparse->SellPrice;
2399 itemTemplate.InventoryType = db2Data->InventoryType;
2400 itemTemplate.AllowableClass = sparse->AllowableClass;
2401 itemTemplate.AllowableRace = sparse->AllowableRace;
2402 itemTemplate.ItemLevel = sparse->ItemLevel;
2403 itemTemplate.RequiredLevel = sparse->RequiredLevel;
2404 itemTemplate.RequiredSkill = sparse->RequiredSkill;
2405 itemTemplate.RequiredSkillRank = sparse->RequiredSkillRank;
2406 itemTemplate.RequiredSpell = sparse->RequiredSpell;
2407 itemTemplate.RequiredHonorRank = sparse->RequiredHonorRank;
2408 itemTemplate.RequiredCityRank = sparse->RequiredCityRank;
2409 itemTemplate.RequiredReputationFaction = sparse->RequiredReputationFaction;
2410 itemTemplate.RequiredReputationRank = sparse->RequiredReputationRank;
2411 itemTemplate.MaxCount = sparse->MaxCount;
2412 itemTemplate.Stackable = sparse->Stackable;
2413 itemTemplate.ContainerSlots = sparse->ContainerSlots;
2414 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
2415 {
2416 itemTemplate.ItemStat[i].ItemStatType = sparse->ItemStatType[i];
2417 itemTemplate.ItemStat[i].ItemStatValue = sparse->ItemStatValue[i];
2418 itemTemplate.ItemStat[i].ItemStatUnk1 = sparse->ItemStatUnk1[i];
2419 itemTemplate.ItemStat[i].ItemStatUnk2 = sparse->ItemStatUnk2[i];
2420 }
2421
2422 itemTemplate.ScalingStatDistribution = sparse->ScalingStatDistribution;
2423
2424 // cache item damage
2425 FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, sparse->ItemLevel,
2426 db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->Delay, sparse->StatScalingFactor,
2427 sparse->InventoryType, sparse->Flags2);
2428
2429 itemTemplate.DamageType = sparse->DamageType;
2430 itemTemplate.Armor = FillItemArmor(sparse->ItemLevel, db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->InventoryType);
2431 itemTemplate.Delay = sparse->Delay;
2432 itemTemplate.RangedModRange = sparse->RangedModRange;
2433 for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
2434 {
2435 itemTemplate.Spells[i].SpellId = sparse->SpellId[i];
2436 itemTemplate.Spells[i].SpellTrigger = sparse->SpellTrigger[i];
2437 itemTemplate.Spells[i].SpellCharges = sparse->SpellCharges[i];
2438 itemTemplate.Spells[i].SpellCooldown = sparse->SpellCooldown[i];
2439 itemTemplate.Spells[i].SpellCategory = sparse->SpellCategory[i];
2440 itemTemplate.Spells[i].SpellCategoryCooldown = sparse->SpellCategoryCooldown[i];
2441 }
2442
2443 itemTemplate.SpellPPMRate = 0.0f;
2444 itemTemplate.Bonding = sparse->Bonding;
2445 itemTemplate.Description = sparse->Description;
2446 itemTemplate.PageText = sparse->PageText;
2447 itemTemplate.LanguageID = sparse->LanguageID;
2448 itemTemplate.PageMaterial = sparse->PageMaterial;
2449 itemTemplate.StartQuest = sparse->StartQuest;
2450 itemTemplate.LockID = sparse->LockID;
2451 itemTemplate.Material = sparse->Material;
2452 itemTemplate.Sheath = sparse->Sheath;
2453 itemTemplate.RandomProperty = sparse->RandomProperty;
2454 itemTemplate.RandomSuffix = sparse->RandomSuffix;
2455 itemTemplate.ItemSet = sparse->ItemSet;
2456 itemTemplate.MaxDurability = FillMaxDurability(db2Data->Class, db2Data->SubClass, sparse->InventoryType, sparse->Quality, sparse->ItemLevel);
2457 itemTemplate.Area = sparse->Area;
2458 itemTemplate.Map = sparse->Map;
2459 itemTemplate.BagFamily = sparse->BagFamily;
2460 itemTemplate.TotemCategory = sparse->TotemCategory;
2461 for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
2462 {
2463 itemTemplate.Socket[i].Color = sparse->Color[i];
2464 itemTemplate.Socket[i].Content = sparse->Content[i];
2465 }
2466
2467 itemTemplate.socketBonus = sparse->SocketBonus;
2468 itemTemplate.GemProperties = sparse->GemProperties;
2469 FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate);
2470
2471 itemTemplate.ArmorDamageModifier = sparse->ArmorDamageModifier;
2472 itemTemplate.Duration = sparse->Duration;
2473 itemTemplate.ItemLimitCategory = sparse->ItemLimitCategory;
2474 itemTemplate.HolidayId = sparse->HolidayId;
2475 itemTemplate.StatScalingFactor = sparse->StatScalingFactor;
2476 itemTemplate.CurrencySubstitutionId = sparse->CurrencySubstitutionId;
2477 itemTemplate.CurrencySubstitutionCount = sparse->CurrencySubstitutionCount;
2478 itemTemplate.ScriptId = 0;
2479 itemTemplate.FoodType = 0;
2480 itemTemplate.MinMoneyLoot = 0;
2481 itemTemplate.MaxMoneyLoot = 0;
2482 ++sparseCount;
2483 }
2484
2485 // Load missing items from item_template AND overwrite data from Item-sparse.db2 (item_template is supposed to contain Item-sparse.adb data)
2486 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
2487 QueryResult result = WorldDatabase.Query("SELECT entry, Class, SubClass, SoundOverrideSubClass, Name, DisplayId, Quality, Flags, FlagsExtra, Unk430_1, Unk430_2, BuyCount, BuyPrice, SellPrice, "
2488 // 14 15 16 17 18 19 20 21
2489 "InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, RequiredSpell, "
2490 // 22 23 24 25 26 27 28
2491 "RequiredHonorRank, RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, MaxCount, Stackable, ContainerSlots, "
2492 // 29 30 31 32 33 34 35 36
2493 "stat_type1, stat_value1, stat_unk1_1, stat_unk2_1, stat_type2, stat_value2, stat_unk1_2, stat_unk2_2, "
2494 // 37 38 39 40 41 42 43 44
2495 "stat_type3, stat_value3, stat_unk1_3, stat_unk2_3, stat_type4, stat_value4, stat_unk1_4, stat_unk2_4, "
2496 // 45 46 47 48 49 50 51 52
2497 "stat_type5, stat_value5, stat_unk1_5, stat_unk2_5, stat_type6, stat_value6, stat_unk1_6, stat_unk2_6, "
2498 // 53 54 55 56 57 58 59 60
2499 "stat_type7, stat_value7, stat_unk1_7, stat_unk2_7, stat_type8, stat_value8, stat_unk1_8, stat_unk2_8, "
2500 // 61 62 63 64 65 66 67 68
2501 "stat_type9, stat_value9, stat_unk1_9, stat_unk2_9, stat_type10, stat_value10, stat_unk1_10, stat_unk2_10, "
2502 // 69 70 71 72
2503 "ScalingStatDistribution, DamageType, Delay, RangedModRange, "
2504 // 73 74 75 76 77 78
2505 "spellid_1, spelltrigger_1, spellcharges_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, "
2506 // 79 80 81 82 83 84
2507 "spellid_2, spelltrigger_2, spellcharges_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, "
2508 // 85 86 87 88 89 90
2509 "spellid_3, spelltrigger_3, spellcharges_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, "
2510 // 91 92 93 94 95 96
2511 "spellid_4, spelltrigger_4, spellcharges_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, "
2512 // 97 98 99 100 101 102
2513 "spellid_5, spelltrigger_5, spellcharges_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, "
2514 // 103 104 105 106 107 108 109 110
2515 "Bonding, Description, PageText, LanguageID, PageMaterial, StartQuest, LockID, Material, "
2516 // 111 112 113 114 115 116 117 118
2517 "Sheath, RandomProperty, RandomSuffix, ItemSet, Area, Map, BagFamily, TotemCategory, "
2518 // 119 120 121 122 123 124 125
2519 "SocketColor_1, SocketContent_1, SocketColor_2, SocketContent_2, SocketColor_3, SocketContent_3, SocketBonus, "
2520 // 126 127 128 129 130 131
2521 "GemProperties, ArmorDamageModifier, Duration, ItemLimitCategory, HolidayId, StatScalingFactor, "
2522 // 132 133
2523 "CurrencySubstitutionId, CurrencySubstitutionCount "
2524 "FROM item_template");
2525
2526 if (result)
2527 {
2528 do
2529 {
2530 Field* fields = result->Fetch();
2531 uint32 itemId = fields[0].GetUInt32();
2532 if (_itemTemplateStore.find(itemId) != _itemTemplateStore.end())
2533 continue;
2534
2535 ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
2536
2537 itemTemplate.ItemId = itemId;
2538 itemTemplate.Class = uint32(fields[1].GetUInt8());
2539 itemTemplate.SubClass = uint32(fields[2].GetUInt8());
2540 itemTemplate.SoundOverrideSubclass = int32(fields[3].GetInt8());
2541 itemTemplate.Name1 = fields[4].GetString();
2542 itemTemplate.DisplayInfoID = fields[5].GetUInt32();
2543 itemTemplate.Quality = uint32(fields[6].GetUInt8());
2544 itemTemplate.Flags = uint32(fields[7].GetInt64());
2545 itemTemplate.Flags2 = fields[8].GetUInt32();
2546 itemTemplate.Unk430_1 = fields[9].GetFloat();
2547 itemTemplate.Unk430_2 = fields[10].GetFloat();
2548 itemTemplate.BuyCount = uint32(fields[11].GetUInt8());
2549 itemTemplate.BuyPrice = int32(fields[12].GetInt64());
2550 itemTemplate.SellPrice = fields[13].GetUInt32();
2551
2552 itemTemplate.InventoryType = uint32(fields[14].GetUInt8());
2553 itemTemplate.AllowableClass = fields[15].GetInt32();
2554 itemTemplate.AllowableRace = fields[16].GetInt32();
2555 itemTemplate.ItemLevel = uint32(fields[17].GetUInt16());
2556 itemTemplate.RequiredLevel = uint32(fields[18].GetUInt8());
2557 itemTemplate.RequiredSkill = uint32(fields[19].GetUInt16());
2558 itemTemplate.RequiredSkillRank = uint32(fields[20].GetUInt16());
2559 itemTemplate.RequiredSpell = fields[21].GetUInt32();
2560 itemTemplate.RequiredHonorRank = fields[22].GetUInt32();
2561 itemTemplate.RequiredCityRank = fields[23].GetUInt32();
2562 itemTemplate.RequiredReputationFaction = uint32(fields[24].GetUInt16());
2563 itemTemplate.RequiredReputationRank = uint32(fields[25].GetUInt16());
2564 itemTemplate.MaxCount = fields[26].GetInt32();
2565 itemTemplate.Stackable = fields[27].GetInt32();
2566 itemTemplate.ContainerSlots = uint32(fields[28].GetUInt8());
2567 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
2568 {
2569 itemTemplate.ItemStat[i].ItemStatType = uint32(fields[29 + i * 4 + 0].GetUInt8());
2570 itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i * 4 + 1].GetInt16());
2571 itemTemplate.ItemStat[i].ItemStatUnk1 = fields[29 + i * 4 + 2].GetInt32();
2572 itemTemplate.ItemStat[i].ItemStatUnk2 = fields[29 + i * 4 + 3].GetInt32();
2573 }
2574
2575 itemTemplate.ScalingStatDistribution = uint32(fields[69].GetUInt16());
2576
2577 // cache item damage
2578 FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, itemTemplate.ItemLevel,
2579 itemTemplate.Class, itemTemplate.SubClass, itemTemplate.Quality, fields[71].GetUInt16(),
2580 fields[131].GetFloat(), itemTemplate.InventoryType, itemTemplate.Flags2);
2581
2582 itemTemplate.DamageType = fields[70].GetUInt8();
2583 itemTemplate.Armor = FillItemArmor(itemTemplate.ItemLevel, itemTemplate.Class,
2584 itemTemplate.SubClass, itemTemplate.Quality,
2585 itemTemplate.InventoryType);
2586
2587 itemTemplate.Delay = fields[71].GetUInt16();
2588 itemTemplate.RangedModRange = fields[72].GetFloat();
2589 for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
2590 {
2591 itemTemplate.Spells[i].SpellId = fields[73 + 6 * i + 0].GetInt32();
2592 itemTemplate.Spells[i].SpellTrigger = uint32(fields[73 + 6 * i + 1].GetUInt8());
2593 itemTemplate.Spells[i].SpellCharges = int32(fields[73 + 6 * i + 2].GetInt16());
2594 itemTemplate.Spells[i].SpellCooldown = fields[73 + 6 * i + 3].GetInt32();
2595 itemTemplate.Spells[i].SpellCategory = uint32(fields[73 + 6 * i + 4].GetUInt16());
2596 itemTemplate.Spells[i].SpellCategoryCooldown = fields[73 + 6 * i + 5].GetInt32();
2597
2598 // Add spell into the store for correct handling
2599 if (itemTemplate.Spells[i].SpellCategory > 0)
2600 sSpellCategoryStore[itemTemplate.Spells[i].SpellCategory].insert(itemTemplate.Spells[i].SpellId);
2601 }
2602
2603 itemTemplate.SpellPPMRate = 0.0f;
2604 itemTemplate.Bonding = uint32(fields[103].GetUInt8());
2605 itemTemplate.Description = fields[104].GetString();
2606 itemTemplate.PageText = fields[105].GetUInt32();
2607 itemTemplate.LanguageID = uint32(fields[106].GetUInt8());
2608 itemTemplate.PageMaterial = uint32(fields[107].GetUInt8());
2609 itemTemplate.StartQuest = fields[108].GetUInt32();
2610 itemTemplate.LockID = fields[109].GetUInt32();
2611 itemTemplate.Material = int32(fields[110].GetInt8());
2612 itemTemplate.Sheath = uint32(fields[111].GetUInt8());
2613 itemTemplate.RandomProperty = fields[112].GetUInt32();
2614 itemTemplate.RandomSuffix = fields[113].GetInt32();
2615 itemTemplate.ItemSet = fields[114].GetUInt32();
2616 itemTemplate.MaxDurability = FillMaxDurability(itemTemplate.Class, itemTemplate.SubClass,
2617 itemTemplate.InventoryType, itemTemplate.Quality, itemTemplate.ItemLevel);
2618
2619 itemTemplate.Area = fields[115].GetUInt32();
2620 itemTemplate.Map = uint32(fields[116].GetUInt16());
2621 itemTemplate.BagFamily = fields[117].GetUInt32();
2622 itemTemplate.TotemCategory = fields[118].GetUInt32();
2623 for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
2624 {
2625 itemTemplate.Socket[i].Color = uint32(fields[119 + i * 2].GetUInt8());
2626 itemTemplate.Socket[i].Content = fields[119 + i * 2 + 1].GetUInt32();
2627 }
2628
2629 itemTemplate.socketBonus = fields[125].GetUInt32();
2630 itemTemplate.GemProperties = fields[126].GetUInt32();
2631 FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate);
2632
2633 itemTemplate.ArmorDamageModifier = fields[127].GetFloat();
2634 itemTemplate.Duration = fields[128].GetUInt32();
2635 itemTemplate.ItemLimitCategory = uint32(fields[129].GetInt16());
2636 itemTemplate.HolidayId = fields[130].GetUInt32();
2637 itemTemplate.StatScalingFactor = fields[131].GetFloat();
2638 itemTemplate.CurrencySubstitutionId = fields[132].GetInt32();
2639 itemTemplate.CurrencySubstitutionCount = fields[133].GetInt32();
2640 itemTemplate.ScriptId = 0;
2641 itemTemplate.FoodType = 0;
2642 itemTemplate.MinMoneyLoot = 0;
2643 itemTemplate.MaxMoneyLoot = 0;
2644 ++dbCount;
2645 } while (result->NextRow());
2646 }
2647
2648 // Check if item templates for DBC referenced character start outfit are present
2649 std::set<uint32> notFoundOutfit;
2650 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
2651 {
2652 CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
2653 if (!entry)
2654 continue;
2655
2656 for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
2657 {
2658 if (entry->ItemId[j] <= 0)
2659 continue;
2660
2661 uint32 item_id = entry->ItemId[j];
2662
2663 if (!GetItemTemplate(item_id))
2664 notFoundOutfit.insert(item_id);
2665 }
2666 }
2667
2668 for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
2669 sLog->outError(LOG_FILTER_SQL, "Item (Entry: %u) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr);
2670
2671 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item templates from Item-sparse.db2 and %u from database in %u ms", sparseCount, dbCount, GetMSTimeDiffToNow(oldMSTime));
2672}
2673
2674void ObjectMgr::LoadItemTemplateAddon()
2675{
2676 uint32 oldMSTime = getMSTime();
2677 uint32 count = 0;
2678
2679 QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance FROM item_template_addon");
2680 if (result)
2681 {
2682 do
2683 {
2684 Field* fields = result->Fetch();
2685 uint32 itemId = fields[0].GetUInt32();
2686 if (!GetItemTemplate(itemId))
2687 {
2688 sLog->outError(LOG_FILTER_SQL, "Item %u specified in `item_template_addon` does not exist, skipped.", itemId);
2689 continue;
2690 }
2691
2692 uint32 minMoneyLoot = fields[3].GetUInt32();
2693 uint32 maxMoneyLoot = fields[4].GetUInt32();
2694 if (minMoneyLoot > maxMoneyLoot)
2695 {
2696 sLog->outError(LOG_FILTER_SQL, "Minimum money loot specified in `item_template_addon` for item %u was greater than maximum amount, swapping.", itemId);
2697 std::swap(minMoneyLoot, maxMoneyLoot);
2698 }
2699 ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
2700 itemTemplate.FlagsCu = fields[1].GetUInt32();
2701 itemTemplate.FoodType = fields[2].GetUInt8();
2702 itemTemplate.MinMoneyLoot = minMoneyLoot;
2703 itemTemplate.MaxMoneyLoot = maxMoneyLoot;
2704 itemTemplate.SpellPPMRate = fields[5].GetFloat();
2705 ++count;
2706 } while (result->NextRow());
2707 }
2708 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item addon templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
2709}
2710
2711void ObjectMgr::LoadItemScriptNames()
2712{
2713 uint32 oldMSTime = getMSTime();
2714 uint32 count = 0;
2715
2716 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names");
2717 if (result)
2718 {
2719 do
2720 {
2721 Field* fields = result->Fetch();
2722 uint32 itemId = fields[0].GetUInt32();
2723 if (!GetItemTemplate(itemId))
2724 {
2725 sLog->outError(LOG_FILTER_SQL, "Item %u specified in `item_script_names` does not exist, skipped.", itemId);
2726 continue;
2727 }
2728
2729 _itemTemplateStore[itemId].ScriptId = GetScriptId(fields[1].GetCString());
2730 ++count;
2731 } while (result->NextRow());
2732 }
2733
2734 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
2735}
2736ItemTemplate const* ObjectMgr::GetItemTemplate(uint32 entry)
2737{
2738 ItemTemplateContainer::const_iterator itr = _itemTemplateStore.find(entry);
2739 if (itr != _itemTemplateStore.end())
2740 return &(itr->second);
2741 return NULL;
2742}
2743
2744void ObjectMgr::LoadVehicleTemplateAccessories()
2745{
2746 uint32 oldMSTime = getMSTime();
2747
2748 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
2749
2750 uint32 count = 0;
2751
2752 // 0 1 2 3 4 5
2753 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
2754
2755 if (!result)
2756 {
2757 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
2758
2759 return;
2760 }
2761
2762 do
2763 {
2764 Field* fields = result->Fetch();
2765
2766 uint32 uiEntry = fields[0].GetUInt32();
2767 uint32 uiAccessory = fields[1].GetUInt32();
2768 int8 uiSeat = int8(fields[2].GetInt8());
2769 bool bMinion = fields[3].GetBool();
2770 uint8 uiSummonType = fields[4].GetUInt8();
2771 uint32 uiSummonTimer = fields[5].GetUInt32();
2772
2773 if (!sObjectMgr->GetCreatureTemplate(uiEntry))
2774 {
2775 sLog->outError(LOG_FILTER_SQL, "Table `vehicle_template_accessory`: creature template entry %u does not exist.", uiEntry);
2776 continue;
2777 }
2778
2779 if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
2780 {
2781 sLog->outError(LOG_FILTER_SQL, "Table `vehicle_template_accessory`: Accessory %u does not exist.", uiAccessory);
2782 continue;
2783 }
2784
2785 if (_spellClickInfoStore.find(uiEntry) == _spellClickInfoStore.end())
2786 {
2787 sLog->outError(LOG_FILTER_SQL, "Table `vehicle_template_accessory`: creature template entry %u has no data in npc_spellclick_spells", uiEntry);
2788 continue;
2789 }
2790
2791 _vehicleTemplateAccessoryStore[uiEntry].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
2792
2793 ++count;
2794 } while (result->NextRow());
2795
2796 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u Vehicle Template Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
2797}
2798
2799void ObjectMgr::LoadVehicleAccessories()
2800{
2801 uint32 oldMSTime = getMSTime();
2802
2803 _vehicleAccessoryStore.clear(); // needed for reload case
2804
2805 uint32 count = 0;
2806
2807 // 0 1 2 3 4 5
2808 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
2809
2810 if (!result)
2811 {
2812 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Vehicle Accessories in %u ms", GetMSTimeDiffToNow(oldMSTime));
2813 return;
2814 }
2815
2816 do
2817 {
2818 Field* fields = result->Fetch();
2819
2820 uint32 uiGUID = fields[0].GetUInt32();
2821 uint32 uiAccessory = fields[1].GetUInt32();
2822 int8 uiSeat = int8(fields[2].GetInt16());
2823 bool bMinion = fields[3].GetBool();
2824 uint8 uiSummonType = fields[4].GetUInt8();
2825 uint32 uiSummonTimer = fields[5].GetUInt32();
2826
2827 if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
2828 {
2829 sLog->outError(LOG_FILTER_SQL, "Table `vehicle_accessory`: Accessory %u does not exist.", uiAccessory);
2830 continue;
2831 }
2832
2833 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
2834
2835 ++count;
2836 } while (result->NextRow());
2837
2838 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u Vehicle Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
2839}
2840
2841void ObjectMgr::LoadPetLevelInfo()
2842{
2843 uint32 oldMSTime = getMSTime();
2844
2845 // 0 1 2 3 4 5 6 7 8 9
2846 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2847
2848 if (!result)
2849 {
2850 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
2851
2852 return;
2853 }
2854
2855 uint32 count = 0;
2856
2857 do
2858 {
2859 Field* fields = result->Fetch();
2860
2861 uint32 creature_id = fields[0].GetUInt32();
2862 if (!sObjectMgr->GetCreatureTemplate(creature_id))
2863 {
2864 sLog->outError(LOG_FILTER_SQL, "Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
2865 continue;
2866 }
2867
2868 uint32 current_level = fields[1].GetUInt8();
2869 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2870 {
2871 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2872 sLog->outError(LOG_FILTER_SQL, "Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
2873 else
2874 {
2875 sLog->outInfo(LOG_FILTER_GENERAL, "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
2876 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2877 }
2878 continue;
2879 }
2880 else if (current_level < 1)
2881 {
2882 sLog->outError(LOG_FILTER_SQL, "Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
2883 continue;
2884 }
2885
2886 PetLevelInfo*& pInfoMapEntry = _petInfoStore[creature_id];
2887
2888 if (pInfoMapEntry == NULL)
2889 pInfoMapEntry = new PetLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
2890
2891 // data for level 1 stored in [0] array element, ...
2892 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
2893
2894 pLevelInfo->health = fields[2].GetUInt32();
2895 pLevelInfo->mana = fields[3].GetUInt32();
2896 pLevelInfo->armor = fields[9].GetUInt32();
2897
2898 for (int i = 0; i < MAX_STATS; i++)
2899 {
2900 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
2901 }
2902
2903 ++count;
2904 } while (result->NextRow());
2905
2906 // Fill gaps and check integrity
2907 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
2908 {
2909 PetLevelInfo* pInfo = itr->second;
2910
2911 // fatal error if no level 1 data
2912 if (!pInfo || pInfo[0].health == 0)
2913 {
2914 sLog->outError(LOG_FILTER_SQL, "Creature %u does not have pet stats data for Level 1!", itr->first);
2915 exit(1);
2916 }
2917
2918 // fill level gaps
2919 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2920 {
2921 if (pInfo[level].health == 0)
2922 {
2923 sLog->outError(LOG_FILTER_SQL, "Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
2924 pInfo[level] = pInfo[level - 1];
2925 }
2926 }
2927 }
2928
2929 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u level pet stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
2930}
2931
2932PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
2933{
2934 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2935 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
2936
2937 PetLevelInfoContainer::const_iterator itr = _petInfoStore.find(creature_id);
2938 if (itr == _petInfoStore.end())
2939 return NULL;
2940
2941 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
2942}
2943
2944void ObjectMgr::PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
2945{
2946 if (count > 0)
2947 _playerInfo[race_][class_].item.push_back(PlayerCreateInfoItem(itemId, count));
2948 else
2949 {
2950 if (count < -1)
2951 sLog->outError(LOG_FILTER_SQL, "Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId);
2952
2953 uint32 RaceClass = (race_) | (class_ << 8);
2954 bool doneOne = false;
2955 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
2956 {
2957 if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i))
2958 {
2959 if (entry->RaceClassGender == RaceClass || entry->RaceClassGender == (RaceClass | (1 << 16)))
2960 {
2961 bool found = false;
2962 for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
2963 {
2964 if (entry->ItemId[x] > 0 && uint32(entry->ItemId[x]) == itemId)
2965 {
2966 found = true;
2967 const_cast<CharStartOutfitEntry*>(entry)->ItemId[x] = 0;
2968 break;
2969 }
2970 }
2971
2972 if (!found)
2973 sLog->outError(LOG_FILTER_SQL, "Item %u specified to be removed from original create info not found in dbc!", itemId);
2974
2975 if (!doneOne)
2976 doneOne = true;
2977 else
2978 break;
2979 }
2980 }
2981 }
2982 }
2983}
2984
2985void ObjectMgr::LoadPlayerInfo()
2986{
2987 // Load playercreate
2988 {
2989 uint32 oldMSTime = getMSTime();
2990 // 0 1 2 3 4 5 6
2991 QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
2992
2993 if (!result)
2994 {
2995
2996 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
2997 exit(1);
2998 }
2999 else
3000 {
3001 uint32 count = 0;
3002
3003 do
3004 {
3005 Field* fields = result->Fetch();
3006
3007 uint32 current_race = fields[0].GetUInt8();
3008 uint32 current_class = fields[1].GetUInt8();
3009 uint32 mapId = fields[2].GetUInt16();
3010 uint32 areaId = fields[3].GetUInt32(); // zone
3011 float positionX = fields[4].GetFloat();
3012 float positionY = fields[5].GetFloat();
3013 float positionZ = fields[6].GetFloat();
3014 float orientation = fields[7].GetFloat();
3015
3016 if (current_race >= MAX_RACES)
3017 {
3018 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
3019 continue;
3020 }
3021
3022 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
3023 if (!rEntry)
3024 {
3025 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
3026 continue;
3027 }
3028
3029 if (current_class >= MAX_CLASSES)
3030 {
3031 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
3032 continue;
3033 }
3034
3035 if (!sChrClassesStore.LookupEntry(current_class))
3036 {
3037 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
3038 continue;
3039 }
3040
3041 // accept DB data only for valid position (and non instanceable)
3042 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3043 {
3044 sLog->outError(LOG_FILTER_SQL, "Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3045 continue;
3046 }
3047
3048 if (sMapStore.LookupEntry(mapId)->Instanceable())
3049 {
3050 sLog->outError(LOG_FILTER_SQL, "Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3051 continue;
3052 }
3053
3054 PlayerInfo* pInfo = &_playerInfo[current_race][current_class];
3055
3056 pInfo->mapId = mapId;
3057 pInfo->areaId = areaId;
3058 pInfo->positionX = positionX;
3059 pInfo->positionY = positionY;
3060 pInfo->positionZ = positionZ;
3061 pInfo->orientation = orientation;
3062
3063 pInfo->displayId_m = rEntry->model_m;
3064 pInfo->displayId_f = rEntry->model_f;
3065
3066 ++count;
3067 } while (result->NextRow());
3068
3069 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u player create definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3070 }
3071 }
3072
3073 // Load playercreate items
3074 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Items Data...");
3075 {
3076 uint32 oldMSTime = getMSTime();
3077 // 0 1 2 3
3078 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
3079
3080 if (!result)
3081 {
3082 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
3083 }
3084 else
3085 {
3086 uint32 count = 0;
3087
3088 do
3089 {
3090 Field* fields = result->Fetch();
3091
3092 uint32 current_race = fields[0].GetUInt8();
3093 if (current_race >= MAX_RACES)
3094 {
3095 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
3096 continue;
3097 }
3098
3099 uint32 current_class = fields[1].GetUInt8();
3100 if (current_class >= MAX_CLASSES)
3101 {
3102 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
3103 continue;
3104 }
3105
3106 uint32 item_id = fields[2].GetUInt32();
3107
3108 if (!GetItemTemplate(item_id))
3109 {
3110 sLog->outError(LOG_FILTER_SQL, "Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.", item_id, current_race, current_class);
3111 continue;
3112 }
3113
3114 int32 amount = fields[3].GetInt8();
3115
3116 if (!amount)
3117 {
3118 sLog->outError(LOG_FILTER_SQL, "Item id %u (class %u race %u) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
3119 continue;
3120 }
3121
3122 if (!current_race || !current_class)
3123 {
3124 uint32 min_race = current_race ? current_race : 1;
3125 uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
3126 uint32 min_class = current_class ? current_class : 1;
3127 uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
3128 for (uint32 r = min_race; r < max_race; ++r)
3129 for (uint32 c = min_class; c < max_class; ++c)
3130 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
3131 }
3132 else
3133 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
3134
3135 ++count;
3136 } while (result->NextRow());
3137
3138 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u custom player create items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3139 }
3140 }
3141
3142 // Load playercreate spells
3143 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Spell Data...");
3144 {
3145 uint32 oldMSTime = getMSTime();
3146
3147 std::string tableName = sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell";
3148 QueryResult result = WorldDatabase.PQuery("SELECT race, class, Spell FROM %s", tableName.c_str());
3149
3150 if (!result)
3151 {
3152 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create spells. DB table `%s` is empty.", sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell");
3153
3154 }
3155 else
3156 {
3157 uint32 count = 0;
3158
3159 do
3160 {
3161 Field* fields = result->Fetch();
3162
3163 uint32 current_race = fields[0].GetUInt8();
3164 if (current_race >= MAX_RACES)
3165 {
3166 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `playercreateinfo_spell` table, ignoring.", current_race);
3167 continue;
3168 }
3169
3170 uint32 current_class = fields[1].GetUInt8();
3171 if (current_class >= MAX_CLASSES)
3172 {
3173 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `playercreateinfo_spell` table, ignoring.", current_class);
3174 continue;
3175 }
3176
3177 if (!current_race || !current_class)
3178 {
3179 uint32 min_race = current_race ? current_race : 1;
3180 uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
3181 uint32 min_class = current_class ? current_class : 1;
3182 uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
3183 for (uint32 r = min_race; r < max_race; ++r)
3184 for (uint32 c = min_class; c < max_class; ++c)
3185 _playerInfo[r][c].spell.push_back(fields[2].GetUInt32());
3186 }
3187 else
3188 _playerInfo[current_race][current_class].spell.push_back(fields[2].GetUInt32());
3189
3190 ++count;
3191 } while (result->NextRow());
3192
3193 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3194 }
3195 }
3196
3197 // Load playercreate actions
3198 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Action Data...");
3199 {
3200 uint32 oldMSTime = getMSTime();
3201
3202 // 0 1 2 3 4
3203 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
3204
3205 if (!result)
3206 {
3207 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
3208
3209 }
3210 else
3211 {
3212 uint32 count = 0;
3213
3214 do
3215 {
3216 Field* fields = result->Fetch();
3217
3218 uint32 current_race = fields[0].GetUInt8();
3219 if (current_race >= MAX_RACES)
3220 {
3221 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
3222 continue;
3223 }
3224
3225 uint32 current_class = fields[1].GetUInt8();
3226 if (current_class >= MAX_CLASSES)
3227 {
3228 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
3229 continue;
3230 }
3231
3232 PlayerInfo* pInfo = &_playerInfo[current_race][current_class];
3233 pInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
3234
3235 ++count;
3236 } while (result->NextRow());
3237
3238 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u player create actions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3239 }
3240 }
3241
3242 // Loading levels data (class/race dependent)
3243 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create Level Stats Data...");
3244 {
3245 uint32 oldMSTime = getMSTime();
3246
3247 // 0 1 2 3 4 5 6 7
3248 QueryResult result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
3249
3250 if (!result)
3251 {
3252 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
3253
3254 exit(1);
3255 }
3256
3257 uint32 count = 0;
3258
3259 do
3260 {
3261 Field* fields = result->Fetch();
3262
3263 uint32 current_race = fields[0].GetUInt8();
3264 if (current_race >= MAX_RACES)
3265 {
3266 sLog->outError(LOG_FILTER_SQL, "Wrong race %u in `player_levelstats` table, ignoring.", current_race);
3267 continue;
3268 }
3269
3270 uint32 current_class = fields[1].GetUInt8();
3271 if (current_class >= MAX_CLASSES)
3272 {
3273 sLog->outError(LOG_FILTER_SQL, "Wrong class %u in `player_levelstats` table, ignoring.", current_class);
3274 continue;
3275 }
3276
3277 uint32 current_level = fields[2].GetUInt8();
3278 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3279 {
3280 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3281 sLog->outError(LOG_FILTER_SQL, "Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3282 else
3283 {
3284 sLog->outInfo(LOG_FILTER_GENERAL, "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_levelstats` table, ignoring.", current_level);
3285 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3286 }
3287 continue;
3288 }
3289
3290 PlayerInfo* pInfo = &_playerInfo[current_race][current_class];
3291
3292 if (!pInfo->levelInfo)
3293 pInfo->levelInfo = new PlayerLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
3294
3295 PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level - 1];
3296
3297 for (int i = 0; i < MAX_STATS; i++)
3298 {
3299 pLevelInfo->stats[i] = fields[i + 3].GetUInt8();
3300 }
3301
3302 ++count;
3303 } while (result->NextRow());
3304
3305 // Fill gaps and check integrity
3306 for (int race = 0; race < MAX_RACES; ++race)
3307 {
3308 // skip non existed races
3309 if (!sChrRacesStore.LookupEntry(race))
3310 continue;
3311
3312 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
3313 {
3314 // skip non existed classes
3315 if (!sChrClassesStore.LookupEntry(class_))
3316 continue;
3317
3318 PlayerInfo* pInfo = &_playerInfo[race][class_];
3319
3320 // skip non loaded combinations
3321 if (!pInfo->displayId_m || !pInfo->displayId_f)
3322 continue;
3323
3324 // skip expansion races if not playing with expansion
3325 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXP_BC && (race == RACE_BLOODELF || race == RACE_DRAENEI))
3326 continue;
3327
3328 // skip expansion classes if not playing with expansion
3329 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXP_WOTLK && class_ == CLASS_DEATH_KNIGHT)
3330 continue;
3331
3332 // skip expansion classes / races if not playing with expansion
3333 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXP_CATACLYSM && (race == RACE_GOBLIN || race == RACE_WORGEN))
3334
3335 // skip expansion classes / races if not playing with expansion
3336 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXP_PANDARIA && (class_ == CLASS_MONK || race == RACE_PANDAREN_NEUTRAL || race == RACE_PANDAREN_ALLI || race == RACE_PANDAREN_HORDE))
3337
3338 // fatal error if no level 1 data
3339 if (!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0)
3340 {
3341 sLog->outError(LOG_FILTER_SQL, "Race %i Class %i Level 1 does not have stats data!", race, class_);
3342 exit(1);
3343 }
3344
3345 // fill level gaps
3346 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3347 {
3348 if (pInfo->levelInfo[level].stats[0] == 0)
3349 {
3350 sLog->outError(LOG_FILTER_SQL, "Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
3351 pInfo->levelInfo[level] = pInfo->levelInfo[level - 1];
3352 }
3353 }
3354 }
3355 }
3356
3357 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u level stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3358 }
3359
3360 // Loading xp per level data
3361 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Player Create XP Data...");
3362 {
3363 uint32 oldMSTime = getMSTime();
3364
3365 _playerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3366 for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3367 _playerXPperLevel[level] = 0;
3368
3369 // 0 1
3370 QueryResult result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
3371
3372 if (!result)
3373 {
3374 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
3375
3376 exit(1);
3377 }
3378
3379 uint32 count = 0;
3380
3381 do
3382 {
3383 Field* fields = result->Fetch();
3384
3385 uint32 current_level = fields[0].GetUInt8();
3386 uint32 current_xp = fields[1].GetUInt32();
3387
3388 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3389 {
3390 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3391 sLog->outError(LOG_FILTER_SQL, "Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3392 else
3393 {
3394 sLog->outInfo(LOG_FILTER_GENERAL, "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
3395 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3396 }
3397 continue;
3398 }
3399 //PlayerXPperLevel
3400 _playerXPperLevel[current_level] = current_xp;
3401 ++count;
3402 } while (result->NextRow());
3403
3404 // fill level gaps
3405 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3406 {
3407 if (_playerXPperLevel[level] == 0)
3408 {
3409 sLog->outError(LOG_FILTER_SQL, "Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
3410 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 100;
3411 }
3412 }
3413
3414 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u xp for level definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
3415 }
3416}
3417
3418void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseHP, uint32& baseMana) const
3419{
3420 if (level < 1 || class_ >= MAX_CLASSES)
3421 return;
3422
3423 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3424 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3425
3426 GtOCTBaseHPByClassEntry const* hp = sGtOCTBaseHPByClassStore.LookupEntry((class_ - 1) * GT_MAX_LEVEL + level - 1);
3427 GtOCTBaseMPByClassEntry const* mp = sGtOCTBaseMPByClassStore.LookupEntry((class_ - 1) * GT_MAX_LEVEL + level - 1);
3428
3429 if (!hp || !mp)
3430 {
3431 sLog->outError(LOG_FILTER_GENERAL, "Tried to get non-existant Class-Level combination data for base hp/mp. Class %u Level %u", class_, level);
3432 return;
3433 }
3434
3435 baseHP = uint32(hp->ratio);
3436 baseMana = uint32(mp->ratio);
3437}
3438
3439void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const
3440{
3441 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
3442 return;
3443
3444 PlayerInfo const* pInfo = &_playerInfo[race][class_];
3445 if (pInfo->displayId_m == 0 || pInfo->displayId_f == 0)
3446 return;
3447
3448 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3449 *info = pInfo->levelInfo[level - 1];
3450 else
3451 BuildPlayerLevelInfo(race, class_, level, info);
3452}
3453
3454void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
3455{
3456 // base data (last known level)
3457 *info = _playerInfo[race][_class].levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
3458
3459 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
3460 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
3461 {
3462 switch (_class)
3463 {
3464 case CLASS_WARRIOR:
3465 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3466 info->stats[STAT_STAMINA] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
3467 info->stats[STAT_AGILITY] += (lvl > 36 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3468 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3469 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3470 break;
3471 case CLASS_PALADIN:
3472 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1 : 0);
3473 info->stats[STAT_STAMINA] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3474 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 7 && !(lvl % 2) ? 1 : 0));
3475 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl % 2) ? 1 : 0);
3476 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1 : 0);
3477 break;
3478 case CLASS_HUNTER:
3479 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1 : 0);
3480 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3481 info->stats[STAT_AGILITY] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
3482 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl % 2) ? 1 : 0);
3483 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3484 break;
3485 case CLASS_ROGUE:
3486 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1 : 0);
3487 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3488 info->stats[STAT_AGILITY] += (lvl > 16 ? 2 : (lvl > 1 ? 1 : 0));
3489 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl % 2) ? 1 : 0);
3490 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
3491 break;
3492 case CLASS_PRIEST:
3493 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3494 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3495 info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3496 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2 : (lvl > 1 ? 1 : 0));
3497 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1 : 0);
3498 break;
3499 case CLASS_SHAMAN:
3500 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3501 info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
3502 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl % 2) ? 1 : 0);
3503 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1 : 0);
3504 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1 : 0);
3505 break;
3506 case CLASS_MAGE:
3507 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3508 info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
3509 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3510 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2 : (lvl > 1 ? 1 : 0));
3511 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3512 break;
3513 case CLASS_WARLOCK:
3514 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3515 info->stats[STAT_STAMINA] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3516 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
3517 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
3518 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
3519 break;
3520 case CLASS_DRUID:
3521 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3522 info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
3523 info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3524 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
3525 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
3526 break;
3527 case CLASS_MONK:
3528 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
3529 info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
3530 info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
3531 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
3532 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
3533 break;
3534
3535 default: break;
3536 }
3537 }
3538}
3539
3540void ObjectMgr::LoadQuests()
3541{
3542 uint32 oldMSTime = getMSTime();
3543
3544 // For reload case
3545 for (QuestMap::const_iterator itr = _questTemplates.begin(); itr != _questTemplates.end(); ++itr)
3546 delete itr->second;
3547 _questTemplates.clear();
3548
3549 mExclusiveQuestGroups.clear();
3550
3551 QueryResult result = WorldDatabase.Query("SELECT "
3552 //0 1 2 3 4 5 6 7 8 9 10 11 12 13
3553 "Id, Method, Level, MinLevel, MaxLevel, ZoneOrSort, Type, SuggestedPlayers, LimitTime, RequiredTeam, RequiredClasses, RequiredRaces, RequiredSkillId, RequiredSkillPoints, "
3554 // 14 15 16 17 18 19 20 21
3555 "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, "
3556 // 22 23 24 25 26 27 28 29 30 31 32
3557 "PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestIdChain, RewardXPId, RewardOrRequiredMoney, RewardMoneyMaxLevel, RewardSpell, RewardSpellCast, RewardHonor, RewardHonorMultiplier, "
3558 // 33 34 35 36 37 38 39 40 41 42 43 44 45
3559 "RewardMailTemplateId, RewardMailDelay, SourceItemId, SourceItemCount, SourceSpellId, Flags, Flags2, SpecialFlags, MinimapTargetMark, RewardTitleId, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
3560 // 46 47 48 49 50 51 52 53 54 55 56 57 58 59
3561 "RewardSkillId, RewardSkillPoints, RewardReputationMask, QuestGiverPortrait, QuestTurnInPortrait, RewardPackageItemId, RewardItemId1, RewardItemId2, RewardItemId3, RewardItemId4, RewardItemCount1, RewardItemCount2, RewardItemCount3, RewardItemCount4, "
3562 // 60 61 62 63 64 65 66 67 68 69 70 71
3563 "RewardChoiceItemId1, RewardChoiceItemId2, RewardChoiceItemId3, RewardChoiceItemId4, RewardChoiceItemId5, RewardChoiceItemId6, RewardChoiceItemCount1, RewardChoiceItemCount2, RewardChoiceItemCount3, RewardChoiceItemCount4, RewardChoiceItemCount5, RewardChoiceItemCount6, "
3564 // 72 73 74 75 76 77 78 79 80 81
3565 "RewardFactionId1, RewardFactionId2, RewardFactionId3, RewardFactionId4, RewardFactionId5, RewardFactionValueId1, RewardFactionValueId2, RewardFactionValueId3, RewardFactionValueId4, RewardFactionValueId5, "
3566 // 82 83 84 85 86
3567 "RewardFactionValueIdOverride1, RewardFactionValueIdOverride2, RewardFactionValueIdOverride3, RewardFactionValueIdOverride4, RewardFactionValueIdOverride5, "
3568 // 87 88 89 90 91 92 93 94 95 96 97
3569 "PointMapId, PointX, PointY, PointOption, Title, Objectives, Details, EndText, CompletedText, OfferRewardText, RequestItemsText, "
3570 // 98 99 100 101 102 103 104 105
3571 "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
3572 // 106 107 108 109 110 111 112 113
3573 "RequiredSourceItemId1, RequiredSourceItemId2, RequiredSourceItemId3, RequiredSourceItemId4, RequiredSourceItemCount1, RequiredSourceItemCount2, RequiredSourceItemCount3, RequiredSourceItemCount4, "
3574 // 114 115 116 117 118 119 120 121 122 123 124 125
3575 "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
3576 // 126 127 128 129 130 131 132 133 134
3577 "RequiredSpell, RequiredSpellCast1, RequiredSpellCast2, RequiredSpellCast3, RequiredSpellCast4, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, "
3578 // 135 136 137 138 139 140 141 142
3579 "RewardCurrencyId1, RewardCurrencyId2, RewardCurrencyId3, RewardCurrencyId4, RewardCurrencyCount1, RewardCurrencyCount2, RewardCurrencyCount3, RewardCurrencyCount4, "
3580 // 143 144 145 146 147 148 149 150
3581 "RequiredCurrencyId1, RequiredCurrencyId2, RequiredCurrencyId3, RequiredCurrencyId4, RequiredCurrencyCount1, RequiredCurrencyCount2, RequiredCurrencyCount3, RequiredCurrencyCount4, "
3582 // 151 152 153 154 155 156
3583 "QuestGiverTextWindow, QuestGiverTargetName, QuestTurnTextWindow, QuestTurnTargetName, SoundAccept, SoundTurnIn, "
3584 // 157 158 159 160 161 162 163 164 165 166
3585 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4, EmoteOnIncomplete, EmoteOnComplete, "
3586 // 167 168 169 170 171 172 173 174
3587 "OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4, OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4, "
3588 // 175 176 177
3589 "StartScript, CompleteScript, WDBVerified"
3590 " FROM quest_template");
3591
3592 if (!result)
3593 {
3594 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
3595 return;
3596 }
3597
3598 // Create multimap previous quest for each existed quest
3599 // some quests can have many previous maps set by NextQuestId in previous quest
3600 // for example set of race quests can lead to single not race specific quest
3601 do
3602 {
3603 Field* fields = result->Fetch();
3604
3605 Quest* newQuest = new Quest(fields);
3606 _questTemplates[newQuest->GetQuestId()] = newQuest;
3607 } while (result->NextRow());
3608
3609 std::map<uint32, uint32> usedMailTemplates;
3610
3611 // Post processing
3612 for (QuestMap::iterator iter = _questTemplates.begin(); iter != _questTemplates.end(); ++iter)
3613 {
3614 // Skip post-loading checks for disabled quests
3615 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, NULL))
3616 continue;
3617
3618 Quest * qinfo = iter->second;
3619
3620 // Additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3621
3622 if (qinfo->GetQuestMethod() >= 3)
3623 sLog->outError(LOG_FILTER_SQL, "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
3624
3625 if (qinfo->SpecialFlags & ~QUEST_SPECIAL_FLAGS_DB_ALLOWED)
3626 {
3627 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
3628 qinfo->GetQuestId(), qinfo->SpecialFlags, QUEST_SPECIAL_FLAGS_DB_ALLOWED);
3629 qinfo->SpecialFlags &= QUEST_SPECIAL_FLAGS_DB_ALLOWED;
3630 }
3631
3632 if (qinfo->Flags & QUEST_FLAGS_DAILY && qinfo->Flags & QUEST_FLAGS_WEEKLY)
3633 {
3634 sLog->outError(LOG_FILTER_SQL, "Weekly Quest %u is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
3635 qinfo->Flags &= ~QUEST_FLAGS_DAILY;
3636 }
3637
3638 if (qinfo->Flags & QUEST_FLAGS_DAILY)
3639 {
3640 if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
3641 {
3642 sLog->outError(LOG_FILTER_SQL, "Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3643 qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
3644 }
3645 }
3646
3647 if (qinfo->Flags & QUEST_FLAGS_WEEKLY)
3648 {
3649 if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
3650 {
3651 sLog->outError(LOG_FILTER_SQL, "Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3652 qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
3653 }
3654 }
3655
3656 if (qinfo->Flags & QUEST_SPECIAL_FLAGS_MONTHLY)
3657 {
3658 if (!(qinfo->Flags & QUEST_SPECIAL_FLAGS_REPEATABLE))
3659 {
3660 sLog->outError(LOG_FILTER_SQL, "Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
3661 qinfo->Flags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
3662 }
3663 }
3664
3665 if (qinfo->Flags & QUEST_FLAGS_AUTO_REWARDED)
3666 {
3667 // At auto-reward can be rewarded only RewardChoiceItemId[0]
3668 for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
3669 {
3670 if (uint32 id = qinfo->RewardChoiceItemId[j])
3671 {
3672 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardChoiceItemId%d` = %u but item from `RewardChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3673 qinfo->GetQuestId(), j + 1, id, j + 1);
3674 // No changes, quest ignore this data
3675 }
3676 }
3677 }
3678
3679 if (qinfo->MinLevel == uint32(-1) || qinfo->MinLevel > DEFAULT_MAX_LEVEL)
3680 {
3681 sLog->outError(LOG_FILTER_SQL, "Quest %u should be disabled because `MinLevel` = %i", qinfo->GetQuestId(), int32(qinfo->MinLevel));
3682 // No changes needed, sending -1 in SMSG_QUEST_QUERY_RESPONSE is valid
3683 }
3684
3685 // Client quest log visual (area case)
3686 if (qinfo->ZoneOrSort > 0)
3687 {
3688 if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
3689 {
3690 sLog->outError(LOG_FILTER_SQL, "Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
3691 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3692 // No changes, quest not dependent from this value but can have problems at client
3693 }
3694 }
3695
3696 // Client quest log visual (sort case)
3697 if (qinfo->ZoneOrSort < 0)
3698 {
3699 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
3700 if (!qSort)
3701 {
3702 sLog->outError(LOG_FILTER_SQL, "Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
3703 qinfo->GetQuestId(), qinfo->ZoneOrSort);
3704 // 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)
3705 }
3706 // Check for proper RequiredSkillId value (skill case)
3707 if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
3708 {
3709 if (qinfo->RequiredSkillId != skill_id)
3710 {
3711 sLog->outError(LOG_FILTER_SQL, "Quest %u has `ZoneOrSort` = %i but `RequiredSkillId` does not have a corresponding value (%d).",
3712 qinfo->GetQuestId(), qinfo->ZoneOrSort, skill_id);
3713 // Override, and force proper value here?
3714 }
3715 }
3716 }
3717
3718 // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
3719 if (qinfo->RequiredClasses)
3720 {
3721 uint32 RequiredClassCheck = qinfo->RequiredClasses > 0 ? qinfo->RequiredClasses : -(qinfo->RequiredClasses);
3722
3723 if (!(qinfo->RequiredClasses & CLASSMASK_ALL_PLAYABLE))
3724 {
3725 sLog->outError(LOG_FILTER_SQL, "Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->RequiredClasses);
3726 qinfo->RequiredClasses = 0;
3727 }
3728 }
3729
3730 // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
3731 if (qinfo->RequiredRaces)
3732 {
3733 uint32 RequiredRacesCheck = qinfo->RequiredRaces > 0 ? qinfo->RequiredRaces : -(qinfo->RequiredRaces);
3734
3735 if (!(qinfo->RequiredRaces & RACEMASK_ALL_PLAYABLE))
3736 {
3737 sLog->outError(LOG_FILTER_SQL, "Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->RequiredRaces);
3738 qinfo->RequiredRaces = 0;
3739 }
3740 }
3741
3742 // RequiredSkillId, can be 0
3743 if (qinfo->RequiredSkillId)
3744 {
3745 if (!sSkillLineStore.LookupEntry(qinfo->RequiredSkillId))
3746 {
3747 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSkillId` = %u but this skill does not exist",
3748 qinfo->GetQuestId(), qinfo->RequiredSkillId);
3749 }
3750 }
3751
3752 if (qinfo->RequiredSkillPoints)
3753 {
3754 if (qinfo->RequiredSkillPoints > sWorld->GetConfigMaxSkillValue())
3755 {
3756 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSkillPoints` = %u but max possible skill is %u, quest can't be done.",
3757 qinfo->GetQuestId(), qinfo->RequiredSkillPoints, sWorld->GetConfigMaxSkillValue());
3758 // No changes, quest can't be done for this requirement
3759 }
3760 }
3761 // Else Skill quests can have 0 skill level, this is ok
3762
3763 if (qinfo->RequiredFactionId2 && !sFactionStore.LookupEntry(qinfo->RequiredFactionId2))
3764 {
3765 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredFactionId2` = %u but faction template %u does not exist, quest can't be done.",
3766 qinfo->GetQuestId(), qinfo->RequiredFactionId2, qinfo->RequiredFactionId2);
3767 // No changes, quest can't be done for this requirement
3768 }
3769
3770 if (qinfo->RequiredFactionId1 && !sFactionStore.LookupEntry(qinfo->RequiredFactionId1))
3771 {
3772 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredFactionId1` = %u but faction template %u does not exist, quest can't be done.",
3773 qinfo->GetQuestId(), qinfo->RequiredFactionId1, qinfo->RequiredFactionId1);
3774 // No changes, quest can't be done for this requirement
3775 }
3776
3777 if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
3778 {
3779 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3780 qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
3781 // No changes, quest can't be done for this requirement
3782 }
3783
3784 if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
3785 {
3786 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3787 qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
3788 // No changes, quest can't be done for this requirement
3789 }
3790
3791 if (qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
3792 {
3793 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
3794 qinfo->GetQuestId(), qinfo->RequiredMinRepValue, ReputationMgr::Reputation_Cap);
3795 // No changes, quest can't be done for this requirement
3796 }
3797
3798 if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
3799 {
3800 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
3801 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
3802 // No changes, quest can't be done for this requirement
3803 }
3804
3805 if (!qinfo->RequiredFactionId1 && qinfo->RequiredFactionValue1 != 0)
3806 {
3807 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredFactionValue1` = %d but `RequiredFactionId1` is 0, value has no effect",
3808 qinfo->GetQuestId(), qinfo->RequiredFactionValue1);
3809 // Warning
3810 }
3811
3812 if (!qinfo->RequiredFactionId2 && qinfo->RequiredFactionValue2 != 0)
3813 {
3814 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredFactionValue2` = %d but `RequiredFactionId2` is 0, value has no effect",
3815 qinfo->GetQuestId(), qinfo->RequiredFactionValue2);
3816 // Warning
3817 }
3818
3819 if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue != 0)
3820 {
3821 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
3822 qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
3823 // Warning
3824 }
3825
3826 if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue != 0)
3827 {
3828 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3829 qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
3830 // Warning
3831 }
3832
3833 if (qinfo->RewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->RewardTitleId))
3834 {
3835 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
3836 qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
3837 qinfo->RewardTitleId = 0;
3838 // Quest can't reward this title
3839 }
3840
3841 if (qinfo->SourceItemId)
3842 {
3843 if (!sObjectMgr->GetItemTemplate(qinfo->SourceItemId))
3844 {
3845 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SourceItemId` = %u but item with entry %u does not exist, quest can't be done.",
3846 qinfo->GetQuestId(), qinfo->SourceItemId, qinfo->SourceItemId);
3847 qinfo->SourceItemId = 0; // Qquest can't be done for this requirement
3848 }
3849 else if (qinfo->SourceItemIdCount == 0)
3850 {
3851 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SourceItemId` = %u but `SourceItemIdCount` = 0, set to 1 but need fix in DB.",
3852 qinfo->GetQuestId(), qinfo->SourceItemId);
3853 qinfo->SourceItemIdCount = 1; // Update to 1 for allow quest work for backward compatibility with DB
3854 }
3855 }
3856 else if (qinfo->SourceItemIdCount > 0)
3857 {
3858 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SourceItemId` = 0 but `SourceItemIdCount` = %u, useless value.",
3859 qinfo->GetQuestId(), qinfo->SourceItemIdCount);
3860 qinfo->SourceItemIdCount = 0; // No quest work changes in fact
3861 }
3862
3863 if (qinfo->SourceSpellid)
3864 {
3865 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->SourceSpellid);
3866 if (!spellInfo)
3867 {
3868 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SourceSpellid` = %u but spell %u doesn't exist, quest can't be done.",
3869 qinfo->GetQuestId(), qinfo->SourceSpellid, qinfo->SourceSpellid);
3870 qinfo->SourceSpellid = 0; // Quest can't be done for this requirement
3871 }
3872 else if (!SpellMgr::IsSpellValid(spellInfo))
3873 {
3874 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SourceSpellid` = %u but spell %u is broken, quest can't be done.",
3875 qinfo->GetQuestId(), qinfo->SourceSpellid, qinfo->SourceSpellid);
3876 qinfo->SourceSpellid = 0; // Quest can't be done for this requirement
3877 }
3878 }
3879
3880 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
3881 {
3882 uint32 id = qinfo->RequiredItemId[j];
3883 if (id)
3884 {
3885 if (qinfo->RequiredItemCount[j] == 0)
3886 {
3887 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredItemId%d` = %u but `RequiredItemCount%d` = 0, quest can't be done.",
3888 qinfo->GetQuestId(), j + 1, id, j + 1);
3889 // No changes, quest can't be done for this requirement
3890 }
3891
3892 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER);
3893
3894 if (!sObjectMgr->GetItemTemplate(id))
3895 {
3896 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3897 qinfo->GetQuestId(), j + 1, id, id);
3898 qinfo->RequiredItemCount[j] = 0; // Prevent incorrect work of quest
3899 }
3900 }
3901 else if (qinfo->RequiredItemCount[j] > 0)
3902 {
3903 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredItemId%d` = 0 but `RequiredItemCount%d` = %u, quest can't be done.",
3904 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RequiredItemCount[j]);
3905 qinfo->RequiredItemCount[j] = 0; // Prevent incorrect work of quest
3906 }
3907 }
3908
3909 for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
3910 {
3911 uint32 id = qinfo->RequiredSourceItemId[j];
3912 if (id)
3913 {
3914 if (!sObjectMgr->GetItemTemplate(id))
3915 {
3916 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSourceItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3917 qinfo->GetQuestId(), j + 1, id, id);
3918 // No changes, quest can't be done for this requirement
3919 }
3920 }
3921 else
3922 {
3923 if (qinfo->RequiredSourceItemCount[j]>0)
3924 {
3925 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSourceItemId%d` = 0 but `RequiredSourceItemCount%d` = %u.",
3926 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RequiredSourceItemCount[j]);
3927 // No changes, quest ignore this data
3928 }
3929 }
3930 }
3931
3932 for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3933 {
3934 uint32 id = qinfo->RequiredSpellCast[j];
3935 if (id)
3936 {
3937 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(id);
3938 if (!spellInfo)
3939 {
3940 sLog->outError(LOG_FILTER_SQL, "Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3941 qinfo->GetQuestId(), j + 1, id, id);
3942 continue;
3943 }
3944
3945 if (!qinfo->RequiredNpcOrGo[j])
3946 {
3947 bool found = false;
3948 for (uint8 k = 0; k < MAX_SPELL_EFFECTS; ++k)
3949 {
3950 if ((spellInfo->Effects[k].Effect == SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->Effects[k].MiscValue) == qinfo->Id) ||
3951 spellInfo->Effects[k].Effect == SPELL_EFFECT_SEND_EVENT)
3952 {
3953 found = true;
3954 break;
3955 }
3956 }
3957
3958 if (found)
3959 {
3960 if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
3961 {
3962 sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and RequiredNpcOrGo%d = 0, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags or RequiredNpcOrGo%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->Id, j + 1, j + 1);
3963
3964 // This will prevent quest completing without objective
3965 const_cast<Quest*>(qinfo)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
3966 }
3967 }
3968 else
3969 {
3970 sLog->outError(LOG_FILTER_SQL, "Quest %u has `ReqSpellCast%d` = %u and RequiredNpcOrGo%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.",
3971 qinfo->GetQuestId(), j + 1, id, j + 1, id);
3972 // No changes, quest can't be done for this requirement
3973 }
3974 }
3975 }
3976 }
3977
3978 for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3979 {
3980 int32 id = qinfo->RequiredNpcOrGo[j];
3981 if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
3982 {
3983 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredNpcOrGo%d` = %i but gameobject %u does not exist, quest can't be done.",
3984 qinfo->GetQuestId(), j + 1, id, uint32(-id));
3985 qinfo->RequiredNpcOrGo[j] = 0; // Quest can't be done for this requirement
3986 }
3987
3988 if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
3989 {
3990 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredNpcOrGo%d` = %i but creature with entry %u does not exist, quest can't be done.",
3991 qinfo->GetQuestId(), j + 1, id, uint32(id));
3992 qinfo->RequiredNpcOrGo[j] = 0; // Quest can't be done for this requirement
3993 }
3994
3995 if (id)
3996 {
3997 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
3998
3999 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_KILL_OR_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO);
4000
4001 if (!qinfo->RequiredNpcOrGoCount[j])
4002 {
4003 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredNpcOrGo%d` = %u but `RequiredNpcOrGoCount%d` = 0, quest can't be done.",
4004 qinfo->GetQuestId(), j + 1, id, j + 1);
4005 // No changes, quest can be incorrectly done, but we already report this
4006 }
4007 }
4008 else if (qinfo->RequiredNpcOrGoCount[j]>0)
4009 {
4010 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredNpcOrGo%d` = 0 but `RequiredNpcOrGoCount%d` = %u.",
4011 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RequiredNpcOrGoCount[j]);
4012 // No changes, quest ignore this data
4013 }
4014 }
4015
4016 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
4017 {
4018 uint32 id = qinfo->RewardChoiceItemId[j];
4019 if (id)
4020 {
4021 if (!sObjectMgr->GetItemTemplate(id))
4022 {
4023 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4024 qinfo->GetQuestId(), j + 1, id, id);
4025 qinfo->RewardChoiceItemId[j] = 0; // No changes, quest will not reward this
4026 }
4027
4028 if (!qinfo->RewardChoiceItemCount[j])
4029 {
4030 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardChoiceItemId%d` = %u but `RewardChoiceItemCount%d` = 0, quest can't be done.",
4031 qinfo->GetQuestId(), j + 1, id, j + 1);
4032 // No changes, quest can't be done
4033 }
4034 }
4035 else if (qinfo->RewardChoiceItemCount[j]>0)
4036 {
4037 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardChoiceItemId%d` = 0 but `RewardChoiceItemCount%d` = %u.",
4038 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardChoiceItemCount[j]);
4039 // No changes, quest ignore this data
4040 }
4041 }
4042
4043 for (uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j)
4044 {
4045 uint32 id = qinfo->RewardItemId[j];
4046 if (id)
4047 {
4048 if (!sObjectMgr->GetItemTemplate(id))
4049 {
4050 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4051 qinfo->GetQuestId(), j + 1, id, id);
4052 qinfo->RewardItemId[j] = 0; // No changes, quest will not reward this item
4053 }
4054
4055 if (!qinfo->RewardItemIdCount[j])
4056 {
4057 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardItemId%d` = %u but `RewardItemIdCount%d` = 0, quest will not reward this item.",
4058 qinfo->GetQuestId(), j + 1, id, j + 1);
4059 // No changes
4060 }
4061 }
4062 else if (qinfo->RewardItemIdCount[j]>0)
4063 {
4064 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardItemId%d` = 0 but `RewardItemIdCount%d` = %u.",
4065 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardItemIdCount[j]);
4066 // No changes, quest ignore this data
4067 }
4068 }
4069
4070 for (uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
4071 {
4072 if (qinfo->RewardFactionId[j])
4073 {
4074 if (abs(qinfo->RewardFactionValueId[j]) > 9)
4075 {
4076 sLog->outError(LOG_FILTER_SQL, "Quest %u has RewardFactionValueId%d = %i. That is outside the range of valid values (-9 to 9).", qinfo->GetQuestId(), j + 1, qinfo->RewardFactionValueId[j]);
4077 }
4078 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
4079 {
4080 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardFactionId%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.", qinfo->GetQuestId(), j + 1, qinfo->RewardFactionId[j], qinfo->RewardFactionId[j]);
4081 qinfo->RewardFactionId[j] = 0; // Quest will not reward this
4082 }
4083 }
4084
4085 else if (qinfo->RewardFactionValueIdOverride[j] != 0)
4086 {
4087 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardFactionId%d` = 0 but `RewardFactionValueIdOverride%d` = %i.",
4088 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardFactionValueIdOverride[j]);
4089 // No changes, quest ignore this data
4090 }
4091 }
4092
4093 if (qinfo->RewardSpell)
4094 {
4095 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RewardSpell);
4096
4097 if (!spellInfo)
4098 {
4099 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u does not exist, spell removed as display reward.",
4100 qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
4101 qinfo->RewardSpell = 0; // No spell reward will display for this quest
4102 }
4103
4104 else if (!SpellMgr::IsSpellValid(spellInfo))
4105 {
4106 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u is broken, quest will not have a spell reward.",
4107 qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
4108 qinfo->RewardSpell = 0; // No spell reward will display for this quest
4109 }
4110
4111 /*else if (GetTalentSpellCost(qinfo->RewardSpell))
4112 {
4113 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4114 qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
4115 qinfo->RewardSpell = 0; // No spell reward will display for this quest
4116 }*/
4117 }
4118
4119 if (qinfo->RewardSpellCast > 0)
4120 {
4121 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RewardSpellCast);
4122
4123 if (!spellInfo)
4124 {
4125 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
4126 qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
4127 qinfo->RewardSpellCast = 0; // No spell will be casted on player
4128 }
4129
4130 else if (!SpellMgr::IsSpellValid(spellInfo))
4131 {
4132 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
4133 qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
4134 qinfo->RewardSpellCast = 0; // No spell will be casted on player
4135 }
4136
4137 /*else if (GetTalentSpellCost(qinfo->RewardSpellCast))
4138 {
4139 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4140 qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
4141 qinfo->RewardSpellCast = 0; // No spell will be casted on player
4142 }*/
4143 }
4144
4145 if (qinfo->RewardMailTemplateId)
4146 {
4147 if (!sMailTemplateStore.LookupEntry(qinfo->RewardMailTemplateId))
4148 {
4149 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
4150 qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId);
4151 qinfo->RewardMailTemplateId = 0; // No mail will send to player
4152 qinfo->RewardMailDelay = 0; // No mail will send to player
4153 }
4154 else if (usedMailTemplates.find(qinfo->RewardMailTemplateId) != usedMailTemplates.end())
4155 {
4156 std::map<uint32, uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewardMailTemplateId);
4157 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
4158 qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId, used_mt_itr->second);
4159 qinfo->RewardMailTemplateId = 0; // No mail will send to player
4160 qinfo->RewardMailDelay = 0; // No mail will send to player
4161 }
4162 else
4163 usedMailTemplates[qinfo->RewardMailTemplateId] = qinfo->GetQuestId();
4164 }
4165
4166 if (qinfo->NextQuestIdChain)
4167 {
4168 QuestMap::iterator qNextItr = _questTemplates.find(qinfo->NextQuestIdChain);
4169 if (qNextItr == _questTemplates.end())
4170 {
4171 sLog->outError(LOG_FILTER_SQL, "Quest %u has `NextQuestIdChain` = %u but quest %u does not exist, quest chain will not work.",
4172 qinfo->GetQuestId(), qinfo->NextQuestIdChain, qinfo->NextQuestIdChain);
4173 qinfo->NextQuestIdChain = 0;
4174 }
4175 else
4176 qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
4177 }
4178
4179 for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
4180 {
4181 if (qinfo->RewardCurrencyId[j])
4182 {
4183 if (qinfo->RewardCurrencyCount[j] == 0)
4184 {
4185 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = %u but `RewardCurrencyCount%d` = 0, quest can't be done.",
4186 qinfo->GetQuestId(), j + 1, qinfo->RewardCurrencyId[j], j + 1);
4187 // No changes, quest can't be done for this requirement
4188 }
4189
4190 if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j]))
4191 {
4192 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.",
4193 qinfo->GetQuestId(), j + 1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]);
4194 qinfo->RewardCurrencyCount[j] = 0; // Prevent incorrect work of quest
4195 }
4196 }
4197 else
4198 {
4199 if (qinfo->RewardCurrencyCount[j] > 0)
4200 {
4201 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardCurrencyId%d` = 0 but `RewardCurrencyCount%d` = %u, quest can't be done.",
4202 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardCurrencyCount[j]);
4203 qinfo->RewardCurrencyCount[j] = 0; // Prevent incorrect work of quest
4204 }
4205 }
4206 }
4207
4208 for (uint8 j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j)
4209 {
4210 if (qinfo->RequiredCurrencyId[j])
4211 {
4212 if (qinfo->RequiredCurrencyCount[j] == 0)
4213 {
4214 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = %u but `RequiredCurrencyCount%d` = 0, quest can't be done.",
4215 qinfo->GetQuestId(), j + 1, qinfo->RequiredCurrencyId[j], j + 1);
4216 // No changes, quest can't be done for this requirement
4217 }
4218
4219 if (!sCurrencyTypesStore.LookupEntry(qinfo->RequiredCurrencyId[j]))
4220 {
4221 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.",
4222 qinfo->GetQuestId(), j + 1, qinfo->RequiredCurrencyId[j], qinfo->RequiredCurrencyId[j]);
4223 qinfo->RequiredCurrencyCount[j] = 0; // Prevent incorrect work of quest
4224 }
4225 }
4226 else if (qinfo->RequiredCurrencyCount[j] > 0)
4227 {
4228 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredCurrencyId%d` = 0 but `RequiredCurrencyCount%d` = %u, quest can't be done.",
4229 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RequiredCurrencyCount[j]);
4230 qinfo->RequiredCurrencyCount[j] = 0; // Prevent incorrect work of quest
4231 }
4232 }
4233
4234 if (qinfo->SoundAccept)
4235 {
4236 if (!sSoundEntriesStore.LookupEntry(qinfo->SoundAccept))
4237 {
4238 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SoundAccept` = %u but sound %u does not exist, set to 0.",
4239 qinfo->GetQuestId(), qinfo->SoundAccept, qinfo->SoundAccept);
4240 qinfo->SoundAccept = 0; // No sound will be played
4241 }
4242 }
4243
4244 if (qinfo->SoundTurnIn)
4245 {
4246 if (!sSoundEntriesStore.LookupEntry(qinfo->SoundTurnIn))
4247 {
4248 sLog->outError(LOG_FILTER_SQL, "Quest %u has `SoundTurnIn` = %u but sound %u does not exist, set to 0.",
4249 qinfo->GetQuestId(), qinfo->SoundTurnIn, qinfo->SoundTurnIn);
4250 qinfo->SoundTurnIn = 0; // No sound will be played
4251 }
4252 }
4253
4254 if (qinfo->RequiredSpell > 0)
4255 {
4256 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RequiredSpell);
4257
4258 if (!spellInfo)
4259 {
4260 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSpell` = %u but spell %u does not exist, quest will not require a spell.",
4261 qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell);
4262 qinfo->RequiredSpell = 0; // No spell will be required
4263 }
4264
4265 else if (!SpellMgr::IsSpellValid(spellInfo))
4266 {
4267 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RequiredSpell` = %u but spell %u is broken, quest will not require a spell.",
4268 qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell);
4269 qinfo->RequiredSpell = 0; // No spell will be required
4270 }
4271
4272 /* Can we require talents?
4273 else if (GetTalentSpellCost(qinfo->RewardSpellCast))
4274 {
4275 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4276 qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
4277 qinfo->RewardSpellCast = 0; // No spell will be casted on player
4278 }*/
4279 }
4280
4281 if (qinfo->RewardSkillId)
4282 {
4283 if (!sSkillLineStore.LookupEntry(qinfo->RewardSkillId))
4284 {
4285 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillId` = %u but this skill does not exist",
4286 qinfo->GetQuestId(), qinfo->RewardSkillId);
4287 }
4288 if (!qinfo->RewardSkillPoints)
4289 {
4290 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillId` = %u but `RewardSkillPoints` is 0",
4291 qinfo->GetQuestId(), qinfo->RewardSkillId);
4292 }
4293 }
4294
4295 if (qinfo->RewardSkillPoints)
4296 {
4297 if (qinfo->RewardSkillPoints > sWorld->GetConfigMaxSkillValue())
4298 {
4299 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillPoints` = %u but max possible skill is %u, quest can't be done.",
4300 qinfo->GetQuestId(), qinfo->RewardSkillPoints, sWorld->GetConfigMaxSkillValue());
4301 // No changes, quest can't be done for this requirement
4302 }
4303 if (!qinfo->RewardSkillId)
4304 {
4305 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardSkillPoints` = %u but `RewardSkillId` is 0",
4306 qinfo->GetQuestId(), qinfo->RewardSkillPoints);
4307 }
4308 }
4309
4310 // Check Reward Package Item Id
4311 if (qinfo->RewardPackageItemId)
4312 if (!sQuestPackageItemStore.LookupEntry(qinfo->RewardPackageItemId))
4313 sLog->outError(LOG_FILTER_SQL, "Quest %u has `RewardPackageItemId` = %u but this package does not exist.",
4314 qinfo->GetQuestId(), qinfo->RewardPackageItemId);
4315
4316 // Fill additional data stores
4317 if (qinfo->PrevQuestId)
4318 {
4319 if (_questTemplates.find(abs(qinfo->GetPrevQuestId())) == _questTemplates.end())
4320 sLog->outError(LOG_FILTER_SQL, "Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
4321 else
4322 qinfo->prevQuests.push_back(qinfo->PrevQuestId);
4323 }
4324
4325 if (qinfo->NextQuestId)
4326 {
4327 QuestMap::iterator qNextItr = _questTemplates.find(abs(qinfo->GetNextQuestId()));
4328 if (qNextItr == _questTemplates.end())
4329 sLog->outError(LOG_FILTER_SQL, "Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
4330 else
4331 {
4332 int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
4333 qNextItr->second->prevQuests.push_back(signedQuestId);
4334 }
4335 }
4336
4337 if (qinfo->ExclusiveGroup)
4338 mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
4339 if (qinfo->LimitTime)
4340 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED);
4341 if (qinfo->RequiredPlayerKills)
4342 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL);
4343 }
4344
4345 // Check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
4346 for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
4347 {
4348 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
4349 if (!spellInfo)
4350 continue;
4351
4352 for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
4353 {
4354 if (spellInfo->Effects[j].Effect != SPELL_EFFECT_QUEST_COMPLETE)
4355 continue;
4356
4357 uint32 quest_id = spellInfo->Effects[j].MiscValue;
4358
4359 Quest const* quest = GetQuestTemplate(quest_id);
4360
4361 // Some quest referenced in spells not exist (outdated spells)
4362 if (!quest)
4363 continue;
4364
4365 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
4366 {
4367 sLog->outError(LOG_FILTER_SQL, "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u, but quest doesn't have QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed and quest modified to enable the objective.", spellInfo->Id, quest_id);
4368
4369 // This will prevent quest completing without objective
4370 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
4371 }
4372 }
4373 }
4374
4375 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu quests definitions in %u ms", (unsigned long)_questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
4376}
4377
4378void ObjectMgr::LoadQuestLocales()
4379{
4380 uint32 oldMSTime = getMSTime();
4381
4382 _questLocaleStore.clear(); // Need for reload case
4383
4384 QueryResult result = WorldDatabase.Query("SELECT entry, "
4385 "Title_loc1, Details_loc1, Objectives_loc1, OfferRewardText_loc1, RequestItemsText_loc1, EndText_loc1, CompletedText_loc1, ObjectiveText1_loc1, ObjectiveText2_loc1, ObjectiveText3_loc1, ObjectiveText4_loc1, QuestGiverTextWindow_loc1, QuestGiverTargetName_loc1, QuestTurnTextWindow_loc1, QuestTurnTargetName_loc1,"
4386 "Title_loc2, Details_loc2, Objectives_loc2, OfferRewardText_loc2, RequestItemsText_loc2, EndText_loc2, CompletedText_loc2, ObjectiveText1_loc2, ObjectiveText2_loc2, ObjectiveText3_loc2, ObjectiveText4_loc2, QuestGiverTextWindow_loc2, QuestGiverTargetName_loc2, QuestTurnTextWindow_loc2, QuestTurnTargetName_loc2,"
4387 "Title_loc3, Details_loc3, Objectives_loc3, OfferRewardText_loc3, RequestItemsText_loc3, EndText_loc3, CompletedText_loc3, ObjectiveText1_loc3, ObjectiveText2_loc3, ObjectiveText3_loc3, ObjectiveText4_loc3, QuestGiverTextWindow_loc3, QuestGiverTargetName_loc3, QuestTurnTextWindow_loc3, QuestTurnTargetName_loc3,"
4388 "Title_loc4, Details_loc4, Objectives_loc4, OfferRewardText_loc4, RequestItemsText_loc4, EndText_loc4, CompletedText_loc4, ObjectiveText1_loc4, ObjectiveText2_loc4, ObjectiveText3_loc4, ObjectiveText4_loc4, QuestGiverTextWindow_loc4, QuestGiverTargetName_loc4, QuestTurnTextWindow_loc4, QuestTurnTargetName_loc4,"
4389 "Title_loc5, Details_loc5, Objectives_loc5, OfferRewardText_loc5, RequestItemsText_loc5, EndText_loc5, CompletedText_loc5, ObjectiveText1_loc5, ObjectiveText2_loc5, ObjectiveText3_loc5, ObjectiveText4_loc5, QuestGiverTextWindow_loc5, QuestGiverTargetName_loc5, QuestTurnTextWindow_loc5, QuestTurnTargetName_loc5,"
4390 "Title_loc6, Details_loc6, Objectives_loc6, OfferRewardText_loc6, RequestItemsText_loc6, EndText_loc6, CompletedText_loc6, ObjectiveText1_loc6, ObjectiveText2_loc6, ObjectiveText3_loc6, ObjectiveText4_loc6, QuestGiverTextWindow_loc6, QuestGiverTargetName_loc6, QuestTurnTextWindow_loc6, QuestTurnTargetName_loc6,"
4391 "Title_loc7, Details_loc7, Objectives_loc7, OfferRewardText_loc7, RequestItemsText_loc7, EndText_loc7, CompletedText_loc7, ObjectiveText1_loc7, ObjectiveText2_loc7, ObjectiveText3_loc7, ObjectiveText4_loc7, QuestGiverTextWindow_loc7, QuestGiverTargetName_loc7, QuestTurnTextWindow_loc7, QuestTurnTargetName_loc7,"
4392 "Title_loc8, Details_loc8, Objectives_loc8, OfferRewardText_loc8, RequestItemsText_loc8, EndText_loc8, CompletedText_loc8, ObjectiveText1_loc8, ObjectiveText2_loc8, ObjectiveText3_loc8, ObjectiveText4_loc8, QuestGiverTextWindow_loc8, QuestGiverTargetName_loc8, QuestTurnTextWindow_loc8, QuestTurnTargetName_loc8,"
4393 "Title_loc9, Details_loc9, Objectives_loc9, OfferRewardText_loc9, RequestItemsText_loc9, EndText_loc9, CompletedText_loc9, ObjectiveText1_loc9, ObjectiveText2_loc9, ObjectiveText3_loc9, ObjectiveText4_loc9, QuestGiverTextWindow_loc9, QuestGiverTargetName_loc9, QuestTurnTextWindow_loc9, QuestTurnTargetName_loc9,"
4394 "Title_loc10, Details_loc10, Objectives_loc10, OfferRewardText_loc10, RequestItemsText_loc10, EndText_loc10, CompletedText_loc10, ObjectiveText1_loc10, ObjectiveText2_loc10, ObjectiveText3_loc10, ObjectiveText4_loc10, QuestGiverTextWindow_loc10, QuestGiverTargetName_loc10, QuestTurnTextWindow_loc10, QuestTurnTargetName_loc10"
4395 " FROM locales_quest");
4396
4397 if (!result)
4398 return;
4399
4400 do
4401 {
4402 Field* fields = result->Fetch();
4403
4404 uint32 entry = fields[0].GetUInt32();
4405
4406 QuestLocale& data = _questLocaleStore[entry];
4407
4408 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
4409 {
4410 LocaleConstant locale = (LocaleConstant)i;
4411
4412 AddLocaleString(fields[1 + 15 * (i - 1)].GetString(), locale, data.Title);
4413 AddLocaleString(fields[1 + 15 * (i - 1) + 1].GetString(), locale, data.Details);
4414 AddLocaleString(fields[1 + 15 * (i - 1) + 2].GetString(), locale, data.Objectives);
4415 AddLocaleString(fields[1 + 15 * (i - 1) + 3].GetString(), locale, data.OfferRewardText);
4416 AddLocaleString(fields[1 + 15 * (i - 1) + 4].GetString(), locale, data.RequestItemsText);
4417 AddLocaleString(fields[1 + 15 * (i - 1) + 5].GetString(), locale, data.EndText);
4418 AddLocaleString(fields[1 + 15 * (i - 1) + 6].GetString(), locale, data.CompletedText);
4419
4420 for (uint8 k = 0; k < 4; ++k)
4421 AddLocaleString(fields[1 + 15 * (i - 1) + 7 + k].GetString(), locale, data.ObjectiveText[k]);
4422
4423 AddLocaleString(fields[1 + 15 * (i - 1) + 11].GetString(), locale, data.QuestGiverTextWindow);
4424 AddLocaleString(fields[1 + 15 * (i - 1) + 12].GetString(), locale, data.QuestGiverTargetName);
4425 AddLocaleString(fields[1 + 15 * (i - 1) + 13].GetString(), locale, data.QuestTurnTextWindow);
4426 AddLocaleString(fields[1 + 15 * (i - 1) + 14].GetString(), locale, data.QuestTurnTargetName);
4427 }
4428 } while (result->NextRow());
4429
4430 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu Quest locale strings in %u ms", (unsigned long)_questLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
4431}
4432
4433void ObjectMgr::LoadScripts(ScriptsType type)
4434{
4435 uint32 oldMSTime = getMSTime();
4436
4437 ScriptMapMap* scripts = GetScriptsMapByType(type);
4438 if (!scripts)
4439 return;
4440
4441 std::string tableName = GetScriptsTableNameByType(type);
4442 if (tableName.empty())
4443 return;
4444
4445 if (sScriptMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
4446 return;
4447
4448 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading %s...", tableName.c_str());
4449
4450 scripts->clear(); // need for reload support
4451
4452 bool isSpellScriptTable = (type == SCRIPTS_SPELL);
4453 // 0 1 2 3 4 5 6 7 8 9
4454 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o%s FROM %s", isSpellScriptTable ? ", effIndex" : "", tableName.c_str());
4455
4456 if (!result)
4457 {
4458 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 script definitions. DB table `%s` is empty!", tableName.c_str());
4459 return;
4460 }
4461
4462 uint32 count = 0;
4463
4464 do
4465 {
4466 Field* fields = result->Fetch();
4467 ScriptInfo tmp;
4468 tmp.type = type;
4469 tmp.id = fields[0].GetUInt32();
4470 if (isSpellScriptTable)
4471 tmp.id |= fields[10].GetUInt8() << 24;
4472 tmp.delay = fields[1].GetUInt32();
4473 tmp.command = ScriptCommands(fields[2].GetUInt32());
4474 tmp.Raw.nData[0] = fields[3].GetUInt32();
4475 tmp.Raw.nData[1] = fields[4].GetUInt32();
4476 tmp.Raw.nData[2] = fields[5].GetInt32();
4477 tmp.Raw.fData[0] = fields[6].GetFloat();
4478 tmp.Raw.fData[1] = fields[7].GetFloat();
4479 tmp.Raw.fData[2] = fields[8].GetFloat();
4480 tmp.Raw.fData[3] = fields[9].GetFloat();
4481
4482 // generic command args check
4483 switch (tmp.command)
4484 {
4485 case SCRIPT_COMMAND_TALK:
4486 {
4487 if (tmp.Talk.ChatType > CHAT_TYPE_WHISPER && tmp.Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER)
4488 {
4489 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",
4490 tableName.c_str(), tmp.Talk.ChatType, tmp.id);
4491 continue;
4492 }
4493 if (!tmp.Talk.TextID)
4494 {
4495 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u",
4496 tableName.c_str(), tmp.Talk.TextID, tmp.id);
4497 continue;
4498 }
4499 if (tmp.Talk.TextID < MIN_DB_SCRIPT_STRING_ID || tmp.Talk.TextID >= MAX_DB_SCRIPT_STRING_ID)
4500 {
4501 sLog->outError(LOG_FILTER_SQL, "Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u",
4502 tableName.c_str(), tmp.Talk.TextID, MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, tmp.id);
4503 continue;
4504 }
4505
4506 break;
4507 }
4508
4509 case SCRIPT_COMMAND_EMOTE:
4510 {
4511 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
4512 {
4513 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",
4514 tableName.c_str(), tmp.Emote.EmoteID, tmp.id);
4515 continue;
4516 }
4517 break;
4518 }
4519
4520 case SCRIPT_COMMAND_TELEPORT_TO:
4521 {
4522 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
4523 {
4524 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
4525 tableName.c_str(), tmp.TeleportTo.MapID, tmp.id);
4526 continue;
4527 }
4528
4529 if (!MoPCore::IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation))
4530 {
4531 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
4532 tableName.c_str(), tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
4533 continue;
4534 }
4535 break;
4536 }
4537
4538 case SCRIPT_COMMAND_QUEST_EXPLORED:
4539 {
4540 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
4541 if (!quest)
4542 {
4543 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
4544 tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
4545 continue;
4546 }
4547
4548 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
4549 {
4550 sLog->outError(LOG_FILTER_SQL, "Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",
4551 tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
4552
4553 // this will prevent quest completing without objective
4554 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
4555
4556 // continue; - quest objective requirement set and command can be allowed
4557 }
4558
4559 if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
4560 {
4561 sLog->outError(LOG_FILTER_SQL, "Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
4562 tableName.c_str(), tmp.QuestExplored.Distance, tmp.id);
4563 continue;
4564 }
4565
4566 if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
4567 {
4568 sLog->outError(LOG_FILTER_SQL, "Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check",
4569 tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE);
4570 continue;
4571 }
4572
4573 if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) < INTERACTION_DISTANCE)
4574 {
4575 sLog->outError(LOG_FILTER_SQL, "Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check",
4576 tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
4577 continue;
4578 }
4579
4580 break;
4581 }
4582
4583 case SCRIPT_COMMAND_KILL_CREDIT:
4584 {
4585 if (!GetCreatureTemplate(tmp.KillCredit.CreatureEntry))
4586 {
4587 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u",
4588 tableName.c_str(), tmp.KillCredit.CreatureEntry, tmp.id);
4589 continue;
4590 }
4591 break;
4592 }
4593
4594 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
4595 {
4596 GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
4597 if (!data)
4598 {
4599 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4600 tableName.c_str(), tmp.RespawnGameobject.GOGuid, tmp.id);
4601 continue;
4602 }
4603
4604 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
4605 if (!info)
4606 {
4607 sLog->outError(LOG_FILTER_SQL, "Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4608 tableName.c_str(), tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
4609 continue;
4610 }
4611
4612 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
4613 info->type == GAMEOBJECT_TYPE_FISHINGHOLE ||
4614 info->type == GAMEOBJECT_TYPE_DOOR ||
4615 info->type == GAMEOBJECT_TYPE_BUTTON ||
4616 info->type == GAMEOBJECT_TYPE_TRAP)
4617 {
4618 sLog->outError(LOG_FILTER_SQL, "Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4619 tableName.c_str(), info->entry, tmp.id);
4620 continue;
4621 }
4622 break;
4623 }
4624
4625 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
4626 {
4627 if (!MoPCore::IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation))
4628 {
4629 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
4630 tableName.c_str(), tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id);
4631 continue;
4632 }
4633
4634 if (!GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry))
4635 {
4636 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
4637 tableName.c_str(), tmp.TempSummonCreature.CreatureEntry, tmp.id);
4638 continue;
4639 }
4640 break;
4641 }
4642
4643 case SCRIPT_COMMAND_OPEN_DOOR:
4644 case SCRIPT_COMMAND_CLOSE_DOOR:
4645 {
4646 GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
4647 if (!data)
4648 {
4649 sLog->outError(LOG_FILTER_SQL, "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
4650 tableName.c_str(), tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4651 continue;
4652 }
4653
4654 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
4655 if (!info)
4656 {
4657 sLog->outError(LOG_FILTER_SQL, "Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",
4658 tableName.c_str(), tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4659 continue;
4660 }
4661
4662 if (info->type != GAMEOBJECT_TYPE_DOOR)
4663 {
4664 sLog->outError(LOG_FILTER_SQL, "Table `%s` has gameobject type (%u) non supported by command %s for script id %u",
4665 tableName.c_str(), info->entry, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4666 continue;
4667 }
4668
4669 break;
4670 }
4671
4672 case SCRIPT_COMMAND_REMOVE_AURA:
4673 {
4674 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID))
4675 {
4676 sLog->outError(LOG_FILTER_SQL, "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
4677 tableName.c_str(), tmp.RemoveAura.SpellID, tmp.id);
4678 continue;
4679 }
4680 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
4681 {
4682 sLog->outError(LOG_FILTER_SQL, "Table `%s` using unknown flags in datalong2 (%u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
4683 tableName.c_str(), tmp.RemoveAura.Flags, tmp.id);
4684 continue;
4685 }
4686 break;
4687 }
4688
4689 case SCRIPT_COMMAND_CAST_SPELL:
4690 {
4691 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID))
4692 {
4693 sLog->outError(LOG_FILTER_SQL, "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4694 tableName.c_str(), tmp.CastSpell.SpellID, tmp.id);
4695 continue;
4696 }
4697 if (tmp.CastSpell.Flags > 4) // targeting type
4698 {
4699 sLog->outError(LOG_FILTER_SQL, "Table `%s` using unknown target in datalong2 (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4700 tableName.c_str(), tmp.CastSpell.Flags, tmp.id);
4701 continue;
4702 }
4703 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
4704 {
4705 sLog->outError(LOG_FILTER_SQL, "Table `%s` using unknown flags in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4706 tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
4707 continue;
4708 }
4709 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
4710 {
4711 sLog->outError(LOG_FILTER_SQL, "Table `%s` using invalid creature entry in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4712 tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
4713 continue;
4714 }
4715 break;
4716 }
4717
4718 case SCRIPT_COMMAND_CREATE_ITEM:
4719 {
4720 if (!GetItemTemplate(tmp.CreateItem.ItemEntry))
4721 {
4722 sLog->outError(LOG_FILTER_SQL, "Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u",
4723 tableName.c_str(), tmp.CreateItem.ItemEntry, tmp.id);
4724 continue;
4725 }
4726 if (!tmp.CreateItem.Amount)
4727 {
4728 sLog->outError(LOG_FILTER_SQL, "Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u",
4729 tableName.c_str(), tmp.CreateItem.Amount, tmp.id);
4730 continue;
4731 }
4732 break;
4733 }
4734 default:
4735 break;
4736 }
4737
4738 if (scripts->find(tmp.id) == scripts->end())
4739 {
4740 ScriptMap emptyMap;
4741 (*scripts)[tmp.id] = emptyMap;
4742 }
4743 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
4744
4745 ++count;
4746 } while (result->NextRow());
4747
4748 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u script definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
4749}
4750
4751void ObjectMgr::LoadGameObjectScripts()
4752{
4753 LoadScripts(SCRIPTS_GAMEOBJECT);
4754
4755 // check ids
4756 for (ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
4757 {
4758 if (!GetGOData(itr->first))
4759 sLog->outError(LOG_FILTER_SQL, "Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id", itr->first);
4760 }
4761}
4762
4763void ObjectMgr::LoadQuestEndScripts()
4764{
4765 LoadScripts(SCRIPTS_QUEST_END);
4766
4767 // check ids
4768 for (ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
4769 {
4770 if (!GetQuestTemplate(itr->first))
4771 sLog->outError(LOG_FILTER_SQL, "Table `quest_end_scripts` has not existing quest (Id: %u) as script id", itr->first);
4772 }
4773}
4774
4775void ObjectMgr::LoadQuestStartScripts()
4776{
4777 LoadScripts(SCRIPTS_QUEST_START);
4778
4779 // check ids
4780 for (ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
4781 {
4782 if (!GetQuestTemplate(itr->first))
4783 sLog->outError(LOG_FILTER_SQL, "Table `quest_start_scripts` has not existing quest (Id: %u) as script id", itr->first);
4784 }
4785}
4786
4787void ObjectMgr::LoadSpellScripts()
4788{
4789 LoadScripts(SCRIPTS_SPELL);
4790
4791 // check ids
4792 for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
4793 {
4794 uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
4795 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
4796
4797 if (!spellInfo)
4798 {
4799 sLog->outError(LOG_FILTER_SQL, "Table `spell_scripts` has not existing spell (Id: %u) as script id", spellId);
4800 continue;
4801 }
4802
4803 uint8 i = (uint8)((uint32(itr->first) >> 24) & 0x000000FF);
4804 //check for correct spellEffect
4805 if (!spellInfo->Effects[i].Effect || (spellInfo->Effects[i].Effect != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->Effects[i].Effect != SPELL_EFFECT_DUMMY))
4806 sLog->outError(LOG_FILTER_SQL, "Table `spell_scripts` - spell %u effect %u is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, i);
4807 }
4808}
4809
4810void ObjectMgr::LoadEventScripts()
4811{
4812 LoadScripts(SCRIPTS_EVENT);
4813
4814 std::set<uint32> evt_scripts;
4815 // Load all possible script entries from gameobjects
4816 GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
4817 for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
4818 if (uint32 eventId = itr->second.GetEventScriptId())
4819 evt_scripts.insert(eventId);
4820
4821 // Load all possible script entries from spells
4822 for (uint32 i = 1; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
4823 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(i))
4824 for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
4825 if (spell->Effects[j].Effect == SPELL_EFFECT_SEND_EVENT)
4826 if (spell->Effects[j].MiscValue)
4827 evt_scripts.insert(spell->Effects[j].MiscValue);
4828
4829 for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
4830 {
4831 for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
4832 {
4833 TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx];
4834
4835 if (node.arrivalEventID)
4836 evt_scripts.insert(node.arrivalEventID);
4837
4838 if (node.departureEventID)
4839 evt_scripts.insert(node.departureEventID);
4840 }
4841 }
4842
4843 // Then check if all scripts are in above list of possible script entries
4844 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
4845 {
4846 std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
4847 if (itr2 == evt_scripts.end())
4848 sLog->outError(LOG_FILTER_SQL, "Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect %u",
4849 itr->first, SPELL_EFFECT_SEND_EVENT);
4850 }
4851}
4852
4853//Load WP Scripts
4854void ObjectMgr::LoadWaypointScripts()
4855{
4856 LoadScripts(SCRIPTS_WAYPOINT);
4857
4858 std::set<uint32> actionSet;
4859
4860 for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
4861 actionSet.insert(itr->first);
4862
4863 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
4864 PreparedQueryResult result = WorldDatabase.Query(stmt);
4865
4866 if (result)
4867 {
4868 do
4869 {
4870 Field* fields = result->Fetch();
4871 uint32 action = fields[0].GetUInt32();
4872
4873 actionSet.erase(action);
4874 } while (result->NextRow());
4875 }
4876
4877 for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
4878 sLog->outError(LOG_FILTER_SQL, "There is no waypoint which links to the waypoint script %u", *itr);
4879}
4880
4881void ObjectMgr::LoadSpellScriptNames()
4882{
4883 uint32 oldMSTime = getMSTime();
4884
4885 _spellScriptsStore.clear(); // need for reload case
4886
4887 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
4888
4889 if (!result)
4890 {
4891 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
4892 return;
4893 }
4894
4895 uint32 count = 0;
4896
4897 do
4898 {
4899
4900 Field* fields = result->Fetch();
4901
4902 int32 spellId = fields[0].GetInt32();
4903 const char *scriptName = fields[1].GetCString();
4904
4905 bool allRanks = false;
4906 if (spellId <= 0)
4907 {
4908 allRanks = true;
4909 spellId = -spellId;
4910 }
4911
4912 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
4913 if (!spellInfo)
4914 {
4915 sLog->outError(LOG_FILTER_SQL, "Scriptname:`%s` spell (spell_id:%d) does not exist in `Spell.dbc`.", scriptName, fields[0].GetInt32());
4916 continue;
4917 }
4918
4919 if (allRanks)
4920 {
4921 if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId))
4922 {
4923 sLog->outError(LOG_FILTER_SQL, "Scriptname:`%s` spell (spell_id:%d) is not first rank of spell.", scriptName, fields[0].GetInt32());
4924 continue;
4925 }
4926 while (spellInfo)
4927 {
4928 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
4929 spellInfo = sSpellMgr->GetSpellInfo(spellInfo->Id)->GetNextRankSpell();
4930 }
4931 }
4932 else
4933 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
4934 ++count;
4935 } while (result->NextRow());
4936
4937 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spell script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
4938}
4939
4940void ObjectMgr::ValidateSpellScripts()
4941{
4942 uint32 oldMSTime = getMSTime();
4943
4944 if (_spellScriptsStore.empty())
4945 {
4946 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Validated 0 scripts.");
4947 return;
4948 }
4949
4950 uint32 count = 0;
4951
4952 for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
4953 {
4954 SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
4955 std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
4956 sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
4957 itr = _spellScriptsStore.upper_bound(itr->first);
4958
4959 for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
4960 {
4961 SpellScript* spellScript = sitr->first->GetSpellScript();
4962 AuraScript* auraScript = sitr->first->GetAuraScript();
4963 bool valid = true;
4964 if (!spellScript && !auraScript)
4965 {
4966 sLog->outError(LOG_FILTER_TSCR, "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second));
4967 valid = false;
4968 }
4969 if (spellScript)
4970 {
4971 spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
4972 spellScript->_Register();
4973 if (!spellScript->_Validate(spellEntry))
4974 valid = false;
4975 delete spellScript;
4976 }
4977 if (auraScript)
4978 {
4979 auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
4980 auraScript->_Register();
4981 if (!auraScript->_Validate(spellEntry))
4982 valid = false;
4983 delete auraScript;
4984 }
4985 if (!valid)
4986 {
4987 _spellScriptsStore.erase(sitr->second);
4988 }
4989 }
4990 ++count;
4991 }
4992
4993 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
4994}
4995
4996void ObjectMgr::LoadPageTexts()
4997{
4998 uint32 oldMSTime = getMSTime();
4999
5000 // 0 1 2
5001 QueryResult result = WorldDatabase.Query("SELECT entry, text, next_page FROM page_text");
5002
5003 if (!result)
5004 {
5005 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 page texts. DB table `page_text` is empty!");
5006 return;
5007 }
5008
5009 uint32 count = 0;
5010 do
5011 {
5012 Field* fields = result->Fetch();
5013
5014 PageText& pageText = _pageTextStore[fields[0].GetUInt32()];
5015
5016 pageText.Text = fields[1].GetString();
5017 pageText.NextPage = fields[2].GetUInt32();
5018
5019 ++count;
5020 } while (result->NextRow());
5021
5022 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
5023 {
5024 if (itr->second.NextPage)
5025 {
5026 PageTextContainer::const_iterator itr2 = _pageTextStore.find(itr->second.NextPage);
5027 if (itr2 == _pageTextStore.end())
5028 sLog->outError(LOG_FILTER_SQL, "Page text (Id: %u) has not existing next page (Id: %u)", itr->first, itr->second.NextPage);
5029
5030 }
5031 }
5032
5033 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u page texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5034}
5035
5036PageText const* ObjectMgr::GetPageText(uint32 pageEntry)
5037{
5038 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
5039 if (itr != _pageTextStore.end())
5040 return &(itr->second);
5041
5042 return NULL;
5043}
5044
5045void ObjectMgr::LoadPageTextLocales()
5046{
5047 uint32 oldMSTime = getMSTime();
5048
5049 _pageTextLocaleStore.clear(); // need for reload case
5050
5051 QueryResult result = WorldDatabase.Query("SELECT entry, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8, text_loc9, text_loc10 FROM locales_page_text");
5052
5053 if (!result)
5054 return;
5055
5056 do
5057 {
5058 Field* fields = result->Fetch();
5059
5060 uint32 entry = fields[0].GetUInt32();
5061
5062 PageTextLocale& data = _pageTextLocaleStore[entry];
5063
5064 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
5065 AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.Text);
5066 } while (result->NextRow());
5067
5068 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu PageText locale strings in %u ms", (unsigned long)_pageTextLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5069}
5070
5071void ObjectMgr::LoadInstanceTemplate()
5072{
5073 uint32 oldMSTime = getMSTime();
5074
5075 // 0 1 2 4
5076 QueryResult result = WorldDatabase.Query("SELECT map, parent, script, allowMount FROM instance_template");
5077
5078 if (!result)
5079 {
5080 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 instance templates. DB table `page_text` is empty!");
5081 return;
5082 }
5083
5084 uint32 count = 0;
5085 do
5086 {
5087 Field* fields = result->Fetch();
5088
5089 uint16 mapID = fields[0].GetUInt16();
5090
5091 if (!MapManager::IsValidMAP(mapID, true))
5092 {
5093 sLog->outError(LOG_FILTER_SQL, "ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", mapID);
5094 continue;
5095 }
5096
5097 InstanceTemplate instanceTemplate;
5098
5099 instanceTemplate.AllowMount = fields[3].GetBool();
5100 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
5101 instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetCString());
5102
5103 _instanceTemplateStore[mapID] = instanceTemplate;
5104
5105 ++count;
5106 } while (result->NextRow());
5107
5108 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u instance templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5109}
5110
5111InstanceTemplate const* ObjectMgr::GetInstanceTemplate(uint32 mapID)
5112{
5113 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
5114 if (itr != _instanceTemplateStore.end())
5115 return &(itr->second);
5116
5117 return NULL;
5118}
5119
5120void ObjectMgr::LoadInstanceEncounters()
5121{
5122 uint32 oldMSTime = getMSTime();
5123
5124 // 0 1 2 3 4
5125 QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDifficulty, lastEncounterDungeon FROM instance_encounters");
5126 if (!result)
5127 {
5128 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 instance encounters, table is empty!");
5129
5130 return;
5131 }
5132
5133 uint32 count = 0;
5134 std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
5135 do
5136 {
5137 Field* fields = result->Fetch();
5138 uint32 entry = fields[0].GetUInt32();
5139 uint8 creditType = fields[1].GetUInt8();
5140 uint32 creditEntry = fields[2].GetUInt32();
5141 uint32 lastEncounterDifficulty = fields[3].GetUInt32();
5142 uint32 lastEncounterDungeon = fields[4].GetUInt16();
5143 DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
5144 if (!dungeonEncounter)
5145 {
5146 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
5147 continue;
5148 }
5149
5150 if (lastEncounterDungeon && !sLFGDungeonStore.LookupEntry(lastEncounterDungeon))
5151 {
5152 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName, lastEncounterDungeon);
5153 continue;
5154 }
5155
5156 std::map<uint32, DungeonEncounterEntry const*>::const_iterator itr = dungeonLastBosses.find(lastEncounterDungeon);
5157 if (lastEncounterDungeon)
5158 {
5159 if (itr != dungeonLastBosses.end() && itr->second->difficulty == lastEncounterDifficulty && itr->second->id == entry)
5160 {
5161 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounter->encounterName, itr->second->id, itr->second->encounterName);
5162 continue;
5163 }
5164
5165 dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
5166 }
5167
5168 switch (creditType)
5169 {
5170 case ENCOUNTER_CREDIT_KILL_CREATURE:
5171 {
5172 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry);
5173 if (!creatureInfo)
5174 {
5175 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName);
5176 continue;
5177 }
5178 const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
5179 break;
5180 }
5181 case ENCOUNTER_CREDIT_CAST_SPELL:
5182 if (!sSpellMgr->GetSpellInfo(creditEntry))
5183 {
5184 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName);
5185 continue;
5186 }
5187 break;
5188 default:
5189 sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName);
5190 continue;
5191 }
5192
5193 // If has no difficulty in DBC and is not last encounter boss check and load for all difficulties.
5194 if (dungeonEncounter->difficulty <= 0 && !lastEncounterDungeon)
5195 {
5196 for (uint32 i = 0; i < MAX_DIFFICULTY; ++i)
5197 {
5198 if (GetMapDifficultyData(dungeonEncounter->mapId, Difficulty(i)))
5199 {
5200 DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, i)];
5201 encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
5202 }
5203 }
5204 }
5205 else
5206 {
5207 if (lastEncounterDifficulty && lastEncounterDungeon) // If has difficulty in db and is last encounter boss check and load for the db difficulty.
5208 {
5209 DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, lastEncounterDifficulty)];
5210 encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
5211 }
5212 else // If has difficulty in DBC and is / is not last encounter boss check and load for the DBC difficulty.
5213 {
5214 DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, dungeonEncounter->difficulty)];
5215 encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
5216 }
5217 }
5218
5219 ++count;
5220 } while (result->NextRow());
5221
5222 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u instance encounters in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5223}
5224
5225// Boss loot quest Id, used for new Loot-based Lockout system.
5226uint32 ObjectMgr::GetWeeklyBossLootQuestId(uint32 creatureEntry, uint32 difficulty)
5227{
5228 QueryResult result = WorldDatabase.Query("SELECT questId FROM boss_loot_weekly_quest WHERE entry = '%u' and difficulty = '%u'", creatureEntry, difficulty);
5229
5230 if (result)
5231 {
5232 Field* fields = result->Fetch();
5233 uint32 questId = fields[0].GetUInt32();
5234
5235 return questId;
5236
5237 }
5238 else
5239 return 0;
5240}
5241
5242GossipText const* ObjectMgr::GetGossipText(uint32 Text_ID) const
5243{
5244 GossipTextContainer::const_iterator itr = _gossipTextStore.find(Text_ID);
5245 if (itr != _gossipTextStore.end())
5246 return &itr->second;
5247 return NULL;
5248}
5249
5250void ObjectMgr::LoadGossipText()
5251{
5252 uint32 oldMSTime = getMSTime();
5253
5254 QueryResult result = WorldDatabase.Query("SELECT * FROM npc_text");
5255
5256 int count = 0;
5257 if (!result)
5258 {
5259 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u npc texts", count);
5260 return;
5261 }
5262 //_gossipTextStore.rehash(result->GetRowCount());
5263
5264 int cic;
5265
5266 do
5267 {
5268 ++count;
5269 cic = 0;
5270
5271 Field* fields = result->Fetch();
5272
5273 uint32 Text_ID = fields[cic++].GetUInt32();
5274 if (!Text_ID)
5275 {
5276 sLog->outError(LOG_FILTER_SQL, "Table `npc_text` has record wit reserved id 0, ignore.");
5277 continue;
5278 }
5279
5280 GossipText& gText = _gossipTextStore[Text_ID];
5281
5282 for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; i++)
5283 {
5284 gText.Options[i].Text_0 = fields[cic++].GetString();
5285 gText.Options[i].Text_1 = fields[cic++].GetString();
5286
5287 gText.Options[i].Language = fields[cic++].GetUInt8();
5288 gText.Options[i].Probability = fields[cic++].GetFloat();
5289
5290 for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
5291 {
5292 gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt16();
5293 gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt16();
5294 }
5295 }
5296 } while (result->NextRow());
5297
5298 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u npc texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5299}
5300
5301void ObjectMgr::LoadNpcTextLocales()
5302{
5303 uint32 oldMSTime = getMSTime();
5304
5305 _npcTextLocaleStore.clear(); // need for reload case
5306
5307 QueryResult result = WorldDatabase.Query("SELECT entry, "
5308 "Text0_0_loc1, Text0_1_loc1, Text1_0_loc1, Text1_1_loc1, Text2_0_loc1, Text2_1_loc1, Text3_0_loc1, Text3_1_loc1, Text4_0_loc1, Text4_1_loc1, Text5_0_loc1, Text5_1_loc1, Text6_0_loc1, Text6_1_loc1, Text7_0_loc1, Text7_1_loc1, Text8_0_loc1, Text8_1_loc1, Text9_0_loc1, Text9_1_loc1, "
5309 "Text0_0_loc2, Text0_1_loc2, Text1_0_loc2, Text1_1_loc2, Text2_0_loc2, Text2_1_loc2, Text3_0_loc2, Text3_1_loc1, Text4_0_loc2, Text4_1_loc2, Text5_0_loc2, Text5_1_loc2, Text6_0_loc2, Text6_1_loc2, Text7_0_loc2, Text7_1_loc2, Text8_0_loc2, Text8_1_loc2, Text9_0_loc2, Text9_1_loc2, "
5310 "Text0_0_loc3, Text0_1_loc3, Text1_0_loc3, Text1_1_loc3, Text2_0_loc3, Text2_1_loc3, Text3_0_loc3, Text3_1_loc1, Text4_0_loc3, Text4_1_loc3, Text5_0_loc3, Text5_1_loc3, Text6_0_loc3, Text6_1_loc3, Text7_0_loc3, Text7_1_loc3, Text8_0_loc3, Text8_1_loc3, Text9_0_loc3, Text9_1_loc3, "
5311 "Text0_0_loc4, Text0_1_loc4, Text1_0_loc4, Text1_1_loc4, Text2_0_loc4, Text2_1_loc4, Text3_0_loc4, Text3_1_loc1, Text4_0_loc4, Text4_1_loc4, Text5_0_loc4, Text5_1_loc4, Text6_0_loc4, Text6_1_loc4, Text7_0_loc4, Text7_1_loc4, Text8_0_loc4, Text8_1_loc4, Text9_0_loc4, Text9_1_loc4, "
5312 "Text0_0_loc5, Text0_1_loc5, Text1_0_loc5, Text1_1_loc5, Text2_0_loc5, Text2_1_loc5, Text3_0_loc5, Text3_1_loc1, Text4_0_loc5, Text4_1_loc5, Text5_0_loc5, Text5_1_loc5, Text6_0_loc5, Text6_1_loc5, Text7_0_loc5, Text7_1_loc5, Text8_0_loc5, Text8_1_loc5, Text9_0_loc5, Text9_1_loc5, "
5313 "Text0_0_loc6, Text0_1_loc6, Text1_0_loc6, Text1_1_loc6, Text2_0_loc6, Text2_1_loc6, Text3_0_loc6, Text3_1_loc1, Text4_0_loc6, Text4_1_loc6, Text5_0_loc6, Text5_1_loc6, Text6_0_loc6, Text6_1_loc6, Text7_0_loc6, Text7_1_loc6, Text8_0_loc6, Text8_1_loc6, Text9_0_loc6, Text9_1_loc6, "
5314 "Text0_0_loc7, Text0_1_loc7, Text1_0_loc7, Text1_1_loc7, Text2_0_loc7, Text2_1_loc7, Text3_0_loc7, Text3_1_loc1, Text4_0_loc7, Text4_1_loc7, Text5_0_loc7, Text5_1_loc7, Text6_0_loc7, Text6_1_loc7, Text7_0_loc7, Text7_1_loc7, Text8_0_loc7, Text8_1_loc7, Text9_0_loc7, Text9_1_loc7, "
5315 "Text0_0_loc8, Text0_1_loc8, Text1_0_loc8, Text1_1_loc8, Text2_0_loc8, Text2_1_loc8, Text3_0_loc8, Text3_1_loc1, Text4_0_loc8, Text4_1_loc8, Text5_0_loc8, Text5_1_loc8, Text6_0_loc8, Text6_1_loc8, Text7_0_loc8, Text7_1_loc8, Text8_0_loc8, Text8_1_loc8, Text9_0_loc8, Text9_1_loc8, "
5316 "Text0_0_loc9, Text0_1_loc9, Text1_0_loc9, Text1_1_loc9, Text2_0_loc9, Text2_1_loc9, Text3_0_loc9, Text3_1_loc9, Text4_0_loc9, Text4_1_loc9, Text5_0_loc9, Text5_1_loc9, Text6_0_loc9, Text6_1_loc9, Text7_0_loc9, Text7_1_loc9, Text8_0_loc9, Text8_1_loc9, Text9_0_loc9, Text9_1_loc9, "
5317 "Text0_0_loc10, Text0_1_loc10, Text1_0_loc10, Text1_1_loc10, Text2_0_loc10, Text2_1_loc10, Text3_0_loc10, Text3_1_loc10, Text4_0_loc10, Text4_1_loc10, Text5_0_loc10, Text5_1_loc10, Text6_0_loc10, Text6_1_loc10, Text7_0_loc10, Text7_1_loc10, Text8_0_loc10, Text8_1_loc10, Text9_0_loc10, Text9_1_loc10 "
5318 " FROM locales_npc_text");
5319
5320 if (!result)
5321 return;
5322
5323 do
5324 {
5325 Field* fields = result->Fetch();
5326
5327 uint32 entry = fields[0].GetUInt32();
5328
5329 NpcTextLocale& data = _npcTextLocaleStore[entry];
5330
5331 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
5332 {
5333 LocaleConstant locale = (LocaleConstant)i;
5334 for (uint8 j = 0; j < MAX_LOCALES; ++j)
5335 {
5336 AddLocaleString(fields[1 + 8 * 2 * (i - 1) + 2 * j].GetString(), locale, data.Text_0[j]);
5337 AddLocaleString(fields[1 + 8 * 2 * (i - 1) + 2 * j + 1].GetString(), locale, data.Text_1[j]);
5338 }
5339 }
5340 } while (result->NextRow());
5341
5342 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu NpcText locale strings in %u ms", (unsigned long)_npcTextLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5343}
5344
5345//not very fast function but it is called only once a day, or on starting-up
5346void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
5347{
5348 uint32 oldMSTime = getMSTime();
5349
5350 time_t curTime = time(NULL);
5351 tm lt;
5352 ACE_OS::localtime_r(&curTime, <);
5353 uint64 basetime(curTime);
5354 sLog->outInfo(LOG_FILTER_GENERAL, "Returning mails current time: hour: %d, minute: %d, second: %d ", lt.tm_hour, lt.tm_min, lt.tm_sec);
5355
5356 // Delete all old mails without item and without body immediately, if starting server
5357 if (!serverUp)
5358 {
5359 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EMPTY_EXPIRED_MAIL);
5360 stmt->setUInt64(0, basetime);
5361 CharacterDatabase.Execute(stmt);
5362 }
5363 PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
5364 stmt->setUInt64(0, basetime);
5365 PreparedQueryResult result = CharacterDatabase.Query(stmt);
5366 if (!result)
5367 {
5368 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> No expired mails found.");
5369 return; // any mails need to be returned or deleted
5370 }
5371
5372 std::map<uint32 /*messageId*/, MailItemInfoVec> itemsCache;
5373 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
5374 stmt->setUInt32(0, (uint32)basetime);
5375 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
5376 {
5377 MailItemInfo item;
5378 do
5379 {
5380 Field* fields = items->Fetch();
5381 item.item_guid = fields[0].GetUInt32();
5382 item.item_template = fields[1].GetUInt32();
5383 uint32 mailId = fields[2].GetUInt32();
5384 itemsCache[mailId].push_back(item);
5385 } while (items->NextRow());
5386 }
5387
5388 uint32 deletedCount = 0;
5389 uint32 returnedCount = 0;
5390 do
5391 {
5392 Field* fields = result->Fetch();
5393 Mail* m = new Mail;
5394 m->messageID = fields[0].GetUInt32();
5395 m->messageType = fields[1].GetUInt8();
5396 m->sender = fields[2].GetUInt32();
5397 m->receiver = fields[3].GetUInt32();
5398 bool has_items = fields[4].GetBool();
5399 m->expire_time = time_t(fields[5].GetUInt32());
5400 m->deliver_time = 0;
5401 m->COD = fields[6].GetUInt64();
5402 m->checked = fields[7].GetUInt8();
5403 m->mailTemplateId = fields[8].GetInt16();
5404
5405 Player* player = NULL;
5406 if (serverUp)
5407 player = ObjectAccessor::FindPlayer((uint64)m->receiver);
5408
5409 if (player && player->m_mailsLoaded)
5410 { // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
5411 // his in mailbox and he has already listed his mails)
5412 delete m;
5413 continue;
5414 }
5415
5416 // Delete or return mail
5417 if (has_items)
5418 {
5419 // read items from cache
5420 m->items.swap(itemsCache[m->messageID]);
5421
5422 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
5423 if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
5424 {
5425 // mail open and then not returned
5426 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
5427 {
5428 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
5429 stmt->setUInt32(0, itr2->item_guid);
5430 CharacterDatabase.Execute(stmt);
5431 }
5432 }
5433 else
5434 {
5435 // Mail will be returned
5436 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
5437 stmt->setUInt32(0, m->receiver);
5438 stmt->setUInt32(1, m->sender);
5439 stmt->setUInt32(2, basetime + 30 * DAY);
5440 stmt->setUInt32(3, basetime);
5441 stmt->setUInt8(4, uint8(MAIL_CHECK_MASK_RETURNED));
5442 stmt->setUInt32(5, m->messageID);
5443 CharacterDatabase.Execute(stmt);
5444 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
5445 {
5446 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
5447 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
5448 stmt->setUInt32(0, m->sender);
5449 stmt->setUInt32(1, itr2->item_guid);
5450 CharacterDatabase.Execute(stmt);
5451
5452 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
5453 stmt->setUInt32(0, m->sender);
5454 stmt->setUInt32(1, itr2->item_guid);
5455 CharacterDatabase.Execute(stmt);
5456 }
5457 delete m;
5458 ++returnedCount;
5459 continue;
5460 }
5461 }
5462
5463 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
5464 stmt->setUInt32(0, m->messageID);
5465 CharacterDatabase.Execute(stmt);
5466 delete m;
5467 ++deletedCount;
5468 } while (result->NextRow());
5469
5470 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Processed %u expired mails: %u deleted and %u returned in %u ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
5471}
5472
5473void ObjectMgr::LoadQuestAreaTriggers()
5474{
5475 uint32 oldMSTime = getMSTime();
5476
5477 _questAreaTriggerStore.clear(); // need for reload case
5478
5479 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
5480
5481 if (!result)
5482 {
5483 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
5484 return;
5485 }
5486
5487 uint32 count = 0;
5488
5489 do
5490 {
5491 ++count;
5492
5493 Field* fields = result->Fetch();
5494
5495 uint32 trigger_ID = fields[0].GetUInt32();
5496 uint32 quest_ID = fields[1].GetUInt32();
5497
5498 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
5499 if (!atEntry)
5500 {
5501 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", trigger_ID);
5502 continue;
5503 }
5504
5505 Quest const* quest = GetQuestTemplate(quest_ID);
5506
5507 if (!quest)
5508 {
5509 sLog->outError(LOG_FILTER_SQL, "Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
5510 continue;
5511 }
5512
5513 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
5514 {
5515 sLog->outError(LOG_FILTER_SQL, "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
5516
5517 // this will prevent quest completing without objective
5518 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
5519
5520 // continue; - quest modified to required objective and trigger can be allowed.
5521 }
5522
5523 _questAreaTriggerStore[trigger_ID] = quest_ID;
5524
5525 } while (result->NextRow());
5526
5527 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u quest trigger points in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5528}
5529
5530void ObjectMgr::LoadTavernAreaTriggers()
5531{
5532 uint32 oldMSTime = getMSTime();
5533
5534 _tavernAreaTriggerStore.clear(); // need for reload case
5535
5536 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
5537
5538 if (!result)
5539 {
5540 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
5541 return;
5542 }
5543
5544 uint32 count = 0;
5545
5546 do
5547 {
5548 ++count;
5549
5550 Field* fields = result->Fetch();
5551
5552 uint32 Trigger_ID = fields[0].GetUInt32();
5553
5554 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5555 if (!atEntry)
5556 {
5557 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
5558 continue;
5559 }
5560
5561 _tavernAreaTriggerStore.insert(Trigger_ID);
5562 } while (result->NextRow());
5563
5564 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u tavern triggers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5565}
5566
5567void ObjectMgr::LoadAreaTriggerScripts()
5568{
5569 uint32 oldMSTime = getMSTime();
5570
5571 _areaTriggerScriptStore.clear(); // need for reload case
5572 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
5573
5574 if (!result)
5575 {
5576 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
5577 return;
5578 }
5579
5580 uint32 count = 0;
5581
5582 do
5583 {
5584 ++count;
5585
5586 Field* fields = result->Fetch();
5587
5588 uint32 Trigger_ID = fields[0].GetUInt32();
5589 const char *scriptName = fields[1].GetCString();
5590
5591 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5592 if (!atEntry)
5593 {
5594 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
5595 continue;
5596 }
5597
5598 _areaTriggerScriptStore[Trigger_ID] = GetScriptId(scriptName);
5599 } while (result->NextRow());
5600
5601 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u areatrigger scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5602}
5603
5604uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
5605{
5606 bool found = false;
5607 float dist = 10000;
5608 uint32 id = 0;
5609
5610 for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
5611 {
5612 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
5613
5614 if (!node || node->map_id != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight
5615 continue;
5616
5617 uint8 field = (uint8)((i - 1) / 8);
5618 uint32 submask = 1 << ((i - 1) % 8);
5619
5620 // skip not taxi network nodes
5621 if ((sTaxiNodesMask[field] & submask) == 0)
5622 continue;
5623
5624 float dist2 = (node->x - x)*(node->x - x) + (node->y - y)*(node->y - y) + (node->z - z)*(node->z - z);
5625 if (found)
5626 {
5627 if (dist2 < dist)
5628 {
5629 dist = dist2;
5630 id = i;
5631 }
5632 }
5633 else
5634 {
5635 found = true;
5636 dist = dist2;
5637 id = i;
5638 }
5639 }
5640
5641 return id;
5642}
5643
5644void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
5645{
5646 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
5647 if (src_i == sTaxiPathSetBySource.end())
5648 {
5649 path = 0;
5650 cost = 0;
5651 return;
5652 }
5653
5654 TaxiPathSetForSource& pathSet = src_i->second;
5655
5656 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
5657 if (dest_i == pathSet.end())
5658 {
5659 path = 0;
5660 cost = 0;
5661 return;
5662 }
5663
5664 cost = dest_i->second.price;
5665 path = dest_i->second.ID;
5666}
5667
5668uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
5669{
5670 uint32 mount_id = 0;
5671
5672 // select mount creature id
5673 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
5674 if (node)
5675 {
5676 uint32 mount_entry = 0;
5677 mount_entry = (team == ALLIANCE) ? node->MountCreatureID[1] : node->MountCreatureID[0];
5678
5679 // Fix for Alliance not being able to use Acherus taxi. Only one mount type for both sides.
5680 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
5681 if (mount_entry == 0 && allowed_alt_team)
5682 mount_entry = (team == ALLIANCE) ? node->MountCreatureID[0] : node->MountCreatureID[1];
5683
5684 CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
5685 if (mount_info)
5686 {
5687 mount_id = mount_info->GetRandomValidModelId();
5688 if (!mount_id)
5689 {
5690 sLog->outError(LOG_FILTER_SQL, "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry);
5691 return false;
5692 }
5693 }
5694 }
5695
5696 // minfo is not actually used but the mount_id was updated
5697 GetCreatureModelRandomGender(&mount_id);
5698
5699 return mount_id;
5700}
5701
5702void ObjectMgr::LoadGraveyardZones()
5703{
5704 uint32 oldMSTime = getMSTime();
5705
5706 GraveYardStore.clear(); // need for reload case
5707
5708 // 0 1 2
5709 QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone");
5710
5711 if (!result)
5712 {
5713 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 graveyard-zone links. DB table `game_graveyard_zone` is empty.");
5714 return;
5715 }
5716
5717 uint32 count = 0;
5718
5719 do
5720 {
5721 ++count;
5722
5723 Field* fields = result->Fetch();
5724
5725 uint32 safeLocId = fields[0].GetUInt32();
5726 uint32 zoneId = fields[1].GetUInt32();
5727 uint32 team = fields[2].GetUInt16();
5728
5729 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
5730 if (!entry)
5731 {
5732 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has a record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
5733 continue;
5734 }
5735
5736 AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId);
5737 if (!areaEntry)
5738 {
5739 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId);
5740 continue;
5741 }
5742
5743 if (areaEntry->zone != 0 && zoneId != 33 && zoneId != 4755 && zoneId != 5287 && zoneId != 6170 && zoneId != 6176 && zoneId != 6450 && zoneId != 6451
5744 && zoneId != 6452 && zoneId != 6453 && zoneId != 6454 && zoneId != 6455 && zoneId != 6456 && zoneId != 6450)
5745 {
5746 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has a record for subzone id (%u) instead of zone, skipped.", zoneId);
5747 continue;
5748 }
5749
5750 if (team != 0 && team != HORDE && team != ALLIANCE)
5751 {
5752 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has a record for non player faction (%u), skipped.", team);
5753 continue;
5754 }
5755
5756 if (!AddGraveYardLink(safeLocId, zoneId, team, false))
5757 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
5758 } while (result->NextRow());
5759
5760 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
5761}
5762
5763WorldSafeLocsEntry const* ObjectMgr::GetDefaultGraveYard(uint32 team)
5764{
5765 enum DefaultGraveyard
5766 {
5767 HORDE_GRAVEYARD = 10, // Crossroads
5768 ALLIANCE_GRAVEYARD = 4, // Westfall
5769 };
5770
5771 if (team == HORDE)
5772 return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
5773 else if (team == ALLIANCE)
5774 return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
5775 else return NULL;
5776}
5777
5778WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
5779{
5780 // search for zone associated closest graveyard
5781 uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z);
5782
5783 if (!zoneId)
5784 {
5785 if (z > -500)
5786 {
5787 sLog->outError(LOG_FILTER_GENERAL, "ZoneId not found for map %u coords (%f, %f, %f)", MapId, x, y, z);
5788 return GetDefaultGraveYard(team);
5789 }
5790 }
5791
5792 // Simulate std. algorithm:
5793 // found some graveyard associated to (ghost_zone, ghost_map)
5794 //
5795 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5796 // then check faction
5797 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5798 // then check faction
5799 GraveYardContainer::const_iterator graveLow = GraveYardStore.lower_bound(zoneId);
5800 GraveYardContainer::const_iterator graveUp = GraveYardStore.upper_bound(zoneId);
5801 MapEntry const* map = sMapStore.LookupEntry(MapId);
5802 // not need to check validity of map object; MapId _MUST_ be valid here
5803
5804 if (graveLow == graveUp && !map->IsBattleArena())
5805 {
5806 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
5807 return GetDefaultGraveYard(team);
5808 }
5809
5810 // at corpse map
5811 bool foundNear = false;
5812 float distNear = 10000;
5813 WorldSafeLocsEntry const* entryNear = NULL;
5814
5815 // at entrance map for corpse map
5816 bool foundEntr = false;
5817 float distEntr = 10000;
5818 WorldSafeLocsEntry const* entryEntr = NULL;
5819
5820 // some where other
5821 WorldSafeLocsEntry const* entryFar = NULL;
5822
5823 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
5824
5825 for (GraveYardContainer::const_iterator itr = graveLow; itr != graveUp; ++itr)
5826 {
5827 GraveYardData const& data = itr->second;
5828
5829 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
5830 if (!entry)
5831 {
5832 sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId);
5833 continue;
5834 }
5835
5836 // skip enemy faction graveyard
5837 // team == 0 case can be at call from .neargrave
5838 if (data.team != 0 && team != 0 && data.team != team)
5839 continue;
5840
5841 // find now nearest graveyard at other map
5842 if (MapId != entry->map_id)
5843 {
5844 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5845 if (!mapEntry
5846 || mapEntry->entrance_map < 0
5847 || uint32(mapEntry->entrance_map) != entry->map_id
5848 || (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
5849 {
5850 // not have any corrdinates for check distance anyway
5851 entryFar = entry;
5852 continue;
5853 }
5854
5855 // at entrance map calculate distance (2D);
5856 float dist2 = (entry->x - mapEntry->entrance_x)*(entry->x - mapEntry->entrance_x)
5857 + (entry->y - mapEntry->entrance_y)*(entry->y - mapEntry->entrance_y);
5858 if (foundEntr)
5859 {
5860 if (dist2 < distEntr)
5861 {
5862 distEntr = dist2;
5863 entryEntr = entry;
5864 }
5865 }
5866 else
5867 {
5868 foundEntr = true;
5869 distEntr = dist2;
5870 entryEntr = entry;
5871 }
5872 }
5873 // find now nearest graveyard at same map
5874 else
5875 {
5876 float dist2 = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z);
5877 if (foundNear)
5878 {
5879 if (dist2 < distNear)
5880 {
5881 distNear = dist2;
5882 entryNear = entry;
5883 }
5884 }
5885 else
5886 {
5887 foundNear = true;
5888 distNear = dist2;
5889 entryNear = entry;
5890 }
5891 }
5892 }
5893
5894 if (entryNear)
5895 return entryNear;
5896
5897 if (entryEntr)
5898 return entryEntr;
5899
5900 return entryFar;
5901}
5902
5903GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
5904{
5905 GraveYardContainer::const_iterator graveLow = GraveYardStore.lower_bound(zoneId);
5906 GraveYardContainer::const_iterator graveUp = GraveYardStore.upper_bound(zoneId);
5907
5908 for (GraveYardContainer::const_iterator itr = graveLow; itr != graveUp; ++itr)
5909 {
5910 if (itr->second.safeLocId == id)
5911 return &itr->second;
5912 }
5913
5914 return NULL;
5915}
5916
5917bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
5918{
5919 if (FindGraveYardData(id, zoneId))
5920 return false;
5921
5922 // add link to loaded data
5923 GraveYardData data;
5924 data.safeLocId = id;
5925 data.team = team;
5926
5927 GraveYardStore.insert(GraveYardContainer::value_type(zoneId, data));
5928
5929 // add link to DB
5930 if (persist)
5931 {
5932 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE);
5933
5934 stmt->setUInt32(0, id);
5935 stmt->setUInt32(1, zoneId);
5936 stmt->setUInt16(2, uint16(team));
5937
5938 WorldDatabase.Execute(stmt);
5939 }
5940
5941 return true;
5942}
5943
5944void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= false*/)
5945{
5946 GraveYardContainer::iterator graveLow = GraveYardStore.lower_bound(zoneId);
5947 GraveYardContainer::iterator graveUp = GraveYardStore.upper_bound(zoneId);
5948 if (graveLow == graveUp)
5949 {
5950 //sLog->outError(LOG_FILTER_SQL, "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
5951 return;
5952 }
5953
5954 bool found = false;
5955
5956 GraveYardContainer::iterator itr;
5957
5958 for (itr = graveLow; itr != graveUp; ++itr)
5959 {
5960 GraveYardData & data = itr->second;
5961
5962 // skip not matching safezone id
5963 if (data.safeLocId != id)
5964 continue;
5965
5966 // skip enemy faction graveyard at same map (normal area, city, or battleground)
5967 // team == 0 case can be at call from .neargrave
5968 if (data.team != 0 && team != 0 && data.team != team)
5969 continue;
5970
5971 found = true;
5972 break;
5973 }
5974
5975 // no match, return
5976 if (!found)
5977 return;
5978
5979 // remove from links
5980 GraveYardStore.erase(itr);
5981
5982 // remove link from DB
5983 if (persist)
5984 {
5985 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE);
5986
5987 stmt->setUInt32(0, id);
5988 stmt->setUInt32(1, zoneId);
5989 stmt->setUInt16(2, uint16(team));
5990
5991 WorldDatabase.Execute(stmt);
5992 }
5993}
5994
5995void ObjectMgr::LoadAreaTriggerTeleports()
5996{
5997 uint32 oldMSTime = getMSTime();
5998
5999 _areaTriggerStore.clear(); // need for reload case
6000
6001 // 0 1 2 3 4 5
6002 QueryResult result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
6003 if (!result)
6004 {
6005 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
6006 return;
6007 }
6008
6009 uint32 count = 0;
6010
6011 do
6012 {
6013 Field* fields = result->Fetch();
6014
6015 ++count;
6016
6017 uint32 Trigger_ID = fields[0].GetUInt32();
6018
6019 AreaTriggerStruct at;
6020
6021 at.target_mapId = fields[1].GetUInt16();
6022 at.target_X = fields[2].GetFloat();
6023 at.target_Y = fields[3].GetFloat();
6024 at.target_Z = fields[4].GetFloat();
6025 at.target_Orientation = fields[5].GetFloat();
6026
6027 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6028 if (!atEntry)
6029 {
6030 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6031 continue;
6032 }
6033
6034 MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
6035 if (!mapEntry)
6036 {
6037 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
6038 continue;
6039 }
6040
6041 if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
6042 {
6043 sLog->outError(LOG_FILTER_SQL, "Area trigger (ID:%u) target coordinates not provided.", Trigger_ID);
6044 continue;
6045 }
6046
6047 _areaTriggerStore[Trigger_ID] = at;
6048
6049 } while (result->NextRow());
6050
6051 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u area trigger teleport definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6052}
6053
6054void ObjectMgr::LoadAccessRequirements()
6055{
6056 uint32 oldMSTime = getMSTime();
6057
6058 _accessRequirementStore.clear(); // need for reload case
6059
6060 // 0 1 2 3 4 5 6 7 8 9 10 11
6061 QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item, item2, quest_done_A, quest_done_H, completed_achievement, itemlevel_min, itemlevel_max, quest_failed_text FROM access_requirement");
6062 if (!result)
6063 {
6064 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
6065 return;
6066 }
6067
6068 uint32 count = 0;
6069
6070 do
6071 {
6072 Field* fields = result->Fetch();
6073
6074 ++count;
6075
6076 uint32 mapid = fields[0].GetUInt32();
6077 uint8 difficulty = fields[1].GetUInt8();
6078 uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
6079
6080 AccessRequirement ar;
6081
6082 ar.levelMin = fields[2].GetUInt8();
6083 ar.levelMax = fields[3].GetUInt8();
6084 ar.item = fields[4].GetUInt32();
6085 ar.item2 = fields[5].GetUInt32();
6086 ar.quest_A = fields[6].GetUInt32();
6087 ar.quest_H = fields[7].GetUInt32();
6088 ar.achievement = fields[8].GetUInt32();
6089 ar.itemlevelMin = fields[9].GetUInt32();
6090 ar.itemlevelMax = fields[10].GetUInt32();
6091 ar.questFailedText = fields[11].GetString();
6092
6093 if (ar.item)
6094 {
6095 ItemTemplate const* pProto = GetItemTemplate(ar.item);
6096 if (!pProto)
6097 {
6098 sLog->outError(LOG_FILTER_GENERAL, "Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item, mapid, difficulty);
6099 ar.item = 0;
6100 }
6101 }
6102
6103 if (ar.item2)
6104 {
6105 ItemTemplate const* pProto = GetItemTemplate(ar.item2);
6106 if (!pProto)
6107 {
6108 sLog->outError(LOG_FILTER_GENERAL, "Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item2, mapid, difficulty);
6109 ar.item2 = 0;
6110 }
6111 }
6112
6113 if (ar.quest_A)
6114 {
6115 if (!GetQuestTemplate(ar.quest_A))
6116 {
6117 sLog->outError(LOG_FILTER_SQL, "Required Alliance Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_A, mapid, difficulty);
6118 ar.quest_A = 0;
6119 }
6120 }
6121
6122 if (ar.quest_H)
6123 {
6124 if (!GetQuestTemplate(ar.quest_H))
6125 {
6126 sLog->outError(LOG_FILTER_SQL, "Required Horde Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_H, mapid, difficulty);
6127 ar.quest_H = 0;
6128 }
6129 }
6130
6131 if (ar.achievement)
6132 {
6133 if (!sAchievementStore.LookupEntry(ar.achievement))
6134 {
6135 sLog->outError(LOG_FILTER_SQL, "Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar.achievement, mapid, difficulty);
6136 ar.achievement = 0;
6137 }
6138 }
6139
6140 _accessRequirementStore[requirement_ID] = ar;
6141 } while (result->NextRow());
6142
6143 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u access requirement definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6144}
6145
6146/*
6147* Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
6148*/
6149AreaTriggerStruct const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
6150{
6151 bool useParentDbValue = false;
6152 uint32 parentId = 0;
6153 const MapEntry* mapEntry = sMapStore.LookupEntry(Map);
6154 if (!mapEntry || mapEntry->entrance_map < 0)
6155 return NULL;
6156
6157 if (mapEntry->IsDungeon())
6158 {
6159 const InstanceTemplate* iTemplate = sObjectMgr->GetInstanceTemplate(Map);
6160
6161 if (!iTemplate)
6162 return NULL;
6163
6164 parentId = iTemplate->Parent;
6165 useParentDbValue = true;
6166 }
6167
6168 uint32 entrance_map = uint32(mapEntry->entrance_map);
6169 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
6170 if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
6171 {
6172 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
6173 if (atEntry && atEntry->mapid == Map)
6174 return &itr->second;
6175 }
6176 return NULL;
6177}
6178
6179/**
6180* Searches for the areatrigger which teleports players to the given map
6181*/
6182AreaTriggerStruct const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
6183{
6184 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
6185 {
6186 if (itr->second.target_mapId == Map)
6187 {
6188 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
6189 if (atEntry)
6190 return &itr->second;
6191 }
6192 }
6193 return NULL;
6194}
6195
6196void ObjectMgr::SetHighestGuids()
6197{
6198 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
6199 if (result)
6200 _hiCharGuid = (*result)[0].GetUInt32() + 1;
6201
6202 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
6203 if (result)
6204 _hiCreatureGuid = (*result)[0].GetUInt32() + 1;
6205
6206 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
6207 if (result)
6208 _hiItemGuid = (*result)[0].GetUInt32() + 1;
6209
6210 // Cleanup other tables from not existed guids ( >= _hiItemGuid)
6211 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", _hiItemGuid); // One-time query
6212 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", _hiItemGuid); // One-time query
6213 CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", _hiItemGuid); // One-time query
6214 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", _hiItemGuid); // One-time query
6215
6216 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
6217 if (result)
6218 _hiGoGuid = (*result)[0].GetUInt32() + 1;
6219
6220 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
6221 if (result)
6222 _hiMoTransGuid = (*result)[0].GetUInt32() + 1;
6223
6224 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
6225 if (result)
6226 _auctionId = (*result)[0].GetUInt32() + 1;
6227
6228 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
6229 if (result)
6230 _mailId = (*result)[0].GetUInt32() + 1;
6231
6232 result = CharacterDatabase.Query("SELECT MAX(corpseGuid) FROM corpse");
6233 if (result)
6234 _hiCorpseGuid = (*result)[0].GetUInt32() + 1;
6235
6236 result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
6237 if (result)
6238 _equipmentSetGuid = (*result)[0].GetUInt64() + 1;
6239
6240 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
6241 if (result)
6242 sGuildMgr->SetNextGuildId((*result)[0].GetUInt32() + 1);
6243
6244 result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups");
6245 if (result)
6246 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32() + 1);
6247
6248 result = CharacterDatabase.Query("SELECT MAX(itemId) from character_void_storage");
6249 if (result)
6250 _voidItemId = (*result)[0].GetUInt64() + 1;
6251}
6252
6253uint32 ObjectMgr::GenerateAuctionID()
6254{
6255 if (_auctionId >= 0xFFFFFFFE)
6256 {
6257 sLog->outError(LOG_FILTER_GENERAL, "Auctions ids overflow!! Can't continue, shutting down server. ");
6258 World::StopNow(ERROR_EXIT_CODE);
6259 }
6260 return _auctionId++;
6261}
6262
6263uint64 ObjectMgr::GenerateEquipmentSetGuid()
6264{
6265 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
6266 {
6267 sLog->outError(LOG_FILTER_GENERAL, "EquipmentSet guid overflow!! Can't continue, shutting down server. ");
6268 World::StopNow(ERROR_EXIT_CODE);
6269 }
6270 return _equipmentSetGuid++;
6271}
6272
6273uint32 ObjectMgr::GenerateMailID()
6274{
6275 if (_mailId >= 0xFFFFFFFE)
6276 {
6277 sLog->outError(LOG_FILTER_GENERAL, "Mail ids overflow!! Can't continue, shutting down server. ");
6278 World::StopNow(ERROR_EXIT_CODE);
6279 }
6280 return _mailId++;
6281}
6282
6283uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
6284{
6285 switch (guidhigh)
6286 {
6287 case HIGHGUID_ITEM:
6288 {
6289 ASSERT(_hiItemGuid < 0xFFFFFFFE && "Item guid overflow!");
6290 return _hiItemGuid++;
6291 }
6292 case HIGHGUID_UNIT:
6293 {
6294 ASSERT(_hiCreatureGuid < 0x00FFFFFE && "Creature guid overflow!");
6295 return _hiCreatureGuid++;
6296 }
6297 case HIGHGUID_PET:
6298 {
6299 ASSERT(_hiPetGuid < 0x00FFFFFE && "Pet guid overflow!");
6300 return _hiPetGuid++;
6301 }
6302 case HIGHGUID_VEHICLE:
6303 {
6304 ASSERT(_hiVehicleGuid < 0x00FFFFFF && "Vehicle guid overflow!");
6305 return _hiVehicleGuid++;
6306 }
6307 case HIGHGUID_PLAYER:
6308 {
6309 ASSERT(_hiCharGuid < 0xFFFFFFFE && "Player guid overflow!");
6310 return _hiCharGuid++;
6311 }
6312 case HIGHGUID_GAMEOBJECT:
6313 {
6314 ASSERT(_hiGoGuid < 0x00FFFFFE && "Gameobject guid overflow!");
6315 return _hiGoGuid++;
6316 }
6317 case HIGHGUID_CORPSE:
6318 {
6319 ASSERT(_hiCorpseGuid < 0xFFFFFFFE && "Corpse guid overflow!");
6320 return _hiCorpseGuid++;
6321 }
6322 case HIGHGUID_DYNAMICOBJECT:
6323 {
6324 ASSERT(_hiDoGuid < 0xFFFFFFFE && "DynamicObject guid overflow!");
6325 return _hiDoGuid++;
6326 }
6327 case HIGHGUID_AREATRIGGER:
6328 {
6329 ASSERT(_hiAreaTriggerGuid < 0xFFFFFFFE && "AreaTrigger guid overflow!");
6330 return _hiAreaTriggerGuid++;
6331 }
6332 case HIGHGUID_MO_TRANSPORT:
6333 {
6334 ASSERT(_hiMoTransGuid < 0xFFFFFFFE && "MO Transport guid overflow!");
6335 return _hiMoTransGuid++;
6336 }
6337 default:
6338 ASSERT(false && "ObjectMgr::GenerateLowGuid - Unknown HIGHGUID type");
6339 return 0;
6340 }
6341}
6342
6343void ObjectMgr::LoadGameObjectLocales()
6344{
6345 uint32 oldMSTime = getMSTime();
6346
6347 _gameObjectLocaleStore.clear(); // need for reload case
6348
6349 QueryResult result = WorldDatabase.Query("SELECT entry, "
6350 "name_loc1, name_loc2, name_loc3, name_loc4, name_loc5, name_loc6, name_loc7, name_loc8, name_loc9, name_loc10, "
6351 "castbarcaption_loc1, castbarcaption_loc2, castbarcaption_loc3, castbarcaption_loc4, "
6352 "castbarcaption_loc5, castbarcaption_loc6, castbarcaption_loc7, castbarcaption_loc8, castbarcaption_loc9, castbarcaption_loc10 FROM locales_gameobject");
6353
6354 if (!result)
6355 return;
6356
6357 do
6358 {
6359 Field* fields = result->Fetch();
6360
6361 uint32 entry = fields[0].GetUInt32();
6362
6363 GameObjectLocale& data = _gameObjectLocaleStore[entry];
6364
6365 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
6366 AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.Name);
6367
6368 for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
6369 AddLocaleString(fields[i + (TOTAL_LOCALES - 1)].GetString(), LocaleConstant(i), data.CastBarCaption);
6370 } while (result->NextRow());
6371
6372 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %lu gameobject locale strings in %u ms", (unsigned long)_gameObjectLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
6373}
6374
6375inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
6376{
6377 if (sLockStore.LookupEntry(dataN))
6378 return;
6379
6380 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
6381 goInfo->entry, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
6382}
6383
6384inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
6385{
6386 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
6387 {
6388 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
6389 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
6390 goInfo->entry, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
6391 }
6392}
6393
6394inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
6395{
6396 if (sSpellMgr->GetSpellInfo(dataN))
6397 return;
6398
6399 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
6400 goInfo->entry, goInfo->type, N, dataN, dataN);
6401}
6402
6403inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32 const& dataN, uint32 N)
6404{
6405 if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR))
6406 return;
6407
6408 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
6409 goInfo->entry, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR);
6410
6411 // prevent client and server unexpected work
6412 const_cast<uint32&>(dataN) = 0;
6413}
6414
6415inline void CheckGONoDamageImmuneId(GameObjectTemplate* goTemplate, uint32 dataN, uint32 N)
6416{
6417 // 0/1 correct values
6418 if (dataN <= 1)
6419 return;
6420
6421 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
6422}
6423
6424inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
6425{
6426 // 0/1 correct values
6427 if (dataN <= 1)
6428 return;
6429
6430 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
6431 goInfo->entry, goInfo->type, N, dataN);
6432}
6433
6434void ObjectMgr::LoadGameObjectTemplate()
6435{
6436 uint32 oldMSTime = getMSTime();
6437
6438 // 0 1 2 3 4 5 6 7 8 9 10 11 12
6439 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, questItem1, questItem2, questItem3, "
6440 // 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
6441 "questItem4, questItem5, questItem6, data0, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, "
6442 // 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
6443 "data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, data24, data25, data26, data27, data28, "
6444 // 45 46 47 48 49 50
6445 "data29, data30, data31, unkInt32, AIName, ScriptName "
6446 "FROM gameobject_template");
6447
6448 if (!result)
6449 {
6450 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
6451 return;
6452 }
6453
6454 //_gameObjectTemplateStore.rehash(result->GetRowCount());
6455 uint32 count = 0;
6456 do
6457 {
6458 Field* fields = result->Fetch();
6459
6460 uint32 entry = fields[0].GetUInt32();
6461
6462 GameObjectTemplate& got = _gameObjectTemplateStore[entry];
6463
6464 got.entry = entry;
6465 got.type = uint32(fields[1].GetUInt8());
6466 got.displayId = fields[2].GetUInt32();
6467 got.name = fields[3].GetString();
6468 got.IconName = fields[4].GetString();
6469 got.castBarCaption = fields[5].GetString();
6470 got.unk1 = fields[6].GetString();
6471 got.faction = uint32(fields[7].GetUInt16());
6472 got.flags = fields[8].GetUInt32();
6473 got.size = fields[9].GetFloat();
6474
6475 for (uint8 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i)
6476 got.questItems[i] = fields[10 + i].GetUInt32();
6477
6478 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
6479 got.raw.data[i] = fields[16 + i].GetUInt32();
6480
6481 got.unkInt32 = fields[48].GetInt32();
6482 got.AIName = fields[49].GetString();
6483 got.ScriptId = GetScriptId(fields[50].GetCString());
6484
6485 // Checks
6486
6487 switch (got.type)
6488 {
6489 case GAMEOBJECT_TYPE_DOOR: //0
6490 {
6491 if (got.door.lockId)
6492 CheckGOLockId(&got, got.door.lockId, 1);
6493 CheckGONoDamageImmuneId(&got, got.door.noDamageImmune, 3);
6494 break;
6495 }
6496 case GAMEOBJECT_TYPE_BUTTON: //1
6497 {
6498 if (got.button.lockId)
6499 CheckGOLockId(&got, got.button.lockId, 1);
6500 CheckGONoDamageImmuneId(&got, got.button.noDamageImmune, 4);
6501 break;
6502 }
6503 case GAMEOBJECT_TYPE_QUESTGIVER: //2
6504 {
6505 if (got.questgiver.lockId)
6506 CheckGOLockId(&got, got.questgiver.lockId, 0);
6507 CheckGONoDamageImmuneId(&got, got.questgiver.noDamageImmune, 5);
6508 break;
6509 }
6510 case GAMEOBJECT_TYPE_CHEST: //3
6511 {
6512 if (got.chest.lockId)
6513 CheckGOLockId(&got, got.chest.lockId, 0);
6514
6515 CheckGOConsumable(&got, got.chest.consumable, 3);
6516
6517 if (got.chest.linkedTrapId) // linked trap
6518 CheckGOLinkedTrapId(&got, got.chest.linkedTrapId, 7);
6519 break;
6520 }
6521 case GAMEOBJECT_TYPE_TRAP: //6
6522 {
6523 if (got.trap.lockId)
6524 CheckGOLockId(&got, got.trap.lockId, 0);
6525 break;
6526 }
6527 case GAMEOBJECT_TYPE_CHAIR: //7
6528 CheckAndFixGOChairHeightId(&got, got.chair.height, 1);
6529 break;
6530 case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
6531 {
6532 if (got.spellFocus.focusId)
6533 {
6534 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.focusId))
6535 sLog->outError(LOG_FILTER_SQL, "GameObject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
6536 entry, got.type, got.spellFocus.focusId, got.spellFocus.focusId);
6537 }
6538
6539 if (got.spellFocus.linkedTrapId) // linked trap
6540 CheckGOLinkedTrapId(&got, got.spellFocus.linkedTrapId, 2);
6541 break;
6542 }
6543 case GAMEOBJECT_TYPE_GOOBER: //10
6544 {
6545 if (got.goober.lockId)
6546 CheckGOLockId(&got, got.goober.lockId, 0);
6547
6548 CheckGOConsumable(&got, got.goober.consumable, 3);
6549
6550 if (got.goober.pageId) // pageId
6551 {
6552 if (!GetPageText(got.goober.pageId))
6553 sLog->outError(LOG_FILTER_SQL, "GameObject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
6554 entry, got.type, got.goober.pageId, got.goober.pageId);
6555 }
6556 CheckGONoDamageImmuneId(&got, got.goober.noDamageImmune, 11);
6557 if (got.goober.linkedTrapId) // linked trap
6558 CheckGOLinkedTrapId(&got, got.goober.linkedTrapId, 12);
6559 break;
6560 }
6561 case GAMEOBJECT_TYPE_TRANSPORT: //11
6562 {
6563 TransportAnimationsByEntry::const_iterator itr = sTransportAnimationsByEntry.find(entry);
6564 if (itr == sTransportAnimationsByEntry.end())
6565 {
6566 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) is a transport but does not have entries in TransportAnimation.dbc! Gameobject is obsolete.", entry, got.type);
6567 break;
6568 }
6569
6570 if (uint32 frame = got.transport.startFrame)
6571 if (itr->second.find(frame) == itr->second.end())
6572 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) has data0 = %u but this frame is not in TransportAnimation.dbc!", entry, got.type, frame);
6573
6574 if (uint32 frame = got.transport.nextFrame1)
6575 if (itr->second.find(frame) == itr->second.end())
6576 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) has data6 = %u but this frame is not in TransportAnimation.dbc!", entry, got.type, frame);
6577
6578 if (uint32 frame = got.transport.nextFrame2)
6579 if (itr->second.find(frame) == itr->second.end())
6580 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) has data8 = %u but this frame is not in TransportAnimation.dbc!", entry, got.type, frame);
6581
6582 if (uint32 frame = got.transport.nextFrame3)
6583 if (itr->second.find(frame) == itr->second.end())
6584 sLog->outError(LOG_FILTER_SQL, "Gameobject (Entry: %u GoType: %u) has data10 = %u but this frame is not in TransportAnimation.dbc!", entry, got.type, frame);
6585
6586 break;
6587 }
6588 case GAMEOBJECT_TYPE_AREADAMAGE: //12
6589 {
6590 if (got.areadamage.lockId)
6591 CheckGOLockId(&got, got.areadamage.lockId, 0);
6592 break;
6593 }
6594 case GAMEOBJECT_TYPE_CAMERA: //13
6595 {
6596 if (got.camera.lockId)
6597 CheckGOLockId(&got, got.camera.lockId, 0);
6598 break;
6599 }
6600 case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
6601 {
6602 if (got.moTransport.taxiPathId)
6603 {
6604 if (got.moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[got.moTransport.taxiPathId].empty())
6605 sLog->outError(LOG_FILTER_SQL, "GameObject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
6606 entry, got.type, got.moTransport.taxiPathId, got.moTransport.taxiPathId);
6607 }
6608 break;
6609 }
6610 case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
6611 break;
6612 case GAMEOBJECT_TYPE_SPELLCASTER: //22
6613 {
6614 // always must have spell
6615 CheckGOSpellId(&got, got.spellcaster.spellId, 0);
6616 break;
6617 }
6618 case GAMEOBJECT_TYPE_FLAGSTAND: //24
6619 {
6620 if (got.flagstand.lockId)
6621 CheckGOLockId(&got, got.flagstand.lockId, 0);
6622 CheckGONoDamageImmuneId(&got, got.flagstand.noDamageImmune, 5);
6623 break;
6624 }
6625 case GAMEOBJECT_TYPE_FISHINGHOLE: //25
6626 {
6627 if (got.fishinghole.lockId)
6628 CheckGOLockId(&got, got.fishinghole.lockId, 4);
6629 break;
6630 }
6631 case GAMEOBJECT_TYPE_FLAGDROP: //26
6632 {
6633 if (got.flagdrop.lockId)
6634 CheckGOLockId(&got, got.flagdrop.lockId, 0);
6635 CheckGONoDamageImmuneId(&got, got.flagdrop.noDamageImmune, 3);
6636 break;
6637 }
6638 case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
6639 CheckAndFixGOChairHeightId(&got, got.barberChair.chairheight, 0);
6640 break;
6641 }
6642
6643 ++count;
6644 } while (result->NextRow());
6645
6646 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u game object templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6647}
6648
6649void ObjectMgr::LoadExplorationBaseXP()
6650{
6651 uint32 oldMSTime = getMSTime();
6652
6653 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
6654
6655 if (!result)
6656 {
6657 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
6658
6659 return;
6660 }
6661
6662 uint32 count = 0;
6663
6664 do
6665 {
6666 Field* fields = result->Fetch();
6667 uint8 level = fields[0].GetUInt8();
6668 uint32 basexp = fields[1].GetInt32();
6669 _baseXPTable[level] = basexp;
6670 ++count;
6671 } while (result->NextRow());
6672
6673 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u BaseXP definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6674}
6675
6676uint32 ObjectMgr::GetBaseXP(uint8 level)
6677{
6678 return _baseXPTable[level] ? _baseXPTable[level] : 0;
6679}
6680
6681uint32 ObjectMgr::GetXPForLevel(uint8 level) const
6682{
6683 if (level < _playerXPperLevel.size())
6684 return _playerXPperLevel[level];
6685 return 0;
6686}
6687
6688void ObjectMgr::LoadPetNames()
6689{
6690 uint32 oldMSTime = getMSTime();
6691 // 0 1 2
6692 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
6693
6694 if (!result)
6695 {
6696 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
6697 return;
6698 }
6699
6700 uint32 count = 0;
6701
6702 do
6703 {
6704 Field* fields = result->Fetch();
6705 std::string word = fields[0].GetString();
6706 uint32 entry = fields[1].GetUInt32();
6707 bool half = fields[2].GetBool();
6708 if (half)
6709 _petHalfName1[entry].push_back(word);
6710 else
6711 _petHalfName0[entry].push_back(word);
6712 ++count;
6713 } while (result->NextRow());
6714
6715 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u pet name parts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6716}
6717
6718void ObjectMgr::LoadPetNumber()
6719{
6720 uint32 oldMSTime = getMSTime();
6721
6722 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
6723 if (result)
6724 {
6725 Field* fields = result->Fetch();
6726 _hiPetNumber = fields[0].GetUInt32() + 1;
6727 }
6728
6729 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded the max pet number: %d in %u ms", _hiPetNumber - 1, GetMSTimeDiffToNow(oldMSTime));
6730}
6731
6732std::string ObjectMgr::GeneratePetName(uint32 entry)
6733{
6734 StringVector & list0 = _petHalfName0[entry];
6735 StringVector & list1 = _petHalfName1[entry];
6736
6737 if (list0.empty() || list1.empty())
6738 {
6739 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
6740 const char* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
6741 if (!petname)
6742 return cinfo->Name;
6743
6744 return std::string(petname);
6745 }
6746
6747 return *(list0.begin() + urand(0, list0.size() - 1)) + *(list1.begin() + urand(0, list1.size() - 1));
6748}
6749
6750uint32 ObjectMgr::GeneratePetNumber()
6751{
6752 return ++_hiPetNumber;
6753}
6754
6755uint64 ObjectMgr::GenerateVoidStorageItemId()
6756{
6757 return ++_voidItemId;
6758}
6759
6760void ObjectMgr::LoadCorpses()
6761{
6762 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
6763 // SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, flags, dynFlags, time, corpseType, instanceId, phaseMask, corpseGuid, guid FROM corpse WHERE corpseType <> 0
6764
6765 uint32 oldMSTime = getMSTime();
6766
6767 PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSES));
6768 if (!result)
6769 {
6770 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 corpses. DB table `corpse` is empty.");
6771 return;
6772 }
6773
6774 uint32 count = 0;
6775 do
6776 {
6777 Field* fields = result->Fetch();
6778 uint32 guid = fields[16].GetUInt32();
6779 CorpseType type = CorpseType(fields[12].GetUInt8());
6780 if (type >= MAX_CORPSE_TYPE)
6781 {
6782 sLog->outError(LOG_FILTER_GENERAL, "Corpse (guid: %u) have wrong corpse type (%u), not loading.", guid, type);
6783 continue;
6784 }
6785
6786 Corpse* corpse = new Corpse(type);
6787 if (!corpse->LoadCorpseFromDB(guid, fields))
6788 {
6789 delete corpse;
6790 continue;
6791 }
6792
6793 sObjectAccessor->AddCorpse(corpse);
6794 ++count;
6795 } while (result->NextRow());
6796
6797 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u corpses in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6798}
6799
6800void ObjectMgr::LoadReputationRewardRate()
6801{
6802 uint32 oldMSTime = getMSTime();
6803
6804 _repRewardRateStore.clear(); // for reload case
6805
6806 uint32 count = 0; // 0 1 2 3
6807 QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, creature_rate, spell_rate FROM reputation_reward_rate");
6808
6809 if (!result)
6810 {
6811 sLog->outError(LOG_FILTER_SQL, ">> Loaded `reputation_reward_rate`, table is empty!");
6812
6813 return;
6814 }
6815
6816 do
6817 {
6818 Field* fields = result->Fetch();
6819
6820 uint32 factionId = fields[0].GetUInt32();
6821
6822 RepRewardRate repRate;
6823
6824 repRate.quest_rate = fields[1].GetFloat();
6825 repRate.creature_rate = fields[2].GetFloat();
6826 repRate.spell_rate = fields[3].GetFloat();
6827
6828 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
6829 if (!factionEntry)
6830 {
6831 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
6832 continue;
6833 }
6834
6835 if (repRate.quest_rate < 0.0f)
6836 {
6837 sLog->outError(LOG_FILTER_SQL, "Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.quest_rate, factionId);
6838 continue;
6839 }
6840
6841 if (repRate.creature_rate < 0.0f)
6842 {
6843 sLog->outError(LOG_FILTER_SQL, "Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creature_rate, factionId);
6844 continue;
6845 }
6846
6847 if (repRate.spell_rate < 0.0f)
6848 {
6849 sLog->outError(LOG_FILTER_SQL, "Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spell_rate, factionId);
6850 continue;
6851 }
6852
6853 _repRewardRateStore[factionId] = repRate;
6854
6855 ++count;
6856 } while (result->NextRow());
6857
6858 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u reputation_reward_rate in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6859}
6860
6861void ObjectMgr::LoadCurrencyOnKill()
6862{
6863 uint32 oldMSTime = getMSTime();
6864
6865 _curOnKillStore.clear();
6866
6867 uint32 count = 0;
6868
6869 QueryResult result = WorldDatabase.Query("SELECT `creature_id`, `CurrencyId1`, `CurrencyId2`, `CurrencyId3`, `CurrencyCount1`, `CurrencyCount2`, `CurrencyCount3` FROM `creature_loot_currency`");
6870
6871 if (!result)
6872 {
6873 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature currency definitions. DB table `creature_currency` is empty.");
6874 return;
6875 }
6876
6877 do
6878 {
6879 Field *fields = result->Fetch();
6880
6881 uint32 creature_id = fields[0].GetUInt32();
6882
6883 CurrencyOnKillEntry currOnKill;
6884 currOnKill.currencyId1 = fields[1].GetUInt16();
6885 currOnKill.currencyId2 = fields[2].GetUInt16();
6886 currOnKill.currencyId3 = fields[3].GetUInt16();
6887 currOnKill.currencyCount1 = fields[4].GetInt32();
6888 currOnKill.currencyCount2 = fields[5].GetInt32();
6889 currOnKill.currencyCount3 = fields[6].GetInt32();
6890
6891 if (!GetCreatureTemplate(creature_id))
6892 {
6893 sLog->outError(LOG_FILTER_SQL, "Table `creature_creature` have data for not existed creature entry (%u), skipped", creature_id);
6894 continue;
6895 }
6896
6897 if (currOnKill.currencyId1)
6898 {
6899 if (!sCurrencyTypesStore.LookupEntry(currOnKill.currencyId1))
6900 {
6901 sLog->outError(LOG_FILTER_SQL, "CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_currency`", currOnKill.currencyId1);
6902 continue;
6903 }
6904 }
6905
6906 if (currOnKill.currencyId2)
6907 {
6908 if (!sCurrencyTypesStore.LookupEntry(currOnKill.currencyId2))
6909 {
6910 sLog->outError(LOG_FILTER_SQL, "CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_currency`", currOnKill.currencyId2);
6911 continue;
6912 }
6913 }
6914
6915 if (currOnKill.currencyId3)
6916 {
6917 if (!sCurrencyTypesStore.LookupEntry(currOnKill.currencyId3))
6918 {
6919 sLog->outError(LOG_FILTER_SQL, "CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_currency`", currOnKill.currencyId3);
6920 continue;
6921 }
6922 }
6923
6924 _curOnKillStore[creature_id] = currOnKill;
6925
6926 ++count;
6927 } while (result->NextRow());
6928
6929 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature currency definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
6930}
6931
6932void ObjectMgr::LoadReputationOnKill()
6933{
6934 uint32 oldMSTime = getMSTime();
6935
6936 // For reload case
6937 _repOnKillStore.clear();
6938
6939 uint32 count = 0;
6940
6941 // 0 1 2
6942 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
6943 // 3 4 5 6 7 8 9
6944 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
6945 "FROM creature_onkill_reputation");
6946
6947 if (!result)
6948 {
6949 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
6950
6951 return;
6952 }
6953
6954 do
6955 {
6956 Field* fields = result->Fetch();
6957
6958 uint32 creature_id = fields[0].GetUInt32();
6959
6960 ReputationOnKillEntry repOnKill;
6961 repOnKill.RepFaction1 = fields[1].GetInt16();
6962 repOnKill.RepFaction2 = fields[2].GetInt16();
6963 repOnKill.IsTeamAward1 = fields[3].GetBool();
6964 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
6965 repOnKill.RepValue1 = fields[5].GetInt32();
6966 repOnKill.IsTeamAward2 = fields[6].GetBool();
6967 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
6968 repOnKill.RepValue2 = fields[8].GetInt32();
6969 repOnKill.TeamDependent = fields[9].GetUInt8();
6970
6971 if (!GetCreatureTemplate(creature_id))
6972 {
6973 sLog->outError(LOG_FILTER_SQL, "Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped", creature_id);
6974 continue;
6975 }
6976
6977 if (repOnKill.RepFaction1)
6978 {
6979 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
6980 if (!factionEntry1)
6981 {
6982 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
6983 continue;
6984 }
6985 }
6986
6987 if (repOnKill.RepFaction2)
6988 {
6989 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
6990 if (!factionEntry2)
6991 {
6992 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
6993 continue;
6994 }
6995 }
6996
6997 _repOnKillStore[creature_id] = repOnKill;
6998
6999 ++count;
7000 } while (result->NextRow());
7001
7002 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature award reputation definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7003}
7004
7005void ObjectMgr::LoadReputationSpilloverTemplate()
7006{
7007 uint32 oldMSTime = getMSTime();
7008
7009 _repSpilloverTemplateStore.clear(); // for reload case
7010
7011 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
7012 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, faction5, rate_5, rank_5 FROM reputation_spillover_template");
7013
7014 if (!result)
7015 {
7016 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded `reputation_spillover_template`, table is empty.");
7017 return;
7018 }
7019
7020 do
7021 {
7022 Field* fields = result->Fetch();
7023
7024 uint32 factionId = fields[0].GetUInt16();
7025
7026 RepSpilloverTemplate repTemplate;
7027
7028 repTemplate.faction[0] = fields[1].GetUInt16();
7029 repTemplate.faction_rate[0] = fields[2].GetFloat();
7030 repTemplate.faction_rank[0] = fields[3].GetUInt8();
7031 repTemplate.faction[1] = fields[4].GetUInt16();
7032 repTemplate.faction_rate[1] = fields[5].GetFloat();
7033 repTemplate.faction_rank[1] = fields[6].GetUInt8();
7034 repTemplate.faction[2] = fields[7].GetUInt16();
7035 repTemplate.faction_rate[2] = fields[8].GetFloat();
7036 repTemplate.faction_rank[2] = fields[9].GetUInt8();
7037 repTemplate.faction[3] = fields[10].GetUInt16();
7038 repTemplate.faction_rate[3] = fields[11].GetFloat();
7039 repTemplate.faction_rank[3] = fields[12].GetUInt8();
7040 repTemplate.faction[4] = fields[13].GetUInt16();
7041 repTemplate.faction_rate[4] = fields[14].GetFloat();
7042 repTemplate.faction_rank[4] = fields[15].GetUInt8();
7043
7044 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
7045
7046 if (!factionEntry)
7047 {
7048 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
7049 continue;
7050 }
7051
7052 if (factionEntry->team == 0)
7053 {
7054 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId);
7055 continue;
7056 }
7057
7058 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
7059 {
7060 if (repTemplate.faction[i])
7061 {
7062 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
7063
7064 if (!factionSpillover)
7065 {
7066 sLog->outError(LOG_FILTER_SQL, "Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate.faction[i], factionId);
7067 continue;
7068 }
7069
7070 if (factionSpillover->reputationListID < 0)
7071 {
7072 sLog->outError(LOG_FILTER_SQL, "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);
7073 continue;
7074 }
7075
7076 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
7077 {
7078 sLog->outError(LOG_FILTER_SQL, "Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
7079 continue;
7080 }
7081 }
7082 }
7083
7084 FactionEntry const* factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]);
7085 if (repTemplate.faction[0] && !factionEntry0)
7086 {
7087 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]);
7088 continue;
7089 }
7090 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]);
7091 if (repTemplate.faction[1] && !factionEntry1)
7092 {
7093 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]);
7094 continue;
7095 }
7096 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]);
7097 if (repTemplate.faction[2] && !factionEntry2)
7098 {
7099 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]);
7100 continue;
7101 }
7102 FactionEntry const* factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]);
7103 if (repTemplate.faction[3] && !factionEntry3)
7104 {
7105 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]);
7106 continue;
7107 }
7108 FactionEntry const* factionEntry4 = sFactionStore.LookupEntry(repTemplate.faction[4]);
7109 if (repTemplate.faction[4] && !factionEntry4)
7110 {
7111 sLog->outError(LOG_FILTER_SQL, "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[4]);
7112 continue;
7113 }
7114
7115 _repSpilloverTemplateStore[factionId] = repTemplate;
7116
7117 ++count;
7118 } while (result->NextRow());
7119
7120 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u reputation_spillover_template in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7121}
7122
7123void ObjectMgr::LoadPointsOfInterest()
7124{
7125 uint32 oldMSTime = getMSTime();
7126
7127 _pointsOfInterestStore.clear(); // need for reload case
7128
7129 uint32 count = 0;
7130
7131 // 0 1 2 3 4 5 6
7132 QueryResult result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
7133
7134 if (!result)
7135 {
7136 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
7137
7138 return;
7139 }
7140
7141 do
7142 {
7143 Field* fields = result->Fetch();
7144
7145 uint32 point_id = fields[0].GetUInt32();
7146
7147 PointOfInterest POI;
7148 POI.x = fields[1].GetFloat();
7149 POI.y = fields[2].GetFloat();
7150 POI.icon = fields[3].GetUInt32();
7151 POI.flags = fields[4].GetUInt32();
7152 POI.data = fields[5].GetUInt32();
7153 POI.icon_name = fields[6].GetString();
7154
7155 if (!MoPCore::IsValidMapCoord(POI.x, POI.y))
7156 {
7157 sLog->outError(LOG_FILTER_SQL, "Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id, POI.x, POI.y);
7158 continue;
7159 }
7160
7161 _pointsOfInterestStore[point_id] = POI;
7162
7163 ++count;
7164 } while (result->NextRow());
7165
7166 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u Points of Interest definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7167}
7168
7169void ObjectMgr::LoadQuestPOI()
7170{
7171 uint32 oldMSTime = getMSTime();
7172
7173 _questPOIStore.clear(); // need for reload case
7174
7175 uint32 count = 0;
7176
7177 // 0 1 2 3 4 5 6 7
7178 QueryResult result = WorldDatabase.Query("SELECT questId, id, objIndex, mapid, WorldMapAreaId, FloorId, unk3, unk4 FROM quest_poi order by questId");
7179
7180 if (!result)
7181 {
7182 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
7183 return;
7184 }
7185
7186 // 0 1 2 3
7187 QueryResult points = WorldDatabase.Query("SELECT questId, id, x, y FROM quest_poi_points ORDER BY questId DESC, idx");
7188
7189 std::vector<std::vector<std::vector<QuestPOIPoint> > > POIs;
7190
7191 if (points)
7192 {
7193 // The first result should have the highest questId
7194 Field* fields = points->Fetch();
7195 uint32 questIdMax = fields[0].GetUInt32();
7196 POIs.resize(questIdMax + 1);
7197
7198 do
7199 {
7200 fields = points->Fetch();
7201
7202 uint32 questId = fields[0].GetUInt32();
7203 uint32 id = fields[1].GetUInt32();
7204 int32 x = fields[2].GetInt32();
7205 int32 y = fields[3].GetInt32();
7206
7207 if (POIs[questId].size() <= id + 1)
7208 POIs[questId].resize(id + 10);
7209
7210 QuestPOIPoint point(x, y);
7211 POIs[questId][id].push_back(point);
7212 } while (points->NextRow());
7213 }
7214
7215 do
7216 {
7217 Field* fields = result->Fetch();
7218
7219 uint32 questId = fields[0].GetUInt32();
7220 uint32 id = fields[1].GetUInt32();
7221 int32 objIndex = fields[2].GetInt32();
7222 uint32 mapId = fields[3].GetUInt32();
7223 uint32 WorldMapAreaId = fields[4].GetUInt32();
7224 uint32 FloorId = fields[5].GetUInt32();
7225 uint32 unk3 = fields[6].GetUInt32();
7226 uint32 unk4 = fields[7].GetUInt32();
7227
7228 QuestPOI POI(id, objIndex, mapId, WorldMapAreaId, FloorId, unk3, unk4);
7229 if (questId < POIs.size() && id < POIs[questId].size())
7230 {
7231 POI.points = POIs[questId][id];
7232 _questPOIStore[questId].push_back(POI);
7233 }
7234 else
7235 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Table quest_poi references unknown quest points for quest %u POI id %u", questId, id);
7236
7237 ++count;
7238 } while (result->NextRow());
7239
7240 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u quest POI definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7241}
7242
7243void ObjectMgr::LoadNPCSpellClickSpells()
7244{
7245 uint32 oldMSTime = getMSTime();
7246
7247 _spellClickInfoStore.clear();
7248 // 0 1 2 3
7249 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
7250
7251 if (!result)
7252 {
7253 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
7254
7255 return;
7256 }
7257
7258 uint32 count = 0;
7259
7260 do
7261 {
7262 Field* fields = result->Fetch();
7263
7264 uint32 npc_entry = fields[0].GetUInt32();
7265 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
7266 if (!cInfo)
7267 {
7268 sLog->outError(LOG_FILTER_SQL, "Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
7269 continue;
7270 }
7271
7272 uint32 spellid = fields[1].GetUInt32();
7273 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid);
7274 if (!spellinfo)
7275 {
7276 sLog->outError(LOG_FILTER_SQL, "Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid);
7277 continue;
7278 }
7279
7280 uint8 userType = fields[3].GetUInt16();
7281 if (userType >= SPELL_CLICK_USER_MAX)
7282 sLog->outError(LOG_FILTER_SQL, "Table npc_spellclick_spells references unknown user type %u. Skipping entry.", uint32(userType));
7283
7284 uint8 castFlags = fields[2].GetUInt8();
7285 SpellClickInfo info;
7286 info.spellId = spellid;
7287 info.castFlags = castFlags;
7288 info.userType = SpellClickUserTypes(userType);
7289 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
7290
7291 ++count;
7292 } while (result->NextRow());
7293
7294 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
7295 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
7296 CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
7297 for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
7298 {
7299 if ((itr->second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && _spellClickInfoStore.find(itr->second.Entry) == _spellClickInfoStore.end())
7300 {
7301 sLog->outError(LOG_FILTER_SQL, "npc_spellclick_spells: Creature template %u has UNIT_NPC_FLAG_SPELLCLICK but no data in spellclick table! Removing flag", itr->second.Entry);
7302 const_cast<CreatureTemplate*>(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
7303 }
7304 }
7305
7306 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spellclick definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7307}
7308
7309void ObjectMgr::DeleteCreatureData(uint32 guid)
7310{
7311 // remove mapid*cellid -> guid_set map
7312 CreatureData const* data = GetCreatureData(guid);
7313 if (data)
7314 RemoveCreatureFromGrid(guid, data);
7315
7316 _creatureDataStore.erase(guid);
7317}
7318
7319void ObjectMgr::DeleteGOData(uint32 guid)
7320{
7321 // remove mapid*cellid -> guid_set map
7322 GameObjectData const* data = GetGOData(guid);
7323 if (data)
7324 RemoveGameobjectFromGrid(guid, data);
7325
7326 _gameObjectDataStore.erase(guid);
7327}
7328
7329void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
7330{
7331 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7332 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid];
7333 cell_guids.corpses[player_guid] = instance;
7334}
7335
7336void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
7337{
7338 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7339 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid];
7340 cell_guids.corpses.erase(player_guid);
7341}
7342
7343void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, std::string table, bool starter, bool go)
7344{
7345 uint32 oldMSTime = getMSTime();
7346
7347 map.clear(); // need for reload case
7348
7349 uint32 count = 0;
7350
7351 QueryResult result = WorldDatabase.PQuery("SELECT id, quest, pool_entry FROM %s qr LEFT JOIN pool_quest pq ON qr.quest = pq.entry", table.c_str());
7352
7353 if (!result)
7354 {
7355 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str());
7356
7357 return;
7358 }
7359
7360 PooledQuestRelation* poolRelationMap = go ? &sPoolMgr->mQuestGORelation : &sPoolMgr->mQuestCreatureRelation;
7361 if (starter)
7362 poolRelationMap->clear();
7363
7364 do
7365 {
7366 uint32 id = result->Fetch()[0].GetUInt32();
7367 uint32 quest = result->Fetch()[1].GetUInt32();
7368 uint32 poolId = result->Fetch()[2].GetUInt32();
7369
7370 if (_questTemplates.find(quest) == _questTemplates.end())
7371 {
7372 sLog->outError(LOG_FILTER_SQL, "Table `%s`: Quest %u listed for entry %u does not exist.", table.c_str(), quest, id);
7373 continue;
7374 }
7375
7376 if (!poolId || !starter)
7377 map.insert(QuestRelations::value_type(id, quest));
7378 else if (starter)
7379 poolRelationMap->insert(PooledQuestRelation::value_type(quest, id));
7380
7381 ++count;
7382 } while (result->NextRow());
7383
7384 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u quest relations from %s in %u ms", count, table.c_str(), GetMSTimeDiffToNow(oldMSTime));
7385}
7386
7387void ObjectMgr::LoadGameobjectQuestRelations()
7388{
7389 LoadQuestRelationsHelper(_goQuestRelations, "gameobject_questrelation", true, true);
7390
7391 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
7392 {
7393 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
7394 if (!goInfo)
7395 sLog->outError(LOG_FILTER_SQL, "Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u", itr->first, itr->second);
7396 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
7397 sLog->outError(LOG_FILTER_SQL, "Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
7398 }
7399}
7400
7401void ObjectMgr::LoadGameobjectInvolvedRelations()
7402{
7403 LoadQuestRelationsHelper(_goQuestInvolvedRelations, "gameobject_involvedrelation", false, true);
7404
7405 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
7406 {
7407 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
7408 if (!goInfo)
7409 sLog->outError(LOG_FILTER_SQL, "Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u", itr->first, itr->second);
7410 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
7411 sLog->outError(LOG_FILTER_SQL, "Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
7412 }
7413}
7414
7415void ObjectMgr::LoadCreatureQuestRelations()
7416{
7417 LoadQuestRelationsHelper(_creatureQuestRelations, "creature_questrelation", true, false);
7418
7419 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
7420 {
7421 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
7422 if (!cInfo)
7423 sLog->outError(LOG_FILTER_SQL, "Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u", itr->first, itr->second);
7424 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
7425 sLog->outError(LOG_FILTER_SQL, "Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
7426 }
7427}
7428
7429void ObjectMgr::LoadCreatureInvolvedRelations()
7430{
7431 LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, "creature_involvedrelation", false, false);
7432
7433 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
7434 {
7435 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
7436 if (!cInfo)
7437 sLog->outError(LOG_FILTER_SQL, "Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u", itr->first, itr->second);
7438 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
7439 sLog->outError(LOG_FILTER_SQL, "Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
7440 }
7441}
7442
7443void ObjectMgr::LoadReservedPlayersNames()
7444{
7445 uint32 oldMSTime = getMSTime();
7446
7447 _reservedNamesStore.clear(); // need for reload case
7448
7449 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
7450
7451 if (!result)
7452 {
7453 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
7454 return;
7455 }
7456
7457 uint32 count = 0;
7458
7459 Field* fields;
7460 do
7461 {
7462 fields = result->Fetch();
7463 std::string name = fields[0].GetString();
7464
7465 std::wstring wstr;
7466 if (!Utf8toWStr(name, wstr))
7467 {
7468 sLog->outError(LOG_FILTER_GENERAL, "Table `reserved_name` have invalid name: %s", name.c_str());
7469 continue;
7470 }
7471
7472 wstrToLower(wstr);
7473
7474 _reservedNamesStore.insert(wstr);
7475 ++count;
7476 } while (result->NextRow());
7477
7478 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u reserved player names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7479}
7480
7481bool ObjectMgr::IsReservedName(const std::string& name) const
7482{
7483 std::wstring wstr;
7484 if (!Utf8toWStr(name, wstr))
7485 return false;
7486
7487 wstrToLower(wstr);
7488
7489 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
7490}
7491
7492enum LanguageType
7493{
7494 LT_BASIC_LATIN = 0x0000,
7495 LT_EXTENDEN_LATIN = 0x0001,
7496 LT_CYRILLIC = 0x0002,
7497 LT_EAST_ASIA = 0x0004,
7498 LT_ANY = 0xFFFF
7499};
7500
7501static LanguageType GetRealmLanguageType(bool create)
7502{
7503 switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
7504 {
7505 case REALM_ZONE_UNKNOWN: // any language
7506 case REALM_ZONE_DEVELOPMENT:
7507 case REALM_ZONE_TEST_SERVER:
7508 case REALM_ZONE_QA_SERVER:
7509 return LT_ANY;
7510 case REALM_ZONE_UNITED_STATES: // extended-Latin
7511 case REALM_ZONE_OCEANIC:
7512 case REALM_ZONE_LATIN_AMERICA:
7513 case REALM_ZONE_ENGLISH:
7514 case REALM_ZONE_GERMAN:
7515 case REALM_ZONE_FRENCH:
7516 case REALM_ZONE_SPANISH:
7517 return LT_EXTENDEN_LATIN;
7518 case REALM_ZONE_KOREA: // East-Asian
7519 case REALM_ZONE_TAIWAN:
7520 case REALM_ZONE_CHINA:
7521 return LT_EAST_ASIA;
7522 case REALM_ZONE_RUSSIAN: // Cyrillic
7523 return LT_CYRILLIC;
7524 default:
7525 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
7526 }
7527}
7528
7529bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
7530{
7531 if (strictMask == 0) // any language, ignore realm
7532 {
7533 if (isExtendedLatinString(wstr, numericOrSpace))
7534 return true;
7535 if (isCyrillicString(wstr, numericOrSpace))
7536 return true;
7537 if (isEastAsianString(wstr, numericOrSpace))
7538 return true;
7539 return false;
7540 }
7541
7542 if (strictMask & 0x2) // realm zone specific
7543 {
7544 LanguageType lt = GetRealmLanguageType(create);
7545 if (lt & LT_EXTENDEN_LATIN)
7546 if (isExtendedLatinString(wstr, numericOrSpace))
7547 return true;
7548 if (lt & LT_CYRILLIC)
7549 if (isCyrillicString(wstr, numericOrSpace))
7550 return true;
7551 if (lt & LT_EAST_ASIA)
7552 if (isEastAsianString(wstr, numericOrSpace))
7553 return true;
7554 }
7555
7556 if (strictMask & 0x1) // basic Latin
7557 {
7558 if (isBasicLatinString(wstr, numericOrSpace))
7559 return true;
7560 }
7561
7562 return false;
7563}
7564
7565uint8 ObjectMgr::CheckPlayerName(const std::string& name, bool create)
7566{
7567 std::wstring wname;
7568 if (!Utf8toWStr(name, wname))
7569 return CHAR_NAME_INVALID_CHARACTER;
7570
7571 if (wname.size() > MAX_PLAYER_NAME)
7572 return CHAR_NAME_TOO_LONG;
7573
7574 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
7575 if (wname.size() < minName)
7576 return CHAR_NAME_TOO_SHORT;
7577
7578 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
7579 if (!isValidString(wname, strictMask, false, create))
7580 return CHAR_NAME_MIXED_LANGUAGES;
7581
7582 wstrToLower(wname);
7583 for (size_t i = 2; i < wname.size(); ++i)
7584 if (wname[i] == wname[i - 1] && wname[i] == wname[i - 2])
7585 return CHAR_NAME_THREE_CONSECUTIVE;
7586
7587 return CHAR_NAME_SUCCESS;
7588}
7589
7590bool ObjectMgr::IsValidCharterName(const std::string& name)
7591{
7592 std::wstring wname;
7593 if (!Utf8toWStr(name, wname))
7594 return false;
7595
7596 if (wname.size() > MAX_CHARTER_NAME)
7597 return false;
7598
7599 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
7600 if (wname.size() < minName)
7601 return false;
7602
7603 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
7604
7605 return isValidString(wname, strictMask, true);
7606}
7607
7608PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name)
7609{
7610 std::wstring wname;
7611 if (!Utf8toWStr(name, wname))
7612 return PET_NAME_INVALID;
7613
7614 if (wname.size() > MAX_PET_NAME)
7615 return PET_NAME_TOO_LONG;
7616
7617 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
7618 if (wname.size() < minName)
7619 return PET_NAME_TOO_SHORT;
7620
7621 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
7622 if (!isValidString(wname, strictMask, false))
7623 return PET_NAME_MIXED_LANGUAGES;
7624
7625 return PET_NAME_SUCCESS;
7626}
7627
7628void ObjectMgr::LoadGameObjectForQuests()
7629{
7630 uint32 oldMSTime = getMSTime();
7631
7632 _gameObjectForQuestStore.clear(); // need for reload case
7633
7634 if (sObjectMgr->GetGameObjectTemplates()->empty())
7635 {
7636 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 GameObjects for quests");
7637 return;
7638 }
7639
7640 uint32 count = 0;
7641
7642 // collect GO entries for GO that must activated
7643 GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
7644 for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
7645 {
7646 switch (itr->second.type)
7647 {
7648 // scan GO chest with loot including quest items
7649 case GAMEOBJECT_TYPE_CHEST:
7650 {
7651 uint32 loot_id = (itr->second.GetLootId());
7652
7653 // find quest loot for GO
7654 if (itr->second.chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
7655 {
7656 _gameObjectForQuestStore.insert(itr->second.entry);
7657 ++count;
7658 }
7659 break;
7660 }
7661 case GAMEOBJECT_TYPE_GENERIC:
7662 {
7663 if (itr->second._generic.questID > 0) //quests objects
7664 {
7665 _gameObjectForQuestStore.insert(itr->second.entry);
7666 count++;
7667 }
7668 break;
7669 }
7670 case GAMEOBJECT_TYPE_GOOBER:
7671 {
7672 if (itr->second.goober.questId > 0) //quests objects
7673 {
7674 _gameObjectForQuestStore.insert(itr->second.entry);
7675 count++;
7676 }
7677 break;
7678 }
7679
7680 default: break;
7681 }
7682 }
7683
7684 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObjects for quests in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7685}
7686
7687bool ObjectMgr::LoadTrinityStrings(const char* table, int32 min_value, int32 max_value)
7688{
7689 uint32 oldMSTime = getMSTime();
7690
7691 int32 start_value = min_value;
7692 int32 end_value = max_value;
7693 // some string can have negative indexes range
7694 if (start_value < 0)
7695 {
7696 if (end_value >= start_value)
7697 {
7698 sLog->outError(LOG_FILTER_SQL, "Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7699 return false;
7700 }
7701
7702 // real range (max+1, min+1) exaple: (-10, -1000) -> -999...-10+1
7703 std::swap(start_value, end_value);
7704 ++start_value;
7705 ++end_value;
7706 }
7707 else
7708 {
7709 if (start_value >= end_value)
7710 {
7711 sLog->outError(LOG_FILTER_SQL, "Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
7712 return false;
7713 }
7714 }
7715
7716 // cleanup affected map part for reloading case
7717 for (TrinityStringLocaleContainer::iterator itr = _trinityStringLocaleStore.begin(); itr != _trinityStringLocaleStore.end();)
7718 {
7719 if (itr->first >= start_value && itr->first < end_value)
7720 _trinityStringLocaleStore.erase(itr++);
7721 else
7722 ++itr;
7723 }
7724
7725 QueryResult result = WorldDatabase.PQuery("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8, content_loc9, content_loc10 FROM %s", table);
7726
7727 if (!result)
7728 {
7729 if (min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings
7730 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.", table);
7731 else
7732 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 string templates. DB table `%s` is empty.", table);
7733
7734 return false;
7735 }
7736
7737 uint32 count = 0;
7738
7739 do
7740 {
7741 Field* fields = result->Fetch();
7742
7743 int32 entry = fields[0].GetInt32();
7744
7745 if (entry == 0)
7746 {
7747 sLog->outError(LOG_FILTER_SQL, "Table `%s` contain reserved entry 0, ignored.", table);
7748 continue;
7749 }
7750 else if (entry < start_value || entry >= end_value)
7751 {
7752 sLog->outError(LOG_FILTER_SQL, "Table `%s` contain entry %i out of allowed range (%d - %d), ignored.", table, entry, min_value, max_value);
7753 continue;
7754 }
7755
7756 TrinityStringLocale& data = _trinityStringLocaleStore[entry];
7757
7758 if (!data.Content.empty())
7759 {
7760 sLog->outError(LOG_FILTER_SQL, "Table `%s` contain data for already loaded entry %i (from another table?), ignored.", table, entry);
7761 continue;
7762 }
7763
7764 data.Content.resize(1);
7765 ++count;
7766
7767 for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
7768 AddLocaleString(fields[i + 1].GetString(), LocaleConstant(i), data.Content);
7769 } while (result->NextRow());
7770
7771 if (min_value == MIN_TRINITY_STRING_ID)
7772 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u Trinity strings from table %s in %u ms", count, table, GetMSTimeDiffToNow(oldMSTime));
7773 else
7774 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u string templates from %s in %u ms", count, table, GetMSTimeDiffToNow(oldMSTime));
7775
7776 return true;
7777}
7778
7779const char *ObjectMgr::GetTrinityString(int32 entry, LocaleConstant locale_idx) const
7780{
7781 if (TrinityStringLocale const* msl = GetTrinityStringLocale(entry))
7782 {
7783 if (msl->Content.size() > size_t(locale_idx) && !msl->Content[locale_idx].empty())
7784 return msl->Content[locale_idx].c_str();
7785
7786 return msl->Content[DEFAULT_LOCALE].c_str();
7787 }
7788
7789 if (entry > 0)
7790 sLog->outError(LOG_FILTER_SQL, "Entry %i not found in `trinity_string` table.", entry);
7791 else
7792 sLog->outError(LOG_FILTER_SQL, "Trinity string entry %i not found in DB.", entry);
7793 return "<error>";
7794}
7795
7796void ObjectMgr::LoadFishingBaseSkillLevel()
7797{
7798 uint32 oldMSTime = getMSTime();
7799
7800 _fishingBaseForAreaStore.clear(); // for reload case
7801
7802 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
7803
7804 if (!result)
7805 {
7806 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
7807
7808 return;
7809 }
7810
7811 uint32 count = 0;
7812
7813 do
7814 {
7815 Field* fields = result->Fetch();
7816 uint32 entry = fields[0].GetUInt32();
7817 int32 skill = fields[1].GetInt16();
7818
7819 AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
7820 if (!fArea)
7821 {
7822 sLog->outError(LOG_FILTER_SQL, "AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
7823 continue;
7824 }
7825
7826 _fishingBaseForAreaStore[entry] = skill;
7827 ++count;
7828 } while (result->NextRow());
7829
7830 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u areas for fishing base skill level in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7831}
7832
7833bool ObjectMgr::CheckDeclinedNames(std::wstring w_ownname, DeclinedName const& names)
7834{
7835 // get main part of the name
7836 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
7837 // prepare flags
7838 bool x = true;
7839 bool y = true;
7840
7841 // check declined names
7842 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
7843 {
7844 std::wstring wname;
7845 if (!Utf8toWStr(names.name[i], wname))
7846 return false;
7847
7848 if (mainpart != GetMainPartOfName(wname, i + 1))
7849 x = false;
7850
7851 if (w_ownname != wname)
7852 y = false;
7853 }
7854 return (x || y);
7855}
7856
7857uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id)
7858{
7859 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
7860 if (i != _areaTriggerScriptStore.end())
7861 return i->second;
7862 return 0;
7863}
7864
7865SpellScriptsBounds ObjectMgr::GetSpellScriptsBounds(uint32 spell_id)
7866{
7867 return SpellScriptsBounds(_spellScriptsStore.lower_bound(spell_id), _spellScriptsStore.upper_bound(spell_id));
7868}
7869
7870SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial)
7871{
7872 switch (pSkill->categoryId)
7873 {
7874 case SKILL_CATEGORY_LANGUAGES:
7875 return SKILL_RANGE_LANGUAGE;
7876 case SKILL_CATEGORY_WEAPON:
7877 return SKILL_RANGE_LEVEL;
7878 case SKILL_CATEGORY_ARMOR:
7879 case SKILL_CATEGORY_CLASS:
7880 if (pSkill->id != SKILL_LOCKPICKING)
7881 return SKILL_RANGE_MONO;
7882 else
7883 return SKILL_RANGE_LEVEL;
7884 case SKILL_CATEGORY_SECONDARY:
7885 case SKILL_CATEGORY_PROFESSION:
7886 // not set skills for professions and racial abilities
7887 if (IsProfessionSkill(pSkill->id))
7888 return SKILL_RANGE_RANK;
7889 else if (racial)
7890 return SKILL_RANGE_NONE;
7891 else
7892 return SKILL_RANGE_MONO;
7893 default:
7894 case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
7895 case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
7896 return SKILL_RANGE_NONE;
7897 }
7898}
7899
7900void ObjectMgr::LoadGameTele()
7901{
7902 uint32 oldMSTime = getMSTime();
7903
7904 _gameTeleStore.clear(); // for reload case
7905
7906 // 0 1 2 3 4 5 6
7907 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
7908
7909 if (!result)
7910 {
7911 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
7912
7913 return;
7914 }
7915
7916 uint32 count = 0;
7917
7918 do
7919 {
7920 Field* fields = result->Fetch();
7921
7922 uint32 id = fields[0].GetUInt32();
7923
7924 GameTele gt;
7925
7926 gt.position_x = fields[1].GetFloat();
7927 gt.position_y = fields[2].GetFloat();
7928 gt.position_z = fields[3].GetFloat();
7929 gt.orientation = fields[4].GetFloat();
7930 gt.mapId = fields[5].GetUInt16();
7931 gt.name = fields[6].GetString();
7932
7933 if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
7934 {
7935 sLog->outError(LOG_FILTER_SQL, "Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
7936 continue;
7937 }
7938
7939 if (!Utf8toWStr(gt.name, gt.wnameLow))
7940 {
7941 sLog->outError(LOG_FILTER_SQL, "Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
7942 continue;
7943 }
7944
7945 wstrToLower(gt.wnameLow);
7946
7947 _gameTeleStore[id] = gt;
7948
7949 ++count;
7950 } while (result->NextRow());
7951
7952 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameTeleports in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
7953}
7954
7955GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
7956{
7957 // explicit name case
7958 std::wstring wname;
7959 if (!Utf8toWStr(name, wname))
7960 return NULL;
7961
7962 // converting string that we try to find to lower case
7963 wstrToLower(wname);
7964
7965 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
7966 const GameTele* alt = NULL;
7967 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
7968 {
7969 if (itr->second.wnameLow == wname)
7970 return &itr->second;
7971 else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
7972 alt = &itr->second;
7973 }
7974
7975 return alt;
7976}
7977
7978bool ObjectMgr::AddGameTele(GameTele& tele)
7979{
7980 // find max id
7981 uint32 new_id = 0;
7982 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
7983 if (itr->first > new_id)
7984 new_id = itr->first;
7985
7986 // use next
7987 ++new_id;
7988
7989 if (!Utf8toWStr(tele.name, tele.wnameLow))
7990 return false;
7991
7992 wstrToLower(tele.wnameLow);
7993
7994 _gameTeleStore[new_id] = tele;
7995
7996 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE);
7997
7998 stmt->setUInt32(0, new_id);
7999 stmt->setFloat(1, tele.position_x);
8000 stmt->setFloat(2, tele.position_y);
8001 stmt->setFloat(3, tele.position_z);
8002 stmt->setFloat(4, tele.orientation);
8003 stmt->setUInt16(5, uint16(tele.mapId));
8004 stmt->setString(6, tele.name);
8005
8006 WorldDatabase.Execute(stmt);
8007
8008 return true;
8009}
8010
8011bool ObjectMgr::DeleteGameTele(const std::string& name)
8012{
8013 // explicit name case
8014 std::wstring wname;
8015 if (!Utf8toWStr(name, wname))
8016 return false;
8017
8018 // converting string that we try to find to lower case
8019 wstrToLower(wname);
8020
8021 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
8022 {
8023 if (itr->second.wnameLow == wname)
8024 {
8025 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE);
8026
8027 stmt->setString(0, itr->second.name);
8028
8029 WorldDatabase.Execute(stmt);
8030
8031 _gameTeleStore.erase(itr);
8032 return true;
8033 }
8034 }
8035
8036 return false;
8037}
8038
8039void ObjectMgr::LoadMailLevelRewards()
8040{
8041 //TODO: remove mail level rewards, we already have achievement reward
8042 return;
8043
8044 uint32 oldMSTime = getMSTime();
8045
8046 _mailLevelRewardStore.clear(); // for reload case
8047
8048 // 0 1 2 3
8049 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
8050
8051 if (!result)
8052 {
8053 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
8054
8055 return;
8056 }
8057
8058 uint32 count = 0;
8059
8060 do
8061 {
8062 Field* fields = result->Fetch();
8063
8064 uint8 level = fields[0].GetUInt8();
8065 uint32 raceMask = fields[1].GetUInt32();
8066 uint32 mailTemplateId = fields[2].GetUInt32();
8067 uint32 senderEntry = fields[3].GetUInt32();
8068
8069 if (level > MAX_LEVEL)
8070 {
8071 sLog->outError(LOG_FILTER_SQL, "Table `mail_level_reward` have data for level %u that more supported by client (%u), ignoring.", level, MAX_LEVEL);
8072 continue;
8073 }
8074
8075 if (!(raceMask & RACEMASK_ALL_PLAYABLE))
8076 {
8077 sLog->outError(LOG_FILTER_SQL, "Table `mail_level_reward` have raceMask (%u) for level %u that not include any player races, ignoring.", raceMask, level);
8078 continue;
8079 }
8080
8081 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
8082 {
8083 sLog->outError(LOG_FILTER_SQL, "Table `mail_level_reward` have invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.", mailTemplateId, level);
8084 continue;
8085 }
8086
8087 if (!GetCreatureTemplate(senderEntry))
8088 {
8089 sLog->outError(LOG_FILTER_SQL, "Table `mail_level_reward` have not existed sender creature entry (%u) for level %u that invalid not include any player races, ignoring.", senderEntry, level);
8090 continue;
8091 }
8092
8093 _mailLevelRewardStore[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
8094
8095 ++count;
8096 } while (result->NextRow());
8097
8098 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u level dependent mail rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8099}
8100
8101void ObjectMgr::AddSpellToTrainer(uint32 entry, uint32 spell, uint32 spellCost, uint32 reqSkill, uint32 reqSkillValue, uint32 reqLevel)
8102{
8103 if (entry >= TRINITY_TRAINER_START_REF)
8104 return;
8105
8106 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
8107 if (!cInfo)
8108 {
8109 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` contains an entry for a non-existing creature template (Entry: %u), ignoring", entry);
8110 return;
8111 }
8112
8113 if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
8114 {
8115 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` contains an entry for a creature template (Entry: %u) without trainer flag, ignoring", entry);
8116 return;
8117 }
8118
8119 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spell);
8120 if (!spellinfo)
8121 {
8122 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u), ignoring", entry, spell);
8123 return;
8124 }
8125
8126 if (!SpellMgr::IsSpellValid(spellinfo))
8127 {
8128 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` contains an entry (Entry: %u) for a broken spell (Spell: %u), ignoring", entry, spell);
8129 return;
8130 }
8131
8132 /* if (GetTalentSpellCost(spell))
8133 {
8134 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u) which is a talent, ignoring", entry, spell);
8135 return;
8136 }*/
8137
8138 TrainerSpellData& data = _cacheTrainerSpellStore[entry];
8139
8140 TrainerSpell& trainerSpell = data.spellList[spell];
8141 trainerSpell.spell = spell;
8142 trainerSpell.spellCost = spellCost;
8143 trainerSpell.reqSkill = reqSkill;
8144 trainerSpell.reqSkillValue = reqSkillValue;
8145 trainerSpell.reqLevel = reqLevel;
8146
8147 if (!trainerSpell.reqLevel)
8148 trainerSpell.reqLevel = spellinfo->SpellLevel;
8149
8150 // calculate learned spell for profession case when stored cast-spell
8151 trainerSpell.learnedSpell[0] = spell;
8152 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
8153 {
8154 if (spellinfo->Effects[i].Effect != SPELL_EFFECT_LEARN_SPELL)
8155 continue;
8156 if (trainerSpell.learnedSpell[0] == spell)
8157 trainerSpell.learnedSpell[0] = 0;
8158 // player must be able to cast spell on himself
8159 if (spellinfo->Effects[i].TargetA.GetTarget() != 0 && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY
8160 && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ANY && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_CASTER)
8161 {
8162 sLog->outError(LOG_FILTER_SQL, "Table `npc_trainer` has spell %u for trainer entry %u with learn effect which has incorrect target type, ignoring learn effect!", spell, entry);
8163 continue;
8164 }
8165
8166 trainerSpell.learnedSpell[i] = spellinfo->Effects[i].TriggerSpell;
8167
8168 if (trainerSpell.learnedSpell[i])
8169 {
8170 SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(trainerSpell.learnedSpell[i]);
8171 if (learnedSpellInfo && learnedSpellInfo->IsProfession())
8172 data.trainerType = 2;
8173 }
8174 }
8175
8176 return;
8177}
8178
8179void ObjectMgr::LoadTrainerSpell()
8180{
8181 uint32 oldMSTime = getMSTime();
8182
8183 // For reload case
8184 _cacheTrainerSpellStore.clear();
8185
8186 QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel FROM npc_trainer AS a "
8187 "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) "
8188 "UNION SELECT * FROM npc_trainer WHERE spell > 0");
8189
8190 if (!result)
8191 {
8192 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Trainers. DB table `npc_trainer` is empty!");
8193
8194 return;
8195 }
8196
8197 uint32 count = 0;
8198
8199 do
8200 {
8201 Field* fields = result->Fetch();
8202
8203 uint32 entry = fields[0].GetUInt32();
8204 uint32 spell = fields[1].GetUInt32();
8205 uint32 spellCost = fields[2].GetUInt32();
8206 uint32 reqSkill = fields[3].GetUInt16();
8207 uint32 reqSkillValue = fields[4].GetUInt16();
8208 uint32 reqLevel = fields[5].GetUInt8();
8209
8210 AddSpellToTrainer(entry, spell, spellCost, reqSkill, reqSkillValue, reqLevel);
8211
8212 ++count;
8213 } while (result->NextRow());
8214
8215 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %d Trainers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8216}
8217
8218int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, uint8 type, std::set<uint32> *skip_vendors)
8219{
8220 // find all items from the reference vendor
8221 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
8222 stmt->setUInt32(0, uint32(item));
8223 stmt->setUInt8(1, type);
8224 PreparedQueryResult result = WorldDatabase.Query(stmt);
8225
8226 if (!result)
8227 return 0;
8228
8229 uint32 count = 0;
8230 do
8231 {
8232 Field* fields = result->Fetch();
8233
8234 int32 item_id = fields[0].GetInt32();
8235
8236 // if item is a negative, its a reference
8237 if (item_id < 0)
8238 count += LoadReferenceVendor(vendor, -item_id, type, skip_vendors);
8239 else
8240 {
8241 int32 maxcount = fields[1].GetUInt32();
8242 uint32 incrtime = fields[2].GetUInt32();
8243 uint32 ExtendedCost = fields[3].GetUInt32();
8244 uint8 type = fields[4].GetUInt8();
8245
8246 if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, type, NULL, skip_vendors))
8247 continue;
8248
8249 VendorItemData& vList = _cacheVendorItemStore[vendor];
8250
8251 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
8252 ++count;
8253 }
8254 } while (result->NextRow());
8255
8256 return count;
8257}
8258
8259void ObjectMgr::LoadVendors()
8260{
8261 uint32 oldMSTime = getMSTime();
8262
8263 // For reload case
8264 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
8265 itr->second.Clear();
8266 _cacheVendorItemStore.clear();
8267
8268 std::set<uint32> skip_vendors;
8269
8270 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type FROM npc_vendor ORDER BY entry, slot ASC");
8271 if (!result)
8272 {
8273
8274 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
8275 return;
8276 }
8277
8278 uint32 count = 0;
8279
8280 do
8281 {
8282 Field* fields = result->Fetch();
8283
8284 uint32 entry = fields[0].GetUInt32();
8285 int32 item_id = fields[1].GetInt32();
8286
8287 // if item is a negative, its a reference
8288 if (item_id < 0)
8289 count += LoadReferenceVendor(entry, -item_id, 0, &skip_vendors);
8290 else
8291 {
8292 uint32 maxcount = fields[2].GetUInt32();
8293 uint32 incrtime = fields[3].GetUInt32();
8294 uint32 ExtendedCost = fields[4].GetUInt32();
8295 uint8 type = fields[5].GetUInt8();
8296
8297 if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, type, NULL, &skip_vendors))
8298 continue;
8299
8300 VendorItemData& vList = _cacheVendorItemStore[entry];
8301
8302 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
8303 ++count;
8304 }
8305 } while (result->NextRow());
8306
8307 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %d Vendors in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8308}
8309
8310void ObjectMgr::LoadGossipMenu()
8311{
8312 uint32 oldMSTime = getMSTime();
8313
8314 _gossipMenusStore.clear();
8315
8316 QueryResult result = WorldDatabase.Query("SELECT entry, text_id FROM gossip_menu");
8317
8318 if (!result)
8319 {
8320 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gossip_menu entries. DB table `gossip_menu` is empty!");
8321
8322 return;
8323 }
8324
8325 uint32 count = 0;
8326
8327 do
8328 {
8329 Field* fields = result->Fetch();
8330
8331 GossipMenus gMenu;
8332
8333 gMenu.entry = fields[0].GetUInt16();
8334 gMenu.text_id = fields[1].GetUInt32();
8335
8336 if (!GetGossipText(gMenu.text_id))
8337 {
8338 sLog->outError(LOG_FILTER_SQL, "Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
8339 continue;
8340 }
8341
8342 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.entry, gMenu));
8343
8344 ++count;
8345 } while (result->NextRow());
8346
8347 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u gossip_menu entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8348}
8349
8350void ObjectMgr::LoadGossipMenuItems()
8351{
8352 uint32 oldMSTime = getMSTime();
8353
8354 _gossipMenuItemsStore.clear();
8355
8356 QueryResult result = WorldDatabase.Query(
8357 // 0 1 2 3 4
8358 "SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
8359 // 5 6 7 8 9
8360 "action_menu_id, action_poi_id, box_coded, box_money, box_text "
8361 "FROM gossip_menu_option ORDER BY menu_id, id");
8362
8363 if (!result)
8364 {
8365 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 gossip_menu_option entries. DB table `gossip_menu_option` is empty!");
8366
8367 return;
8368 }
8369
8370 uint32 count = 0;
8371
8372 do
8373 {
8374 Field* fields = result->Fetch();
8375
8376 GossipMenuItems gMenuItem;
8377
8378 gMenuItem.MenuId = fields[0].GetUInt16();
8379 gMenuItem.OptionIndex = fields[1].GetUInt16();
8380 gMenuItem.OptionIcon = fields[2].GetUInt32();
8381 gMenuItem.OptionText = fields[3].GetString();
8382 gMenuItem.OptionType = fields[4].GetUInt8();
8383 gMenuItem.OptionNpcflag = fields[5].GetUInt32();
8384 gMenuItem.ActionMenuId = fields[6].GetUInt32();
8385 gMenuItem.ActionPoiId = fields[7].GetUInt32();
8386 gMenuItem.BoxCoded = fields[8].GetBool();
8387 gMenuItem.BoxMoney = fields[9].GetUInt32();
8388 gMenuItem.BoxText = fields[10].GetString();
8389
8390 if (gMenuItem.OptionIcon >= GOSSIP_ICON_MAX)
8391 {
8392 sLog->outError(LOG_FILTER_SQL, "Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.MenuId, gMenuItem.OptionIndex, gMenuItem.OptionIcon);
8393 gMenuItem.OptionIcon = GOSSIP_ICON_CHAT;
8394 }
8395
8396 if (gMenuItem.OptionType >= GOSSIP_OPTION_MAX)
8397 sLog->outError(LOG_FILTER_SQL, "Table gossip_menu_option for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem.MenuId, gMenuItem.OptionIndex, gMenuItem.OptionType);
8398
8399 if (gMenuItem.ActionPoiId && !GetPointOfInterest(gMenuItem.ActionPoiId))
8400 {
8401 sLog->outError(LOG_FILTER_SQL, "Table gossip_menu_option for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.MenuId, gMenuItem.OptionIndex, gMenuItem.ActionPoiId);
8402 gMenuItem.ActionPoiId = 0;
8403 }
8404
8405 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuId, gMenuItem));
8406 ++count;
8407 } while (result->NextRow());
8408
8409 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u gossip_menu_option entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8410}
8411
8412void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist /*= true*/)
8413{
8414 VendorItemData& vList = _cacheVendorItemStore[entry];
8415 vList.AddItem(item, maxcount, incrtime, extendedCost, type);
8416
8417 if (persist)
8418 {
8419 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
8420
8421 stmt->setUInt32(0, entry);
8422 stmt->setUInt32(1, item);
8423 stmt->setUInt8(2, maxcount);
8424 stmt->setUInt32(3, incrtime);
8425 stmt->setUInt32(4, extendedCost);
8426 stmt->setUInt8(5, type);
8427
8428 WorldDatabase.Execute(stmt);
8429 }
8430}
8431
8432bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/)
8433{
8434 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
8435 if (iter == _cacheVendorItemStore.end())
8436 return false;
8437
8438 if (!iter->second.RemoveItem(item, type))
8439 return false;
8440
8441 if (persist)
8442 {
8443 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR);
8444
8445 stmt->setUInt32(0, entry);
8446 stmt->setUInt32(1, item);
8447 stmt->setUInt8(2, type);
8448
8449 WorldDatabase.Execute(stmt);
8450 }
8451
8452 return true;
8453}
8454
8455bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, uint8 type, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
8456{
8457 CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
8458 if (!cInfo)
8459 {
8460 if (player)
8461 ChatHandler(player).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
8462 else
8463 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
8464 return false;
8465 }
8466
8467 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
8468 {
8469 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
8470 {
8471 if (player)
8472 ChatHandler(player).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
8473 else
8474 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
8475
8476 if (skip_vendors)
8477 skip_vendors->insert(vendor_entry);
8478 }
8479 return false;
8480 }
8481
8482 if ((type == ITEM_VENDOR_TYPE_ITEM && !sObjectMgr->GetItemTemplate(id)) ||
8483 (type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(id)))
8484 {
8485 if (player)
8486 ChatHandler(player).PSendSysMessage(LANG_ITEM_NOT_FOUND, id, type);
8487 else
8488 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u, type %u), ignore", vendor_entry, id, type);
8489 return false;
8490 }
8491
8492 if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
8493 {
8494 if (player)
8495 ChatHandler(player).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
8496 else
8497 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", id, ExtendedCost, vendor_entry);
8498 return false;
8499 }
8500
8501 if (type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
8502 {
8503 if (maxcount > 0 && incrtime == 0)
8504 {
8505 if (player)
8506 ChatHandler(player).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
8507 else
8508 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, id, vendor_entry);
8509 return false;
8510 }
8511 else if (maxcount == 0 && incrtime > 0)
8512 {
8513 if (player)
8514 ChatHandler(player).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
8515 else
8516 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", id, vendor_entry);
8517 return false;
8518 }
8519 }
8520
8521 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
8522 if (!vItems)
8523 return true; // later checks for non-empty lists
8524
8525 if (vItems->FindItemCostPair(id, ExtendedCost, type))
8526 {
8527 if (player)
8528 ChatHandler(player).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, id, ExtendedCost, type);
8529 else
8530 sLog->outError(LOG_FILTER_SQL, "Table `npc_vendor` has duplicate items %u (with extended cost %u, type %u) for vendor (Entry: %u), ignoring", id, ExtendedCost, type, vendor_entry);
8531 return false;
8532 }
8533
8534 if (vItems->GetItemCount() >= MAX_VENDOR_ITEMS) // FIXME: GetItemCount range 0...255 MAX_VENDOR_ITEMS = 300
8535 {
8536 if (player)
8537 ChatHandler(player).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
8538 else
8539 sLog->outError(LOG_FILTER_SQL, "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
8540 return false;
8541 }
8542
8543 if (type == ITEM_VENDOR_TYPE_CURRENCY && maxcount == 0)
8544 {
8545 sLog->outError(LOG_FILTER_SQL, "Table `(game_event_)npc_vendor` have Item (Entry: %u, type: %u) with missing maxcount for vendor (%u), ignore", id, type, ExtendedCost, vendor_entry);
8546 return false;
8547 }
8548
8549 return true;
8550}
8551
8552void ObjectMgr::LoadScriptNames()
8553{
8554 uint32 oldMSTime = getMSTime();
8555
8556 _scriptNamesStore.push_back("");
8557 QueryResult result = WorldDatabase.Query(
8558 "SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
8559 "UNION "
8560 "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
8561 "UNION "
8562 "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
8563 "UNION "
8564 "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
8565 "UNION "
8566 "SELECT DISTINCT(ScriptName) FROM item_script_names WHERE ScriptName <> '' "
8567 "UNION "
8568 "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
8569 "UNION "
8570 "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
8571 "UNION "
8572 "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
8573 "UNION "
8574 "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
8575 "UNION "
8576 "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
8577 "UNION "
8578 "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
8579 "UNION "
8580 "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
8581
8582 if (!result)
8583 {
8584
8585 sLog->outError(LOG_FILTER_SQL, ">> Loaded empty set of Script Names!");
8586 return;
8587 }
8588
8589 uint32 count = 1;
8590
8591 do
8592 {
8593 _scriptNamesStore.push_back((*result)[0].GetString());
8594 ++count;
8595 } while (result->NextRow());
8596
8597 std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
8598 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %d Script Names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8599}
8600
8601uint32 ObjectMgr::GetScriptId(const char *name)
8602{
8603 // use binary search to find the script name in the sorted vector
8604 // assume "" is the first element
8605 if (!name)
8606 return 0;
8607
8608 ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name);
8609 if (itr == _scriptNamesStore.end() || *itr != name)
8610 return 0;
8611
8612 return uint32(itr - _scriptNamesStore.begin());
8613}
8614
8615void ObjectMgr::CheckScripts(ScriptsType type, std::set<int32>& ids)
8616{
8617 ScriptMapMap* scripts = GetScriptsMapByType(type);
8618 if (!scripts)
8619 return;
8620
8621 for (ScriptMapMap::const_iterator itrMM = scripts->begin(); itrMM != scripts->end(); ++itrMM)
8622 {
8623 for (ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM)
8624 {
8625 switch (itrM->second.command)
8626 {
8627 case SCRIPT_COMMAND_TALK:
8628 {
8629 if (!GetTrinityStringLocale(itrM->second.Talk.TextID))
8630 sLog->outError(LOG_FILTER_SQL, "Table `%s` references invalid text id %u from `db_script_string`, script id: %u.", GetScriptsTableNameByType(type).c_str(), itrM->second.Talk.TextID, itrMM->first);
8631
8632 if (ids.find(itrM->second.Talk.TextID) != ids.end())
8633 ids.erase(itrM->second.Talk.TextID);
8634 }
8635 default:
8636 break;
8637 }
8638 }
8639 }
8640}
8641
8642void ObjectMgr::LoadDbScriptStrings()
8643{
8644 LoadTrinityStrings("db_script_string", MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID);
8645
8646 std::set<int32> ids;
8647
8648 for (int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i)
8649 if (GetTrinityStringLocale(i))
8650 ids.insert(i);
8651
8652 for (int type = SCRIPTS_FIRST; type < SCRIPTS_LAST; ++type)
8653 CheckScripts(ScriptsType(type), ids);
8654
8655 for (std::set<int32>::const_iterator itr = ids.begin(); itr != ids.end(); ++itr)
8656 sLog->outError(LOG_FILTER_SQL, "Table `db_script_string` has unused string id %u", *itr);
8657}
8658
8659bool LoadTrinityStrings(const char* table, int32 start_value, int32 end_value)
8660{
8661 // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
8662 // start/end reversed for negative values
8663 if (start_value > MAX_DB_SCRIPT_STRING_ID || end_value >= start_value)
8664 {
8665 sLog->outError(LOG_FILTER_SQL, "Table '%s' load attempted with range (%d - %d) reserved by Trinity, strings not loaded.", table, start_value, end_value + 1);
8666 return false;
8667 }
8668
8669 return sObjectMgr->LoadTrinityStrings(table, start_value, end_value);
8670}
8671
8672CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unitClass)
8673{
8674 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
8675
8676 if (it != _creatureBaseStatsStore.end())
8677 return &(it->second);
8678
8679 struct DefaultCreatureBaseStats : public CreatureBaseStats
8680 {
8681 DefaultCreatureBaseStats()
8682 {
8683 BaseArmor = 1;
8684 for (uint8 j = 0; j < MAX_CREATURE_BASE_HP; ++j)
8685 BaseHealth[j] = 1;
8686 BaseMana = 0;
8687 }
8688 };
8689 static const DefaultCreatureBaseStats def_stats;
8690 return &def_stats;
8691}
8692
8693void ObjectMgr::LoadCreatureClassLevelStats()
8694{
8695 uint32 oldMSTime = getMSTime();
8696
8697 QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basehp4, basemana, basearmor FROM creature_classlevelstats");
8698
8699 if (!result)
8700 {
8701 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
8702 return;
8703 }
8704
8705 uint32 count = 0;
8706 do
8707 {
8708 Field* fields = result->Fetch();
8709
8710 uint8 index = 0;
8711
8712 uint8 Level = fields[index++].GetInt8();
8713 uint8 Class = fields[index++].GetInt8();
8714
8715 CreatureBaseStats stats;
8716
8717 for (uint8 i = 0; i < MAX_CREATURE_BASE_HP; ++i)
8718 stats.BaseHealth[i] = fields[index++].GetUInt32();
8719
8720 stats.BaseMana = fields[index++].GetUInt32();
8721 stats.BaseArmor = fields[index++].GetUInt32();
8722
8723 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
8724 sLog->outError(LOG_FILTER_SQL, "Creature base stats for level %u has invalid class %u", Level, Class);
8725
8726 for (uint8 i = 0; i < MAX_CREATURE_BASE_HP; ++i)
8727 {
8728 if (stats.BaseHealth[i] < 1)
8729 {
8730 sLog->outError(LOG_FILTER_SQL, "Creature base stats for class %u, level %u has invalid zero base HP[%u] - set to 1", Class, Level, i);
8731 stats.BaseHealth[i] = 1;
8732 }
8733 }
8734
8735 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
8736
8737 ++count;
8738 } while (result->NextRow());
8739
8740 CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
8741 for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
8742 {
8743 for (uint16 lvl = itr->second.minlevel; lvl <= itr->second.maxlevel; ++lvl)
8744 {
8745 if (_creatureBaseStatsStore.find(MAKE_PAIR16(lvl, itr->second.unit_class)) == _creatureBaseStatsStore.end())
8746 sLog->outError(LOG_FILTER_SQL, "Missing base stats for creature class %u level %u", itr->second.unit_class, lvl);
8747 }
8748 }
8749
8750 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u creature base stats in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8751}
8752
8753void ObjectMgr::LoadFactionChangeAchievements()
8754{
8755 uint32 oldMSTime = getMSTime();
8756
8757 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
8758
8759 if (!result)
8760 {
8761 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
8762
8763 return;
8764 }
8765
8766 uint32 count = 0;
8767
8768 do
8769 {
8770 Field* fields = result->Fetch();
8771
8772 uint32 alliance = fields[0].GetUInt32();
8773 uint32 horde = fields[1].GetUInt32();
8774
8775 if (!sAchievementStore.LookupEntry(alliance))
8776 sLog->outError(LOG_FILTER_SQL, "Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
8777 else if (!sAchievementStore.LookupEntry(horde))
8778 sLog->outError(LOG_FILTER_SQL, "Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
8779 else
8780 FactionChange_Achievements[alliance] = horde;
8781
8782 ++count;
8783 } while (result->NextRow());
8784
8785 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change achievement pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8786}
8787
8788void ObjectMgr::LoadFactionChangeItems()
8789{
8790 uint32 oldMSTime = getMSTime();
8791
8792 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
8793
8794 if (!result)
8795 {
8796 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
8797 return;
8798 }
8799
8800 uint32 count = 0;
8801
8802 do
8803 {
8804 Field* fields = result->Fetch();
8805
8806 uint32 alliance = fields[0].GetUInt32();
8807 uint32 horde = fields[1].GetUInt32();
8808
8809 if (!GetItemTemplate(alliance))
8810 sLog->outError(LOG_FILTER_SQL, "Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
8811 else if (!GetItemTemplate(horde))
8812 sLog->outError(LOG_FILTER_SQL, "Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
8813 else
8814 FactionChange_Items[alliance] = horde;
8815
8816 ++count;
8817 } while (result->NextRow());
8818
8819 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change item pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8820}
8821
8822void ObjectMgr::LoadFactionChangeSpells()
8823{
8824 uint32 oldMSTime = getMSTime();
8825
8826 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
8827
8828 if (!result)
8829 {
8830 sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
8831
8832 return;
8833 }
8834
8835 uint32 count = 0;
8836
8837 do
8838 {
8839 Field* fields = result->Fetch();
8840
8841 uint32 alliance = fields[0].GetUInt32();
8842 uint32 horde = fields[1].GetUInt32();
8843
8844 if (!sSpellMgr->GetSpellInfo(alliance))
8845 sLog->outError(LOG_FILTER_SQL, "Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
8846 else if (!sSpellMgr->GetSpellInfo(horde))
8847 sLog->outError(LOG_FILTER_SQL, "Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
8848 else
8849 FactionChange_Spells[alliance] = horde;
8850
8851 ++count;
8852 } while (result->NextRow());
8853
8854 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change spell pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8855}
8856
8857void ObjectMgr::LoadFactionChangeReputations()
8858{
8859 uint32 oldMSTime = getMSTime();
8860
8861 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
8862
8863 if (!result)
8864 {
8865 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
8866 return;
8867 }
8868
8869 uint32 count = 0;
8870
8871 do
8872 {
8873 Field* fields = result->Fetch();
8874
8875 uint32 alliance = fields[0].GetUInt32();
8876 uint32 horde = fields[1].GetUInt32();
8877
8878 if (!sFactionStore.LookupEntry(alliance))
8879 sLog->outError(LOG_FILTER_SQL, "Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
8880 else if (!sFactionStore.LookupEntry(horde))
8881 sLog->outError(LOG_FILTER_SQL, "Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
8882 else
8883 FactionChange_Reputation[alliance] = horde;
8884
8885 ++count;
8886 } while (result->NextRow());
8887
8888 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change reputation pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8889}
8890
8891void ObjectMgr::LoadFactionChangeTitles()
8892{
8893 uint32 oldMSTime = getMSTime();
8894
8895 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
8896
8897 if (!result)
8898 {
8899 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
8900 return;
8901 }
8902
8903 uint32 count = 0;
8904
8905 do
8906 {
8907 Field* fields = result->Fetch();
8908
8909 uint32 alliance = fields[0].GetUInt32();
8910 uint32 horde = fields[1].GetUInt32();
8911
8912 if (!sCharTitlesStore.LookupEntry(alliance))
8913 sLog->outError(LOG_FILTER_SQL, "Title %u referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
8914 else if (!sCharTitlesStore.LookupEntry(horde))
8915 sLog->outError(LOG_FILTER_SQL, "Title %u referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
8916 else
8917 FactionChange_Titles[alliance] = horde;
8918
8919 ++count;
8920 } while (result->NextRow());
8921
8922 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u faction change title pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8923}
8924
8925void ObjectMgr::LoadHotfixData()
8926{
8927 uint32 oldMSTime = getMSTime();
8928
8929 QueryResult result = WorldDatabase.Query("SELECT entry, type, UNIX_TIMESTAMP(hotfixDate) FROM hotfix_data");
8930
8931 if (!result)
8932 {
8933 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 hotfix info entries. DB table `hotfix_data` is empty.");
8934 return;
8935 }
8936
8937 uint32 count = 0;
8938
8939 _hotfixData.reserve(result->GetRowCount());
8940
8941 do
8942 {
8943 Field* fields = result->Fetch();
8944
8945 HotfixInfo info;
8946 info.Entry = fields[0].GetUInt32();
8947 info.Type = fields[1].GetUInt32();
8948 info.Timestamp = fields[2].GetUInt64();
8949 _hotfixData.push_back(info);
8950 ++count;
8951 } while (result->NextRow());
8952
8953 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u hotfix info entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
8954}
8955
8956void ObjectMgr::LoadPhaseDefinitions()
8957{
8958 _PhaseDefinitionStore.clear();
8959
8960 uint32 oldMSTime = getMSTime();
8961
8962 // 0 1 2 3 4 5 6
8963 QueryResult result = WorldDatabase.Query("SELECT zoneId, entry, phasemask, phaseId, terrainswapmap, worldmaparea, flags FROM `phase_definitions` ORDER BY `entry` ASC");
8964
8965 if (!result)
8966 {
8967 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 phasing definitions. DB table `phase_definitions` is empty.");
8968 return;
8969 }
8970
8971 uint32 count = 0;
8972
8973 do
8974 {
8975 Field* fields = result->Fetch();
8976
8977 PhaseDefinition pd;
8978
8979 pd.zoneId = fields[0].GetUInt32(); // Zone ID. THis and Entry are PK's.
8980 pd.entry = fields[1].GetUInt32(); // Ordered entry (1 - X) for the zone.
8981 pd.phasemask = fields[2].GetUInt32(); // Actual Phasemask.
8982 pd.phaseId = fields[3].GetUInt32(); // From Phase.dbc, relation with changes, flags etc.
8983 pd.terrainswapmap = fields[4].GetUInt32(); // From Map.dbc, actual map chunk replaced ingame.
8984 pd.worldmaparea = fields[5].GetUInt32(); // From WorldMapArea.dbc, world map display changes (using M).
8985 pd.flags = fields[6].GetUInt8(); // Flags (Override, negate etc. - check PhaseMgr.h).
8986
8987 // Checks
8988 if ((pd.flags & PHASE_FLAG_OVERWRITE_EXISTING) && (pd.flags & PHASE_FLAG_NEGATE_PHASE))
8989 {
8990 sLog->outError(LOG_FILTER_SQL, "Flags defined in phase_definitions in zoneId %d and entry %u does contain PHASE_FLAG_OVERWRITE_EXISTING and PHASE_FLAG_NEGATE_PHASE. Setting flags to PHASE_FLAG_OVERWRITE_EXISTING", pd.zoneId, pd.entry);
8991 pd.flags &= ~PHASE_FLAG_NEGATE_PHASE;
8992 }
8993
8994 _PhaseDefinitionStore[pd.zoneId].push_back(pd);
8995
8996 ++count;
8997 } while (result->NextRow());
8998
8999 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u phasing definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
9000}
9001
9002void ObjectMgr::LoadSpellPhaseInfo()
9003{
9004 _SpellPhaseStore.clear();
9005
9006 uint32 oldMSTime = getMSTime();
9007
9008 // 0 1 2 3
9009 QueryResult result = WorldDatabase.Query("SELECT id, phasemask, terrainswapmap, worldmaparea FROM `spell_phase`");
9010
9011 if (!result)
9012 {
9013 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 spell dbc infos. DB table `spell_phase` is empty.");
9014 return;
9015 }
9016
9017 uint32 count = 0;
9018 do
9019 {
9020 Field* fields = result->Fetch();
9021
9022 SpellPhaseInfo spellPhaseInfo;
9023 spellPhaseInfo.spellId = fields[0].GetUInt32();
9024
9025 SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellPhaseInfo.spellId);
9026 if (!spell)
9027 {
9028 sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not exists, skipped.", spellPhaseInfo.spellId);
9029 continue;
9030 }
9031
9032 if (!spell->HasAura(SPELL_AURA_PHASE))
9033 {
9034 sLog->outError(LOG_FILTER_SQL, "Spell %u defined in `spell_phase` does not have aura effect type SPELL_AURA_PHASE, useless value.", spellPhaseInfo.spellId);
9035 continue;
9036 }
9037
9038 spellPhaseInfo.phasemask = fields[1].GetUInt32();
9039 spellPhaseInfo.terrainswapmap = fields[2].GetUInt32();
9040 spellPhaseInfo.worldmaparea = fields[3].GetUInt32();
9041
9042 _SpellPhaseStore[spellPhaseInfo.spellId] = spellPhaseInfo;
9043
9044 ++count;
9045 } while (result->NextRow());
9046 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u spell dbc infos in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
9047}
9048
9049
9050GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry)
9051{
9052 GameObjectTemplateContainer::const_iterator itr = _gameObjectTemplateStore.find(entry);
9053 if (itr != _gameObjectTemplateStore.end())
9054 return &(itr->second);
9055
9056 return NULL;
9057}
9058
9059CreatureTemplate const* ObjectMgr::GetCreatureTemplate(uint32 entry)
9060{
9061 CreatureTemplateContainer::const_iterator itr = _creatureTemplateStore.find(entry);
9062 if (itr != _creatureTemplateStore.end())
9063 return &(itr->second);
9064
9065 return NULL;
9066}
9067
9068VehicleAccessoryList const* ObjectMgr::GetVehicleAccessoryList(Vehicle* veh) const
9069{
9070 if (Creature* cre = veh->GetBase()->ToCreature())
9071 {
9072 // Give preference to GUID-based accessories
9073 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetDBTableGUIDLow());
9074 if (itr != _vehicleAccessoryStore.end())
9075 return &itr->second;
9076 }
9077
9078 // Otherwise return entry-based
9079 VehicleAccessoryContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
9080 if (itr != _vehicleTemplateAccessoryStore.end())
9081 return &itr->second;
9082 return NULL;
9083}
9084
9085void ObjectMgr::LoadResearchSiteZones()
9086{
9087 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, zone FROM research_site");
9088 if (!result)
9089 {
9090 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 research site zones. DB table `research_site` is empty.");
9091 return;
9092 }
9093
9094 uint32 counter = 0;
9095
9096 do
9097 {
9098 Field *fields = result->Fetch();
9099
9100 uint32 siteId = 0;
9101 uint32 mapId = 0;
9102 uint32 POIid = fields[0].GetUInt32();
9103 uint32 zoneId = fields[3].GetUInt16();
9104
9105 bool bFound = false;
9106 for (std::set<ResearchSiteEntry const*>::const_iterator itr = sResearchSiteSet.begin(); itr != sResearchSiteSet.end(); ++itr)
9107 if ((*itr)->POIid == POIid)
9108 {
9109 bFound = true;
9110 siteId = (*itr)->ID;
9111 mapId = (*itr)->mapId;
9112 break;
9113 }
9114 if (!bFound)
9115 continue;
9116
9117 ResearchZoneEntry &ptr = _researchZoneMap[siteId];
9118 ptr.coords.push_back(ResearchPOIPoint(fields[1].GetInt32(), fields[2].GetInt32()));
9119 ptr.map = mapId;
9120 ptr.zone = zoneId;
9121 ptr.level = 0;
9122 for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i)
9123 {
9124 AreaTableEntry const* area = sAreaStore.LookupEntry(i);
9125 if (!area)
9126 continue;
9127
9128 if (area->mapid == ptr.map && area->zone == ptr.zone)
9129 {
9130 ptr.level = area->area_level;
9131 break;
9132 }
9133 }
9134 ++counter;
9135 } while (result->NextRow());
9136
9137 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u research site zones.", counter);
9138}
9139
9140void ObjectMgr::LoadResearchSiteLoot()
9141{
9142 QueryResult result = WorldDatabase.Query("SELECT site_id, x, y, z, race FROM research_loot");
9143 if (!result)
9144 {
9145 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 research loot. DB table `research_loot` is empty.");
9146 return;
9147 }
9148
9149 uint32 counter = 0;
9150
9151 do
9152 {
9153 ResearchLootEntry dg;
9154 {
9155 Field *fields = result->Fetch();
9156
9157 dg.id = uint16(fields[0].GetUInt32());
9158 dg.x = fields[1].GetFloat();
9159 dg.y = fields[2].GetFloat();
9160 dg.z = fields[3].GetFloat();
9161 dg.race = fields[4].GetUInt8();
9162 }
9163
9164 _researchLoot.push_back(dg);
9165
9166 ++counter;
9167 } while (result->NextRow());
9168
9169 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u research site loot.", counter);
9170}
9171
9172void ObjectMgr::LoadSkipUpdateZone()
9173{
9174 skipData.clear();
9175
9176 _skipUpdateCount = ConfigMgr::GetIntDefault("ZoneSkipUpdate.count", 1);
9177
9178 QueryResult result = WorldDatabase.PQuery("SELECT zone FROM zone_skip_update");
9179 if (!result)
9180 return;
9181
9182 uint32 count = 0;
9183
9184 do
9185 {
9186 Field* fields = result->Fetch();
9187 uint32 zoneId = fields[0].GetUInt32();
9188 skipData[zoneId] = true;
9189 count++;
9190 } while (result->NextRow());
9191
9192 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u zone skip update.", count);
9193}
9194
9195void ObjectMgr::RestructCreatureGUID(uint32 nbLigneToRestruct)
9196{
9197 QueryResult result = WorldDatabase.PQuery("SELECT guid FROM creature ORDER BY guid DESC LIMIT %u;", nbLigneToRestruct);
9198
9199 if (!result)
9200 {
9201 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Soucis lors du select de la fonction 'restructCreatureGUID' (nombre de lignes : %u)", nbLigneToRestruct);
9202 return;
9203 }
9204
9205 std::vector<uint32> guidList;
9206
9207 do
9208 {
9209 Field *fields = result->Fetch();
9210 guidList.push_back(fields[0].GetUInt32());
9211 } while (result->NextRow());
9212
9213 uint32 upperGUID = 0;
9214 uint32 lowerGUID = 0;
9215
9216 std::map<uint32, uint32> newGUIDList;
9217
9218 for (int32 i = guidList.size() - 2; i >= 0; --i)
9219 {
9220 upperGUID = guidList[i];
9221 lowerGUID = guidList[i + 1];
9222
9223 if (upperGUID != lowerGUID + 1)
9224 {
9225 newGUIDList[upperGUID] = lowerGUID + 1;
9226 guidList[i] = lowerGUID + 1;
9227 }
9228 }
9229
9230 uint32 oldGUID = 0;
9231 uint32 newGUID = 0;
9232
9233 SQLTransaction worldTrans = WorldDatabase.BeginTransaction();
9234
9235 for (std::map<uint32, uint32>::iterator Itr = newGUIDList.begin(); Itr != newGUIDList.end(); ++Itr)
9236 {
9237 oldGUID = Itr->first;
9238 newGUID = Itr->second;
9239
9240 // World Database
9241 std::ostringstream creature_ss;
9242 creature_ss << "UPDATE creature SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9243 worldTrans->Append(creature_ss.str().c_str());
9244
9245 std::ostringstream addon_ss;
9246 addon_ss << "UPDATE creature_addon SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9247 worldTrans->Append(addon_ss.str().c_str());
9248
9249 std::ostringstream formation1_ss;
9250 formation1_ss << "UPDATE creature_formations SET leaderGUID = " << newGUID << " WHERE leaderGUID = " << oldGUID << "; ";
9251 worldTrans->Append(formation1_ss.str().c_str());
9252
9253 std::ostringstream formation2_ss;
9254 formation2_ss << "UPDATE creature_formations SET memberGUID = " << newGUID << " WHERE memberGUID = " << oldGUID << "; ";
9255 worldTrans->Append(formation2_ss.str().c_str());
9256
9257 std::ostringstream transport_ss;
9258 transport_ss << "UPDATE creature_transport SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9259 worldTrans->Append(transport_ss.str().c_str());
9260
9261 std::ostringstream game_event_ss;
9262 game_event_ss << "UPDATE game_event_creature SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9263 worldTrans->Append(game_event_ss.str().c_str());
9264
9265 std::ostringstream pool_ss;
9266 pool_ss << "UPDATE pool_creature SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9267 worldTrans->Append(pool_ss.str().c_str());
9268 }
9269
9270 std::ostringstream increment_ss;
9271 // Le dernier newGUID est le plus haut
9272 increment_ss << "ALTER TABLE creature AUTO_INCREMENT = " << newGUID << ";";
9273 worldTrans->Append(increment_ss.str().c_str());
9274
9275 WorldDatabase.CommitTransaction(worldTrans);
9276
9277 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "%u guids were reassigned.", nbLigneToRestruct);
9278}
9279
9280void ObjectMgr::RestructGameObjectGUID(uint32 nbLigneToRestruct)
9281{
9282 QueryResult result = WorldDatabase.PQuery("SELECT guid FROM gameobject ORDER BY guid DESC LIMIT %u;", nbLigneToRestruct);
9283
9284 if (!result)
9285 {
9286 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Soucis lors du select de la fonction 'RestructGameObjectGUID' (nombre de lignes : %u)", nbLigneToRestruct);
9287 return;
9288 }
9289
9290 std::vector<uint32> guidList;
9291
9292 do
9293 {
9294 Field *fields = result->Fetch();
9295 guidList.push_back(fields[0].GetUInt32());
9296 } while (result->NextRow());
9297
9298 uint32 upperGUID = 0;
9299 uint32 lowerGUID = 0;
9300
9301 std::map<uint32, uint32> newGUIDList;
9302
9303 for (int32 i = guidList.size() - 2; i >= 0; --i)
9304 {
9305 upperGUID = guidList[i];
9306 lowerGUID = guidList[i + 1];
9307
9308 if (upperGUID != lowerGUID + 1)
9309 {
9310 newGUIDList[upperGUID] = lowerGUID + 1;
9311 guidList[i] = lowerGUID + 1;
9312 }
9313 }
9314
9315 uint32 oldGUID = 0;
9316 uint32 newGUID = 0;
9317
9318 SQLTransaction worldTrans = WorldDatabase.BeginTransaction();
9319
9320 for (std::map<uint32, uint32>::iterator Itr = newGUIDList.begin(); Itr != newGUIDList.end(); ++Itr)
9321 {
9322 oldGUID = Itr->first;
9323 newGUID = Itr->second;
9324
9325 // World Database
9326 std::ostringstream gameobject_ss;
9327 gameobject_ss << "UPDATE gameobject SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9328 worldTrans->Append(gameobject_ss.str().c_str());
9329
9330 std::ostringstream game_event_ss;
9331 game_event_ss << "UPDATE game_event_gameobject SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9332 worldTrans->Append(game_event_ss.str().c_str());
9333
9334 std::ostringstream pool_ss;
9335 pool_ss << "UPDATE pool_gameobject SET guid = " << newGUID << " WHERE guid = " << oldGUID << "; ";
9336 worldTrans->Append(pool_ss.str().c_str());
9337 }
9338
9339 std::ostringstream increment_ss;
9340 // Le dernier newGUID est le plus haut
9341 increment_ss << "ALTER TABLE creature AUTO_INCREMENT = " << newGUID << ";";
9342 worldTrans->Append(increment_ss.str().c_str());
9343
9344 WorldDatabase.CommitTransaction(worldTrans);
9345
9346 sLog->outInfo(LOG_FILTER_SERVER_LOADING, "%u guids were reassigned.", nbLigneToRestruct);
9347}
9348
9349void ObjectMgr::LoadItemExtendedCost()
9350{
9351 QueryResult result = WorldDatabase.PQuery("SELECT ID, RequiredArenaSlot, RequiredItem1, RequiredItem2, RequiredItem3, RequiredItem4, RequiredItem5, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5,RequiredPersonalArenaRating, RequiredCurrency1, RequiredCurrency2, RequiredCurrency3, RequiredCurrency4, RequiredCurrency5, RequiredCurrencyCount1, RequiredCurrencyCount2, RequiredCurrencyCount3, RequiredCurrencyCount4, RequiredCurrencyCount5 FROM item_extended_cost");
9352
9353 if (!result)
9354 {
9355 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 item extended cost info. DB table `item_extended_cost` is empty.");
9356 return;
9357 }
9358
9359 uint32 counter = 0;
9360
9361 do
9362 {
9363 Field* field = result->Fetch();
9364 int index = 0;
9365 counter++;
9366
9367 ItemExtendedCostEntry* extendedCost = new ItemExtendedCostEntry();
9368 extendedCost->ID = field[index++].GetUInt32();
9369 extendedCost->RequiredArenaSlot = field[index++].GetUInt32();
9370
9371 for (uint32 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; i++)
9372 extendedCost->RequiredItem[i] = field[index++].GetUInt32();
9373
9374 for (uint32 i = 0; i < MAX_ITEM_EXT_COST_ITEMS; i++)
9375 extendedCost->RequiredItemCount[i] = field[index++].GetUInt32();
9376
9377 extendedCost->RequiredPersonalArenaRating = field[index++].GetUInt32();
9378
9379 for (uint32 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; i++)
9380 extendedCost->RequiredCurrency[i] = field[index++].GetUInt32();
9381
9382 for (uint32 i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; i++)
9383 extendedCost->RequiredCurrencyCount[i] = field[index++].GetUInt32();
9384
9385 sItemExtendedCostStore.EraseEntry(extendedCost->ID);
9386 sItemExtendedCostStore.AddEntry(extendedCost->ID, (const ItemExtendedCostEntry*)extendedCost);
9387 _overwriteExtendedCosts.insert(extendedCost->ID);
9388
9389 } while (result->NextRow());
9390
9391 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u item extended cost info.", counter);
9392}
9393
9394void ObjectMgr::LoadGuildChallengeRewardInfo()
9395{
9396 uint32 oldMSTime = getMSTime();
9397 QueryResult result = WorldDatabase.Query("SELECT Type, Experience, Gold, Gold2, Count FROM guild_challenge_reward");
9398 if (!result)
9399 {
9400 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 guild challenge reward data.");
9401 return;
9402 }
9403
9404 _challengeRewardData.reserve(result->GetRowCount());
9405
9406 uint32 count = 0;
9407
9408 do
9409 {
9410 Field* fields = result->Fetch();
9411
9412 uint32 type = fields[0].GetUInt32();
9413 if (type >= CHALLENGE_MAX)
9414 {
9415 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> guild_challenge_reward has unknown challenge type %u, skip.", type);
9416 continue;
9417 }
9418
9419 GuildChallengeReward reward;
9420 {
9421 reward.Experience = fields[1].GetUInt32();
9422 reward.Gold = fields[2].GetUInt32();
9423 reward.Gold2 = fields[3].GetUInt32();
9424 reward.ChallengeCount = fields[4].GetUInt32();
9425 }
9426
9427 _challengeRewardData.push_back(reward);
9428 ++count;
9429 } while (result->NextRow());
9430
9431 sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u guild challenge reward data in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
9432}