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