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