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