OregonCore  revision 3611e8a-git
Your Favourite TBC server
ObjectMgr.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "Common.h"
19 #include "Database/DatabaseEnv.h"
20 #include "Database/SQLStorage.h"
22 #include "PoolMgr.h"
23 #include "Log.h"
24 #include "MapManager.h"
25 #include "ObjectMgr.h"
26 #include "SpellMgr.h"
27 #include "UpdateMask.h"
28 #include "World.h"
29 #include "WorldSession.h"
30 #include "Group.h"
31 #include "Guild.h"
32 #include "ArenaTeam.h"
33 #include "Transports.h"
34 #include "Language.h"
35 #include "GameEventMgr.h"
36 #include "Spell.h"
37 #include "Chat.h"
38 #include "AccountMgr.h"
39 #include "InstanceSaveMgr.h"
40 #include "SpellAuras.h"
41 #include "Util.h"
42 #include "WaypointManager.h"
43 #include "GossipDef.h"
44 #include "DisableMgr.h"
45 
47 
55 
57 {
58  std::string res = "";
59  switch (type)
60  {
61  case SCRIPTS_QUEST_END:
62  res = "quest_end_scripts";
63  break;
65  res = "quest_start_scripts";
66  break;
67  case SCRIPTS_SPELL:
68  res = "spell_scripts";
69  break;
70  case SCRIPTS_GAMEOBJECT:
71  res = "gameobject_scripts";
72  break;
73  case SCRIPTS_EVENT:
74  res = "event_scripts";
75  break;
76  case SCRIPTS_WAYPOINT:
77  res = "waypoint_scripts";
78  break;
79  case SCRIPTS_GOSSIP:
80  res = "gossip_scripts";
81  break;
82  default:
83  break;
84  }
85  return res;
86 }
87 
89 {
90  ScriptMapMap* res = NULL;
91  switch (type)
92  {
93  case SCRIPTS_QUEST_END:
94  res = &sQuestEndScripts;
95  break;
97  res = &sQuestStartScripts;
98  break;
99  case SCRIPTS_SPELL:
100  res = &sSpellScripts;
101  break;
102  case SCRIPTS_GAMEOBJECT:
103  res = &sGameObjectScripts;
104  break;
105  case SCRIPTS_EVENT:
106  res = &sEventScripts;
107  break;
108  case SCRIPTS_WAYPOINT:
109  res = &sWaypointScripts;
110  break;
111  case SCRIPTS_GOSSIP:
112  res = &sGossipScripts;
113  break;
114  default:
115  break;
116  }
117  return res;
118 }
119 
121 {
122  std::string res = "";
123  switch (command)
124  {
125  case SCRIPT_COMMAND_TALK:
126  res = "SCRIPT_COMMAND_TALK";
127  break;
129  res = "SCRIPT_COMMAND_EMOTE";
130  break;
132  res = "SCRIPT_COMMAND_FIELD_SET";
133  break;
135  res = "SCRIPT_COMMAND_MOVE_TO";
136  break;
138  res = "SCRIPT_COMMAND_FLAG_SET";
139  break;
141  res = "SCRIPT_COMMAND_FLAG_REMOVE";
142  break;
144  res = "SCRIPT_COMMAND_TELEPORT_TO";
145  break;
147  res = "SCRIPT_COMMAND_QUEST_EXPLORED";
148  break;
150  res = "SCRIPT_COMMAND_KILL_CREDIT";
151  break;
153  res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT";
154  break;
156  res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE";
157  break;
159  res = "SCRIPT_COMMAND_OPEN_DOOR";
160  break;
162  res = "SCRIPT_COMMAND_CLOSE_DOOR";
163  break;
165  res = "SCRIPT_COMMAND_ACTIVATE_OBJECT";
166  break;
168  res = "SCRIPT_COMMAND_REMOVE_AURA";
169  break;
171  res = "SCRIPT_COMMAND_CAST_SPELL";
172  break;
174  res = "SCRIPT_COMMAND_PLAY_SOUND";
175  break;
177  res = "SCRIPT_COMMAND_CREATE_ITEM";
178  break;
180  res = "SCRIPT_COMMAND_DESPAWN_SELF";
181  break;
183  res = "SCRIPT_COMMAND_DO_NOTHING";
184  break;
186  res = "SCRIPT_COMMAND_LOAD_PATH";
187  break;
189  res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT";
190  break;
191  case SCRIPT_COMMAND_KILL:
192  res = "SCRIPT_COMMAND_KILL";
193  break;
194  // Oregon only
196  res = "SCRIPT_COMMAND_ORIENTATION";
197  break;
199  res = "SCRIPT_COMMAND_EQUIP";
200  break;
202  res = "SCRIPT_COMMAND_MODEL";
203  break;
205  res = "SCRIPT_COMMAND_CLOSE_GOSSIP";
206  break;
207  default:
208  {
209  char sz[32];
210  sprintf(sz, "Unknown command: %d", command);
211  res = sz;
212  break;
213  }
214  }
215  return res;
216 }
217 
218 std::string ScriptInfo::GetDebugInfo() const
219 {
220  char sz[256];
221  sprintf(sz, "%s ('%s' script id: %u)", GetScriptCommandName(command).c_str(), GetScriptsTableNameByType(type).c_str(), id);
222  return std::string(sz);
223 }
224 
225 bool normalizePlayerName(std::string& name)
226 {
227  if (name.empty())
228  return false;
229 
230  wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME + 1];
231  size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
232 
233  if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
234  return false;
235 
236  wstr_buf[0] = wcharToUpper(wstr_buf[0]);
237  for (size_t i = 1; i < wstr_len; ++i)
238  wstr_buf[i] = wcharToLower(wstr_buf[i]);
239 
240  if (!WStrToUtf8(wstr_buf, wstr_len, name))
241  return false;
242 
243  return true;
244 }
245 
247 {
248  { LANG_ADDON, 0, 0 },
249  { LANG_UNIVERSAL, 0, 0 },
250  { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
254  { LANG_COMMON, 668, SKILL_LANG_COMMON },
256  { LANG_TITAN, 816, SKILL_LANG_TITAN },
260  { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
261  { LANG_TROLL, 7341, SKILL_LANG_TROLL },
263  { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
264  { LANG_ZOMBIE, 0, 0 },
265  { LANG_GNOMISH_BINARY, 0, 0 },
266  { LANG_GOBLIN_BINARY, 0, 0 }
267 };
268 
270 {
271  for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
272  {
273  if (uint32(lang_description[i].lang_id) == lang)
274  return &lang_description[i];
275  }
276 
277  return NULL;
278 }
279 
281 {
282  m_hiCharGuid = 1;
283  m_hiCreatureGuid = 1;
284  m_hiPetGuid = 1;
285  m_hiItemGuid = 1;
286  m_hiGoGuid = 1;
287  m_hiDoGuid = 1;
288  m_hiCorpseGuid = 1;
289  m_hiPetNumber = 1;
290  m_ItemTextId = 1;
291  m_mailid = 1;
292  m_guildId = 1;
293  m_arenaTeamId = 1;
294  m_auctionid = 1;
295 
296  mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS);
297  mGuildBankTabPrice[0] = 100;
298  mGuildBankTabPrice[1] = 250;
299  mGuildBankTabPrice[2] = 500;
300  mGuildBankTabPrice[3] = 1000;
301  mGuildBankTabPrice[4] = 2500;
302  mGuildBankTabPrice[5] = 5000;
303 }
304 
306 {
307  for (QuestMap::iterator i = mQuestTemplates.begin(); i != mQuestTemplates.end(); ++i)
308  delete i->second;
309  mQuestTemplates.clear();
310 
311  for (GossipTextMap::iterator i = mGossipText.begin(); i != mGossipText.end(); ++i)
312  delete i->second;
313  mGossipText.clear();
314 
315  mAreaTriggers.clear();
316 
317  for (PetLevelInfoMap::iterator i = petInfo.begin(); i != petInfo.end(); ++i)
318  delete[] i->second;
319  petInfo.clear();
320 
321  // free only if loaded
322  for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
323  delete[] playerClassInfo[class_].levelInfo;
324 
325  for (int race = 0; race < MAX_RACES; ++race)
326  for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
327  delete[] playerInfo[race][class_].levelInfo;
328 
329  // free group and guild objects
330  for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
331  delete (*itr);
332 
333  for (GuildMap::iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
334  delete itr->second;
335  mGuildMap.clear();
336 
337  for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
338  delete itr->second;
339 
340  for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
341  itr->second.Clear();
342 
343  for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
344  itr->second.Clear();
345 }
346 
348 {
349  for (GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
350  if ((*itr)->GetLeaderGUID() == guid)
351  return *itr;
352 
353  return NULL;
354 }
355 
356 Guild* ObjectMgr::GetGuildById(const uint32 GuildId) const
357 {
358  GuildMap::const_iterator itr = mGuildMap.find(GuildId);
359  if (itr != mGuildMap.end())
360  return itr->second;
361 
362  return NULL;
363 }
364 
365 Guild* ObjectMgr::GetGuildByName(const std::string& guildname) const
366 {
367  for (GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
368  if (itr->second->GetName() == guildname)
369  return itr->second;
370 
371  return NULL;
372 }
373 
374 std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const
375 {
376  GuildMap::const_iterator itr = mGuildMap.find(GuildId);
377  if (itr != mGuildMap.end())
378  return itr->second->GetName();
379 
380  return "";
381 }
382 
384 {
385  for (GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
386  if (itr->second->GetLeader() == guid)
387  return itr->second;
388 
389  return NULL;
390 }
391 
393 {
394  mGuildMap[guild->GetId()] = guild;
395 }
396 
398 {
399  mGuildMap.erase(Id);
400 }
401 
403 {
404  ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid);
405  if (itr != mArenaTeamMap.end())
406  return itr->second;
407 
408  return NULL;
409 }
410 
411 ArenaTeam* ObjectMgr::GetArenaTeamByName(const std::string& arenateamname) const
412 {
413  for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
414  if (itr->second->GetName() == arenateamname)
415  return itr->second;
416 
417  return NULL;
418 }
419 
421 {
422  for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
423  if (itr->second->GetCaptain() == guid)
424  return itr->second;
425 
426  return NULL;
427 }
428 
430 {
431  mArenaTeamMap[arenaTeam->GetId()] = arenaTeam;
432 }
433 
435 {
436  mArenaTeamMap.erase(Id);
437 }
438 
440 {
442 }
443 
445 {
446  mCreatureLocaleMap.clear(); // need for reload case
447 
448  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
449 
450  if (!result)
451  {
452 
453 
454  sLog.outString(">> Loaded 0 creature locale strings. DB table locales_creature is empty.");
455  return;
456  }
457 
458 
459  do
460  {
461  Field* fields = result->Fetch();
462 
463  uint32 entry = fields[0].GetUInt32();
464 
465  CreatureLocale& data = mCreatureLocaleMap[entry];
466 
467  for (uint8 i = 1; i < MAX_LOCALE; ++i)
468  {
469  std::string str = fields[1 + 2 * (i - 1)].GetCppString();
470  if (!str.empty())
471  {
472  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
473  if (idx >= 0)
474  {
475  if (data.Name.size() <= idx)
476  data.Name.resize(idx + 1);
477 
478  data.Name[idx] = str;
479  }
480  }
481  str = fields[1 + 2 * (i - 1) + 1].GetCppString();
482  if (!str.empty())
483  {
484  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
485  if (idx >= 0)
486  {
487  if (data.SubName.size() <= idx)
488  data.SubName.resize(idx + 1);
489 
490  data.SubName[idx] = str;
491  }
492  }
493  }
494  }
495  while (result->NextRow());
496 
497  sLog.outString(">> Loaded %lu creature locale strings", mCreatureLocaleMap.size());
498 }
499 
501 {
502  mGossipMenuItemsLocaleMap.clear(); // need for reload case
503 
504  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT menu_id,id,"
505  "option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
506  "option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
507  "option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
508  "option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
509  "FROM locales_gossip_menu_option");
510 
511  if (!result)
512  {
513 
514 
515  sLog.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
516  return;
517  }
518 
519 
520  do
521  {
522  Field* fields = result->Fetch();
523 
524  uint16 menuId = fields[0].GetUInt16();
525  uint16 id = fields[1].GetUInt16();
526 
527  GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId, id)];
528 
529  for (uint8 i = 1; i < MAX_LOCALE; ++i)
530  {
531  std::string str = fields[1 + 2 * (i - 1)].GetCppString();
532  if (!str.empty())
533  {
534  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
535  if (idx >= 0)
536  {
537  if (data.OptionText.size() <= idx)
538  data.OptionText.resize(idx + 1);
539 
540  data.OptionText[idx] = str;
541  }
542  }
543  str = fields[1 + 2 * (i - 1) + 1].GetCppString();
544  if (!str.empty())
545  {
546  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
547  if (idx >= 0)
548  {
549  if (data.BoxText.size() <= idx)
550  data.BoxText.resize(idx + 1);
551 
552  data.BoxText[idx] = str;
553  }
554  }
555  }
556  }
557  while (result->NextRow());
558 
559  sLog.outString(">> Loaded %u gossip_menu_option locale strings", mGossipMenuItemsLocaleMap.size());
560 }
561 
562 struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader>
563 {
564  template<class D>
565  void convert_from_str(uint32 field_pos, char* src, D& dst)
566  {
567  dst = D(sObjectMgr.GetScriptId(src));
568  }
569 };
570 
572 {
573  SQLCreatureLoader loader;
574  loader.Load(sCreatureStorage);
575 
576  sLog.outString(">> Loaded %u creature definitions", sCreatureStorage.RecordCount);
577 
578  std::set<uint32> heroicEntries; // already loaded heroic value in creatures
579  std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
580 
581  // check data correctness
582  for (uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
583  {
585  if (!cInfo)
586  continue;
587 
588  if (cInfo->HeroicEntry)
589  {
590  CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
591  if (!heroicInfo)
592  {
593  sLog.outErrorDb("Creature (Entry: %u) has heroic_entry=%u but creature entry %u does not exist.", cInfo->Entry, cInfo->HeroicEntry, cInfo->HeroicEntry);
594  continue;
595  }
596 
597  if (heroicEntries.find(i) != heroicEntries.end())
598  {
599  sLog.outErrorDb("Creature (Entry: %u) listed as heroic but has value in heroic_entry.", i);
600  continue;
601  }
602 
603  if (heroicEntries.find(cInfo->HeroicEntry) != heroicEntries.end())
604  {
605  sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.", cInfo->HeroicEntry);
606  continue;
607  }
608 
609  if (hasHeroicEntries.find(cInfo->HeroicEntry) != hasHeroicEntries.end())
610  {
611  sLog.outErrorDb("Creature (Entry: %u) has heroic_entry=%u but creature entry %u also has heroic entry.", i, cInfo->HeroicEntry, cInfo->HeroicEntry);
612  continue;
613  }
614 
615  if (cInfo->unit_class != heroicInfo->unit_class)
616  {
617  sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in heroic mode (Entry: %u, class %u).",i, cInfo->unit_class, cInfo->HeroicEntry, heroicInfo->unit_class);
618  continue;
619  }
620 
621  if (cInfo->npcflag != heroicInfo->npcflag)
622  {
623  sLog.outErrorDb("Creature (Entry: %u) listed in creature_template_substitution has different npcflag in heroic mode.", i);
624  continue;
625  }
626 
627  if (cInfo->classNum != heroicInfo->classNum)
628  {
629  sLog.outErrorDb("Creature (Entry: %u) listed in creature_template_substitution has different classNum in heroic mode.", i);
630  continue;
631  }
632 
633  if (cInfo->race != heroicInfo->race)
634  {
635  sLog.outErrorDb("Creature (Entry: %u) listed in creature_template_substitution has different race in heroic mode.", i);
636  continue;
637  }
638 
639  if (cInfo->trainer_type != heroicInfo->trainer_type)
640  {
641  sLog.outErrorDb("Creature (Entry: %u) listed in creature_template_substitution has different trainer_type in heroic mode.", i);
642  continue;
643  }
644 
645  if (cInfo->trainer_spell != heroicInfo->trainer_spell)
646  {
647  sLog.outErrorDb("Creature (Entry: %u) listed in creature_template_substitution has different trainer_spell in heroic mode.", i);
648  continue;
649  }
650 
651  hasHeroicEntries.insert(i);
652  heroicEntries.insert(cInfo->HeroicEntry);
653  }
654 
655  FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
656  if (!factionTemplate)
657  sLog.outErrorDb("Creature (Entry: %u) has invalid faction template (%u)", cInfo->Entry, cInfo->faction);
658 
659  // check model ids, supplying and sending non-existent ids to the client might crash them
661  {
662  sLog.outErrorDb("Creature (Entry: %u) has invalid modelid1 (%u), setting it to 0", cInfo->Entry, cInfo->modelid1);
663  const_cast<CreatureInfo*>(cInfo)->modelid1 = 0;
664  }
666  {
667  sLog.outErrorDb("Creature (Entry: %u) has invalid modelid2 (%u), setting it to 0", cInfo->Entry, cInfo->modelid2);
668  const_cast<CreatureInfo*>(cInfo)->modelid2 = 0;
669  }
671  {
672  sLog.outErrorDb("Creature (Entry: %u) has invalid modelid3 (%u), setting it to 0", cInfo->Entry, cInfo->modelid3);
673  const_cast<CreatureInfo*>(cInfo)->modelid3 = 0;
674  }
676  {
677  sLog.outErrorDb("Creature (Entry: %u) has invalid modelid4 (%u), setting it to 0", cInfo->Entry, cInfo->modelid4);
678  const_cast<CreatureInfo*>(cInfo)->modelid4 = 0;
679  }
680 
681  for (int k = 0; k < MAX_KILL_CREDIT; ++k)
682  {
683  if (cInfo->KillCredit[k])
684  {
685  if (!GetCreatureTemplate(cInfo->KillCredit[k]))
686  {
687  sLog.outErrorDb("Creature (Entry: %u) has not existed creature entry in `KillCredit%d` (%u)", cInfo->Entry, k + 1, cInfo->KillCredit[k]);
688  const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0;
689  }
690  }
691  }
692 
693  if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
694  {
695  sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class (%u) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
696  const_cast<CreatureInfo*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
697  }
698 
699  if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
700  {
701  sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in dmgschool", cInfo->Entry, cInfo->dmgschool);
702  const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
703  }
704 
705  if (cInfo->baseattacktime == 0)
706  const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
707 
708  if (cInfo->rangeattacktime == 0)
709  const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
710 
711  if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
712  sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u", cInfo->Entry, cInfo->trainer_type);
713 
714  if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
715  {
716  sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in InhabitType, creature will not correctly walk/swim/fly", cInfo->Entry, cInfo->InhabitType);
717  const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
718  }
719 
720  if (cInfo->PetSpellDataId)
721  {
722  CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
723  if (!spellDataId)
724  sLog.outErrorDb("Creature (Entry: %u) has invalid PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
725  }
726 
727  for (int i = 0; i < CREATURE_MAX_SPELLS; ++i)
728  {
729  if (cInfo->spells[i] && !sSpellStore.LookupEntry(cInfo->spells[i]))
730  {
731  sLog.outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0", cInfo->Entry, i + 1, cInfo->spells[i]);
732  const_cast<CreatureInfo*>(cInfo)->spells[i] = 0;
733  }
734  }
735 
736  if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
737  {
738  sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.", cInfo->Entry, cInfo->MovementType);
739  const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
740  }
741 
742  if (cInfo->equipmentId > 0) // 0 no equipment
743  {
744  if (!GetEquipmentInfo(cInfo->equipmentId) && !GetEquipmentInfoRaw(cInfo->equipmentId))
745  {
746  sLog.outErrorDb("Table creature_template has creature (Entry: %u) with equipment_id %u not found in table creature_equip_template, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
747  const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
748  }
749  }
750 
751  // if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
752  if (cInfo->scale <= 0.0f)
753  {
754  uint32 modelid = cInfo->GetFirstValidModelId();
755  CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(modelid);
756  const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f;
757  }
758  }
759 }
760 
761 void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
762 {
763  // Now add the auras, format "spellid effectindex spellid effectindex..."
764  char* p, *s;
765  std::vector<int> val;
766  s = p = (char*)reinterpret_cast<char const*>(addon->auras);
767  if (p)
768  {
769  while (p[0] != 0)
770  {
771  ++p;
772  if (p[0] == ' ')
773  {
774  val.push_back(atoi(s));
775  s = ++p;
776  }
777  }
778  if (p != s)
779  val.push_back(atoi(s));
780 
781  // free char* loaded memory
782  delete[] (char*)reinterpret_cast<char const*>(addon->auras);
783 
784  // wrong list
785  if (val.size() % 2)
786  {
787  addon->auras = NULL;
788  sLog.outErrorDb("Creature (%s: %u) has wrong auras data in %s.", guidEntryStr, addon->guidOrEntry, table);
789  return;
790  }
791  }
792 
793  // empty list
794  if (val.empty())
795  {
796  addon->auras = NULL;
797  return;
798  }
799 
800  // replace by new structures array
801  const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size() / 2 + 1];
802 
803  uint32 i = 0;
804  for (size_t j = 0; j < val.size() / 2; ++j)
805  {
806  CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
807  cAura.spell_id = (uint32)val[2 * j + 0];
808  cAura.effect_idx = (uint32)val[2 * j + 1];
809  if (cAura.effect_idx > 2)
810  {
811  sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in auras field in %s.", guidEntryStr, addon->guidOrEntry, cAura.effect_idx, cAura.spell_id, table);
812  continue;
813  }
814  SpellEntry const* AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
815  if (!AdditionalSpellInfo)
816  {
817  sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in auras field in %s.", guidEntryStr, addon->guidOrEntry, cAura.spell_id, table);
818  continue;
819  }
820 
821  if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
822  {
823  sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in auras field in %s.", guidEntryStr, addon->guidOrEntry, cAura.effect_idx, cAura.spell_id, table);
824  continue;
825  }
826 
827  ++i;
828  }
829 
830  // fill terminator element (after last added)
831  CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
832  endAura.spell_id = 0;
833  endAura.effect_idx = 0;
834 }
835 
837 {
839 
840  sLog.outString(">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount);
841 
842  // check data correctness and convert 'auras'
843  for (uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
844  {
846  if (!addon)
847  continue;
848 
849  if (!sEmotesStore.LookupEntry(addon->emote))
850  sLog.outErrorDb("Creature (GUID: %u) has invalid emote (%u) defined in creature_template_addon.", addon->guidOrEntry, addon->emote);
851 
852  ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry");
853 
855  sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in creature_template_addon", addon->guidOrEntry);
856  }
857 
859 
860  sLog.outString(">> Loaded %u creature addons", sCreatureDataAddonStorage.RecordCount);
861 
862  // check data correctness and convert 'auras'
863  for (uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
864  {
866  if (!addon)
867  continue;
868 
869  if (!sEmotesStore.LookupEntry(addon->emote))
870  sLog.outErrorDb("Creature (GUID: %u) has invalid emote (%u) defined in creature_addon.", addon->guidOrEntry, addon->emote);
871 
872  ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow");
873 
874  if (mCreatureDataMap.find(addon->guidOrEntry) == mCreatureDataMap.end())
875  sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in creature_addon", addon->guidOrEntry);
876  }
877 }
878 
880 {
881  // initialize data array
882  memset(&m_creatureClassLvlStats, 0, sizeof(m_creatureClassLvlStats));
883 
884  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basemana, basearmor, attackpower, rangedattackpower, basedamage_exp0, basedamage_exp1 FROM creature_classlevelstats");
885 
886  if (!result)
887  {
888  sLog.outErrorDb(">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
889  return;
890  }
891 
892  uint32 count = 0;
893  do
894  {
895  Field* fields = result->Fetch();
896 
897  uint32 creatureClass = fields[1].GetUInt32();
898  uint32 creatureLevel = fields[0].GetUInt32();
899 
900  if (creatureLevel == 0 || creatureLevel > DEFAULT_MAX_CREATURE_LEVEL)
901  {
902  sLog.outErrorDb("Found stats for creature level %u, incorrect level for this core. Skipped.", creatureLevel);
903  continue;
904  }
905 
906  if (((1 << (creatureClass - 1)) & CLASSMASK_ALL_CREATURES) == 0)
907  {
908  sLog.outErrorDb("Creature base stats for level %u has invalid class %u. Skipped.", creatureLevel, creatureClass);
909  continue;
910  }
911 
912  uint32 baseMana = fields[4].GetUInt32();
913  uint32 baseArmor = fields[5].GetUInt32();
914  float baseMeleeAttackPower = fields[6].GetFloat();
915  float baseRangedAttackPower = fields[7].GetFloat();
916 
917 
918  for (int i = 0; i <= MAX_EXPANSION; ++i)
919  {
920  CreatureBaseStats &cCLS = m_creatureClassLvlStats[creatureLevel][classToIndex[creatureClass]][i];
921 
922  cCLS.BaseMana = baseMana;
923  cCLS.BaseMeleeAttackPower = baseMeleeAttackPower;
924  cCLS.BaseRangedAttackPower = baseRangedAttackPower;
925  cCLS.BaseArmor = baseArmor;
926 
927  cCLS.BaseHealth = fields[2 + (i * 1)].GetUInt32();
928  cCLS.BaseDamage = fields[8 + (i * 1)].GetFloat();
929  }
930  ++count;
931  }
932  while (result->NextRow());
933 
934  sLog.outString(">> Loaded %u creature base stats", count);
935 }
936 
938 {
939  if (expansion < 0)
940  return NULL;
941 
942  CreatureBaseStats const* cCLS = &m_creatureClassLvlStats[level][classToIndex[unitClass]][expansion];
943 
944  if (cCLS->BaseHealth != 0 && cCLS->BaseDamage != 0.0f)
945  return cCLS;
946 
947  return NULL;
948 }
949 
951 {
953 }
954 
956 {
958 }
959 
961 {
963 
964  for (uint32 i = 0; i < sEquipmentStorage.MaxEntry; ++i)
965  {
967 
968  if (!eqInfo)
969  continue;
970 
971  for (uint8 j = 0; j < 3; ++j)
972  {
973  if (!eqInfo->equipentry[j])
974  continue;
975 
976  ItemTemplate const* itemProto = GetItemTemplate(eqInfo->equipentry[j]);
977  if (!itemProto)
978  {
979  sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j + 1, i);
980  const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
981  continue;
982  }
983 
984  if (itemProto->InventoryType != INVTYPE_WEAPON &&
985  itemProto->InventoryType != INVTYPE_SHIELD &&
986  itemProto->InventoryType != INVTYPE_RANGED &&
987  itemProto->InventoryType != INVTYPE_2HWEAPON &&
988  itemProto->InventoryType != INVTYPE_WEAPONMAINHAND &&
989  itemProto->InventoryType != INVTYPE_WEAPONOFFHAND &&
990  itemProto->InventoryType != INVTYPE_HOLDABLE &&
991  itemProto->InventoryType != INVTYPE_THROWN &&
992  itemProto->InventoryType != INVTYPE_RANGEDRIGHT &&
993  itemProto->InventoryType != INVTYPE_RELIC)
994  {
995  sLog.outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo->equipentry[j], j + 1, i);
996  const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
997  }
998  }
999  }
1000 
1001  sLog.outString(">> Loaded %u equipment template", sEquipmentStorage.RecordCount);
1002 
1004  for (uint32 i = 1; i < sEquipmentStorageRaw.MaxEntry; ++i)
1007  sLog.outErrorDb("Table 'creature_equip_template_raw` have redundant data for ID %u ('creature_equip_template` already have data)", i);
1008 
1009  sLog.outString( ">> Loaded %u equipment template (deprecated format)", sEquipmentStorageRaw.RecordCount );
1010 }
1011 
1013 {
1015 }
1016 
1018 {
1019  // Load creature model (display id)
1020  uint32 display_id = 0;
1021 
1022  if (!data || data->displayid == 0)
1023  display_id = cinfo->GetRandomValidModelId();
1024  else
1025  display_id = data->displayid;
1026 
1027  return display_id;
1028 }
1029 
1031 {
1032  CreatureModelInfo const* minfo = GetCreatureModelInfo(display_id);
1033  if (!minfo)
1034  return NULL;
1035 
1036  // If a model for another gender exists, 50% chance to use it
1037  if (minfo->modelid_other_gender != 0 && urand(0, 1) == 0)
1038  {
1039  CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
1040  if (!minfo_tmp)
1041  {
1042  sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table creature_model_info. ", minfo->modelid, minfo->modelid_other_gender);
1043  return minfo; // not fatal, just use the previous one
1044  }
1045  else
1046  return minfo_tmp;
1047  }
1048  else
1049  return minfo;
1050 }
1051 
1053 {
1055 
1056  sLog.outString(">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount);
1057 
1058  // check if combat_reach is valid
1059  for (uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
1060  {
1062  if (!mInfo)
1063  continue;
1064 
1065  if (mInfo->combat_reach < 0.1f)
1066  {
1067  //sLog.outErrorDb("Creature model (Entry: %u) has invalid combat reach (%f), setting it to 0.5", mInfo->modelid, mInfo->combat_reach);
1068  const_cast<CreatureModelInfo*>(mInfo)->combat_reach = DEFAULT_COMBAT_REACH;
1069  }
1070  }
1071 }
1072 
1074 {
1075  const CreatureData* const slave = GetCreatureData(guid);
1076  const CreatureData* const master = GetCreatureData(linkedGuid);
1077 
1078  if (!slave || !master) // they must have a corresponding entry in db
1079  {
1080  sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' which doesn't exist", guid, linkedGuid);
1081  return false;
1082  }
1083 
1084  const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
1085 
1086  if (master->mapid != slave->mapid // link only to same map
1087  && (!map || map->Instanceable())) // or to unistanced world
1088  {
1089  sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' on an unpermitted map", guid, linkedGuid);
1090  return false;
1091  }
1092 
1093  if (!(master->spawnMask & slave->spawnMask) // they must have a possibility to meet (normal/heroic difficulty)
1094  && (!map || map->Instanceable()))
1095  {
1096  sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guid, linkedGuid);
1097  return false;
1098  }
1099 
1100  return true;
1101 }
1102 
1104 {
1105  mCreatureLinkedRespawnMap.clear();
1106  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT guid, linkedGuid FROM creature_linked_respawn ORDER BY guid ASC");
1107 
1108  if (!result)
1109  {
1110 
1111 
1112  sLog.outErrorDb(">> Loaded 0 linked respawns. DB table creature_linked_respawn is empty.");
1113  return;
1114  }
1115 
1116 
1117  do
1118  {
1119  Field* fields = result->Fetch();
1120 
1121  uint32 guid = fields[0].GetUInt32();
1122  uint32 linkedGuid = fields[1].GetUInt32();
1123 
1124  if (CheckCreatureLinkedRespawn(guid, linkedGuid))
1125  mCreatureLinkedRespawnMap[guid] = linkedGuid;
1126 
1127  }
1128  while (result->NextRow());
1129 
1130  sLog.outString(">> Loaded %lu linked respawns", mCreatureLinkedRespawnMap.size());
1131 }
1132 
1134 {
1135  if (!guid)
1136  return false;
1137 
1138  if (!linkedGuid) // we're removing the linking
1139  {
1140  mCreatureLinkedRespawnMap.erase(guid);
1141  WorldDatabase.DirectPExecute("DELETE FROM creature_linked_respawn WHERE guid = '%u'", guid);
1142  return true;
1143  }
1144 
1145  if (CheckCreatureLinkedRespawn(guid, linkedGuid)) // we add/change linking
1146  {
1147  mCreatureLinkedRespawnMap[guid] = linkedGuid;
1148  WorldDatabase.DirectPExecute("REPLACE INTO creature_linked_respawn(guid,linkedGuid) VALUES ('%u','%u')", guid, linkedGuid);
1149  return true;
1150  }
1151  return false;
1152 }
1153 
1155 {
1156  uint32 oldMSTime = getMSTime();
1157 
1158  _tempSummonDataStore.clear(); // needed for reload case
1159 
1160  // 0 1 2 3 4 5 6 7 8 9
1161  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
1162 
1163  if (!result)
1164  {
1165  sLog.outString(">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
1166  return;
1167  }
1168 
1169  uint32 count = 0;
1170  do
1171  {
1172  Field* fields = result->Fetch();
1173 
1174  uint32 summonerId = fields[0].GetUInt32();
1175  SummonerType summonerType = SummonerType(fields[1].GetUInt8());
1176  uint8 group = fields[2].GetUInt8();
1177 
1178  switch (summonerType)
1179  {
1181  if (!GetCreatureTemplate(summonerId))
1182  {
1183  sLog.outError("Table `creature_summon_groups` has summoner with non existing entry %u for creature summoner type, skipped.", summonerId);
1184  continue;
1185  }
1186  break;
1188  if (!GetGameObjectInfo(summonerId))
1189  {
1190  sLog.outError("Table `creature_summon_groups` has summoner with non existing entry %u for gameobject summoner type, skipped.", summonerId);
1191  continue;
1192  }
1193  break;
1194  case SUMMONER_TYPE_MAP:
1195  if (!sMapStore.LookupEntry(summonerId))
1196  {
1197  sLog.outError("Table `creature_summon_groups` has summoner with non existing entry %u for map summoner type, skipped.", summonerId);
1198  continue;
1199  }
1200  break;
1201  default:
1202  sLog.outError("Table `creature_summon_groups` has unhandled summoner type %u for summoner %u, skipped.", summonerType, summonerId);
1203  continue;
1204  }
1205 
1206  TempSummonData data;
1207  data.entry = fields[3].GetUInt32();
1208 
1209  if (!GetCreatureTemplate(data.entry))
1210  {
1211  sLog.outError("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);
1212  continue;
1213  }
1214 
1215  float posX = fields[4].GetFloat();
1216  float posY = fields[5].GetFloat();
1217  float posZ = fields[6].GetFloat();
1218  float orientation = fields[7].GetFloat();
1219 
1220  data.pos.Relocate(posX, posY, posZ, orientation);
1221 
1222  data.type = TempSummonType(fields[8].GetUInt8());
1223 
1224  if (data.type > TEMPSUMMON_MANUAL_DESPAWN)
1225  {
1226  sLog.outError("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);
1227  continue;
1228  }
1229 
1230  data.time = fields[9].GetUInt32();
1231 
1232  TempSummonGroupKey key(summonerId, summonerType, group);
1233  _tempSummonDataStore[key].push_back(data);
1234 
1235  ++count;
1236 
1237  } while (result->NextRow());
1238 
1239  sLog.outString(">> Loaded %u temp summons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
1240 }
1241 
1243 {
1244  uint32 count = 0;
1245  // 0 1 2 3
1246  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
1247  //4 5 6 7 8 9 10 11
1248  "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
1249  //12 13 14 15 16 17
1250  "curhealth, curmana, MovementType, spawnMask, event, pool_entry "
1251  "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1252  "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
1253 
1254  if (!result)
1255  {
1256 
1257 
1258  sLog.outErrorDb(">> Loaded 0 creature. DB table creature is empty.");
1259  return;
1260  }
1261 
1262  // build single time for check creature data
1263  std::set<uint32> heroicCreatures;
1264  for (uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
1265  if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1266  if (cInfo->HeroicEntry)
1267  heroicCreatures.insert(cInfo->HeroicEntry);
1268 
1269 
1270  do
1271  {
1272  Field* fields = result->Fetch();
1273 
1274  uint32 guid = fields[ 0].GetUInt32();
1275  uint32 entry = fields[ 1].GetUInt32();
1276 
1277  CreatureInfo const* cInfo = GetCreatureTemplate(entry);
1278  if (!cInfo)
1279  {
1280  sLog.outErrorDb("Table `creature` has creature (GUID: %u) with invalid creature entry %u, skipped.", guid, entry);
1281  continue;
1282  }
1283 
1284  CreatureData& data = mCreatureDataMap[guid];
1285 
1286  data.id = entry;
1287  data.mapid = fields[ 2].GetUInt32();
1288  data.displayid = fields[ 3].GetUInt32();
1289  data.equipmentId = fields[ 4].GetUInt32();
1290  data.posX = fields[ 5].GetFloat();
1291  data.posY = fields[ 6].GetFloat();
1292  data.posZ = fields[ 7].GetFloat();
1293  data.orientation = fields[ 8].GetFloat();
1294  data.spawntimesecs = fields[ 9].GetUInt32();
1295  data.spawndist = fields[10].GetFloat();
1296  data.currentwaypoint = fields[11].GetUInt32();
1297  data.curhealth = fields[12].GetUInt32();
1298  data.curmana = fields[13].GetUInt32();
1299  data.movementType = fields[14].GetUInt8();
1300  data.spawnMask = fields[15].GetUInt8();
1301  int16 gameEvent = fields[16].GetInt16();
1302  int32 PoolId = fields[17].GetInt32();
1303 
1304  MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
1305  if (!mapEntry)
1306  {
1307  sLog.outErrorDb("Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
1308  continue;
1309  }
1310 
1311  if (heroicCreatures.find(data.id) != heroicCreatures.end())
1312  {
1313  sLog.outErrorDb("Table creature has creature (GUID: %u, Entry %u) that is listed as heroic template in creature_template_substitution, skipped.", guid, data.id);
1314  continue;
1315  }
1316 
1317  if (data.equipmentId > 0) // -1 no equipment, 0 use default
1318  {
1319  if (!GetEquipmentInfo(data.equipmentId) && !GetEquipmentInfoRaw(data.equipmentId))
1320  {
1321  sLog.outErrorDb("Table creature has creature (Entry: %u) with equipment_id %u not found in table creature_equip_template or creature_equip_template, set to no equipment.", data.id, data.equipmentId);
1322  data.equipmentId = -1;
1323  }
1324  }
1325 
1327  {
1328  if (!mapEntry || !mapEntry->IsDungeon())
1329  sLog.outErrorDb("Table `creature` has creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature is not in instance.", guid, data.id);
1330  }
1331 
1332  if (data.spawndist < 0.0f)
1333  {
1334  sLog.outErrorDb("Table `creature` has creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
1335  data.spawndist = 0.0f;
1336  }
1337  else if (data.movementType == RANDOM_MOTION_TYPE)
1338  {
1339  if (data.spawndist == 0.0f)
1340  {
1341  sLog.outErrorDb("Table `creature` has creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid, data.id);
1343  }
1344  }
1345  else if (data.movementType == IDLE_MOTION_TYPE)
1346  {
1347  if (data.spawndist != 0.0f)
1348  {
1349  sLog.outErrorDb("Table `creature` has creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
1350  data.spawndist = 0.0f;
1351  }
1352  }
1353 
1354  // Add to grid if not managed by the game event or pool system
1355  if (gameEvent == 0 && PoolId == 0)
1356  AddCreatureToGrid(guid, &data);
1357  }
1358  while (result->NextRow());
1359 
1360  sLog.outString(">> Loaded %lu creatures", mCreatureDataMap.size());
1361 }
1362 
1364 {
1365  uint8 mask = data->spawnMask;
1366  for (uint8 i = 0; mask != 0; i++, mask >>= 1)
1367  {
1368  if (mask & 1)
1369  {
1370  CellCoord cellCoord = Oregon::ComputeCellCoord(data->posX, data->posY);
1371  CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1372  cell_guids.creatures.insert(guid);
1373  }
1374  }
1375 }
1376 
1378 {
1379  uint8 mask = data->spawnMask;
1380  for (uint8 i = 0; mask != 0; i++, mask >>= 1)
1381  {
1382  if (mask & 1)
1383  {
1384  CellCoord cellCoord = Oregon::ComputeCellCoord(data->posX, data->posY);
1385  CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1386  cell_guids.creatures.erase(guid);
1387  }
1388  }
1389 }
1390 
1391 uint32 ObjectMgr::AddGOData(uint32 entry, uint32 artKit, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
1392 {
1393  GameObjectInfo const* goinfo = GetGameObjectInfo(entry);
1394  if (!goinfo)
1395  return 0;
1396 
1397  Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId));
1398  if (!map)
1399  return 0;
1400 
1401  uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT);
1402  GameObjectData& data = NewGOData(guid);
1403  data.id = entry;
1404  data.mapid = mapId;
1405  data.posX = x;
1406  data.posY = y;
1407  data.posZ = z;
1408  data.orientation = o;
1409  data.rotation0 = rotation0;
1410  data.rotation1 = rotation1;
1411  data.rotation2 = rotation2;
1412  data.rotation3 = rotation3;
1413  data.spawntimesecs = spawntimedelay;
1414  data.animprogress = 100;
1415  data.spawnMask = 1;
1416  data.go_state = GO_STATE_READY;
1417  data.artKit = artKit;
1418  data.dbData = false;
1419 
1420  AddGameobjectToGrid(guid, &data);
1421 
1422  // Spawn if necessary (loaded grids only)
1423  // We use spawn coords to spawn
1424  if (!map->Instanceable() && map->IsGridLoaded(x, y))
1425  {
1426  GameObject* go = new GameObject;
1427  if (!go->LoadGameObjectFromDB(guid, map))
1428  {
1429  sLog.outError("AddGOData: cannot add gameobject entry %u to map", entry);
1430  delete go;
1431  return 0;
1432  }
1433  }
1434 
1435  return guid;
1436 }
1437 
1438 uint32 ObjectMgr::AddCreData(uint32 entry, uint32 /*team*/, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay)
1439 {
1440  CreatureInfo const* cInfo = GetCreatureTemplate(entry);
1441  if (!cInfo)
1442  return 0;
1443 
1444  uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
1445  CreatureData& data = NewOrExistCreatureData(guid);
1446  data.id = entry;
1447  data.mapid = mapId;
1448  data.displayid = 0;
1449  data.equipmentId = cInfo->equipmentId;
1450  data.posX = x;
1451  data.posY = y;
1452  data.posZ = z;
1453  data.orientation = o;
1454  data.spawntimesecs = spawntimedelay;
1455  data.spawndist = 0;
1456  data.currentwaypoint = 0;
1457  data.curhealth = cInfo->maxhealth;
1458  data.curmana = cInfo->maxmana;
1459  data.movementType = cInfo->MovementType;
1460  data.spawnMask = 1;
1461  data.dbData = false;
1462 
1463  AddCreatureToGrid(guid, &data);
1464 
1465  // Spawn if necessary (loaded grids only)
1466  if (Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId)))
1467  {
1468  // We use spawn coords to spawn
1469  if (!map->Instanceable() && !map->IsRemovalGrid(x, y))
1470  {
1471  Creature* creature = new Creature;
1472  if (!creature->LoadCreatureFromDB(guid, map))
1473  {
1474  sLog.outError("AddCreature: cannot add creature entry %u to map", entry);
1475  delete creature;
1476  return 0;
1477  }
1478  }
1479  }
1480 
1481  return guid;
1482 }
1483 
1485 {
1486  uint32 count = 0;
1487 
1488  // 0 1 2 3 4 5 6
1489  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
1490  // 7 8 9 10 11 12 13 14 15 16
1491  "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event, pool_entry "
1492  "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1493  "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");
1494 
1495  if (!result)
1496  {
1497 
1498 
1499  sLog.outErrorDb(">> Loaded 0 gameobjects. DB table gameobject is empty.");
1500  return;
1501  }
1502 
1503 
1504  do
1505  {
1506  Field* fields = result->Fetch();
1507 
1508  uint32 guid = fields[ 0].GetUInt32();
1509  uint32 entry = fields[ 1].GetUInt32();
1510 
1511  GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
1512  if (!gInfo)
1513  {
1514  sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with invalid gameobject entry %u, skipped.", guid, entry);
1515  continue;
1516  }
1517 
1518  GameObjectData& data = mGameObjectDataMap[guid];
1519 
1520  data.id = entry;
1521  data.mapid = fields[ 2].GetUInt32();
1522  data.posX = fields[ 3].GetFloat();
1523  data.posY = fields[ 4].GetFloat();
1524  data.posZ = fields[ 5].GetFloat();
1525  data.orientation = fields[ 6].GetFloat();
1526  data.rotation0 = fields[ 7].GetFloat();
1527  data.rotation1 = fields[ 8].GetFloat();
1528  data.rotation2 = fields[ 9].GetFloat();
1529  data.rotation3 = fields[10].GetFloat();
1530  data.spawntimesecs = fields[11].GetInt32();
1531  data.animprogress = fields[12].GetUInt32();
1532  data.artKit = 0;
1533 
1534  uint32 go_state = fields[13].GetUInt32();
1535  if (go_state >= MAX_GO_STATE)
1536  {
1537  sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skipped.", guid, data.id, go_state);
1538  continue;
1539  }
1540  data.go_state = GOState(go_state);
1541 
1542  data.spawnMask = fields[14].GetUInt8();
1543  int16 gameEvent = fields[15].GetInt16();
1544  int32 PoolId = fields[16].GetInt32();
1545 
1546 
1547  if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
1548  {
1549  sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
1550  continue;
1551  }
1552 
1553  if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
1554  {
1555  sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
1556  continue;
1557  }
1558 
1559  if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
1560  {
1561  sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
1562  continue;
1563  }
1564 
1565  if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
1566  AddGameobjectToGrid(guid, &data);
1567  ++count;
1568 
1569  }
1570  while (result->NextRow());
1571 
1572  sLog.outString(">> Loaded %lu gameobjects", mGameObjectDataMap.size());
1573 }
1574 
1576 {
1577  uint8 mask = data->spawnMask;
1578  for (uint8 i = 0; mask != 0; i++, mask >>= 1)
1579  {
1580  if (mask & 1)
1581  {
1582  CellCoord cellCoord = Oregon::ComputeCellCoord(data->posX, data->posY);
1583  CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1584  cell_guids.gameobjects.insert(guid);
1585  }
1586  }
1587 }
1588 
1590 {
1591  uint8 mask = data->spawnMask;
1592  for (uint8 i = 0; mask != 0; i++, mask >>= 1)
1593  {
1594  if (mask & 1)
1595  {
1596  CellCoord cellCoord = Oregon::ComputeCellCoord(data->posX, data->posY);
1597  CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
1598  cell_guids.gameobjects.erase(guid);
1599  }
1600  }
1601 }
1602 
1604 {
1605  uint32 count = 0;
1606 
1607  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
1608 
1609  if (!result)
1610  {
1611 
1612 
1613  sLog.outString(">> Loaded 0 creature respawn time.");
1614  return;
1615  }
1616 
1617 
1618  do
1619  {
1620  Field* fields = result->Fetch();
1621 
1622  uint32 loguid = fields[0].GetUInt32();
1623  uint64 respawn_time = fields[1].GetUInt64();
1624  uint32 instance = fields[2].GetUInt32();
1625 
1626  mCreatureRespawnTimes[MAKE_PAIR64(loguid, instance)] = time_t(respawn_time);
1627 
1628  ++count;
1629  }
1630  while (result->NextRow());
1631 
1632  sLog.outString(">> Loaded %lu creature respawn times", mCreatureRespawnTimes.size());
1633 }
1634 
1636 {
1637  // remove outdated data
1638  WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
1639 
1640  uint32 count = 0;
1641 
1642  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
1643 
1644  if (!result)
1645  {
1646 
1647 
1648  sLog.outString(">> Loaded 0 gameobject respawn time.");
1649  return;
1650  }
1651 
1652 
1653  do
1654  {
1655  Field* fields = result->Fetch();
1656 
1657  uint32 loguid = fields[0].GetUInt32();
1658  uint64 respawn_time = fields[1].GetUInt64();
1659  uint32 instance = fields[2].GetUInt32();
1660 
1661  mGORespawnTimes[MAKE_PAIR64(loguid, instance)] = time_t(respawn_time);
1662 
1663  ++count;
1664  }
1665  while (result->NextRow());
1666 
1667  sLog.outString(">> Loaded %lu gameobject respawn times", mGORespawnTimes.size());
1668 }
1669 
1670 // name must be checked to correctness (if received) before call this function
1672 {
1673  uint64 guid = 0;
1674 
1676 
1677  // Player name safe to sending to DB (checked at login) and this function using
1678  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
1679  if (result)
1680  guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
1681 
1682  return guid;
1683 }
1684 
1685 bool ObjectMgr::GetPlayerNameByGUID(const uint64& guid, std::string& name) const
1686 {
1687  // prevent DB access for online player
1688  if (Player* player = GetPlayer(guid))
1689  {
1690  name = player->GetName();
1691  return true;
1692  }
1693 
1694  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1695 
1696  if (result)
1697  {
1698  name = (*result)[0].GetCppString();
1699  return true;
1700  }
1701 
1702  return false;
1703 }
1704 
1706 {
1707  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1708 
1709  if (result)
1710  {
1711  uint8 race = (*result)[0].GetUInt8();
1712  return Player::TeamForRace(race);
1713  }
1714 
1715  return 0;
1716 }
1717 
1719 {
1720  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1721  if (result)
1722  {
1723  uint32 acc = (*result)[0].GetUInt32();
1724  return acc;
1725  }
1726 
1727  return 0;
1728 }
1729 
1731 {
1732  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
1733  if (result)
1734  {
1735  uint32 acc = (*result)[0].GetUInt32();
1736  return acc;
1737  }
1738 
1739  return 0;
1740 }
1741 
1743 {
1744  mItemLocaleMap.clear(); // need for reload case
1745 
1746  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
1747 
1748  if (!result)
1749  {
1750 
1751 
1752  sLog.outString(">> Loaded 0 Item locale strings. DB table locales_item is empty.");
1753  return;
1754  }
1755 
1756 
1757  do
1758  {
1759  Field* fields = result->Fetch();
1760 
1761  uint32 entry = fields[0].GetUInt32();
1762 
1763  ItemLocale& data = mItemLocaleMap[entry];
1764 
1765  for (int i = 1; i < MAX_LOCALE; ++i)
1766  {
1767  std::string str = fields[1 + 2 * (i - 1)].GetCppString();
1768  if (!str.empty())
1769  {
1770  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1771  if (idx >= 0)
1772  {
1773  if (data.Name.size() <= idx)
1774  data.Name.resize(idx + 1);
1775 
1776  data.Name[idx] = str;
1777  }
1778  }
1779 
1780  str = fields[1 + 2 * (i - 1) + 1].GetCppString();
1781  if (!str.empty())
1782  {
1783  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1784  if (idx >= 0)
1785  {
1786  if (data.Description.size() <= idx)
1787  data.Description.resize(idx + 1);
1788 
1789  data.Description[idx] = str;
1790  }
1791  }
1792  }
1793  }
1794  while (result->NextRow());
1795 
1796  sLog.outString(">> Loaded %lu Item locale strings", mItemLocaleMap.size());
1797 }
1798 
1799 struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader>
1800 {
1801  template<class D>
1802  void convert_from_str(uint32 field_pos, char* src, D& dst)
1803  {
1804  dst = D(sObjectMgr.GetScriptId(src));
1805  }
1806 };
1807 
1809 {
1810  SQLItemLoader loader;
1811  loader.Load(sItemStorage);
1812  sLog.outString(">> Loaded %u item prototypes", sItemStorage.RecordCount);
1813 
1814  // check data correctness
1815  for (uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
1816  {
1817  ItemTemplate const* proto = sItemStorage.LookupEntry<ItemTemplate >(i);
1818  ItemEntry const* dbcitem = sItemStore.LookupEntry(i);
1819  if (!proto)
1820  {
1821  /* to many errors, and possible not all items really used in game
1822  if (dbcitem)
1823  sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
1824  */
1825  continue;
1826  }
1827 
1828  if (dbcitem)
1829  {
1830  if (proto->InventoryType != dbcitem->InventoryType)
1831  {
1832  sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).", i, proto->InventoryType, dbcitem->InventoryType);
1833  // It safe let use InventoryType from DB
1834  }
1835 
1836  if (proto->DisplayInfoID != dbcitem->DisplayId)
1837  {
1838  sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).", i, proto->DisplayInfoID, dbcitem->DisplayId);
1839  const_cast<ItemTemplate*>(proto)->DisplayInfoID = dbcitem->DisplayId;
1840  }
1841  if (proto->Sheath != dbcitem->Sheath)
1842  {
1843  sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).", i, proto->Sheath, dbcitem->Sheath);
1844  const_cast<ItemTemplate*>(proto)->Sheath = dbcitem->Sheath;
1845  }
1846  }
1847  else
1848  sLog.outErrorDb("Item (Entry: %u) not correct (not found in list of existing items).", i);
1849 
1850  if (proto->Class >= MAX_ITEM_CLASS)
1851  {
1852  sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)", i, proto->Class);
1853  const_cast<ItemTemplate*>(proto)->Class = ITEM_CLASS_JUNK;
1854  }
1855 
1856  if (proto->SubClass >= MaxItemSubclassValues[proto->Class])
1857  {
1858  sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u", i, proto->SubClass, proto->Class);
1859  const_cast<ItemTemplate*>(proto)->SubClass = 0;// exist for all item classes
1860  }
1861 
1862  if (proto->Quality >= MAX_ITEM_QUALITY)
1863  {
1864  sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)", i, proto->Quality);
1865  const_cast<ItemTemplate*>(proto)->Quality = ITEM_QUALITY_NORMAL;
1866  }
1867 
1868  if (proto->BuyCount <= 0)
1869  {
1870  sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", i, proto->BuyCount);
1871  const_cast<ItemTemplate*>(proto)->BuyCount = 1;
1872  }
1873 
1874  if (proto->InventoryType >= MAX_INVTYPE)
1875  {
1876  sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)", i, proto->InventoryType);
1877  const_cast<ItemTemplate*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
1878  }
1879 
1880  if (proto->RequiredSkill >= MAX_SKILL_TYPE)
1881  {
1882  sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)", i, proto->RequiredSkill);
1883  const_cast<ItemTemplate*>(proto)->RequiredSkill = 0;
1884  }
1885 
1886  if (!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
1887  sLog.outErrorDb("Item (Entry: %u) has no playable classes (%u) in AllowableClass and can't be equipped.", i, proto->AllowableClass);
1888 
1889  if (!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
1890  sLog.outErrorDb("Item (Entry: %u) has no playable races (%u) in AllowableRace and can't be equipped.", i, proto->AllowableRace);
1891 
1892  if (proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
1893  {
1894  sLog.outErrorDb("Item (Entry: %u) has invalid spell in RequiredSpell (%u)", i, proto->RequiredSpell);
1895  const_cast<ItemTemplate*>(proto)->RequiredSpell = 0;
1896  }
1897 
1899  sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", i, proto->RequiredReputationRank);
1900 
1901  if (proto->RequiredReputationFaction)
1902  {
1903  if (!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
1904  {
1905  sLog.outErrorDb("Item (Entry: %u) has invalid faction in RequiredReputationFaction (%u)", i, proto->RequiredReputationFaction);
1906  const_cast<ItemTemplate*>(proto)->RequiredReputationFaction = 0;
1907  }
1908 
1910  sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", i);
1911  }
1912  else if (proto->RequiredReputationRank > MIN_REPUTATION_RANK)
1913  sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction == 0 but RequiredReputationRank > 0, rank setting is useless.", i);
1914 
1915  if (proto->Stackable == 0)
1916  {
1917  sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.", i, proto->Stackable);
1918  const_cast<ItemTemplate*>(proto)->Stackable = 1;
1919  }
1920  else if (proto->Stackable > 255)
1921  {
1922  sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).", i, proto->Stackable);
1923  const_cast<ItemTemplate*>(proto)->Stackable = 255;
1924  }
1925 
1926  if (proto->ContainerSlots > MAX_BAG_SIZE)
1927  {
1928  sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", i, proto->ContainerSlots, MAX_BAG_SIZE);
1929  const_cast<ItemTemplate*>(proto)->ContainerSlots = MAX_BAG_SIZE;
1930  }
1931 
1932  for (int j = 0; j < 10; j++)
1933  {
1934  // for ItemStatValue != 0
1935  if (proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
1936  {
1937  sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
1938  const_cast<ItemTemplate*>(proto)->ItemStat[j].ItemStatType = 0;
1939  }
1940  }
1941 
1942  for (int j = 0; j < 5; j++)
1943  {
1944  if (proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
1945  {
1946  sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)", i, j + 1, proto->Damage[j].DamageType);
1947  const_cast<ItemTemplate*>(proto)->Damage[j].DamageType = 0;
1948  }
1949  }
1950 
1951  // special format
1952  if (proto->Spells[0].SpellId == SPELL_GENERIC_LEARN)
1953  {
1954  // spell_1
1955  if (proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
1956  {
1957  sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format", i, 0 + 1, proto->Spells[0].SpellTrigger);
1958  const_cast<ItemTemplate*>(proto)->Spells[0].SpellId = 0;
1959  const_cast<ItemTemplate*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1960  const_cast<ItemTemplate*>(proto)->Spells[1].SpellId = 0;
1961  const_cast<ItemTemplate*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1962  }
1963 
1964  // spell_2 have learning spell
1966  {
1967  sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.", i, 1 + 1, proto->Spells[1].SpellTrigger);
1968  const_cast<ItemTemplate*>(proto)->Spells[0].SpellId = 0;
1969  const_cast<ItemTemplate*>(proto)->Spells[1].SpellId = 0;
1970  const_cast<ItemTemplate*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1971  }
1972  else if (!proto->Spells[1].SpellId)
1973  {
1974  sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.", i, 1 + 1);
1975  const_cast<ItemTemplate*>(proto)->Spells[0].SpellId = 0;
1976  const_cast<ItemTemplate*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1977  }
1978  else
1979  {
1980  SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
1981  if (!spellInfo || sDisableMgr.IsDisabledFor(DISABLE_TYPE_SPELL, spellInfo->Id, NULL))
1982  {
1983  sLog.outErrorDb("Item (Entry: %u) has invalid spell in spellid_%d (%u)", i, 1 + 1, proto->Spells[1].SpellId);
1984  const_cast<ItemTemplate*>(proto)->Spells[0].SpellId = 0;
1985  const_cast<ItemTemplate*>(proto)->Spells[1].SpellId = 0;
1986  const_cast<ItemTemplate*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1987  }
1988  // allowed only in special format
1989  else if (proto->Spells[1].SpellId == SPELL_GENERIC_LEARN)
1990  {
1991  sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i, 1 + 1, proto->Spells[1].SpellId);
1992  const_cast<ItemTemplate*>(proto)->Spells[0].SpellId = 0;
1993  const_cast<ItemTemplate*>(proto)->Spells[1].SpellId = 0;
1994  const_cast<ItemTemplate*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1995  }
1996  }
1997 
1998  // spell_3*,spell_4*,spell_5* is empty
1999  for (int j = 2; j < 5; j++)
2000  {
2001  if (proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
2002  {
2003  sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
2004  const_cast<ItemTemplate*>(proto)->Spells[j].SpellId = 0;
2005  const_cast<ItemTemplate*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2006  }
2007  else if (proto->Spells[j].SpellId != 0)
2008  {
2009  sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format", i, j + 1, proto->Spells[j].SpellId);
2010  const_cast<ItemTemplate*>(proto)->Spells[j].SpellId = 0;
2011  }
2012  }
2013  }
2014  // normal spell list
2015  else
2016  {
2017  for (int j = 0; j < 5; j++)
2018  {
2020  {
2021  sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
2022  const_cast<ItemTemplate*>(proto)->Spells[j].SpellId = 0;
2023  const_cast<ItemTemplate*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
2024  }
2025 
2026  if (proto->Spells[j].SpellId)
2027  {
2028  SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
2029  if (!spellInfo || sDisableMgr.IsDisabledFor(DISABLE_TYPE_SPELL, spellInfo->Id, NULL))
2030  {
2031  sLog.outErrorDb("Item (Entry: %u) has invalid spell in spellid_%d (%u)", i, j + 1, proto->Spells[j].SpellId);
2032  const_cast<ItemTemplate*>(proto)->Spells[j].SpellId = 0;
2033  }
2034  // allowed only in special format
2035  else if (proto->Spells[j].SpellId == SPELL_GENERIC_LEARN)
2036  {
2037  sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i, j + 1, proto->Spells[j].SpellId);
2038  const_cast<ItemTemplate*>(proto)->Spells[j].SpellId = 0;
2039  }
2040  }
2041  }
2042  }
2043 
2044  if (proto->Bonding >= MAX_BIND_TYPE)
2045  sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)", i, proto->Bonding);
2046 
2047  if (proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
2048  sLog.outErrorDb("Item (Entry: %u) has invalid first page (Id:%u)", i, proto->PageText);
2049 
2050  if (proto->LockID && !sLockStore.LookupEntry(proto->LockID))
2051  sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)", i, proto->LockID);
2052 
2053  if (proto->Sheath >= MAX_SHEATHETYPE)
2054  {
2055  sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)", i, proto->Sheath);
2056  const_cast<ItemTemplate*>(proto)->Sheath = SHEATHETYPE_NONE;
2057  }
2058 
2059  if (proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
2060  {
2061  sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in item_enchantment_template) RandomProperty (%u)", i, proto->RandomProperty);
2062  const_cast<ItemTemplate*>(proto)->RandomProperty = 0;
2063  }
2064 
2065  if (proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
2066  {
2067  sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)", i, proto->RandomSuffix);
2068  const_cast<ItemTemplate*>(proto)->RandomSuffix = 0;
2069  }
2070 
2071  if (proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
2072  {
2073  sLog.outErrorDb("Item (Entry: %u) has invalid ItemSet (%u)", i, proto->ItemSet);
2074  const_cast<ItemTemplate*>(proto)->ItemSet = 0;
2075  }
2076 
2077  if (proto->Area && !GetAreaEntryByAreaID(proto->Area))
2078  sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)", i, proto->Area);
2079 
2080  if (proto->Map && !sMapStore.LookupEntry(proto->Map))
2081  sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)", i, proto->Map);
2082 
2083  if (proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
2084  sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)", i, proto->TotemCategory);
2085 
2086  for (int j = 0; j < 3; j++)
2087  {
2088  if (proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
2089  {
2090  sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)", i, j + 1, proto->Socket[j].Color);
2091  const_cast<ItemTemplate*>(proto)->Socket[j].Color = 0;
2092  }
2093  }
2094 
2095  if (proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
2096  sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)", i, proto->GemProperties);
2097 
2098  if (proto->FoodType >= MAX_PET_DIET)
2099  {
2100  sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)", i, proto->FoodType);
2101  const_cast<ItemTemplate*>(proto)->FoodType = 0;
2102  }
2103  }
2104 
2105  // this DBC used currently only for check item templates in DB.
2106  sItemStore.Clear();
2107 }
2108 
2110 {
2111  // Loading levels data
2112  {
2113  // 0 1 2 3 4 5 6 7 8 9
2114  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2115 
2116  uint32 count = 0;
2117 
2118  if (!result)
2119  {
2120 
2121  sLog.outString(">> Loaded %u level pet stats definitions", count);
2122  sLog.outErrorDb("Error loading pet_levelstats table or empty table.");
2123  return;
2124  }
2125 
2126 
2127  do
2128  {
2129  Field* fields = result->Fetch();
2130 
2131  uint32 creature_id = fields[0].GetUInt32();
2132  if (!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
2133  {
2134  sLog.outErrorDb("Wrong creature id %u in pet_levelstats table, ignoring.", creature_id);
2135  continue;
2136  }
2137 
2138  uint32 current_level = fields[1].GetUInt32();
2139  if (current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2140  {
2141  if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2142  sLog.outErrorDb("Wrong (> %u) level %u in pet_levelstats table, ignoring.", STRONG_MAX_LEVEL, current_level);
2143  else
2144  sLog.outDetail("Unused (> MaxPlayerLevel in Oregond.conf) level %u in pet_levelstats table, ignoring.", current_level);
2145  continue;
2146  }
2147  else if (current_level < 1)
2148  {
2149  sLog.outErrorDb("Wrong (<1) level %u in pet_levelstats table, ignoring.", current_level);
2150  continue;
2151  }
2152 
2153  PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
2154 
2155  if (pInfoMapEntry == NULL)
2156  pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2157 
2158  // data for level 1 stored in [0] array element, ...
2159  PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
2160 
2161  pLevelInfo->health = fields[2].GetUInt16();
2162  pLevelInfo->mana = fields[3].GetUInt16();
2163  pLevelInfo->armor = fields[9].GetUInt16();
2164 
2165  for (int i = 0; i < MAX_STATS; i++)
2166  pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
2167 
2168  ++count;
2169  }
2170  while (result->NextRow());
2171 
2172  sLog.outString(">> Loaded %u level pet stats definitions", count);
2173  }
2174 
2175  // Fill gaps and check integrity
2176  for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
2177  {
2178  PetLevelInfo* pInfo = itr->second;
2179 
2180  // fatal error if no level 1 data
2181  if (!pInfo || pInfo[0].health == 0)
2182  sLog.outFatal("Creature %u does not have pet stats data for Level 1!", itr->first);
2183 
2184  // fill level gaps
2185  for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2186  {
2187  if (pInfo[level].health == 0)
2188  {
2189  sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
2190  pInfo[level] = pInfo[level - 1];
2191  }
2192  }
2193  }
2194 }
2195 
2196 PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
2197 {
2198  if (level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2199  level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
2200 
2201  PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
2202  if (itr == petInfo.end())
2203  return NULL;
2204 
2205  return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
2206 }
2207 
2209 {
2210  // Load playercreate
2211  {
2212  // 0 1 2 3 4 5 6 7
2213  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
2214 
2215  uint32 count = 0;
2216 
2217  if (!result)
2218  {
2219  sLog.outString(">> Loaded %u player create definitions", count);
2220  sLog.outFatal("Error loading playercreateinfo table or empty table.");
2221  }
2222 
2223 
2224  do
2225  {
2226  Field* fields = result->Fetch();
2227 
2228  uint32 current_race = fields[0].GetUInt32();
2229  uint32 current_class = fields[1].GetUInt32();
2230  uint32 mapId = fields[2].GetUInt32();
2231  uint32 areaId = fields[3].GetUInt32();
2232  float positionX = fields[4].GetFloat();
2233  float positionY = fields[5].GetFloat();
2234  float positionZ = fields[6].GetFloat();
2235  float orientation = fields[7].GetFloat();
2236 
2237  if (current_race >= MAX_RACES)
2238  {
2239  sLog.outErrorDb("Wrong race %u in playercreateinfo table, ignoring.", current_race);
2240  continue;
2241  }
2242 
2243  ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2244  if (!rEntry)
2245  {
2246  sLog.outErrorDb("Wrong race %u in playercreateinfo table, ignoring.", current_race);
2247  continue;
2248  }
2249 
2250  if (current_class >= MAX_CLASSES)
2251  {
2252  sLog.outErrorDb("Wrong class %u in playercreateinfo table, ignoring.", current_class);
2253  continue;
2254  }
2255 
2256  if (!sChrClassesStore.LookupEntry(current_class))
2257  {
2258  sLog.outErrorDb("Wrong class %u in playercreateinfo table, ignoring.", current_class);
2259  continue;
2260  }
2261 
2262  // accept DB data only for valid position (and non instanceable)
2263  if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
2264  {
2265  sLog.outErrorDb("Wrong home position for class %u race %u pair in playercreateinfo table, ignoring.", current_class, current_race);
2266  continue;
2267  }
2268 
2269  if (sMapStore.LookupEntry(mapId)->Instanceable())
2270  {
2271  sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in playercreateinfo table, ignoring.", current_class, current_race);
2272  continue;
2273  }
2274 
2275  PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2276 
2277  pInfo->mapId = mapId;
2278  pInfo->areaId = areaId;
2279  pInfo->positionX = positionX;
2280  pInfo->positionY = positionY;
2281  pInfo->positionZ = positionZ;
2282  pInfo->orientation = orientation;
2283 
2284  pInfo->displayId_m = rEntry->model_m;
2285  pInfo->displayId_f = rEntry->model_f;
2286 
2287  ++count;
2288  }
2289  while (result->NextRow());
2290 
2291  sLog.outString(">> Loaded %u player create definitions", count);
2292  }
2293 
2294  // Load playercreate items
2295  {
2296  // 0 1 2 3
2297  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2298 
2299  uint32 count = 0;
2300 
2301  if (!result)
2302 
2303 
2304  sLog.outString(">> Loaded %u custom player create items", count);
2305  else
2306  {
2307 
2308  do
2309  {
2310  Field* fields = result->Fetch();
2311 
2312  uint32 current_race = fields[0].GetUInt32();
2313  if (current_race >= MAX_RACES)
2314  {
2315  sLog.outErrorDb("Wrong race %u in playercreateinfo_item table, ignoring.", current_race);
2316  continue;
2317  }
2318 
2319  uint32 current_class = fields[1].GetUInt32();
2320  if (current_class >= MAX_CLASSES)
2321  {
2322  sLog.outErrorDb("Wrong class %u in playercreateinfo_item table, ignoring.", current_class);
2323  continue;
2324  }
2325 
2326  PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2327 
2328  uint32 item_id = fields[2].GetUInt32();
2329 
2330  if (!GetItemTemplate(item_id))
2331  {
2332  sLog.outErrorDb("Item id %u (race %u class %u) in playercreateinfo_item table but not listed in item_template, ignoring.", item_id, current_race, current_class);
2333  continue;
2334  }
2335 
2336  uint32 amount = fields[3].GetUInt32();
2337 
2338  if (!amount)
2339  {
2340  sLog.outErrorDb("Item id %u (class %u race %u) has amount == 0 in playercreateinfo_item table, ignoring.", item_id, current_race, current_class);
2341  continue;
2342  }
2343 
2344  pInfo->item.push_back(PlayerCreateInfoItem(item_id, amount));
2345 
2346  ++count;
2347  }
2348  while (result->NextRow());
2349 
2350  sLog.outString(">> Loaded %u custom player create items", count);
2351  }
2352  }
2353 
2354  // Load playercreate spells
2355  {
2356 
2358  if (sWorld.getConfig(CONFIG_START_ALL_SPELLS))
2359  result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
2360  else
2361  result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell");
2362 
2363  uint32 count = 0;
2364 
2365  if (!result)
2366  {
2367 
2368  sLog.outString(">> Loaded %u player create spells", count);
2369  sLog.outErrorDb("Error loading player starting spells or empty table.");
2370  }
2371  else
2372  {
2373 
2374  do
2375  {
2376  Field* fields = result->Fetch();
2377 
2378  uint32 current_race = fields[0].GetUInt32();
2379  if (current_race >= MAX_RACES)
2380  {
2381  sLog.outErrorDb("Wrong race %u in playercreateinfo_spell table, ignoring.", current_race);
2382  continue;
2383  }
2384 
2385  uint32 current_class = fields[1].GetUInt32();
2386  if (current_class >= MAX_CLASSES)
2387  {
2388  sLog.outErrorDb("Wrong class %u in playercreateinfo_spell table, ignoring.", current_class);
2389  continue;
2390  }
2391 
2392  PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2393  pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8()));
2394 
2395  ++count;
2396  }
2397  while (result->NextRow());
2398 
2399  sLog.outString(">> Loaded %u player create spells", count);
2400  }
2401  }
2402 
2403  // Load playercreate actions
2404  {
2405  // 0 1 2 3 4 5
2406  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action");
2407 
2408  uint32 count = 0;
2409 
2410  if (!result)
2411  {
2412 
2413  sLog.outString(">> Loaded %u player create actions", count);
2414  sLog.outErrorDb("Error loading playercreateinfo_action table or empty table.");
2415  }
2416  else
2417  {
2418 
2419  do
2420  {
2421  Field* fields = result->Fetch();
2422 
2423  uint32 current_race = fields[0].GetUInt32();
2424  if (current_race >= MAX_RACES)
2425  {
2426  sLog.outErrorDb("Wrong race %u in playercreateinfo_action table, ignoring.", current_race);
2427  continue;
2428  }
2429 
2430  uint32 current_class = fields[1].GetUInt32();
2431  if (current_class >= MAX_CLASSES)
2432  {
2433  sLog.outErrorDb("Wrong class %u in playercreateinfo_action table, ignoring.", current_class);
2434  continue;
2435  }
2436 
2437  PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2438  pInfo->action[0].push_back(fields[2].GetUInt16());
2439  pInfo->action[1].push_back(fields[3].GetUInt16());
2440  pInfo->action[2].push_back(fields[4].GetUInt16());
2441  pInfo->action[3].push_back(fields[5].GetUInt16());
2442 
2443  ++count;
2444  }
2445  while (result->NextRow());
2446 
2447  sLog.outString(">> Loaded %u player create actions", count);
2448  }
2449  }
2450 
2451  // Loading levels data (class only dependent)
2452  {
2453  // 0 1 2 3
2454  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
2455 
2456  uint32 count = 0;
2457 
2458  if (!result)
2459  {
2460 
2461  sLog.outString(">> Loaded %u level health/mana definitions", count);
2462  sLog.outFatal("Error loading player_classlevelstats table or empty table.");
2463  }
2464 
2465 
2466  do
2467  {
2468  Field* fields = result->Fetch();
2469 
2470  uint32 current_class = fields[0].GetUInt32();
2471  if (current_class >= MAX_CLASSES)
2472  {
2473  sLog.outErrorDb("Wrong class %u in player_classlevelstats table, ignoring.", current_class);
2474  continue;
2475  }
2476 
2477  uint32 current_level = fields[1].GetUInt32();
2478  if (current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2479  {
2480  if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2481  sLog.outErrorDb("Wrong (> %u) level %u in player_classlevelstats table, ignoring.", STRONG_MAX_LEVEL, current_level);
2482  else
2483  sLog.outDetail("Unused (> MaxPlayerLevel in Oregond.conf) level %u in player_classlevelstats table, ignoring.", current_level);
2484  continue;
2485  }
2486 
2487  PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
2488 
2489  if (!pClassInfo->levelInfo)
2490  pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2491 
2492  PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level - 1];
2493 
2494  pClassLevelInfo->basehealth = fields[2].GetUInt16();
2495  pClassLevelInfo->basemana = fields[3].GetUInt16();
2496 
2497  ++count;
2498  }
2499  while (result->NextRow());
2500 
2501  sLog.outString(">> Loaded %u level health/mana definitions", count);
2502  }
2503 
2504  // Fill gaps and check integrity
2505  for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2506  {
2507  // skip non existed classes
2508  if (!sChrClassesStore.LookupEntry(class_))
2509  continue;
2510 
2511  PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
2512 
2513  // fatal error if no level 1 data
2514  if (!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
2515  sLog.outFatal("Class %i Level 1 does not have health/mana data!", class_);
2516 
2517  // fill level gaps
2518  for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2519  {
2520  if (pClassInfo->levelInfo[level].basehealth == 0)
2521  {
2522  sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.", class_, level + 1, level);
2523  pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
2524  }
2525  }
2526  }
2527 
2528  // Loading levels data (class/race dependent)
2529  {
2530  // 0 1 2 3 4 5 6 7
2531  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
2532 
2533  uint32 count = 0;
2534 
2535  if (!result)
2536  {
2537  sLog.outString(">> Loaded %u level stats definitions", count);
2538  sLog.outFatal("Error loading player_levelstats table or empty table.");
2539  }
2540 
2541 
2542  do
2543  {
2544  Field* fields = result->Fetch();
2545 
2546  uint32 current_race = fields[0].GetUInt32();
2547  if (current_race >= MAX_RACES)
2548  {
2549  sLog.outErrorDb("Wrong race %u in player_levelstats table, ignoring.", current_race);
2550  continue;
2551  }
2552 
2553  uint32 current_class = fields[1].GetUInt32();
2554  if (current_class >= MAX_CLASSES)
2555  {
2556  sLog.outErrorDb("Wrong class %u in player_levelstats table, ignoring.", current_class);
2557  continue;
2558  }
2559 
2560  uint32 current_level = fields[2].GetUInt32();
2561  if (current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2562  {
2563  if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2564  sLog.outErrorDb("Wrong (> %u) level %u in player_levelstats table, ignoring.", STRONG_MAX_LEVEL, current_level);
2565  else
2566  sLog.outDetail("Unused (> MaxPlayerLevel in Oregond.conf) level %u in player_levelstats table, ignoring.", current_level);
2567  continue;
2568  }
2569 
2570  PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2571 
2572  if (!pInfo->levelInfo)
2573  pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2574 
2575  PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level - 1];
2576 
2577  for (int i = 0; i < MAX_STATS; i++)
2578  pLevelInfo->stats[i] = fields[i + 3].GetUInt8();
2579 
2580  ++count;
2581  }
2582  while (result->NextRow());
2583 
2584  sLog.outString(">> Loaded %u level stats definitions", count);
2585  }
2586 
2587  // Fill gaps and check integrity
2588  for (int race = 0; race < MAX_RACES; ++race)
2589  {
2590  // skip non existed races
2591  if (!sChrRacesStore.LookupEntry(race))
2592  continue;
2593 
2594  for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2595  {
2596  // skip non existed classes
2597  if (!sChrClassesStore.LookupEntry(class_))
2598  continue;
2599 
2600  PlayerInfo* pInfo = &playerInfo[race][class_];
2601 
2602  // skip non loaded combinations
2603  if (!pInfo->displayId_m || !pInfo->displayId_f)
2604  continue;
2605 
2606  // skip expansion races if not playing with expansion
2607  if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
2608  continue;
2609 
2610  // skip expansion classes if not playing with expansion
2611  if (sWorld.getConfig(CONFIG_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
2612  continue;
2613 
2614  // fatal error if no level 1 data
2615  if (!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0)
2616  sLog.outFatal("Race %i Class %i Level 1 does not have stats data!", race, class_);
2617 
2618  // fill level gaps
2619  for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2620  {
2621  if (pInfo->levelInfo[level].stats[0] == 0)
2622  {
2623  sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
2624  pInfo->levelInfo[level] = pInfo->levelInfo[level - 1];
2625  }
2626  }
2627  }
2628  }
2629 }
2630 
2632 {
2633  if (level < 1 || class_ >= MAX_CLASSES)
2634  return;
2635 
2636  PlayerClassInfo const* pInfo = &playerClassInfo[class_];
2637 
2638  if (level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2639  level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
2640 
2641  *info = pInfo->levelInfo[level - 1];
2642 }
2643 
2644 void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
2645 {
2646  if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
2647  return;
2648 
2649  PlayerInfo const* pInfo = &playerInfo[race][class_];
2650  if (pInfo->displayId_m == 0 || pInfo->displayId_f == 0)
2651  return;
2652 
2653  if (level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2654  *info = pInfo->levelInfo[level - 1];
2655  else
2656  BuildPlayerLevelInfo(race, class_, level, info);
2657 }
2658 
2659 void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
2660 {
2661  // base data (last known level)
2662  *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
2663 
2664  for (int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
2665  {
2666  switch (_class)
2667  {
2668  case CLASS_WARRIOR:
2669  info->stats[STAT_STRENGTH] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
2670  info->stats[STAT_STAMINA] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
2671  info->stats[STAT_AGILITY] += (lvl > 36 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
2672  info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2673  info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2674  break;
2675  case CLASS_PALADIN:
2676  info->stats[STAT_STRENGTH] += (lvl > 3 ? 1 : 0);
2677  info->stats[STAT_STAMINA] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
2678  info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 7 && !(lvl % 2) ? 1 : 0));
2679  info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl % 2) ? 1 : 0);
2680  info->stats[STAT_SPIRIT] += (lvl > 7 ? 1 : 0);
2681  break;
2682  case CLASS_HUNTER:
2683  info->stats[STAT_STRENGTH] += (lvl > 4 ? 1 : 0);
2684  info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
2685  info->stats[STAT_AGILITY] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
2686  info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl % 2) ? 1 : 0);
2687  info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
2688  break;
2689  case CLASS_ROGUE:
2690  info->stats[STAT_STRENGTH] += (lvl > 5 ? 1 : 0);
2691  info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
2692  info->stats[STAT_AGILITY] += (lvl > 16 ? 2 : (lvl > 1 ? 1 : 0));
2693  info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl % 2) ? 1 : 0);
2694  info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
2695  break;
2696  case CLASS_PRIEST:
2697  info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2698  info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
2699  info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 8 && (lvl % 2) ? 1 : 0));
2700  info->stats[STAT_INTELLECT] += (lvl > 22 ? 2 : (lvl > 1 ? 1 : 0));
2701  info->stats[STAT_SPIRIT] += (lvl > 3 ? 1 : 0);
2702  break;
2703  case CLASS_SHAMAN:
2704  info->stats[STAT_STRENGTH] += (lvl > 34 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
2705  info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
2706  info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl % 2) ? 1 : 0);
2707  info->stats[STAT_INTELLECT] += (lvl > 5 ? 1 : 0);
2708  info->stats[STAT_SPIRIT] += (lvl > 4 ? 1 : 0);
2709  break;
2710  case CLASS_MAGE:
2711  info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2712  info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
2713  info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2714  info->stats[STAT_INTELLECT] += (lvl > 24 ? 2 : (lvl > 1 ? 1 : 0));
2715  info->stats[STAT_SPIRIT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
2716  break;
2717  case CLASS_WARLOCK:
2718  info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2719  info->stats[STAT_STAMINA] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
2720  info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
2721  info->stats[STAT_INTELLECT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
2722  info->stats[STAT_SPIRIT] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
2723  break;
2724  case CLASS_DRUID:
2725  info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
2726  info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
2727  info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
2728  info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
2729  info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
2730  }
2731  }
2732 }
2733 
2735 {
2736  Guild* newGuild;
2737  uint32 count = 0;
2738 
2739  // 0 1 2 3 4 5 6
2740  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT guild.guildid,guild.name,leaderguid,EmblemStyle,EmblemColor,BorderStyle,BorderColor,"
2741  // 7 8 9 10 11 12
2742  "BackgroundColor,info,motd,createdate,BankMoney,COUNT(guild_bank_tab.guildid) "
2743  "FROM guild LEFT JOIN guild_bank_tab ON guild.guildid = guild_bank_tab.guildid GROUP BY guild.guildid ORDER BY guildid ASC");
2744 
2745  if (!result)
2746  {
2747 
2748 
2749 
2750  sLog.outString(">> Loaded %u guild definitions", count);
2751  return;
2752  }
2753 
2754  // load guild ranks
2755  // 0 1 2 3 4
2756  QueryResult_AutoPtr guildRanksResult = CharacterDatabase.Query("SELECT guildid,rid,rname,rights,BankMoneyPerDay FROM guild_rank ORDER BY guildid ASC, rid ASC");
2757 
2758  // load guild members
2759  // 0 1 2 3 4 5 6
2760  QueryResult_AutoPtr guildMembersResult = CharacterDatabase.Query("SELECT guildid,guild_member.guid,rank,pnote,offnote,BankResetTimeMoney,BankRemMoney,"
2761  // 7 8 9 10 11 12
2762  "BankResetTimeTab0,BankRemSlotsTab0,BankResetTimeTab1,BankRemSlotsTab1,BankResetTimeTab2,BankRemSlotsTab2,"
2763  // 13 14 15 16 17 18
2764  "BankResetTimeTab3,BankRemSlotsTab3,BankResetTimeTab4,BankRemSlotsTab4,BankResetTimeTab5,BankRemSlotsTab5,"
2765  // 19 20 21 22 23 24
2766  "characters.name, characters.level, characters.class, characters.zone, characters.logout_time, characters.account "
2767  "FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid ORDER BY guildid ASC");
2768 
2769  // load guild bank tab rights
2770  // 0 1 2 3 4
2771  QueryResult_AutoPtr guildBankTabRightsResult = CharacterDatabase.Query("SELECT guildid,TabId,rid,gbright,SlotPerDay FROM guild_bank_right ORDER BY guildid ASC, TabId ASC");
2772 
2773 
2774  do
2775  {
2776  //Field *fields = result->Fetch();
2777 
2778  ++count;
2779 
2780  newGuild = new Guild;
2781  if (!newGuild->LoadGuildFromDB(result) ||
2782  !newGuild->LoadRanksFromDB(guildRanksResult) ||
2783  !newGuild->LoadMembersFromDB(guildMembersResult) ||
2784  !newGuild->LoadBankRightsFromDB(guildBankTabRightsResult) ||
2785  !newGuild->CheckGuildStructure()
2786  )
2787  {
2788  newGuild->Disband();
2789  delete newGuild;
2790  continue;
2791  }
2792  newGuild->LoadGuildEventLogFromDB();
2793  newGuild->LoadGuildBankEventLogFromDB();
2794  newGuild->LoadGuildBankFromDB();
2795  AddGuild(newGuild);
2796 
2797  }
2798  while (result->NextRow());
2799 
2800  sLog.outString(">> Loaded %u guild definitions", count);
2801 }
2802 
2804 {
2805  uint32 count = 0;
2806 
2807  // 0 1 2 3 4 5
2808  QueryResult_AutoPtr result = CharacterDatabase.Query( "SELECT arena_team.arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,"
2809  // 6 7 8 9 10 11 12 13 14
2810  "EmblemColor,BorderStyle,BorderColor, rating,games,wins,played,wins2,rank "
2811  "FROM arena_team LEFT JOIN arena_team_stats ON arena_team.arenateamid = arena_team_stats.arenateamid ORDER BY arena_team.arenateamid ASC" );
2812 
2813  if (!result)
2814  {
2815 
2816 
2817 
2818  sLog.outString(">> Loaded %u arenateam definitions", count);
2819  return;
2820  }
2821 
2822  // load arena_team members
2823  QueryResult_AutoPtr arenaTeamMembersResult = CharacterDatabase.Query(
2824  // 0 1 2 3 4 5 6 7 8
2825  "SELECT arenateamid,member.guid,played_week,wons_week,played_season,wons_season,personal_rating,name,class "
2826  "FROM arena_team_member member LEFT JOIN characters chars on member.guid = chars.guid ORDER BY member.arenateamid ASC");
2827 
2828 
2829  do
2830  {
2831  ++count;
2832 
2833  ArenaTeam* newArenaTeam = new ArenaTeam;
2834  if (!newArenaTeam->LoadArenaTeamFromDB(result) ||
2835  !newArenaTeam->LoadMembersFromDB(arenaTeamMembersResult))
2836  {
2837  newArenaTeam->Disband(NULL);
2838  delete newArenaTeam;
2839  continue;
2840  }
2841  AddArenaTeam(newArenaTeam);
2842  }
2843  while (result->NextRow());
2844 
2845  sLog.outString(">> Loaded %u arenateam definitions", count);
2846 }
2847 
2849 {
2850  QueryResult_AutoPtr result = LoginDatabase.Query("SELECT id1, id2 FROM account_referred");
2851  if (!result)
2852  {
2853 
2854  sLog.outString(">> Loaded 0 Referred Friends");
2855  return;
2856  }
2857 
2858  uint64 Id1;
2859  uint64 Id2;
2860  ReferFriendMap::const_iterator it;
2861 
2862  // clear these in case of reload
2863  m_referrerFriends.clear();
2864  m_referredFriends.clear();
2865 
2866  do
2867  {
2868  Field* field = result->Fetch();
2869 
2870  Id1 = field[0].GetUInt64(); // referrer
2871  Id2 = field[1].GetUInt64(); // referred
2872 
2873  if ((it = m_referredFriends.find(Id2)) != m_referredFriends.end())
2874  {
2875  sLog.outErrorDb("RAF Conflict! Account " UI64FMTD " is already linked with " UI64FMTD " (attempt to double link with account " UI64FMTD ")", Id2, it->second, Id1);
2876  continue;
2877  }
2878 
2879  // We use double link for fast checking
2880  m_referrerFriends[Id1] = Id2;
2881  m_referredFriends[Id2] = Id1;
2882  }
2883  while (result->NextRow());
2884 
2885  sLog.outString(">> Loaded " UI64FMTD " Referred Friends", result->GetRowCount());
2886 }
2887 
2889 {
2890  RAFLinkStatus status = RAF_LINK_NONE;
2891  ReferFriendMap::const_iterator i = m_referrerFriends.find(account);
2892 
2893  if (i != m_referrerFriends.end())
2894  status = RAF_LINK_REFERRER;
2895  else
2896  {
2897  i = m_referredFriends.find(account);
2898  if (i != m_referredFriends.end())
2899  status = RAF_LINK_REFERRED;
2900  }
2901 
2902  if (linked)
2903  *linked = (status == RAF_LINK_NONE) ? RAF_LINK_NONE : i->second;
2904 
2905  return status;
2906 }
2907 
2909 {
2910  ReferFriendMap::const_iterator i = m_referrerFriends.find(AccOne), j;
2911  if (i != m_referrerFriends.end())
2912  {
2913  j = m_referredFriends.find(AccTwo);
2914  if (j == m_referredFriends.end())
2915  return RAF_LINK_NONE;
2916 
2917  if (i->first == j->second &&
2918  i->second == j->first)
2919  return RAF_LINK_REFERRER;
2920 
2921  return RAF_LINK_NONE;
2922  }
2923 
2924  i = m_referredFriends.find(AccOne);
2925  if (i != m_referredFriends.end())
2926  {
2927  j = m_referrerFriends.find(AccTwo);
2928  if (j == m_referrerFriends.end())
2929  return RAF_LINK_NONE;
2930 
2931  if (i->first == j->second &&
2932  i->second == j->first)
2933  return RAF_LINK_REFERRED;
2934 
2935  // fall down
2936  }
2937 
2938  return RAF_LINK_NONE;
2939 }
2940 
2941 
2942 #define UPDATE_PLAYER(plr) \
2943  if (Player* player = plr) \
2944  { \
2945  player->SetRAFStatus(RAF_LINK_NONE); \
2946  player->SetRestBonus(player->GetRestBonus()); \
2947  }
2948 
2950 {
2951  m_referrerFriends[AccOne] = AccTwo;
2952  m_referredFriends[AccTwo] = AccOne;
2953  LoginDatabase.PExecute("REPLACE INTO account_referred (id1, id2) VALUES (" UI64FMTD ", " UI64FMTD ")", AccOne, AccTwo);
2954  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccOne))
2955  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccTwo))
2956 }
2957 
2959 {
2960  uint64 AccTwo;
2961  switch (GetRAFLinkStatus(AccOne, &AccTwo))
2962  {
2963  case RAF_LINK_NONE:
2964  return;
2965  case RAF_LINK_REFERRER:
2966  m_referrerFriends.erase(m_referrerFriends.find(AccOne));
2967  m_referredFriends.erase(m_referredFriends.find(AccTwo));
2968  LoginDatabase.PExecute("DELETE FROM account_referred WHERE id1 = " UI64FMTD " AND id2 = " UI64FMTD "", AccOne, AccTwo);
2969  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccOne))
2970  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccTwo))
2971  break;
2972  case RAF_LINK_REFERRED:
2973  m_referrerFriends.erase(m_referrerFriends.find(AccTwo));
2974  m_referredFriends.erase(m_referredFriends.find(AccOne));
2975  LoginDatabase.PExecute("DELETE FROM account_referred WHERE id1 = " UI64FMTD " AND id2 = " UI64FMTD "", AccTwo, AccOne);
2976  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccOne))
2977  UPDATE_PLAYER(ObjectAccessor::Instance().FindPlayerByAccountId(AccTwo))
2978  break;
2979  }
2980 }
2981 
2982 #undef UPDATE_PLAYER
2983 
2985 {
2986  ReferFriendMap::const_iterator it = m_referrerFriends.find(plr1->GetSession()->GetAccountId());
2987  if (it == m_referrerFriends.end())
2988  {
2989  it = m_referredFriends.find(plr1->GetSession()->GetAccountId());
2990  if (it == m_referredFriends.end())
2991  return NULL;
2992  }
2993 
2994  return ObjectAccessor::Instance().FindPlayerByAccountId(it->second);
2995 }
2996 
2998 {
2999  // -- loading groups --
3000  Group* group = NULL;
3001  uint64 leaderGuid = 0;
3002  uint32 count = 0;
3003  // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3004  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
3005 
3006  if (!result)
3007  {
3008 
3009 
3010  sLog.outString(">> Loaded %u group definitions", count);
3011  return;
3012  }
3013 
3014 
3015  do
3016  {
3017  Field* fields = result->Fetch();
3018  ++count;
3019  leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(), 0, HIGHGUID_PLAYER);
3020 
3021  group = new Group;
3022  if (!group->LoadGroupFromDB(leaderGuid, result, false))
3023  {
3024  group->Disband();
3025  delete group;
3026  continue;
3027  }
3028  AddGroup(group);
3029  }
3030  while (result->NextRow());
3031 
3032  sLog.outString(">> Loaded %u group definitions", count);
3033 
3034  // -- loading members --
3035  count = 0;
3036  group = NULL;
3037  leaderGuid = 0;
3038  // 0 1 2 3
3039  result = CharacterDatabase.Query("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
3040  if (!result)
3041  {
3042  }
3043  else
3044  {
3045  do
3046  {
3047  Field* fields = result->Fetch();
3048  count++;
3049  leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
3050  if (!group || group->GetLeaderGUID() != leaderGuid)
3051  {
3052  group = GetGroupByLeader(leaderGuid);
3053  if (!group)
3054  {
3055  sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
3056  CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
3057  continue;
3058  }
3059  }
3060 
3061  if (!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
3062  {
3063  sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
3064  CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
3065  }
3066  }
3067  while (result->NextRow());
3068  }
3069 
3070  // clean groups
3071  // @todo maybe delete from the DB before loading in this case
3072  for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
3073  {
3074  if ((*itr)->GetMembersCount() < 2)
3075  {
3076  (*itr)->Disband();
3077  delete *itr;
3078  mGroupSet.erase(itr++);
3079  }
3080  else
3081  ++itr;
3082  }
3083 
3084  // -- loading instances --
3085  count = 0;
3086  group = NULL;
3087  leaderGuid = 0;
3088  result = CharacterDatabase.Query(
3089  // 0 1 2 3 4 5
3090  "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
3091  // 6
3092  "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
3093  "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
3094  );
3095 
3096  if (!result)
3097  {
3098  }
3099  else
3100  {
3101  do
3102  {
3103  Field* fields = result->Fetch();
3104  count++;
3105  leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
3106  if (!group || group->GetLeaderGUID() != leaderGuid)
3107  {
3108  group = GetGroupByLeader(leaderGuid);
3109  if (!group)
3110  {
3111  sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
3112  continue;
3113  }
3114  }
3115 
3116  MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32());
3117  if (!mapEntry || !mapEntry->IsDungeon())
3118  {
3119  sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32());
3120  continue;
3121  }
3122 
3123  InstanceSave* save = sInstanceSaveMgr.AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), (DungeonDifficulty)fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
3124  group->BindToInstance(save, fields[3].GetBool(), true);
3125  }
3126  while (result->NextRow());
3127  }
3128 
3129  sLog.outString(">> Loaded %u group-instance binds total", count);
3130 
3131  sLog.outString(">> Loaded %u group members total", count);
3132 }
3133 
3135 {
3136  // For reload case
3137  for (QuestMap::const_iterator itr = mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
3138  delete itr->second;
3139  mQuestTemplates.clear();
3140 
3141  mExclusiveQuestGroups.clear();
3142 
3143  // 0 1 2 3 4 5 6 7 8 9
3144  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, MinLevel, QuestLevel, Type, RequiredClasses, RequiredRaces, RequiredSkill, RequiredSkillValue,"
3145  // 10 11 12 13 14 15 16 17
3146  "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
3147  // 18 19 20 21 22 23 24 25 26 27
3148  "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
3149  // 28 29 30 31 32 33 34 35 36 37
3150  "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
3151  // 38 39 40 41 42 43 44 45
3152  "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
3153  // 46 47 48 49 50 51 52 53
3154  "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4,"
3155  // 54 55 56 57 58 59 60 61
3156  "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
3157  // 62 63 64 65
3158  "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
3159  // 66 67 68 69 70 71 72
3160  "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
3161  // 73 74 75 76 78 79
3162  "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
3163  // 80 81 82 83 84 85 86 87
3164  "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
3165  // 88 89 90 91 92
3166  "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
3167  // 93 94 95 96 97 98 99 100 101 102 103
3168  "RewHonorableKills, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
3169  // 104 105 106 107 108 109 110 111 112 113
3170  "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
3171  // 114 115
3172  "StartScript, CompleteScript"
3173  " FROM quest_template");
3174 
3175  if (!result)
3176  {
3177 
3178  sLog.outString(">> Loaded 0 quests definitions");
3179  sLog.outErrorDb("quest_template table is empty!");
3180  return;
3181  }
3182 
3183  // create multimap previous quest for each existed quest
3184  // some quests can have many previous maps set by NextQuestId in previous quest
3185  // for example set of race quests can lead to single not race specific quest
3186  do
3187  {
3188  Field* fields = result->Fetch();
3189 
3190  Quest* newQuest = new Quest(fields);
3191  mQuestTemplates[newQuest->GetQuestId()] = newQuest;
3192  }
3193  while (result->NextRow());
3194 
3195  // Post processing
3196  for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
3197  {
3198  // skip post-loading checks for disabled quests
3199  if (sDisableMgr.IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, NULL))
3200  continue;
3201 
3202  Quest* qinfo = iter->second;
3203 
3204  // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3205 
3206  if (qinfo->GetQuestMethod() >= 3)
3207  sLog.outErrorDb("Quest %u has Method = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
3208 
3210  {
3211  sLog.outErrorDb("Quest %u has SpecialFlags = %u > max allowed value. Correct SpecialFlags to value <= %u",
3214  }
3215 
3216  if (qinfo->QuestFlags & QUEST_FLAGS_DAILY)
3217  {
3219  {
3220  sLog.outErrorDb("Daily Quest %u not marked as repeatable in SpecialFlags, added.", qinfo->GetQuestId());
3222  }
3223  }
3224 
3226  {
3227  // at auto-reward can be rewarded only RewChoiceItemId[0]
3228  for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
3229  {
3230  if (uint32 id = qinfo->RewChoiceItemId[j])
3231  {
3232  sLog.outErrorDb("Quest %u has RewChoiceItemId%d = %u but item from RewChoiceItemId%d can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3233  qinfo->GetQuestId(), j + 1, id, j + 1);
3234  // no changes, quest ignore this data
3235  }
3236  }
3237  }
3238 
3239  // client quest log visual (area case)
3240  if (qinfo->ZoneOrSort > 0)
3241  {
3242  if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
3243  {
3244  sLog.outErrorDb("Quest %u has ZoneOrSort = %u (zone case) but zone with this id does not exist.",
3245  qinfo->GetQuestId(), qinfo->ZoneOrSort);
3246  // no changes, quest not dependent from this value but can have problems at client
3247  }
3248  }
3249  // client quest log visual (sort case)
3250  if (qinfo->ZoneOrSort < 0)
3251  {
3252  QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
3253  if (!qSort)
3254  {
3255  sLog.outErrorDb("Quest %u has ZoneOrSort = %i (sort case) but quest sort with this id does not exist.",
3256  qinfo->GetQuestId(), qinfo->ZoneOrSort);
3257  // 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)
3258  }
3259  //check for proper RequiredSkill value (skill case)
3260  if (int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
3261  {
3262  // skill is positive value in SkillOrClass
3263  if (qinfo->RequiredSkill != skill_id)
3264  {
3265  sLog.outErrorDb("Quest %u has ZoneOrSort = %i but `RequiredSkill` does not have a corresponding value (%i).",
3266  qinfo->GetQuestId(), qinfo->ZoneOrSort, skill_id);
3267  //override, and force proper value here?
3268  }
3269  }
3270  }
3271 
3272  // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
3273  if (qinfo->RequiredClasses)
3274  {
3275  if (!(qinfo->RequiredClasses & CLASSMASK_ALL_PLAYABLE))
3276  {
3277  sLog.outErrorDb("Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->RequiredClasses);
3278  qinfo->RequiredClasses = 0;
3279  }
3280  }
3281 
3282  // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
3283  if (qinfo->RequiredRaces)
3284  {
3285  if (!(qinfo->RequiredRaces & RACEMASK_ALL_PLAYABLE))
3286  {
3287  sLog.outErrorDb("Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->RequiredRaces);
3288  qinfo->RequiredRaces = 0;
3289  }
3290  }
3291 
3292  // RequiredSkill, can be 0
3293  if (qinfo->RequiredSkill)
3294  {
3295  if (!sSkillLineStore.LookupEntry(qinfo->RequiredSkill))
3296  {
3297  sLog.outErrorDb("Quest %u has `RequiredSkill` = %u but this skill does not exist",
3298  qinfo->GetQuestId(), qinfo->RequiredSkill);
3299  }
3300  }
3301 
3302  if (qinfo->RequiredSkillValue)
3303  {
3304  if (qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue())
3305  {
3306  sLog.outErrorDb("Quest %u has RequiredSkillValue = %u but max possible skill is %u, quest cannot be completed.",
3307  qinfo->GetQuestId(), qinfo->RequiredSkillValue, sWorld.GetConfigMaxSkillValue());
3308  // no changes, quest can't be done for this requirement
3309  }
3310  }
3311  // else Skill quests can have 0 skill level, this is ok
3312 
3313  if (qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
3314  {
3315  sLog.outErrorDb("Quest %u has RepObjectiveFaction = %u but faction template %u does not exist, quest cannot be completed.",
3316  qinfo->GetQuestId(), qinfo->RepObjectiveFaction, qinfo->RepObjectiveFaction);
3317  // no changes, quest can't be done for this requirement
3318  }
3319 
3320  if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
3321  {
3322  sLog.outErrorDb("Quest %u has RequiredMinRepFaction = %u but faction template %u does not exist, quest cannot be completed.",
3323  qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
3324  // no changes, quest can't be done for this requirement
3325  }
3326 
3327  if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
3328  {
3329  sLog.outErrorDb("Quest %u has RequiredMaxRepFaction = %u but faction template %u does not exist, quest cannot be completed.",
3330  qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
3331  // no changes, quest can't be done for this requirement
3332  }
3333 
3335  {
3336  sLog.outErrorDb("Quest %u has RequiredMinRepValue = %d but max reputation is %u, quest cannot be completed.",
3338  // no changes, quest can't be done for this requirement
3339  }
3340 
3341  if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
3342  {
3343  sLog.outErrorDb("Quest %u has RequiredMaxRepValue = %d and RequiredMinRepValue = %d, quest cannot be completed.",
3344  qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
3345  // no changes, quest can't be done for this requirement
3346  }
3347 
3348  if (!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0)
3349  {
3350  sLog.outErrorDb("Quest %u has RepObjectiveValue = %d but RepObjectiveFaction is 0, value has no effect",
3351  qinfo->GetQuestId(), qinfo->RepObjectiveValue);
3352  // warning
3353  }
3354 
3355  if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0)
3356  {
3357  sLog.outErrorDb("Quest %u has RequiredMinRepValue = %d but RequiredMinRepFaction is 0, value has no effect",
3358  qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
3359  // warning
3360  }
3361 
3362  if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0)
3363  {
3364  sLog.outErrorDb("Quest %u has RequiredMaxRepValue = %d but RequiredMaxRepFaction is 0, value has no effect",
3365  qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
3366  // warning
3367  }
3368 
3369  if (qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
3370  {
3371  sLog.outErrorDb("Quest %u has CharTitleId = %u but CharTitle Id %u does not exist, quest will not reward title.",
3372  qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
3373  qinfo->CharTitleId = 0;
3374  // quest can't reward this title
3375  }
3376 
3377  if (qinfo->SrcItemId)
3378  {
3380  {
3381  sLog.outErrorDb("Quest %u has SrcItemId = %u but item with entry %u does not exist, quest cannot be completed.",
3382  qinfo->GetQuestId(), qinfo->SrcItemId, qinfo->SrcItemId);
3383  qinfo->SrcItemId = 0; // quest can't be done for this requirement
3384  }
3385  else if (qinfo->SrcItemCount == 0)
3386  {
3387  sLog.outErrorDb("Quest %u has SrcItemId = %u but SrcItemCount = 0, set to 1 but needs fixed in DB.",
3388  qinfo->GetQuestId(), qinfo->SrcItemId);
3389  qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward compatibility with DB
3390  }
3391  }
3392  else if (qinfo->SrcItemCount > 0)
3393  {
3394  sLog.outErrorDb("Quest %u has SrcItemId = 0 but SrcItemCount = %u, useless value.",
3395  qinfo->GetQuestId(), qinfo->SrcItemCount);
3396  qinfo->SrcItemCount = 0; // no quest work changes in fact
3397  }
3398 
3399  if (qinfo->SrcSpell)
3400  {
3401  SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
3402  if (!spellInfo)
3403  {
3404  sLog.outErrorDb("Quest %u has SrcSpell = %u but spell %u doesn't exist, quest cannot be completed.",
3405  qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3406  qinfo->SrcSpell = 0; // quest can't be done for this requirement
3407  }
3408  else if (!SpellMgr::IsSpellValid(spellInfo))
3409  {
3410  sLog.outErrorDb("Quest %u has SrcSpell = %u but spell %u is broken, quest cannot be completed.",
3411  qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
3412  qinfo->SrcSpell = 0; // quest can't be done for this requirement
3413  }
3414  }
3415 
3416  for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3417  {
3418  uint32 id = qinfo->ReqItemId[j];
3419  if (id)
3420  {
3421  if (qinfo->ReqItemCount[j] == 0)
3422  {
3423  sLog.outErrorDb("Quest %u has ReqItemId%d = %u but ReqItemCount%d = 0, quest cannot be completed.",
3424  qinfo->GetQuestId(), j + 1, id, j + 1);
3425  // no changes, quest can't be done for this requirement
3426  }
3427 
3429 
3431  {
3432  sLog.outErrorDb("Quest %u has ReqItemId%d = %u but item with entry %u does not exist, quest cannot be completed.",
3433  qinfo->GetQuestId(), j + 1, id, id);
3434  qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3435  }
3436  }
3437  else if (qinfo->ReqItemCount[j] > 0)
3438  {
3439  sLog.outErrorDb("Quest %u has ReqItemId%d = 0 but ReqItemCount%d = %u, quest cannot be completed.",
3440  qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqItemCount[j]);
3441  qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3442  }
3443  }
3444 
3445  for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
3446  {
3447  uint32 id = qinfo->ReqSourceId[j];
3448  if (id)
3449  {
3451  {
3452  sLog.outErrorDb("Quest %u has ReqSourceId%d = %u but item with entry %u does not exist, quest cannot be completed.",
3453  qinfo->GetQuestId(), j + 1, id, id);
3454  // no changes, quest can't be done for this requirement
3455  }
3456  }
3457  else
3458  {
3459  if (qinfo->ReqSourceCount[j] > 0)
3460  {
3461  sLog.outErrorDb("Quest %u has ReqSourceId%d = 0 but ReqSourceCount%d = %u.",
3462  qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqSourceCount[j]);
3463  // no changes, quest ignore this data
3464  }
3465  }
3466  }
3467 
3468  for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3469  {
3470  uint32 id = qinfo->ReqSpell[j];
3471  if (id)
3472  {
3473  SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
3474  if (!spellInfo)
3475  {
3476  sLog.outErrorDb("Quest %u has ReqSpellCast%d = %u but spell %u does not exist, quest cannot be completed.",
3477  qinfo->GetQuestId(), j + 1, id, id);
3478  // no changes, quest can't be done for this requirement
3479  }
3480 
3481  if (!qinfo->ReqCreatureOrGOId[j])
3482  {
3483  bool found = false;
3484  for (int k = 0; k < 3; ++k)
3485  {
3486  if ((spellInfo->Effect[k] == SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k]) == qinfo->QuestId) ||
3487  spellInfo->Effect[k] == SPELL_EFFECT_SEND_EVENT)
3488  {
3489  found = true;
3490  break;
3491  }
3492  }
3493 
3494  if (found)
3495  {
3497  {
3498  sLog.outErrorDb("Spell (id: %u) has SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest does not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->QuestId, j + 1, j + 1);
3499 
3500  // this will prevent quest completing without objective
3501  //const_cast<Quest*>(qinfo)->SetFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
3502  }
3503  }
3504  else
3505  {
3506  sLog.outErrorDb("Quest %u has ReqSpellCast%d = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest cannot be completed.",
3507  qinfo->GetQuestId(), j + 1, id, j + 1, id);
3508  // no changes, quest can't be done for this requirement
3509  }
3510  }
3511  }
3512  }
3513 
3514  for (int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
3515  {
3516  int32 id = qinfo->ReqCreatureOrGOId[j];
3517  if (id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
3518  {
3519  sLog.outErrorDb("Quest %u has ReqCreatureOrGOId%d = %i but gameobject %u does not exist, quest cannot be completed.",
3520  qinfo->GetQuestId(), j + 1, id, uint32(-id));
3521  qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3522  }
3523 
3524  if (id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
3525  {
3526  sLog.outErrorDb("Quest %u has ReqCreatureOrGOId%d = %i but creature with entry %u does not exist, quest cannot be completed.",
3527  qinfo->GetQuestId(), j + 1, id, uint32(id));
3528  qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3529  }
3530 
3531  if (id)
3532  {
3533  // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
3534 
3536 
3537  if (!qinfo->ReqCreatureOrGOCount[j])
3538  {
3539  sLog.outErrorDb("Quest %u has ReqCreatureOrGOId%d = %u but ReqCreatureOrGOCount%d = 0, quest cannot be completed.",
3540  qinfo->GetQuestId(), j + 1, id, j + 1);
3541  // no changes, quest can be incorrectly done, but we already report this
3542  }
3543  }
3544  else if (qinfo->ReqCreatureOrGOCount[j] > 0)
3545  {
3546  sLog.outErrorDb("Quest %u has ReqCreatureOrGOId%d = 0 but ReqCreatureOrGOCount%d = %u.",
3547  qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqCreatureOrGOCount[j]);
3548  // no changes, quest ignore this data
3549  }
3550  }
3551 
3552  for (int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
3553  {
3554  uint32 id = qinfo->RewChoiceItemId[j];
3555  if (id)
3556  {
3558  {
3559  sLog.outErrorDb("Quest %u has RewChoiceItemId%d = %u but item with entry %u does not exist, quest will not reward this item.",
3560  qinfo->GetQuestId(), j + 1, id, id);
3561  qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
3562  }
3563 
3564  if (!qinfo->RewChoiceItemCount[j])
3565  {
3566  sLog.outErrorDb("Quest %u has RewChoiceItemId%d = %u but RewChoiceItemCount%d = 0, quest cannot be completed.",
3567  qinfo->GetQuestId(), j + 1, id, j + 1);
3568  // no changes, quest can't be done
3569  }
3570  }
3571  else if (qinfo->RewChoiceItemCount[j] > 0)
3572  {
3573  sLog.outErrorDb("Quest %u has RewChoiceItemId%d = 0 but RewChoiceItemCount%d = %u.",
3574  qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewChoiceItemCount[j]);
3575  // no changes, quest ignore this data
3576  }
3577  }
3578 
3579  for (int j = 0; j < QUEST_REWARDS_COUNT; ++j)
3580  {
3581  uint32 id = qinfo->RewItemId[j];
3582  if (id)
3583  {
3585  {
3586  sLog.outErrorDb("Quest %u has RewItemId%d = %u but item with entry %u does not exist, quest will not reward this item.",
3587  qinfo->GetQuestId(), j + 1, id, id);
3588  qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
3589  }
3590 
3591  if (!qinfo->RewItemCount[j])
3592  {
3593  sLog.outErrorDb("Quest %u has RewItemId%d = %u but RewItemCount%d = 0, quest will not reward this item.",
3594  qinfo->GetQuestId(), j + 1, id, j + 1);
3595  // no changes
3596  }
3597  }
3598  else if (qinfo->RewItemCount[j] > 0)
3599  {
3600  sLog.outErrorDb("Quest %u has RewItemId%d = 0 but RewItemCount%d = %u.",
3601  qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewItemCount[j]);
3602  // no changes, quest ignore this data
3603  }
3604  }
3605 
3606  for (int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
3607  {
3608  if (qinfo->RewRepFaction[j])
3609  {
3610  if (!qinfo->RewRepValue[j])
3611  {
3612  sLog.outErrorDb("Quest %u has RewRepFaction%d = %u but RewRepValue%d = 0, quest will not reward this reputation.",
3613  qinfo->GetQuestId(), j + 1, qinfo->RewRepValue[j], j + 1);
3614  // no changes
3615  }
3616 
3617  if (!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
3618  {
3619  sLog.outErrorDb("Quest %u has RewRepFaction%d = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
3620  qinfo->GetQuestId(), j + 1, qinfo->RewRepFaction[j] , qinfo->RewRepFaction[j]);
3621  qinfo->RewRepFaction[j] = 0; // quest will not reward this
3622  }
3623  }
3624  else if (qinfo->RewRepValue[j] != 0)
3625  {
3626  sLog.outErrorDb("Quest %u has RewRepFaction%d = 0 but RewRepValue%d = %u.",
3627  qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewRepValue[j]);
3628  // no changes, quest ignore this data
3629  }
3630  }
3631 
3632  if (qinfo->RewSpell)
3633  {
3634  SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
3635 
3636  if (!spellInfo)
3637  {
3638  sLog.outErrorDb("Quest %u has RewSpell = %u but spell %u does not exist, spell removed as display reward.",
3639  qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
3640  qinfo->RewSpell = 0; // no spell reward will display for this quest
3641  }
3642 
3643  else if (!SpellMgr::IsSpellValid(spellInfo))
3644  {
3645  sLog.outErrorDb("Quest %u has RewSpell = %u but spell %u is broken, quest cannot be completed.",
3646  qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
3647  qinfo->RewSpell = 0; // no spell reward will display for this quest
3648  }
3649 
3650  }
3651 
3652  if (qinfo->RewSpellCast)
3653  {
3654  SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
3655 
3656  if (!spellInfo)
3657  {
3658  sLog.outErrorDb("Quest %u has RewSpellCast = %u but spell %u does not exist, quest will not have a spell reward.",
3659  qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
3660  qinfo->RewSpellCast = 0; // no spell will be casted on player
3661  }
3662 
3663  else if (!SpellMgr::IsSpellValid(spellInfo))
3664  {
3665  sLog.outErrorDb("Quest %u has RewSpellCast = %u but spell %u is broken, quest cannot be completed.",
3666  qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
3667  qinfo->RewSpellCast = 0; // no spell will be casted on player
3668  }
3669 
3670  }
3671 
3672  if (qinfo->RewMailTemplateId)
3673  {
3674  if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
3675  {
3676  sLog.outErrorDb("Quest %u has RewMailTemplateId = %u but mail template %u does not exist, quest will not have a mail reward.",
3677  qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId);
3678  qinfo->RewMailTemplateId = 0; // no mail will send to player
3679  qinfo->RewMailDelaySecs = 0; // no mail will send to player
3680  }
3681  }
3682 
3683  if (qinfo->NextQuestInChain)
3684  {
3685  if (mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end())
3686  {
3687  sLog.outErrorDb("Quest %u has NextQuestInChain = %u but quest %u does not exist, quest chain will not work.",
3688  qinfo->GetQuestId(), qinfo->NextQuestInChain , qinfo->NextQuestInChain);
3689  qinfo->NextQuestInChain = 0;
3690  }
3691  else
3692  mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId());
3693  }
3694 
3695  // fill additional data stores
3696  if (qinfo->PrevQuestId)
3697  {
3698  if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
3699  sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
3700  else
3701  qinfo->prevQuests.push_back(qinfo->PrevQuestId);
3702  }
3703 
3704  if (qinfo->NextQuestId)
3705  {
3706  if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end())
3707  sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
3708  else
3709  {
3710  int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
3711  mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId);
3712  }
3713  }
3714 
3715  if (qinfo->ExclusiveGroup)
3716  mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
3717  if (qinfo->LimitTime)
3719  }
3720 
3721  // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
3722  for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
3723  {
3724  SpellEntry const* spellInfo = sSpellStore.LookupEntry(i);
3725  if (!spellInfo)
3726  continue;
3727 
3728  for (int j = 0; j < 3; ++j)
3729  {
3730  if (spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
3731  continue;
3732 
3733  uint32 quest_id = spellInfo->EffectMiscValue[j];
3734 
3735  Quest const* quest = GetQuestTemplate(quest_id);
3736 
3737  // some quest referenced in spells not exist (outdated spells)
3738  if (!quest)
3739  continue;
3740 
3742  {
3743  sLog.outErrorDb("Spell (id: %u) has SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest does not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id);
3744 
3745  // this will prevent quest completing without objective
3746  const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
3747  }
3748  }
3749  }
3750 
3751  sLog.outString(">> Loaded %u quest definitions", mQuestTemplates.size());
3752 }
3753 
3755 {
3756  mQuestLocaleMap.clear(); // need for reload case
3757 
3758  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,"
3759  "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
3760  "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
3761  "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
3762  "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
3763  "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
3764  "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
3765  "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
3766  "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
3767  " FROM locales_quest"
3768  );
3769 
3770  if (!result)
3771  {
3772 
3773 
3774  sLog.outString(">> Loaded 0 Quest locale strings. DB table locales_quest is empty.");
3775  return;
3776  }
3777 
3778 
3779  do
3780  {
3781  Field* fields = result->Fetch();
3782 
3783  uint32 entry = fields[0].GetUInt32();
3784 
3785  QuestLocale& data = mQuestLocaleMap[entry];
3786 
3787  for (int i = 1; i < MAX_LOCALE; ++i)
3788  {
3789  std::string str = fields[1 + 10 * (i - 1)].GetCppString();
3790  if (!str.empty())
3791  {
3792  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3793  if (idx >= 0)
3794  {
3795  if (data.Title.size() <= idx)
3796  data.Title.resize(idx + 1);
3797 
3798  data.Title[idx] = str;
3799  }
3800  }
3801  str = fields[1 + 10 * (i - 1) + 1].GetCppString();
3802  if (!str.empty())
3803  {
3804  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3805  if (idx >= 0)
3806  {
3807  if (data.Details.size() <= idx)
3808  data.Details.resize(idx + 1);
3809 
3810  data.Details[idx] = str;
3811  }
3812  }
3813  str = fields[1 + 10 * (i - 1) + 2].GetCppString();
3814  if (!str.empty())
3815  {
3816  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3817  if (idx >= 0)
3818  {
3819  if (data.Objectives.size() <= idx)
3820  data.Objectives.resize(idx + 1);
3821 
3822  data.Objectives[idx] = str;
3823  }
3824  }
3825  str = fields[1 + 10 * (i - 1) + 3].GetCppString();
3826  if (!str.empty())
3827  {
3828  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3829  if (idx >= 0)
3830  {
3831  if (data.OfferRewardText.size() <= idx)
3832  data.OfferRewardText.resize(idx + 1);
3833 
3834  data.OfferRewardText[idx] = str;
3835  }
3836  }
3837  str = fields[1 + 10 * (i - 1) + 4].GetCppString();
3838  if (!str.empty())
3839  {
3840  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3841  if (idx >= 0)
3842  {
3843  if (data.RequestItemsText.size() <= idx)
3844  data.RequestItemsText.resize(idx + 1);
3845 
3846  data.RequestItemsText[idx] = str;
3847  }
3848  }
3849  str = fields[1 + 10 * (i - 1) + 5].GetCppString();
3850  if (!str.empty())
3851  {
3852  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3853  if (idx >= 0)
3854  {
3855  if (data.EndText.size() <= idx)
3856  data.EndText.resize(idx + 1);
3857 
3858  data.EndText[idx] = str;
3859  }
3860  }
3861  for (int k = 0; k < 4; ++k)
3862  {
3863  str = fields[1 + 10 * (i - 1) + 6 + k].GetCppString();
3864  if (!str.empty())
3865  {
3866  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3867  if (idx >= 0)
3868  {
3869  if (data.ObjectiveText[k].size() <= idx)
3870  data.ObjectiveText[k].resize(idx + 1);
3871 
3872  data.ObjectiveText[k][idx] = str;
3873  }
3874  }
3875  }
3876  }
3877  }
3878  while (result->NextRow());
3879 
3880  sLog.outString(">> Loaded %u Quest locale strings", mQuestLocaleMap.size());
3881 }
3882 
3884 {
3885  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
3886  if (!result)
3887  {
3888 
3889  sLog.outString(">> Loaded 0 pet create spells");
3890  sLog.outErrorDb("petcreateinfo_spell table is empty!");
3891  return;
3892  }
3893 
3894  uint32 count = 0;
3895 
3896 
3897  mPetCreateSpell.clear();
3898 
3899  do
3900  {
3901  Field* fields = result->Fetch();
3902 
3903  uint32 creature_id = fields[0].GetUInt32();
3904 
3905  if (!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
3906  continue;
3907 
3908  PetCreateSpellEntry PetCreateSpell;
3909  for (int i = 0; i < 4; i++)
3910  {
3911  PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
3912 
3913  if (PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i]))
3914  sLog.outErrorDb("Spell %u listed in petcreateinfo_spell does not exist", PetCreateSpell.spellid[i]);
3915  }
3916 
3917  mPetCreateSpell[creature_id] = PetCreateSpell;
3918 
3919  ++count;
3920  }
3921  while (result->NextRow());
3922 
3923  sLog.outString(">> Loaded %u pet create spells", count);
3924 }
3925 
3927 {
3928  ScriptMapMap* scripts = GetScriptsMapByType(type);
3929  if (!scripts)
3930  return;
3931 
3932  std::string tableName = GetScriptsTableNameByType(type);
3933  if (tableName.empty())
3934  return;
3935 
3936  if (sWorld.IsScriptScheduled()) // function don't must be called in time scripts use.
3937  return;
3938 
3939  scripts->clear(); // need for reload support
3940 
3941  QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT id,delay,command,datalong,datalong2,dataint, x, y, z, o FROM %s", tableName.c_str());
3942 
3943  uint32 count = 0;
3944 
3945  if (!result)
3946  {
3947 
3948  sLog.outString(">> Loaded %u script definitions", count);
3949  return;
3950  }
3951 
3952 
3953  do
3954  {
3955 
3956  Field* fields = result->Fetch();
3957  ScriptInfo tmp;
3958  tmp.type = type;
3959  tmp.id = fields[0].GetUInt32();
3960  tmp.delay = fields[1].GetUInt32();
3961  tmp.command = ScriptCommands(fields[2].GetUInt32());
3962  tmp.Raw.nData[0] = fields[3].GetUInt32();
3963  tmp.Raw.nData[1] = fields[4].GetUInt32();
3964  tmp.Raw.nData[2] = fields[5].GetInt32();
3965  tmp.Raw.fData[0] = fields[6].GetFloat();
3966  tmp.Raw.fData[1] = fields[7].GetFloat();
3967  tmp.Raw.fData[2] = fields[8].GetFloat();
3968  tmp.Raw.fData[3] = fields[9].GetFloat();
3969 
3970  // generic command args check
3971  switch (tmp.command)
3972  {
3973  case SCRIPT_COMMAND_TALK:
3974  {
3975  if (tmp.Talk.ChatType > CHAT_TYPE_WHISPER && tmp.Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER)
3976  {
3977  sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",
3978  tableName.c_str(), tmp.Talk.ChatType, tmp.id);
3979  continue;
3980  }
3981  if (!tmp.Talk.TextID)
3982  {
3983  sLog.outErrorDb("Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u",
3984  tableName.c_str(), tmp.Talk.TextID, tmp.id);
3985  continue;
3986  }
3987  if (tmp.Talk.TextID < MIN_DB_SCRIPT_STRING_ID || tmp.Talk.TextID >= MAX_DB_SCRIPT_STRING_ID)
3988  {
3989  sLog.outErrorDb("Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u",
3990  tableName.c_str(), tmp.Talk.TextID, MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, tmp.id);
3991  continue;
3992  }
3993 
3994  break;
3995  }
3996 
3997  case SCRIPT_COMMAND_EMOTE:
3998  {
3999  if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
4000  {
4001  sLog.outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",
4002  tableName.c_str(), tmp.Emote.EmoteID, tmp.id);
4003  continue;
4004  }
4005  break;
4006  }
4007 
4009  {
4010  if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
4011  {
4012  sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
4013  tableName.c_str(), tmp.TeleportTo.MapID, tmp.id);
4014  continue;
4015  }
4016 
4017  if (!Oregon::IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation))
4018  {
4019  sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
4020  tableName.c_str(), tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
4021  continue;
4022  }
4023  break;
4024  }
4025 
4027  {
4028  Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
4029  if (!quest)
4030  {
4031  sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
4032  tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
4033  continue;
4034  }
4035 
4037  {
4038  sLog.outErrorDb("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.",
4039  tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
4040 
4041  // this will prevent quest completing without objective
4042  const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
4043 
4044  // continue; - quest objective requirement set and command can be allowed
4045  }
4046 
4047  if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
4048  {
4049  sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
4050  tableName.c_str(), tmp.QuestExplored.Distance, tmp.id);
4051  continue;
4052  }
4053 
4054  if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
4055  {
4056  sLog.outErrorDb("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",
4057  tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE);
4058  continue;
4059  }
4060 
4061  if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) < INTERACTION_DISTANCE)
4062  {
4063  sLog.outErrorDb("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",
4064  tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
4065  continue;
4066  }
4067 
4068  break;
4069  }
4070 
4072  {
4073  if (!GetCreatureTemplate(tmp.KillCredit.CreatureEntry))
4074  {
4075  sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u",
4076  tableName.c_str(), tmp.KillCredit.CreatureEntry, tmp.id);
4077  continue;
4078  }
4079  break;
4080  }
4081 
4083  {
4084  GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
4085  if (!data)
4086  {
4087  sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4088  tableName.c_str(), tmp.RespawnGameobject.GOGuid, tmp.id);
4089  continue;
4090  }
4091 
4092  GameObjectInfo const* info = GetGameObjectInfo(data->id);
4093  if (!info)
4094  {
4095  sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4096  tableName.c_str(), tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
4097  continue;
4098  }
4099 
4100  if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
4101  info->type == GAMEOBJECT_TYPE_FISHINGHOLE ||
4102  info->type == GAMEOBJECT_TYPE_DOOR ||
4103  info->type == GAMEOBJECT_TYPE_BUTTON ||
4104  info->type == GAMEOBJECT_TYPE_TRAP)
4105  {
4106  sLog.outErrorDb("Table `%s` has gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
4107  tableName.c_str(), info->id, tmp.id);
4108  continue;
4109  }
4110  break;
4111  }
4112 
4114  {
4115  if (!Oregon::IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation))
4116  {
4117  sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
4118  tableName.c_str(), tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id);
4119  continue;
4120  }
4121 
4122  if (!GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry))
4123  {
4124  sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
4125  tableName.c_str(), tmp.TempSummonCreature.CreatureEntry, tmp.id);
4126  continue;
4127  }
4128  break;
4129  }
4130 
4133  {
4134  GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
4135  if (!data)
4136  {
4137  sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
4138  tableName.c_str(), tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4139  continue;
4140  }
4141 
4142  GameObjectInfo const* info = GetGameObjectInfo(data->id);
4143  if (!info)
4144  {
4145  sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",
4146  tableName.c_str(), tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4147  continue;
4148  }
4149 
4150  if (info->type != GAMEOBJECT_TYPE_DOOR)
4151  {
4152  sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u",
4153  tableName.c_str(), info->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
4154  continue;
4155  }
4156 
4157  break;
4158  }
4159 
4161  {
4162  if (!sSpellStore.LookupEntry(tmp.RemoveAura.SpellID))
4163  {
4164  sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
4165  tableName.c_str(), tmp.RemoveAura.SpellID, tmp.id);
4166  continue;
4167  }
4168  if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0,1)
4169  {
4170  sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
4171  tableName.c_str(), tmp.RemoveAura.Flags, tmp.id);
4172  continue;
4173  }
4174  break;
4175  }
4176 
4178  {
4179  if (!sSpellStore.LookupEntry(tmp.CastSpell.SpellID))
4180  {
4181  sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4182  tableName.c_str(), tmp.CastSpell.SpellID, tmp.id);
4183  continue;
4184  }
4185  if (tmp.CastSpell.Flags > 4) // targeting type
4186  {
4187  sLog.outErrorDb("Table `%s` using unknown target in datalong2 (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4188  tableName.c_str(), tmp.CastSpell.Flags, tmp.id);
4189  continue;
4190  }
4191  if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0,1)
4192  {
4193  sLog.outErrorDb("Table `%s` using unknown flags in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4194  tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
4195  continue;
4196  }
4197  else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
4198  {
4199  sLog.outErrorDb("Table `%s` using invalid creature entry in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
4200  tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
4201  continue;
4202  }
4203  break;
4204  }
4205 
4207  {
4208  if (!GetItemTemplate(tmp.CreateItem.ItemEntry))
4209  {
4210  sLog.outErrorDb("Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u",
4211  tableName.c_str(), tmp.CreateItem.ItemEntry, tmp.id);
4212  continue;
4213  }
4214  if (!tmp.CreateItem.Amount)
4215  {
4216  sLog.outErrorDb("Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u",
4217  tableName.c_str(), tmp.CreateItem.Amount, tmp.id);
4218  continue;
4219  }
4220  break;
4221  }
4222  default:
4223  break;
4224  }
4225 
4226  if (scripts->find(tmp.id) == scripts->end())
4227  {
4228  ScriptMap emptyMap;
4229  (*scripts)[tmp.id] = emptyMap;
4230  }
4231  (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
4232 
4233  ++count;
4234  }
4235  while (result->NextRow());
4236 
4237  sLog.outString(">> Loaded %u script definitions", count);
4238 }
4239 
4241 {
4242  LoadScripts(SCRIPTS_GAMEOBJECT);
4243 
4244  // check ids
4245  for (ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
4246  {
4247  if (!GetGOData(itr->first))
4248  sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id", itr->first);
4249  }
4250 }
4251 
4253 {
4254  LoadScripts(SCRIPTS_QUEST_END);
4255 
4256  // check ids
4257  for (ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
4258  {
4259  if (!GetQuestTemplate(itr->first))
4260  sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id", itr->first);
4261  }
4262 }
4263 
4265 {
4266  LoadScripts(SCRIPTS_QUEST_START);
4267 
4268  // check ids
4269  for (ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
4270  {
4271  if (!GetQuestTemplate(itr->first))
4272  sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id", itr->first);
4273  }
4274 }
4275 
4277 {
4278  LoadScripts(SCRIPTS_SPELL);
4279 
4280  // check ids
4281  for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
4282  {
4283  SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
4284 
4285  if (!spellInfo)
4286  {
4287  sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id", itr->first);
4288  continue;
4289  }
4290 
4291  //check for correct spellEffect
4292  bool found = false;
4293  for (int i = 0; i < 3; ++i)
4294  {
4295  // skip empty effects
4296  if (!spellInfo->Effect[i])
4297  continue;
4298 
4299  if (spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT)
4300  {
4301  found = true;
4302  break;
4303  }
4304  }
4305 
4306  if (!found)
4307  sLog.outErrorDb("Table spell_scripts has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect", itr->first, SPELL_EFFECT_SCRIPT_EFFECT);
4308  }
4309 }
4310 
4312 {
4313  LoadScripts(SCRIPTS_EVENT);
4314 
4315  std::set<uint32> evt_scripts;
4316  // Load all possible script entries from gameobjects
4317  for (uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
4318  {
4320  if (goInfo)
4321  {
4322  switch (goInfo->type)
4323  {
4325  if (goInfo->goober.eventId)
4326  evt_scripts.insert(goInfo->goober.eventId);
4327  break;
4328  case GAMEOBJECT_TYPE_CHEST:
4329  if (goInfo->chest.eventId)
4330  evt_scripts.insert(goInfo->chest.eventId);
4331  break;
4333  if (goInfo->camera.eventID)
4334  evt_scripts.insert(goInfo->camera.eventID);
4335  default:
4336  break;
4337  }
4338  }
4339  }
4340  // Load all possible script entries from spells
4341  for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
4342  {
4343  SpellEntry const* spell = sSpellStore.LookupEntry(i);
4344  if (spell)
4345  {
4346  for (uint8 j = 0; j < 3; ++j)
4347  {
4348  if (spell->Effect[j] == SPELL_EFFECT_SEND_EVENT)
4349  {
4350  if (spell->EffectMiscValue[j])
4351  evt_scripts.insert(spell->EffectMiscValue[j]);
4352  }
4353  }
4354  }
4355  }
4356 
4357  for(size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
4358  {
4359  for(size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
4360  {
4361  TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx];
4362 
4363  if (node.arrivalEventID)
4364  evt_scripts.insert(node.arrivalEventID);
4365 
4366  if (node.departureEventID)
4367  evt_scripts.insert(node.departureEventID);
4368  }
4369  }
4370 
4371  // Then check if all scripts are in above list of possible script entries
4372  for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
4373  {
4374  std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
4375  if (itr2 == evt_scripts.end())
4376  sLog.outErrorDb("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 or path taxi node data",
4377  itr->first, SPELL_EFFECT_SEND_EVENT);
4378  }
4379 }
4380 
4381 //Load WP Scripts
4383 {
4384  LoadScripts(SCRIPTS_WAYPOINT);
4385 
4386  std::set<uint32> actionSet;
4387 
4388  for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
4389  actionSet.insert(itr->first);
4390 
4391  QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT DISTINCT(`action`) FROM waypoint_data");
4392  if (result)
4393  {
4394  do
4395  {
4396  Field* fields = result->Fetch();
4397  uint32 action = fields[0].GetUInt32();
4398 
4399  actionSet.erase(action);
4400 
4401  }
4402  while (result->NextRow());
4403  }
4404 
4405  for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
4406  sLog.outErrorDb("There is no waypoint which links to the waypoint script %u", *itr);
4407 }
4408 
4410 {
4411  LoadScripts(SCRIPTS_GOSSIP);
4412 
4413  // checks are done in LoadGossipMenuItems
4414 }
4415 
4417 {
4418  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT id, text FROM item_text");
4419 
4420  uint32 count = 0;
4421 
4422  if (!result)
4423  {
4424 
4425  sLog.outString(">> Loaded %u item pages", count);
4426  return;
4427  }
4428 
4429 
4430  Field* fields;
4431  do
4432  {
4433 
4434  fields = result->Fetch();
4435 
4436  mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
4437 
4438  ++count;
4439 
4440  }
4441  while (result->NextRow());
4442 
4443  sLog.outString(">> Loaded %u item texts", count);
4444 }
4445 
4447 {
4448  sPageTextStore.Load();
4449  sLog.outString(">> Loaded %u page texts", sPageTextStore.RecordCount);
4450 
4451  for (uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
4452  {
4453  // check data correctness
4454  PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
4455  if (!page)
4456  continue;
4457 
4458  if (page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
4459  {
4460  sLog.outErrorDb("Page text (Id: %u) has invalid next page (Id:%u)", i, page->Next_Page);
4461  continue;
4462  }
4463 
4464  // detect circular reference
4465  std::set<uint32> checkedPages;
4466  for (PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
4467  {
4468  if (!pageItr->Next_Page)
4469  break;
4470  checkedPages.insert(pageItr->Page_ID);
4471  if (checkedPages.find(pageItr->Next_Page) != checkedPages.end())
4472  {
4473  std::ostringstream ss;
4474  ss << "The text page(s) ";
4475  for (std::set<uint32>::iterator itr = checkedPages.begin(); itr != checkedPages.end(); ++itr)
4476  ss << *itr << " ";
4477  ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4478  << pageItr->Page_ID << " to 0";
4479  sLog.outErrorDb("%s", ss.str().c_str());
4480  const_cast<PageText*>(pageItr)->Next_Page = 0;
4481  break;
4482  }
4483  }
4484  }
4485 }
4486 
4488 {
4489  mPageTextLocaleMap.clear(); // need for reload case
4490 
4491  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
4492 
4493  if (!result)
4494  {
4495 
4496 
4497  sLog.outString(">> Loaded 0 PageText locale strings. DB table locales_page_text is empty.");
4498  return;
4499  }
4500 
4501 
4502  do
4503  {
4504  Field* fields = result->Fetch();
4505 
4506  uint32 entry = fields[0].GetUInt32();
4507 
4508  PageTextLocale& data = mPageTextLocaleMap[entry];
4509 
4510  for (uint8 i = 1; i < MAX_LOCALE; ++i)
4511  {
4512  std::string str = fields[i].GetCppString();
4513  if (str.empty())
4514  continue;
4515 
4516  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4517  if (idx >= 0)
4518  {
4519  if (data.Text.size() <= idx)
4520  data.Text.resize(idx + 1);
4521 
4522  data.Text[idx] = str;
4523  }
4524  }
4525 
4526  }
4527  while (result->NextRow());
4528 
4529  sLog.outString(">> Loaded %lu PageText locale strings", mPageTextLocaleMap.size());
4530 }
4531 
4532 struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader>
4533 {
4534  template<class D>
4535  void convert_from_str(uint32 field_pos, char* src, D& dst)
4536  {
4537  dst = D(sObjectMgr.GetScriptId(src));
4538  }
4539 };
4540 
4542 {
4543  SQLInstanceLoader loader;
4544  loader.Load(sInstanceTemplate);
4545 
4546  for (uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
4547  {
4548  InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
4549  if (!temp) continue;
4550  const MapEntry* entry = sMapStore.LookupEntry(temp->map);
4551  if (!entry)
4552  {
4553  sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
4554  continue;
4555  }
4556  else if (!entry->HasResetTime())
4557  continue;
4558 
4559  if (temp->reset_delay == 0)
4560  {
4561  // use defaults from the DBC
4562  if (entry->SupportsHeroicMode())
4563  temp->reset_delay = entry->resetTimeHeroic / DAY;
4564  else if (entry->resetTimeRaid && entry->IsRaid())
4565  temp->reset_delay = entry->resetTimeRaid / DAY;
4566  }
4567 
4568  // the reset_delay must be at least one day
4569  temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
4570  }
4571 
4572  sLog.outString(">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount);
4573 }
4574 
4576 {
4577  ASSERT(pGText->Text_ID);
4578  ASSERT(mGossipText.find(pGText->Text_ID) == mGossipText.end());
4579  mGossipText[pGText->Text_ID] = pGText;
4580 }
4581 
4583 {
4584  GossipTextMap::const_iterator itr;
4585  for (itr = mGossipText.begin(); itr != mGossipText.end(); ++itr)
4586  {
4587  if (itr->second->Text_ID == Text_ID)
4588  return itr->second;
4589  }
4590  return NULL;
4591 }
4592 
4594 {
4595  GossipText* pGText;
4596  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT * FROM npc_text");
4597 
4598  int count = 0;
4599  if (!result)
4600  {
4601 
4602  sLog.outString(">> Loaded %u npc texts", count);
4603  return;
4604  }
4605 
4606  int cic;
4607 
4608 
4609  do
4610  {
4611  ++count;
4612  cic = 0;
4613 
4614  Field* fields = result->Fetch();
4615 
4616 
4617  pGText = new GossipText;
4618  pGText->Text_ID = fields[cic++].GetUInt32();
4619 
4620  for (int i = 0; i < 8; i++)
4621  {
4622  pGText->Options[i].Text_0 = fields[cic++].GetCppString();
4623  pGText->Options[i].Text_1 = fields[cic++].GetCppString();
4624 
4625  pGText->Options[i].Language = fields[cic++].GetUInt32();
4626  pGText->Options[i].Probability = fields[cic++].GetFloat();
4627 
4628  pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32();
4629  pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32();
4630 
4631  pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32();
4632  pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32();
4633 
4634  pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32();
4635  pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32();
4636  }
4637 
4638  if (!pGText->Text_ID)
4639  {
4640  delete pGText;
4641  continue;
4642  }
4643 
4644  AddGossipText(pGText);
4645 
4646  }
4647  while (result->NextRow());
4648 
4649  sLog.outString(">> Loaded %u npc texts", count);
4650 }
4651 
4653 {
4654  mNpcTextLocaleMap.clear(); // need for reload case
4655 
4656  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,"
4657  "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,"
4658  "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,"
4659  "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,"
4660  "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,"
4661  "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,"
4662  "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,"
4663  "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, "
4664  "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 "
4665  " FROM locales_npc_text");
4666 
4667  if (!result)
4668  {
4669 
4670 
4671  sLog.outString(">> Loaded 0 Quest locale strings. DB table locales_npc_text is empty.");
4672  return;
4673  }
4674 
4675 
4676  do
4677  {
4678  Field* fields = result->Fetch();
4679 
4680  uint32 entry = fields[0].GetUInt32();
4681 
4682  NpcTextLocale& data = mNpcTextLocaleMap[entry];
4683 
4684  for (uint8 i = 1; i < MAX_LOCALE; ++i)
4685  {
4686  for (uint8 j = 0; j < 8; ++j)
4687  {
4688  std::string str0 = fields[1 + 8 * 2 * (i - 1) + 2 * j].GetCppString();
4689  if (!str0.empty())
4690  {
4691  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4692  if (idx >= 0)
4693  {
4694  if (data.Text_0[j].size() <= idx)
4695  data.Text_0[j].resize(idx + 1);
4696 
4697  data.Text_0[j][idx] = str0;
4698  }
4699  }
4700  std::string str1 = fields[1 + 8 * 2 * (i - 1) + 2 * j + 1].GetCppString();
4701  if (!str1.empty())
4702  {
4703  int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4704  if (idx >= 0)
4705  {
4706  if (data.Text_1[j].size() <= idx)
4707  data.Text_1[j].resize(idx + 1);
4708 
4709  data.Text_1[j][idx] = str1;
4710  }
4711  }
4712  }
4713  }
4714  }
4715  while (result->NextRow());
4716 
4717  sLog.outString(">> Loaded %lu NpcText locale strings", mNpcTextLocaleMap.size());
4718 }
4719 
4720 //not very fast function but it is called only once a day, or on starting-up
4722 {
4723  time_t basetime = time(NULL);
4724  sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
4725  //delete all old mails without item and without body immediately, if starting server
4726  if (!serverUp)
4727  CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
4728  // 0 1 2 3 4 5 6 7 8 9
4729  QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD "'", (uint64)basetime);
4730  if (!result)
4731  return; // any mails need to be returned or deleted
4732  Field* fields;
4733  //std::ostringstream delitems, delmails; //will be here for optimization
4734  //bool deletemail = false, deleteitem = false;
4735  //delitems << "DELETE FROM item_instance WHERE guid IN (";
4736  //delmails << "DELETE FROM mail WHERE id IN ("
4737  do
4738  {
4739  fields = result->Fetch();
4740  Mail* m = new Mail;
4741  m->messageID = fields[0].GetUInt32();
4742  m->messageType = fields[1].GetUInt8();
4743  m->sender = fields[2].GetUInt32();
4744  m->receiver = fields[3].GetUInt32();
4745  m->itemTextId = fields[4].GetUInt32();
4746  bool has_items = fields[5].GetBool();
4747  m->expire_time = (time_t)fields[6].GetUInt64();
4748  m->deliver_time = 0;
4749  m->COD = fields[7].GetUInt32();
4750  m->checked = fields[8].GetUInt32();
4751  m->mailTemplateId = fields[9].GetInt16();
4752 
4753  Player* pl = 0;
4754  if (serverUp)
4755  pl = GetPlayer((uint64)m->receiver);
4756  if (pl && pl->m_mailsLoaded)
4757  {
4758  //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
4759  //his in mailbox and he has already listed his mails)
4760  delete m;
4761  continue;
4762  }
4763  //delete or return mail:
4764  if (has_items)
4765  {
4766  QueryResult_AutoPtr resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
4767  if (resultItems)
4768  {
4769  do
4770  {
4771  Field* fields2 = resultItems->Fetch();
4772 
4773  uint32 item_guid_low = fields2[0].GetUInt32();
4774  uint32 item_template = fields2[1].GetUInt32();
4775 
4776  m->AddItem(item_guid_low, item_template);
4777  }
4778  while (resultItems->NextRow());
4779  }
4780  // if it is mail from AH, it shouldn't be returned, but deleted
4782  {
4783  // mail open and then not returned
4784  for (std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
4785  CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
4786  }
4787  else
4788  {
4789  //mail will be returned:
4790  CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30 * DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
4791  delete m;
4792  continue;
4793  }
4794  }
4795 
4796  if (m->itemTextId)
4797  CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
4798 
4799  //deletemail = true;
4800  //delmails << m->messageID << ", ";
4801  CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
4802  delete m;
4803  }
4804  while (result->NextRow());
4805 }
4806 
4808 {
4809  mQuestAreaTriggerMap.clear(); // need for reload case
4810 
4811  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id,quest FROM areatrigger_involvedrelation");
4812 
4813  uint32 count = 0;
4814 
4815  if (!result)
4816  {
4817 
4818  sLog.outString(">> Loaded %u quest trigger points", count);
4819  return;
4820  }
4821 
4822 
4823  do
4824  {
4825  ++count;
4826 
4827  Field* fields = result->Fetch();
4828 
4829  uint32 trigger_ID = fields[0].GetUInt32();
4830  uint32 quest_ID = fields[1].GetUInt32();
4831 
4832  AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
4833  if (!atEntry)
4834  {
4835  sLog.outErrorDb("Area trigger (ID:%u) does not exist in AreaTrigger.dbc.", trigger_ID);
4836  continue;
4837  }
4838 
4839  Quest const* quest = GetQuestTemplate(quest_ID);
4840 
4841  if (!quest)
4842  {
4843  sLog.outErrorDb("Table areatrigger_involvedrelation has record (id: %u) for invalid quest %u", trigger_ID, quest_ID);
4844  continue;
4845  }
4846 
4848  {
4849  sLog.outErrorDb("Table areatrigger_involvedrelation has record (id: %u) for not quest %u, but quest does not have flag QUEST_OREGON_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
4850 
4851  // this will prevent quest completing without objective
4852  const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
4853 
4854  // continue; - quest modified to required objective and trigger can be allowed.
4855  }
4856 
4857  mQuestAreaTriggerMap[trigger_ID] = quest_ID;
4858 
4859  }
4860  while (result->NextRow());
4861 
4862  sLog.outString(">> Loaded %u quest trigger points", count);
4863 }
4864 
4866 {
4867  mTavernAreaTriggerSet.clear(); // need for reload case
4868 
4869  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
4870 
4871  uint32 count = 0;
4872 
4873  if (!result)
4874  {
4875 
4876  sLog.outString(">> Loaded %u tavern triggers", count);
4877  return;
4878  }
4879 
4880 
4881  do
4882  {
4883  ++count;
4884 
4885  Field* fields = result->Fetch();
4886 
4887  uint32 Trigger_ID = fields[0].GetUInt32();
4888 
4889  AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4890  if (!atEntry)
4891  {
4892  sLog.outErrorDb("Area trigger (ID:%u) does not exist in AreaTrigger.dbc.", Trigger_ID);
4893  continue;
4894  }
4895 
4896  mTavernAreaTriggerSet.insert(Trigger_ID);
4897  }
4898  while (result->NextRow());
4899 
4900  sLog.outString(">> Loaded %u tavern triggers", count);
4901 }
4902 
4904 {
4905  mAreaTriggerScripts.clear(); // need for reload case
4906  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
4907 
4908  uint32 count = 0;
4909 
4910  if (!result)
4911  {
4912 
4913  sLog.outString(">> Loaded %u areatrigger scripts", count);
4914  return;
4915  }
4916 
4917 
4918  do
4919  {
4920  ++count;
4921 
4922  Field* fields = result->Fetch();
4923 
4924  uint32 Trigger_ID = fields[0].GetUInt32();
4925  const char* scriptName = fields[1].GetString();
4926 
4927  AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4928  if (!atEntry)
4929  {
4930  sLog.outErrorDb("Area trigger (ID:%u) does not exist in AreaTrigger.dbc.", Trigger_ID);
4931  continue;
4932  }
4933  mAreaTriggerScripts[Trigger_ID] = GetScriptId(scriptName);
4934  }
4935  while (result->NextRow());
4936 
4937  sLog.outString(">> Loaded %u areatrigger scripts", count);
4938 }
4939 
4940 uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid)
4941 {
4942  bool found = false;
4943  float dist;
4944  uint32 id = 0;
4945 
4946  for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
4947  {
4948  TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
4949  if (node && node->map_id == mapid)
4950  {
4951  float dist2 = (node->x - x) * (node->x - x) + (node->y - y) * (node->y - y) + (node->z - z) * (node->z - z);
4952  if (found)
4953  {
4954  if (dist2 < dist)
4955  {
4956  dist = dist2;
4957  id = i;
4958  }
4959  }
4960  else
4961  {
4962  found = true;
4963  dist = dist2;
4964  id = i;
4965  }
4966  }
4967  }
4968 
4969  return id;
4970 }
4971 
4972 void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32& path, uint32& cost)
4973 {
4974  TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
4975  if (src_i == sTaxiPathSetBySource.end())
4976  {
4977  path = 0;
4978  cost = 0;
4979  return;
4980  }
4981 
4982  TaxiPathSetForSource& pathSet = src_i->second;
4983 
4984  TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
4985  if (dest_i == pathSet.end())
4986  {
4987  path = 0;
4988  cost = 0;
4989  return;
4990  }
4991 
4992  cost = dest_i->second.price;
4993  path = dest_i->second.ID;
4994 }
4995 
4997 {
4998  uint16 mount_entry = 0;
4999  uint16 mount_id = 0;
5000 
5001  TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
5002  if (node)
5003  {
5004  if (team == ALLIANCE) mount_entry = node->alliance_mount_type;
5005  else mount_entry = node->horde_mount_type;
5006 
5007  CreatureInfo const* cinfo = GetCreatureTemplate(mount_entry);
5008  if (cinfo)
5009  {
5010  if (! (mount_id = cinfo->GetRandomValidModelId()))
5011  {
5012  sLog.outErrorDb("No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry);
5013  return false;
5014  }
5015  }
5016  }
5017 
5018  CreatureModelInfo const* minfo = GetCreatureModelInfo(mount_id);
5019  if (!minfo)
5020  {
5021  sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table creature_model_info, can't load. ",
5022  mount_entry, id, team, mount_id);
5023 
5024  return false;
5025  }
5026  if (minfo->modelid_other_gender != 0)
5027  mount_id = urand(0, 1) ? mount_id : minfo->modelid_other_gender;
5028 
5029  return mount_id;
5030 }
5031 
5033 {
5034  mGraveYardMap.clear(); // need for reload case
5035 
5036  QueryResult_AutoPtr result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
5037 
5038  uint32 count = 0;
5039 
5040  if (!result)
5041  {
5042 
5043  sLog.outString(">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
5044  return;
5045  }
5046 
5047  do
5048  {
5049  ++count;
5050 
5051  Field* fields = result->Fetch();
5052 
5053  uint32 safeLocId = fields[0].GetUInt32();
5054  uint32 zoneId = fields[1].GetUInt32();
5055  uint32 team = fields[2].GetUInt32();
5056 
5057  WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
5058  if (!entry)
5059  {
5060  sLog.outErrorDb("Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: %u), skipped.", safeLocId);
5061  continue;
5062  }
5063 
5064  AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId);
5065  if (!areaEntry)
5066  {
5067  sLog.outErrorDb("Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId);
5068  continue;
5069  }
5070 
5071  if (areaEntry->zone != 0)
5072  {
5073  sLog.outErrorDb("Table `graveyard_zone` has a record for SubZone (ID: %u) instead of zone, skipped.", zoneId);
5074  continue;
5075  }
5076 
5077  if (team != 0 && team != HORDE && team != ALLIANCE)
5078  {
5079  sLog.outErrorDb("Table `graveyard_zone` has a record for non player faction (%u), skipped.", team);
5080  continue;
5081  }
5082 
5083  if (!AddGraveYardLink(safeLocId, zoneId, team, false))
5084  sLog.outErrorDb("Table `graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
5085  }
5086  while (result->NextRow());
5087 
5088  sLog.outString(">> Loaded %u graveyard-zone links", count);
5089 }
5090 
5092 {
5093  enum DefaultGraveyard
5094  {
5095  HORDE_GRAVEYARD = 10, // Crossroads
5096  ALLIANCE_GRAVEYARD = 4 // Westfall
5097  };
5098 
5099  if (team == HORDE)
5100  return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
5101  else if (team == ALLIANCE)
5102  return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
5103  else return NULL;
5104 }
5105 
5106 WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
5107 {
5108  // search for zone associated closest graveyard
5109  uint32 zoneId = MapManager::Instance().GetZoneId(MapId, x, y, z);
5110 
5111  if (!zoneId)
5112  {
5113  if (z > -500)
5114  {
5115  sLog.outError("ZoneId not found for map %u coords (%f, %f, %f)", MapId, x, y, z);
5116  return GetDefaultGraveYard(team);
5117  }
5118  }
5119  // Simulate std. algorithm:
5120  // found some graveyard associated to (ghost_zone,ghost_map)
5121  //
5122  // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5123  // then check faction
5124  // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5125  // then check faction
5126  GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
5127  GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
5128  if (graveLow == graveUp)
5129  {
5130  sLog.outErrorDb("Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
5131  return NULL;
5132  }
5133 
5134  // at corpse map
5135  bool foundNear = false;
5136  float distNear = 10000;
5137  WorldSafeLocsEntry const* entryNear = NULL;
5138 
5139  // at entrance map for corpse map
5140  bool foundEntr = false;
5141  float distEntr = 10000;
5142  WorldSafeLocsEntry const* entryEntr = NULL;
5143 
5144  // some where other
5145  WorldSafeLocsEntry const* entryFar = NULL;
5146 
5147  MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
5148 
5149  for (GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
5150  {
5151  GraveYardData const& data = itr->second;
5152 
5153  WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
5154  if (!entry)
5155  {
5156  sLog.outErrorDb("Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID %u), skipped.", data.safeLocId);
5157  continue;
5158  }
5159 
5160  // skip enemy faction graveyard
5161  // team == 0 case can be at call from .neargrave
5162  if (data.team != 0 && team != 0 && data.team != team)
5163  continue;
5164 
5165  // find now nearest graveyard at other map
5166  if (MapId != entry->map_id)
5167  {
5168  // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5169  if (!mapEntry
5170  || mapEntry->entrance_map < 0
5171  || mapEntry->entrance_map != entry->map_id
5172  || mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0)
5173  {
5174  // not have any corrdinates for check distance anyway
5175  entryFar = entry;
5176  continue;
5177  }
5178 
5179  // at entrance map calculate distance (2D);
5180  float dist2 = (entry->x - mapEntry->entrance_x) * (entry->x - mapEntry->entrance_x)
5181  + (entry->y - mapEntry->entrance_y) * (entry->y - mapEntry->entrance_y);
5182  if (foundEntr)
5183  {
5184  if (dist2 < distEntr)
5185  {
5186  distEntr = dist2;
5187  entryEntr = entry;
5188  }
5189  }
5190  else
5191  {
5192  foundEntr = true;
5193  distEntr = dist2;
5194  entryEntr = entry;
5195  }
5196  }
5197  // find now nearest graveyard at same map
5198  else
5199  {
5200  float dist2 = (entry->x - x) * (entry->x - x) + (entry->y - y) * (entry->y - y) + (entry->z - z) * (entry->z - z);
5201  if (foundNear)
5202  {
5203  if (dist2 < distNear)
5204  {
5205  distNear = dist2;
5206  entryNear = entry;
5207  }
5208  }
5209  else
5210  {
5211  foundNear = true;
5212  distNear = dist2;
5213  entryNear = entry;
5214  }
5215  }
5216  }
5217 
5218  if (entryNear)
5219  return entryNear;
5220 
5221  if (entryEntr)
5222  return entryEntr;
5223 
5224  return entryFar;
5225 }
5226 
5228 {
5229  GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
5230  GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
5231 
5232  for (GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
5233  {
5234  if (itr->second.safeLocId == id)
5235  return &itr->second;
5236  }
5237 
5238  return NULL;
5239 }
5240 
5241 bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
5242 {
5243  if (FindGraveYardData(id, zoneId))
5244  return false;
5245 
5246  // add link to loaded data
5247  GraveYardData data;
5248  data.safeLocId = id;
5249  data.team = team;
5250 
5251  mGraveYardMap.insert(GraveYardMap::value_type(zoneId, data));
5252 
5253  // add link to DB
5254  if (inDB)