Advertisement
Guest User

Untitled

a guest
Feb 2nd, 2018
185
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 400.91 KB | None | 0 0
  1. /*
  2.  * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
  3.  * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License as published by the
  7.  * Free Software Foundation; either version 2 of the License, or (at your
  8.  * option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful, but WITHOUT
  11.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13.  * more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License along
  16.  * with this program. If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. #include "ObjectMgr.h"
  20. #include "AchievementMgr.h"
  21. #include "ArenaTeamMgr.h"
  22. #include "Bag.h"
  23. #include "Chat.h"
  24. #include "Containers.h"
  25. #include "CreatureAIFactory.h"
  26. #include "DatabaseEnv.h"
  27. #include "DisableMgr.h"
  28. #include "GameObject.h"
  29. #include "GameObjectAIFactory.h"
  30. #include "GameTime.h"
  31. #include "GossipDef.h"
  32. #include "GroupMgr.h"
  33. #include "GuildMgr.h"
  34. #include "InstanceSaveMgr.h"
  35. #include "InstanceScript.h"
  36. #include "Language.h"
  37. #include "LFGMgr.h"
  38. #include "Log.h"
  39. #include "LootMgr.h"
  40. #include "Mail.h"
  41. #include "MapManager.h"
  42. #include "MotionMaster.h"
  43. #include "ObjectAccessor.h"
  44. #include "Player.h"
  45. #include "PoolMgr.h"
  46. #include "QueryPackets.h"
  47. #include "Random.h"
  48. #include "ReputationMgr.h"
  49. #include "ScriptMgr.h"
  50. #include "SpellAuras.h"
  51. #include "SpellMgr.h"
  52. #include "SpellScript.h"
  53. #include "TemporarySummon.h"
  54. #include "UpdateMask.h"
  55. #include "Util.h"
  56. #include "Vehicle.h"
  57. #include "World.h"
  58.  
  59. ScriptMapMap sSpellScripts;
  60. ScriptMapMap sEventScripts;
  61. ScriptMapMap sWaypointScripts;
  62.  
  63. std::string GetScriptsTableNameByType(ScriptsType type)
  64. {
  65.     std::string res = "";
  66.     switch (type)
  67.     {
  68.         case SCRIPTS_SPELL:         res = "spell_scripts";      break;
  69.         case SCRIPTS_EVENT:         res = "event_scripts";      break;
  70.         case SCRIPTS_WAYPOINT:      res = "waypoint_scripts";   break;
  71.         default: break;
  72.     }
  73.     return res;
  74. }
  75.  
  76. ScriptMapMap* GetScriptsMapByType(ScriptsType type)
  77. {
  78.     ScriptMapMap* res = nullptr;
  79.     switch (type)
  80.     {
  81.         case SCRIPTS_SPELL:         res = &sSpellScripts;       break;
  82.         case SCRIPTS_EVENT:         res = &sEventScripts;       break;
  83.         case SCRIPTS_WAYPOINT:      res = &sWaypointScripts;    break;
  84.         default: break;
  85.     }
  86.     return res;
  87. }
  88.  
  89. std::string GetScriptCommandName(ScriptCommands command)
  90. {
  91.     std::string res = "";
  92.     switch (command)
  93.     {
  94.         case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
  95.         case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
  96.         case SCRIPT_COMMAND_FIELD_SET: res = "SCRIPT_COMMAND_FIELD_SET"; break;
  97.         case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
  98.         case SCRIPT_COMMAND_FLAG_SET: res = "SCRIPT_COMMAND_FLAG_SET"; break;
  99.         case SCRIPT_COMMAND_FLAG_REMOVE: res = "SCRIPT_COMMAND_FLAG_REMOVE"; break;
  100.         case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
  101.         case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
  102.         case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
  103.         case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
  104.         case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
  105.         case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
  106.         case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
  107.         case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
  108.         case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
  109.         case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
  110.         case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
  111.         case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
  112.         case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
  113.         case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
  114.         case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
  115.         case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
  116.         // TrinityCore only
  117.         case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
  118.         case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
  119.         case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
  120.         case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
  121.         case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
  122.         case SCRIPT_COMMAND_MOVEMENT: res = "SCRIPT_COMMAND_MOVEMENT"; break;
  123.         default:
  124.         {
  125.             char sz[32];
  126.             sprintf(sz, "Unknown command: %d", command);
  127.             res = sz;
  128.             break;
  129.         }
  130.     }
  131.     return res;
  132. }
  133.  
  134. std::string ScriptInfo::GetDebugInfo() const
  135. {
  136.     char sz[256];
  137.     sprintf(sz, "%s ('%s' script id: %u)", GetScriptCommandName(command).c_str(), GetScriptsTableNameByType(type).c_str(), id);
  138.     return std::string(sz);
  139. }
  140.  
  141. bool normalizePlayerName(std::string& name)
  142. {
  143.     if (name.empty())
  144.         return false;
  145.  
  146.     wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
  147.     size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
  148.  
  149.     if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
  150.         return false;
  151.  
  152.     wstr_buf[0] = wcharToUpper(wstr_buf[0]);
  153.     for (size_t i = 1; i < wstr_len; ++i)
  154.         wstr_buf[i] = wcharToLower(wstr_buf[i]);
  155.  
  156.     if (!WStrToUtf8(wstr_buf, wstr_len, name))
  157.         return false;
  158.  
  159.     return true;
  160. }
  161.  
  162. LanguageDesc lang_description[LANGUAGES_COUNT] =
  163. {
  164.     { LANG_ADDON,           0, 0                       },
  165.     { LANG_UNIVERSAL,       0, 0                       },
  166.     { LANG_ORCISH,        669, SKILL_LANG_ORCISH       },
  167.     { LANG_DARNASSIAN,    671, SKILL_LANG_DARNASSIAN   },
  168.     { LANG_TAURAHE,       670, SKILL_LANG_TAURAHE      },
  169.     { LANG_DWARVISH,      672, SKILL_LANG_DWARVEN      },
  170.     { LANG_COMMON,        668, SKILL_LANG_COMMON       },
  171.     { LANG_DEMONIC,       815, SKILL_LANG_DEMON_TONGUE },
  172.     { LANG_TITAN,         816, SKILL_LANG_TITAN        },
  173.     { LANG_THALASSIAN,    813, SKILL_LANG_THALASSIAN   },
  174.     { LANG_DRACONIC,      814, SKILL_LANG_DRACONIC     },
  175.     { LANG_KALIMAG,       817, SKILL_LANG_OLD_TONGUE   },
  176.     { LANG_GNOMISH,      7340, SKILL_LANG_GNOMISH      },
  177.     { LANG_TROLL,        7341, SKILL_LANG_TROLL        },
  178.     { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK  },
  179.     { LANG_DRAENEI,     29932, SKILL_LANG_DRAENEI      },
  180.     { LANG_ZOMBIE,          0, 0                       },
  181.     { LANG_GNOMISH_BINARY,  0, 0                       },
  182.     { LANG_GOBLIN_BINARY,   0, 0                       }
  183. };
  184.  
  185. LanguageDesc const* GetLanguageDescByID(uint32 lang)
  186. {
  187.     for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
  188.     {
  189.         if (uint32(lang_description[i].lang_id) == lang)
  190.             return &lang_description[i];
  191.     }
  192.  
  193.     return nullptr;
  194. }
  195.  
  196. bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
  197. {
  198.     Player const* playerClicker = clicker->ToPlayer();
  199.     if (!playerClicker)
  200.         return true;
  201.  
  202.     Unit const* summoner = nullptr;
  203.     // Check summoners for party
  204.     if (clickee->IsSummon())
  205.         summoner = clickee->ToTempSummon()->GetSummoner();
  206.     if (!summoner)
  207.         summoner = clickee;
  208.  
  209.     // This only applies to players
  210.     switch (userType)
  211.     {
  212.         case SPELL_CLICK_USER_FRIEND:
  213.             if (!playerClicker->IsFriendlyTo(summoner))
  214.                 return false;
  215.             break;
  216.         case SPELL_CLICK_USER_RAID:
  217.             if (!playerClicker->IsInRaidWith(summoner))
  218.                 return false;
  219.             break;
  220.         case SPELL_CLICK_USER_PARTY:
  221.             if (!playerClicker->IsInPartyWith(summoner))
  222.                 return false;
  223.             break;
  224.         default:
  225.             break;
  226.     }
  227.  
  228.     return true;
  229. }
  230.  
  231. ObjectMgr::ObjectMgr():
  232.     _auctionId(1),
  233.     _equipmentSetGuid(1),
  234.     _mailId(1),
  235.     _hiPetNumber(1),
  236.     _creatureSpawnId(1),
  237.     _gameObjectSpawnId(1),
  238.     DBCLocaleIndex(LOCALE_enUS)
  239. {
  240. }
  241.  
  242. ObjectMgr* ObjectMgr::instance()
  243. {
  244.     static ObjectMgr instance;
  245.     return &instance;
  246. }
  247.  
  248. ObjectMgr::~ObjectMgr()
  249. {
  250. }
  251.  
  252. void ObjectMgr::AddLocaleString(std::string const& value, LocaleConstant localeConstant, std::vector<std::string>& data)
  253. {
  254.     if (!value.empty())
  255.     {
  256.         if (data.size() <= size_t(localeConstant))
  257.             data.resize(localeConstant + 1);
  258.  
  259.         data[localeConstant] = value;
  260.     }
  261. }
  262.  
  263. void ObjectMgr::LoadCreatureLocales()
  264. {
  265.     uint32 oldMSTime = getMSTime();
  266.  
  267.     _creatureLocaleStore.clear();                              // need for reload case
  268.  
  269.     //                                               0      1       2     3
  270.     QueryResult result = WorldDatabase.Query("SELECT entry, locale, Name, Title FROM creature_template_locale");
  271.     if (!result)
  272.         return;
  273.  
  274.     do
  275.     {
  276.         Field* fields = result->Fetch();
  277.  
  278.         uint32 id               = fields[0].GetUInt32();
  279.         std::string localeName  = fields[1].GetString();
  280.  
  281.         std::string name        = fields[2].GetString();
  282.         std::string title       = fields[3].GetString();
  283.  
  284.         CreatureLocale& data = _creatureLocaleStore[id];
  285.         LocaleConstant locale = GetLocaleByName(localeName);
  286.         if (locale == LOCALE_enUS)
  287.             continue;
  288.  
  289.         AddLocaleString(name,       locale, data.Name);
  290.         AddLocaleString(title,      locale, data.Title);
  291.  
  292.     } while (result->NextRow());
  293.  
  294.     TC_LOG_INFO("server.loading", ">> Loaded %u creature locale strings in %u ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  295. }
  296.  
  297. void ObjectMgr::LoadGossipMenuItemsLocales()
  298. {
  299.     uint32 oldMSTime = getMSTime();
  300.  
  301.     _gossipMenuItemsLocaleStore.clear();                              // need for reload case
  302.  
  303.     //                                               0       1            2       3           4
  304.     QueryResult result = WorldDatabase.Query("SELECT MenuID, OptionID, Locale, OptionText, BoxText FROM gossip_menu_option_locale");
  305.  
  306.     if (!result)
  307.         return;
  308.  
  309.     do
  310.     {
  311.         Field* fields = result->Fetch();
  312.  
  313.         uint16 menuId           = fields[0].GetUInt16();
  314.         uint16 optionId         = fields[1].GetUInt16();
  315.         std::string localeName  = fields[2].GetString();
  316.         std::string optionText  = fields[3].GetString();
  317.         std::string boxText     = fields[4].GetString();
  318.  
  319.         GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[MAKE_PAIR32(menuId, optionId)];
  320.         LocaleConstant locale       = GetLocaleByName(localeName);
  321.         if (locale == LOCALE_enUS)
  322.             continue;
  323.  
  324.         AddLocaleString(optionText, locale, data.OptionText);
  325.         AddLocaleString(boxText, locale, data.BoxText);
  326.     } while (result->NextRow());
  327.  
  328.     TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu_option locale strings in %u ms", uint32(_gossipMenuItemsLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  329. }
  330.  
  331. void ObjectMgr::LoadPointOfInterestLocales()
  332. {
  333.     uint32 oldMSTime = getMSTime();
  334.  
  335.     _pointOfInterestLocaleStore.clear();                              // need for reload case
  336.  
  337.     //                                               0   1       2
  338.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM points_of_interest_locale");
  339.  
  340.     if (!result)
  341.         return;
  342.  
  343.     do
  344.     {
  345.         Field* fields = result->Fetch();
  346.  
  347.         uint32 id               = fields[0].GetUInt32();
  348.         std::string localeName  = fields[1].GetString();
  349.         std::string name        = fields[2].GetString();
  350.  
  351.         PointOfInterestLocale& data = _pointOfInterestLocaleStore[id];
  352.         LocaleConstant locale = GetLocaleByName(localeName);
  353.  
  354.         AddLocaleString(name, locale, data.Name);
  355.     } while (result->NextRow());
  356.  
  357.     TC_LOG_INFO("server.loading", ">> Loaded %u points_of_interest locale strings in %u ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  358. }
  359.  
  360. void ObjectMgr::LoadCreatureTemplates()
  361. {
  362.     uint32 oldMSTime = getMSTime();
  363.  
  364.     //                                               0      1                   2                   3                   4            5            6         7         8
  365.     QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, "
  366.     //                                        9         10    11       12        13              14        15        16   17       18       19          20
  367.                                              "modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, "
  368.     //                                        21     22    23         24              25               26            27             28          29          30
  369.                                              "scale, rank, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, "
  370.     //                                        31            32      33            34             35             36            37
  371.                                              "dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, "
  372.     //                                        38          39      40              41        42           43           44           45           46           47           48            49          50          51
  373.                                              "type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, resistance7, resistance8, resistance9, spell1, "
  374.     //                                        52      53      54      55      56      57      58      59              60         61       62       63     64
  375.                                              "spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
  376.     //                                        65           66           67              68            69             70              71                  72
  377.                                              "InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, "
  378.     //                                        73          74           75                    76                        77           78
  379.                                              "movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName "
  380.                                              "FROM creature_template");
  381.  
  382.     if (!result)
  383.     {
  384.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
  385.         return;
  386.     }
  387.  
  388.     _creatureTemplateStore.reserve(result->GetRowCount());
  389.     do
  390.     {
  391.         Field* fields = result->Fetch();
  392.         LoadCreatureTemplate(fields);
  393.     } while (result->NextRow());
  394.  
  395.     // Checking needs to be done after loading because of the difficulty self referencing
  396.     for (auto const& ctPair : _creatureTemplateStore)
  397.         CheckCreatureTemplate(&ctPair.second);
  398.  
  399.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " creature definitions in %u ms", _creatureTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
  400. }
  401.  
  402. void ObjectMgr::LoadCreatureTemplate(Field* fields)
  403. {
  404.     uint32 entry = fields[0].GetUInt32();
  405.     CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
  406.  
  407.     creatureTemplate.Entry = entry;
  408.  
  409.     for (uint8 i = 0; i < MAX_DIFFICULTY - 1; ++i)
  410.         creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32();
  411.  
  412.     for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
  413.         creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
  414.  
  415.     creatureTemplate.Modelid1         = fields[6].GetUInt32();
  416.     creatureTemplate.Modelid2         = fields[7].GetUInt32();
  417.     creatureTemplate.Modelid3         = fields[8].GetUInt32();
  418.     creatureTemplate.Modelid4         = fields[9].GetUInt32();
  419.     creatureTemplate.Name             = fields[10].GetString();
  420.     creatureTemplate.Title            = fields[11].GetString();
  421.     creatureTemplate.IconName         = fields[12].GetString();
  422.     creatureTemplate.GossipMenuId     = fields[13].GetUInt32();
  423.     creatureTemplate.minlevel         = fields[14].GetUInt8();
  424.     creatureTemplate.maxlevel         = fields[15].GetUInt8();
  425.     creatureTemplate.expansion        = uint32(fields[16].GetInt16());
  426.     creatureTemplate.faction          = fields[17].GetUInt16();
  427.     creatureTemplate.npcflag          = fields[18].GetUInt32();
  428.     creatureTemplate.speed_walk       = fields[19].GetFloat();
  429.     creatureTemplate.speed_run        = fields[20].GetFloat();
  430.     creatureTemplate.scale            = fields[21].GetFloat();
  431.     creatureTemplate.rank             = fields[22].GetUInt8();
  432.     creatureTemplate.dmgschool        = uint32(fields[23].GetInt8());
  433.     creatureTemplate.BaseAttackTime   = fields[24].GetUInt32();
  434.     creatureTemplate.RangeAttackTime  = fields[25].GetUInt32();
  435.     creatureTemplate.BaseVariance     = fields[26].GetFloat();
  436.     creatureTemplate.RangeVariance    = fields[27].GetFloat();
  437.     creatureTemplate.unit_class       = fields[28].GetUInt8();
  438.     creatureTemplate.unit_flags       = fields[29].GetUInt32();
  439.     creatureTemplate.unit_flags2      = fields[30].GetUInt32();
  440.     creatureTemplate.dynamicflags     = fields[31].GetUInt32();
  441.     creatureTemplate.family           = CreatureFamily(fields[32].GetUInt8());
  442.     creatureTemplate.trainer_type     = fields[33].GetUInt8();
  443.     creatureTemplate.trainer_spell    = fields[34].GetUInt32();
  444.     creatureTemplate.trainer_class    = fields[35].GetUInt8();
  445.     creatureTemplate.trainer_race     = fields[36].GetUInt8();
  446.     creatureTemplate.type             = fields[37].GetUInt8();
  447.     creatureTemplate.type_flags       = fields[38].GetUInt32();
  448.     creatureTemplate.lootid           = fields[39].GetUInt32();
  449.     creatureTemplate.pickpocketLootId = fields[40].GetUInt32();
  450.     creatureTemplate.SkinLootId       = fields[41].GetUInt32();
  451.    
  452.    for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
  453.         creatureTemplate.resistance[i] = fields[42 + i - 1].GetInt16();
  454.  
  455.     for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
  456.         creatureTemplate.spells[i] = fields[51 + i].GetUInt32();
  457.  
  458.     creatureTemplate.PetSpellDataId = fields[59].GetUInt32();
  459.     creatureTemplate.VehicleId      = fields[60].GetUInt32();
  460.     creatureTemplate.mingold        = fields[61].GetUInt32();
  461.     creatureTemplate.maxgold        = fields[62].GetUInt32();
  462.     creatureTemplate.AIName         = fields[63].GetString();
  463.     creatureTemplate.MovementType   = fields[64].GetUInt8();
  464.     creatureTemplate.InhabitType    = fields[65].GetUInt8();
  465.     creatureTemplate.HoverHeight    = fields[66].GetFloat();
  466.     creatureTemplate.ModHealth      = fields[67].GetFloat();
  467.     creatureTemplate.ModMana        = fields[68].GetFloat();
  468.     creatureTemplate.ModArmor       = fields[69].GetFloat();
  469.     creatureTemplate.ModDamage      = fields[70].GetFloat();
  470.     creatureTemplate.ModExperience  = fields[71].GetFloat();
  471.     creatureTemplate.RacialLeader   = fields[72].GetBool();
  472.  
  473.     creatureTemplate.movementId            = fields[73].GetUInt32();
  474.     creatureTemplate.RegenHealth           = fields[74].GetBool();
  475.     creatureTemplate.MechanicImmuneMask    = fields[75].GetUInt32();
  476.     creatureTemplate.SpellSchoolImmuneMask = fields[76].GetUInt32();
  477.     creatureTemplate.flags_extra           = fields[77].GetUInt32();
  478.     creatureTemplate.ScriptID              = GetScriptId(fields[78].GetString());
  479. }
  480.  
  481. void ObjectMgr::LoadCreatureTemplateAddons()
  482. {
  483.     uint32 oldMSTime = getMSTime();
  484.  
  485.     //                                                0       1       2      3       4       5      6
  486.     QueryResult result = WorldDatabase.Query("SELECT entry, path_id, mount, bytes1, bytes2, emote, auras FROM creature_template_addon");
  487.  
  488.     if (!result)
  489.     {
  490.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
  491.         return;
  492.     }
  493.  
  494.     uint32 count = 0;
  495.     do
  496.     {
  497.         Field* fields = result->Fetch();
  498.  
  499.         uint32 entry = fields[0].GetUInt32();
  500.  
  501.         if (!sObjectMgr->GetCreatureTemplate(entry))
  502.         {
  503.             TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_template_addon`", entry);
  504.             continue;
  505.         }
  506.  
  507.         CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
  508.  
  509.         creatureAddon.path_id = fields[1].GetUInt32();
  510.         creatureAddon.mount   = fields[2].GetUInt32();
  511.         creatureAddon.bytes1  = fields[3].GetUInt32();
  512.         creatureAddon.bytes2  = fields[4].GetUInt32();
  513.         creatureAddon.emote   = fields[5].GetUInt32();
  514.  
  515.         Tokenizer tokens(fields[6].GetString(), ' ');
  516.         uint8 i = 0;
  517.         creatureAddon.auras.resize(tokens.size());
  518.         for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
  519.         {
  520.             SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(atoul(*itr));
  521.             if (!AdditionalSpellInfo)
  522.             {
  523.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong spell %lu defined in `auras` field in `creature_template_addon`.", entry, atoul(*itr));
  524.                 continue;
  525.             }
  526.  
  527.             if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
  528.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has SPELL_AURA_CONTROL_VEHICLE aura %lu defined in `auras` field in `creature_template_addon`.", entry, atoul(*itr));
  529.  
  530.             if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), atoul(*itr)) != creatureAddon.auras.end())
  531.             {
  532.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has duplicate aura (spell %lu) in `auras` field in `creature_template_addon`.", entry, atoul(*itr));
  533.                 continue;
  534.             }
  535.  
  536.             creatureAddon.auras[i++] = atoul(*itr);
  537.         }
  538.  
  539.         if (creatureAddon.mount)
  540.         {
  541.             if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
  542.             {
  543.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid displayInfoId (%u) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
  544.                 creatureAddon.mount = 0;
  545.             }
  546.         }
  547.  
  548.         if (!sEmotesStore.LookupEntry(creatureAddon.emote))
  549.         {
  550.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid emote (%u) defined in `creature_template_addon`.", entry, creatureAddon.emote);
  551.             creatureAddon.emote = 0;
  552.         }
  553.  
  554.         ++count;
  555.     }
  556.     while (result->NextRow());
  557.  
  558.     TC_LOG_INFO("server.loading", ">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  559. }
  560.  
  561. void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
  562. {
  563.     if (!cInfo)
  564.         return;
  565.  
  566.     bool ok = true;                                     // bool to allow continue outside this loop
  567.     for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
  568.     {
  569.         if (!cInfo->DifficultyEntry[diff])
  570.             continue;
  571.         ok = false;                                     // will be set to true at the end of this loop again
  572.  
  573.         CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
  574.         if (!difficultyInfo)
  575.         {
  576.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u does not exist.",
  577.                 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
  578.             continue;
  579.         }
  580.  
  581.         bool ok2 = true;
  582.         for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
  583.         {
  584.             ok2 = false;
  585.             if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end())
  586.             {
  587.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) is listed as `difficulty_entry_%u` of another creature, but itself lists %u in `difficulty_entry_%u`.",
  588.                     cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
  589.                 continue;
  590.             }
  591.  
  592.             if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end())
  593.             {
  594.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) already listed as `difficulty_entry_%u` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
  595.                 continue;
  596.             }
  597.  
  598.             if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end())
  599.             {
  600.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u has itself a value in `difficulty_entry_%u`.",
  601.                     cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
  602.                 continue;
  603.             }
  604.             ok2 = true;
  605.         }
  606.  
  607.         if (!ok2)
  608.             continue;
  609.  
  610.         if (cInfo->expansion > difficultyInfo->expansion)
  611.         {
  612.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, exp: %u) has different `exp` in difficulty %u mode (Entry: %u, exp: %u).",
  613.                 cInfo->Entry, cInfo->expansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->expansion);
  614.         }
  615.  
  616.         if (cInfo->minlevel > difficultyInfo->minlevel)
  617.         {
  618.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, minlevel: %u) has lower `minlevel` in difficulty %u mode (Entry: %u, minlevel: %u).",
  619.                 cInfo->Entry, cInfo->minlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->minlevel);
  620.         }
  621.  
  622.         if (cInfo->maxlevel > difficultyInfo->maxlevel)
  623.         {
  624.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, maxlevel: %u) has lower `maxlevel` in difficulty %u mode (Entry: %u, maxlevel: %u).",
  625.                 cInfo->Entry, cInfo->maxlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->maxlevel);
  626.         }
  627.  
  628.         if (cInfo->faction != difficultyInfo->faction)
  629.         {
  630.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, faction: %u) has different `faction` in difficulty %u mode (Entry: %u, faction: %u).",
  631.                 cInfo->Entry, cInfo->faction, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->faction);
  632.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `faction`=%u WHERE `entry`=%u;",
  633.                 cInfo->faction, cInfo->DifficultyEntry[diff]);
  634.         }
  635.  
  636.         if (cInfo->unit_class != difficultyInfo->unit_class)
  637.         {
  638.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, class: %u) has different `unit_class` in difficulty %u mode (Entry: %u, class: %u).",
  639.                 cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
  640.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_class`=%u WHERE `entry`=%u;",
  641.                 cInfo->unit_class, cInfo->DifficultyEntry[diff]);
  642.             continue;
  643.         }
  644.  
  645.         uint32 differenceMask = cInfo->npcflag ^ difficultyInfo->npcflag;
  646.         if (cInfo->npcflag != difficultyInfo->npcflag)
  647.         {
  648.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, `npcflag`: %u) has different `npcflag` in difficulty %u mode (Entry: %u, `npcflag`: %u).",
  649.                 cInfo->Entry, cInfo->npcflag, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->npcflag);
  650.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^%u WHERE `entry`=%u;",
  651.                 differenceMask, cInfo->DifficultyEntry[diff]);
  652.         }
  653.  
  654.         if (cInfo->dmgschool != difficultyInfo->dmgschool)
  655.         {
  656.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, `dmgschool`: %u) has different `dmgschool` in difficulty %u mode (Entry: %u, `dmgschool`: %u).",
  657.                 cInfo->Entry, cInfo->dmgschool, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->dmgschool);
  658.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `dmgschool`=%u WHERE `entry`=%u;",
  659.                 cInfo->dmgschool, cInfo->DifficultyEntry[diff]);
  660.         }
  661.  
  662.         differenceMask = cInfo->unit_flags2 ^ difficultyInfo->unit_flags2;
  663.         if (cInfo->unit_flags2 != difficultyInfo->unit_flags2)
  664.         {
  665.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, `unit_flags2`: %u) has different `unit_flags2` in difficulty %u mode (Entry: %u, `unit_flags2`: %u).",
  666.                 cInfo->Entry, cInfo->unit_flags2, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_flags2);
  667.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^%u WHERE `entry`=%u;",
  668.                 differenceMask, cInfo->DifficultyEntry[diff]);
  669.         }
  670.  
  671.         if (cInfo->family != difficultyInfo->family)
  672.         {
  673.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, family: %u) has different `family` in difficulty %u mode (Entry: %u, family: %u).",
  674.                 cInfo->Entry, cInfo->family, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->family);
  675.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `family`=%u WHERE `entry`=%u;",
  676.                 cInfo->family, cInfo->DifficultyEntry[diff]);
  677.         }
  678.  
  679.         if (cInfo->trainer_class != difficultyInfo->trainer_class)
  680.         {
  681.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_class: %u) has different `trainer_class` in difficulty %u mode (Entry: %u, trainer_class: %u).",
  682.                 cInfo->Entry, cInfo->trainer_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_class);
  683.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_class`=%u WHERE `entry`=%u;",
  684.                 cInfo->trainer_class, cInfo->DifficultyEntry[diff]);
  685.             continue;
  686.         }
  687.  
  688.         if (cInfo->trainer_race != difficultyInfo->trainer_race)
  689.         {
  690.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_race: %u) has different `trainer_race` in difficulty %u mode (Entry: %u, trainer_race: %u).",
  691.                 cInfo->Entry, cInfo->trainer_race, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_race);
  692.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_race`=%u WHERE `entry`=%u;",
  693.                 cInfo->trainer_race, cInfo->DifficultyEntry[diff]);
  694.             continue;
  695.         }
  696.  
  697.         if (cInfo->trainer_type != difficultyInfo->trainer_type)
  698.         {
  699.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_type: %u) has different `trainer_type` in difficulty %u mode (Entry: %u, trainer_type: %u).",
  700.                 cInfo->Entry, cInfo->trainer_type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_type);
  701.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_type`=%u WHERE `entry`=%u;",
  702.                 cInfo->trainer_type, cInfo->DifficultyEntry[diff]);
  703.             continue;
  704.         }
  705.  
  706.         if (cInfo->trainer_spell != difficultyInfo->trainer_spell)
  707.         {
  708.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
  709.             continue;
  710.         }
  711.  
  712.         if (cInfo->type != difficultyInfo->type)
  713.         {
  714.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, type: %u) has different `type` in difficulty %u mode (Entry: %u, type: %u).",
  715.                 cInfo->Entry, cInfo->type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->type);
  716.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `type`=%u WHERE `entry`=%u;",
  717.                 cInfo->type, cInfo->DifficultyEntry[diff]);
  718.         }
  719.  
  720.         if (!cInfo->VehicleId && difficultyInfo->VehicleId)
  721.         {
  722.             TC_LOG_ERROR("sql.sql", "Non-vehicle Creature (Entry: %u, VehicleId: %u) has `VehicleId` set in difficulty %u mode (Entry: %u, VehicleId: %u).",
  723.                 cInfo->Entry, cInfo->VehicleId, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->VehicleId);
  724.         }
  725.  
  726.         if (cInfo->RegenHealth != difficultyInfo->RegenHealth)
  727.         {
  728.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, RegenHealth: %u) has different `RegenHealth` in difficulty %u mode (Entry: %u, RegenHealth: %u).",
  729.                 cInfo->Entry, cInfo->RegenHealth, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->RegenHealth);
  730.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `RegenHealth`=%u WHERE `entry`=%u;",
  731.                 cInfo->RegenHealth, cInfo->DifficultyEntry[diff]);
  732.         }
  733.  
  734.         differenceMask = cInfo->MechanicImmuneMask & (~difficultyInfo->MechanicImmuneMask);
  735.         if (differenceMask)
  736.         {
  737.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, mechanic_immune_mask: %u) has weaker immunities in difficulty %u mode (Entry: %u, mechanic_immune_mask: %u).",
  738.                 cInfo->Entry, cInfo->MechanicImmuneMask, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->MechanicImmuneMask);
  739.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|%u WHERE `entry`=%u;",
  740.                 differenceMask, cInfo->DifficultyEntry[diff]);
  741.         }
  742.  
  743.         differenceMask = (cInfo->flags_extra ^ difficultyInfo->flags_extra) & (~CREATURE_FLAG_EXTRA_INSTANCE_BIND);
  744.         if (differenceMask)
  745.         {
  746.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, flags_extra: %u) has different `flags_extra` in difficulty %u mode (Entry: %u, flags_extra: %u).",
  747.                 cInfo->Entry, cInfo->flags_extra, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->flags_extra);
  748.             TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^%u WHERE `entry`=%u;",
  749.                 differenceMask, cInfo->DifficultyEntry[diff]);
  750.         }
  751.  
  752.         if (!difficultyInfo->AIName.empty())
  753.         {
  754.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists difficulty %u mode entry %u with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
  755.                 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
  756.             continue;
  757.         }
  758.  
  759.         if (difficultyInfo->ScriptID)
  760.         {
  761.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists difficulty %u mode entry %u with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
  762.                 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
  763.             continue;
  764.         }
  765.  
  766.         _hasDifficultyEntries[diff].insert(cInfo->Entry);
  767.         _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
  768.         ok = true;
  769.     }
  770.  
  771.     if (cInfo->mingold > cInfo->maxgold)
  772.     {
  773.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has `mingold` %u which is greater than `maxgold` %u, setting `maxgold` to %u.",
  774.             cInfo->Entry, cInfo->mingold, cInfo->maxgold, cInfo->mingold);
  775.         const_cast<CreatureTemplate*>(cInfo)->maxgold = cInfo->mingold;
  776.     }
  777.  
  778.     if (!cInfo->AIName.empty())
  779.     {
  780.         auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
  781.         if (!registryItem)
  782.         {
  783.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-registered `AIName` '%s' set, removing", cInfo->Entry, cInfo->AIName.c_str());
  784.             const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
  785.         }
  786.         else
  787.         {
  788.             DBPermit const* permit = dynamic_cast<DBPermit const*>(registryItem);
  789.             if (!ASSERT_NOTNULL(permit)->IsScriptNameAllowedInDB())
  790.             {
  791.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has not-allowed `AIName` '%s' set, removing", cInfo->Entry, cInfo->AIName.c_str());
  792.                 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
  793.             }
  794.         }
  795.     }
  796.  
  797.     FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
  798.     if (!factionTemplate)
  799.     {
  800.         TC_LOG_FATAL("sql.sql", "Creature (Entry: %u) has non-existing faction template (%u). This can lead to crashes, aborting.", cInfo->Entry, cInfo->faction);
  801.         ABORT();
  802.     }
  803.  
  804.     // used later for scale
  805.     CreatureDisplayInfoEntry const* displayScaleEntry = nullptr;
  806.  
  807.     if (cInfo->Modelid1)
  808.     {
  809.         CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
  810.         if (!displayEntry)
  811.         {
  812.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
  813.             const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
  814.         }
  815.         else
  816.             displayScaleEntry = displayEntry;
  817.  
  818.         CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
  819.         if (!modelInfo)
  820.             TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry);
  821.     }
  822.  
  823.     if (cInfo->Modelid2)
  824.     {
  825.         CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
  826.         if (!displayEntry)
  827.         {
  828.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
  829.             const_cast<CreatureTemplate*>(cInfo)->Modelid2 = 0;
  830.         }
  831.         else if (!displayScaleEntry)
  832.             displayScaleEntry = displayEntry;
  833.  
  834.         CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);
  835.         if (!modelInfo)
  836.             TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry);
  837.     }
  838.  
  839.     if (cInfo->Modelid3)
  840.     {
  841.         CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
  842.         if (!displayEntry)
  843.         {
  844.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
  845.             const_cast<CreatureTemplate*>(cInfo)->Modelid3 = 0;
  846.         }
  847.         else if (!displayScaleEntry)
  848.             displayScaleEntry = displayEntry;
  849.  
  850.         CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
  851.         if (!modelInfo)
  852.             TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry);
  853.     }
  854.  
  855.     if (cInfo->Modelid4)
  856.     {
  857.         CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
  858.         if (!displayEntry)
  859.         {
  860.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
  861.             const_cast<CreatureTemplate*>(cInfo)->Modelid4 = 0;
  862.         }
  863.         else if (!displayScaleEntry)
  864.             displayScaleEntry = displayEntry;
  865.  
  866.         CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);
  867.         if (!modelInfo)
  868.             TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry);
  869.     }
  870.  
  871.     if (!displayScaleEntry)
  872.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
  873.  
  874.     for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
  875.     {
  876.         if (cInfo->KillCredit[k])
  877.         {
  878.             if (!GetCreatureTemplate(cInfo->KillCredit[k]))
  879.             {
  880.                 TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing creature entry %u in `KillCredit%d`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
  881.                 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
  882.             }
  883.         }
  884.     }
  885.  
  886.     if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
  887.     {
  888.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid unit_class (%u) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
  889.         const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
  890.     }
  891.  
  892.     if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
  893.     {
  894.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
  895.         const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
  896.     }
  897.  
  898.     if (cInfo->BaseAttackTime == 0)
  899.         const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime  = BASE_ATTACK_TIME;
  900.  
  901.     if (cInfo->RangeAttackTime == 0)
  902.         const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
  903.  
  904.     if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
  905.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong trainer type %u.", cInfo->Entry, cInfo->trainer_type);
  906.  
  907.     if (cInfo->speed_walk == 0.0f)
  908.     {
  909.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
  910.         const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
  911.     }
  912.  
  913.     if (cInfo->speed_run == 0.0f)
  914.     {
  915.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
  916.         const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
  917.     }
  918.  
  919.     if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
  920.     {
  921.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature type (%u) in `type`.", cInfo->Entry, cInfo->type);
  922.         const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
  923.     }
  924.  
  925.     // must exist or used hidden but used in data horse case
  926.     if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
  927.     {
  928.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family);
  929.         const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
  930.     }
  931.  
  932.     if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
  933.     {
  934.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly.", cInfo->Entry, cInfo->InhabitType);
  935.         const_cast<CreatureTemplate*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
  936.     }
  937.  
  938.     if (cInfo->HoverHeight < 0.0f)
  939.     {
  940.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in `HoverHeight`", cInfo->Entry, cInfo->HoverHeight);
  941.         const_cast<CreatureTemplate*>(cInfo)->HoverHeight = 1.0f;
  942.     }
  943.  
  944.     if (cInfo->VehicleId)
  945.     {
  946.         VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
  947.         if (!vehId)
  948.         {
  949.              TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has a non-existing VehicleId (%u). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
  950.              const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
  951.         }
  952.     }
  953.  
  954.     if (cInfo->PetSpellDataId)
  955.     {
  956.         CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
  957.         if (!spellDataId)
  958.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId);
  959.     }
  960.  
  961.     for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
  962.     {
  963.         if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j]))
  964.         {
  965.             TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing Spell%d (%u), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
  966.             const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
  967.         }
  968.     }
  969.  
  970.     if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
  971.     {
  972.         TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong movement generator type (%u), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
  973.         const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
  974.     }
  975.  
  976.     /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
  977.     if (cInfo->scale <= 0.0f)
  978.     {
  979.         if (displayScaleEntry)
  980.             const_cast<CreatureTemplate*>(cInfo)->scale = displayScaleEntry->scale;
  981.         else
  982.             const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f;
  983.     }
  984.  
  985.     if (cInfo->expansion > (MAX_EXPANSIONS - 1))
  986.     {
  987.         TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with expansion %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
  988.         const_cast<CreatureTemplate*>(cInfo)->expansion = 0;
  989.     }
  990.  
  991.     if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
  992.     {
  993.         TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with disallowed `flags_extra` %u, removing incorrect flag.", cInfo->Entry, badFlags);
  994.         const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
  995.     }
  996.  
  997.     const_cast<CreatureTemplate*>(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank);
  998. }
  999.  
  1000. void ObjectMgr::LoadCreatureAddons()
  1001. {
  1002.     uint32 oldMSTime = getMSTime();
  1003.  
  1004.     //                                                0       1       2      3       4       5      6
  1005.     QueryResult result = WorldDatabase.Query("SELECT guid, path_id, mount, bytes1, bytes2, emote, auras FROM creature_addon");
  1006.  
  1007.     if (!result)
  1008.     {
  1009.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
  1010.         return;
  1011.     }
  1012.  
  1013.     uint32 count = 0;
  1014.     do
  1015.     {
  1016.         Field* fields = result->Fetch();
  1017.  
  1018.         ObjectGuid::LowType guid = fields[0].GetUInt32();
  1019.  
  1020.         CreatureData const* creData = GetCreatureData(guid);
  1021.         if (!creData)
  1022.         {
  1023.             TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) does not exist but has a record in `creature_addon`", guid);
  1024.             continue;
  1025.         }
  1026.  
  1027.         CreatureAddon& creatureAddon = _creatureAddonStore[guid];
  1028.  
  1029.         creatureAddon.path_id = fields[1].GetUInt32();
  1030.         if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.path_id)
  1031.         {
  1032.             const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
  1033.             TC_LOG_ERROR("sql.sql", "Creature (GUID %u) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
  1034.         }
  1035.  
  1036.         creatureAddon.mount   = fields[2].GetUInt32();
  1037.         creatureAddon.bytes1  = fields[3].GetUInt32();
  1038.         creatureAddon.bytes2  = fields[4].GetUInt32();
  1039.         creatureAddon.emote   = fields[5].GetUInt32();
  1040.  
  1041.         Tokenizer tokens(fields[6].GetString(), ' ');
  1042.         uint8 i = 0;
  1043.         creatureAddon.auras.resize(tokens.size());
  1044.         for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
  1045.         {
  1046.             SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(atoul(*itr));
  1047.             if (!AdditionalSpellInfo)
  1048.             {
  1049.                 TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has wrong spell %lu defined in `auras` field in `creature_addon`.", guid, atoul(*itr));
  1050.                 continue;
  1051.             }
  1052.  
  1053.             if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
  1054.                 TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has SPELL_AURA_CONTROL_VEHICLE aura %lu defined in `auras` field in `creature_addon`.", guid, atoul(*itr));
  1055.  
  1056.             if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), atoul(*itr)) != creatureAddon.auras.end())
  1057.             {
  1058.                 TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has duplicate aura (spell %lu) in `auras` field in `creature_addon`.", guid, atoul(*itr));
  1059.                 continue;
  1060.             }
  1061.  
  1062.             creatureAddon.auras[i++] = atoul(*itr);
  1063.         }
  1064.  
  1065.         if (creatureAddon.mount)
  1066.         {
  1067.             if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
  1068.             {
  1069.                 TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid displayInfoId (%u) for mount defined in `creature_addon`", guid, creatureAddon.mount);
  1070.                 creatureAddon.mount = 0;
  1071.             }
  1072.         }
  1073.  
  1074.         if (!sEmotesStore.LookupEntry(creatureAddon.emote))
  1075.         {
  1076.             TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);
  1077.             creatureAddon.emote = 0;
  1078.         }
  1079.  
  1080.         ++count;
  1081.     }
  1082.     while (result->NextRow());
  1083.  
  1084.     TC_LOG_INFO("server.loading", ">> Loaded %u creature addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1085. }
  1086.  
  1087. void ObjectMgr::LoadGameObjectAddons()
  1088. {
  1089.     uint32 oldMSTime = getMSTime();
  1090.  
  1091.     //                                               0     1                 2                 3                 4                 5                 6
  1092.     QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue FROM gameobject_addon");
  1093.  
  1094.     if (!result)
  1095.     {
  1096.         TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
  1097.         return;
  1098.     }
  1099.  
  1100.     uint32 count = 0;
  1101.     do
  1102.     {
  1103.         Field* fields = result->Fetch();
  1104.  
  1105.         ObjectGuid::LowType guid = fields[0].GetUInt32();
  1106.  
  1107.         GameObjectData const* goData = GetGameObjectData(guid);
  1108.         if (!goData)
  1109.         {
  1110.             TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid);
  1111.             continue;
  1112.         }
  1113.  
  1114.         GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
  1115.         gameObjectAddon.ParentRotation = QuaternionData(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
  1116.         gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
  1117.         gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
  1118.  
  1119.         if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
  1120.         {
  1121.             TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
  1122.             gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
  1123.             gameObjectAddon.InvisibilityValue = 0;
  1124.         }
  1125.  
  1126.         if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue)
  1127.         {
  1128.             TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid);
  1129.             gameObjectAddon.InvisibilityValue = 1;
  1130.         }
  1131.  
  1132.         if (!gameObjectAddon.ParentRotation.isUnit())
  1133.         {
  1134.             TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid parent rotation in `gameobject_addon`, set to default", guid);
  1135.             gameObjectAddon.ParentRotation = QuaternionData();
  1136.         }
  1137.  
  1138.         ++count;
  1139.     }
  1140.     while (result->NextRow());
  1141.  
  1142.     TC_LOG_INFO("server.loading", ">> Loaded %u gameobject addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1143. }
  1144.  
  1145. GameObjectAddon const* ObjectMgr::GetGameObjectAddon(ObjectGuid::LowType lowguid) const
  1146. {
  1147.     GameObjectAddonContainer::const_iterator itr = _gameObjectAddonStore.find(lowguid);
  1148.     if (itr != _gameObjectAddonStore.end())
  1149.         return &(itr->second);
  1150.  
  1151.     return nullptr;
  1152. }
  1153.  
  1154. CreatureAddon const* ObjectMgr::GetCreatureAddon(ObjectGuid::LowType lowguid) const
  1155. {
  1156.     CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
  1157.     if (itr != _creatureAddonStore.end())
  1158.         return &(itr->second);
  1159.  
  1160.     return nullptr;
  1161. }
  1162.  
  1163. CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry) const
  1164. {
  1165.     CreatureAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
  1166.     if (itr != _creatureTemplateAddonStore.end())
  1167.         return &(itr->second);
  1168.  
  1169.     return nullptr;
  1170. }
  1171.  
  1172. EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry, int8& id) const
  1173. {
  1174.     EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
  1175.     if (itr == _equipmentInfoStore.end())
  1176.         return nullptr;
  1177.  
  1178.     if (itr->second.empty())
  1179.         return nullptr;
  1180.  
  1181.     if (id == -1) // select a random element
  1182.     {
  1183.         EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
  1184.         std::advance(ritr, urand(0u, itr->second.size() - 1));
  1185.         id = std::distance(itr->second.begin(), ritr) + 1;
  1186.         return &ritr->second;
  1187.     }
  1188.     else
  1189.     {
  1190.         EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
  1191.         if (itr2 != itr->second.end())
  1192.             return &itr2->second;
  1193.     }
  1194.  
  1195.     return nullptr;
  1196. }
  1197.  
  1198. void ObjectMgr::LoadEquipmentTemplates()
  1199. {
  1200.     uint32 oldMSTime = getMSTime();
  1201.  
  1202.     //                                                 0         1       2       3       4
  1203.     QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, ItemID2, ItemID3 FROM creature_equip_template");
  1204.  
  1205.     if (!result)
  1206.     {
  1207.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
  1208.         return;
  1209.     }
  1210.  
  1211.     uint32 count = 0;
  1212.     do
  1213.     {
  1214.         Field* fields = result->Fetch();
  1215.  
  1216.         uint32 entry = fields[0].GetUInt32();
  1217.  
  1218.         if (!sObjectMgr->GetCreatureTemplate(entry))
  1219.         {
  1220.             TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_equip_template`", entry);
  1221.             continue;
  1222.         }
  1223.  
  1224.         uint8 id = fields[1].GetUInt8();
  1225.         if (!id)
  1226.         {
  1227.             TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature %u, skipped.", entry);
  1228.             continue;
  1229.         }
  1230.  
  1231.         EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
  1232.  
  1233.         equipmentInfo.ItemEntry[0] = fields[2].GetUInt32();
  1234.         equipmentInfo.ItemEntry[1] = fields[3].GetUInt32();
  1235.         equipmentInfo.ItemEntry[2] = fields[4].GetUInt32();
  1236.  
  1237.         for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
  1238.         {
  1239.             if (!equipmentInfo.ItemEntry[i])
  1240.                 continue;
  1241.  
  1242.             ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
  1243.  
  1244.             if (!dbcItem)
  1245.             {
  1246.                 TC_LOG_ERROR("sql.sql", "Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u and id=%u, forced to 0.",
  1247.                     equipmentInfo.ItemEntry[i], i+1, entry, id);
  1248.                 equipmentInfo.ItemEntry[i] = 0;
  1249.                 continue;
  1250.             }
  1251.  
  1252.             if (dbcItem->InventoryType != INVTYPE_WEAPON &&
  1253.                 dbcItem->InventoryType != INVTYPE_SHIELD &&
  1254.                 dbcItem->InventoryType != INVTYPE_RANGED &&
  1255.                 dbcItem->InventoryType != INVTYPE_2HWEAPON &&
  1256.                 dbcItem->InventoryType != INVTYPE_WEAPONMAINHAND &&
  1257.                 dbcItem->InventoryType != INVTYPE_WEAPONOFFHAND &&
  1258.                 dbcItem->InventoryType != INVTYPE_HOLDABLE &&
  1259.                 dbcItem->InventoryType != INVTYPE_THROWN &&
  1260.                 dbcItem->InventoryType != INVTYPE_RANGEDRIGHT)
  1261.             {
  1262.                 TC_LOG_ERROR("sql.sql", "Item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u and id = %u is not equipable in a hand, forced to 0.",
  1263.                     equipmentInfo.ItemEntry[i], i+1, entry, id);
  1264.                 equipmentInfo.ItemEntry[i] = 0;
  1265.             }
  1266.         }
  1267.  
  1268.         ++count;
  1269.     }
  1270.     while (result->NextRow());
  1271.  
  1272.     TC_LOG_INFO("server.loading", ">> Loaded %u equipment templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1273. }
  1274.  
  1275. CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId) const
  1276. {
  1277.     CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
  1278.     if (itr != _creatureModelStore.end())
  1279.         return &(itr->second);
  1280.  
  1281.     return nullptr;
  1282. }
  1283.  
  1284. uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
  1285. {
  1286.     // Load creature model (display id)
  1287.     if (data && data->displayid)
  1288.         return data->displayid;
  1289.  
  1290.     if (!(cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER))
  1291.         return cinfo->GetRandomValidModelId();
  1292.  
  1293.     // Triggers by default receive the invisible model
  1294.     return cinfo->GetFirstInvisibleModel();
  1295. }
  1296.  
  1297. void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, CreatureData const* data /*= nullptr*/)
  1298. {
  1299.     npcflag = cinfo->npcflag;
  1300.     unit_flags = cinfo->unit_flags;
  1301.     dynamicflags = cinfo->dynamicflags;
  1302.  
  1303.     if (data)
  1304.     {
  1305.         if (data->npcflag)
  1306.             npcflag = data->npcflag;
  1307.  
  1308.         if (data->unit_flags)
  1309.             unit_flags = data->unit_flags;
  1310.  
  1311.         if (data->dynamicflags)
  1312.             dynamicflags = data->dynamicflags;
  1313.     }
  1314. }
  1315.  
  1316. CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* displayID) const
  1317. {
  1318.     CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID);
  1319.     if (!modelInfo)
  1320.         return nullptr;
  1321.  
  1322.     // If a model for another gender exists, 50% chance to use it
  1323.     if (modelInfo->modelid_other_gender != 0 && urand(0, 1) == 0)
  1324.     {
  1325.         CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->modelid_other_gender);
  1326.         if (!minfo_tmp)
  1327.             TC_LOG_ERROR("sql.sql", "Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", *displayID, modelInfo->modelid_other_gender);
  1328.         else
  1329.         {
  1330.             // Model ID changed
  1331.             *displayID = modelInfo->modelid_other_gender;
  1332.             return minfo_tmp;
  1333.         }
  1334.     }
  1335.  
  1336.     return modelInfo;
  1337. }
  1338.  
  1339. void ObjectMgr::LoadCreatureModelInfo()
  1340. {
  1341.     uint32 oldMSTime = getMSTime();
  1342.     //                                                   0             1             2          3               4
  1343.     QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, Gender, DisplayID_Other_Gender FROM creature_model_info");
  1344.  
  1345.     if (!result)
  1346.     {
  1347.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
  1348.         return;
  1349.     }
  1350.  
  1351.     _creatureModelStore.rehash(result->GetRowCount());
  1352.     uint32 count = 0;
  1353.  
  1354.     do
  1355.     {
  1356.         Field* fields = result->Fetch();
  1357.  
  1358.         uint32 modelId = fields[0].GetUInt32();
  1359.         CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(modelId);
  1360.         if (!creatureDisplay)
  1361.         {
  1362.             TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has model for nonexistent display id (%u).", modelId);
  1363.             continue;
  1364.         }
  1365.  
  1366.         CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
  1367.  
  1368.         modelInfo.bounding_radius      = fields[1].GetFloat();
  1369.         modelInfo.combat_reach         = fields[2].GetFloat();
  1370.         modelInfo.gender               = fields[3].GetUInt8();
  1371.         modelInfo.modelid_other_gender = fields[4].GetUInt32();
  1372.         modelInfo.is_trigger           = false;
  1373.  
  1374.         // Checks
  1375.  
  1376.         if (modelInfo.gender > GENDER_NONE)
  1377.         {
  1378.             TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(modelInfo.gender), modelId);
  1379.             modelInfo.gender = GENDER_MALE;
  1380.         }
  1381.  
  1382.         if (modelInfo.modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.modelid_other_gender))
  1383.         {
  1384.             TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has nonexistent alt.gender model (%u) for existed display id (%u).", modelInfo.modelid_other_gender, modelId);
  1385.             modelInfo.modelid_other_gender = 0;
  1386.         }
  1387.  
  1388.         if (modelInfo.combat_reach < 0.1f)
  1389.             modelInfo.combat_reach = DEFAULT_PLAYER_COMBAT_REACH;
  1390.  
  1391.         if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelId))
  1392.             modelInfo.is_trigger = strstr(modelData->ModelPath, "InvisibleStalker") != nullptr;
  1393.  
  1394.         ++count;
  1395.     }
  1396.     while (result->NextRow());
  1397.  
  1398.     TC_LOG_INFO("server.loading", ">> Loaded %u creature model based info in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1399. }
  1400.  
  1401. void ObjectMgr::LoadLinkedRespawn()
  1402. {
  1403.     uint32 oldMSTime = getMSTime();
  1404.  
  1405.     _linkedRespawnStore.clear();
  1406.     //                                                 0        1          2
  1407.     QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
  1408.  
  1409.     if (!result)
  1410.     {
  1411.         TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
  1412.         return;
  1413.     }
  1414.  
  1415.     do
  1416.     {
  1417.         Field* fields = result->Fetch();
  1418.  
  1419.         ObjectGuid::LowType guidLow = fields[0].GetUInt32();
  1420.         ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt32();
  1421.         uint8  linkType = fields[2].GetUInt8();
  1422.  
  1423.         ObjectGuid guid, linkedGuid;
  1424.         bool error = false;
  1425.         switch (linkType)
  1426.         {
  1427.             case LINKED_RESPAWN_CREATURE_TO_CREATURE:
  1428.             {
  1429.                 CreatureData const* slave = GetCreatureData(guidLow);
  1430.                 if (!slave)
  1431.                 {
  1432.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '%u' not found in creature table", guidLow);
  1433.                     error = true;
  1434.                     break;
  1435.                 }
  1436.  
  1437.                 CreatureData const* master = GetCreatureData(linkedGuidLow);
  1438.                 if (!master)
  1439.                 {
  1440.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '%u' not found in creature table", linkedGuidLow);
  1441.                     error = true;
  1442.                     break;
  1443.                 }
  1444.  
  1445.                 MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
  1446.                 if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
  1447.                 {
  1448.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1449.                     error = true;
  1450.                     break;
  1451.                 }
  1452.  
  1453.                 if (!(master->spawnMask & slave->spawnMask))  // they must have a possibility to meet (normal/heroic difficulty)
  1454.                 {
  1455.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1456.                     error = true;
  1457.                     break;
  1458.                 }
  1459.  
  1460.                 guid = ObjectGuid(HighGuid::Unit, slave->id, guidLow);
  1461.                 linkedGuid = ObjectGuid(HighGuid::Unit, master->id, linkedGuidLow);
  1462.                 break;
  1463.             }
  1464.             case LINKED_RESPAWN_CREATURE_TO_GO:
  1465.             {
  1466.                 CreatureData const* slave = GetCreatureData(guidLow);
  1467.                 if (!slave)
  1468.                 {
  1469.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '%u' not found in creature table", guidLow);
  1470.                     error = true;
  1471.                     break;
  1472.                 }
  1473.  
  1474.                 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
  1475.                 if (!master)
  1476.                 {
  1477.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
  1478.                     error = true;
  1479.                     break;
  1480.                 }
  1481.  
  1482.                 MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
  1483.                 if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
  1484.                 {
  1485.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1486.                     error = true;
  1487.                     break;
  1488.                 }
  1489.  
  1490.                 if (!(master->spawnMask & slave->spawnMask))  // they must have a possibility to meet (normal/heroic difficulty)
  1491.                 {
  1492.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1493.                     error = true;
  1494.                     break;
  1495.                 }
  1496.  
  1497.                 guid = ObjectGuid(HighGuid::Unit, slave->id, guidLow);
  1498.                 linkedGuid = ObjectGuid(HighGuid::GameObject, master->id, linkedGuidLow);
  1499.                 break;
  1500.             }
  1501.             case LINKED_RESPAWN_GO_TO_GO:
  1502.             {
  1503.                 GameObjectData const* slave = GetGameObjectData(guidLow);
  1504.                 if (!slave)
  1505.                 {
  1506.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
  1507.                     error = true;
  1508.                     break;
  1509.                 }
  1510.  
  1511.                 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
  1512.                 if (!master)
  1513.                 {
  1514.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
  1515.                     error = true;
  1516.                     break;
  1517.                 }
  1518.  
  1519.                 MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
  1520.                 if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
  1521.                 {
  1522.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1523.                     error = true;
  1524.                     break;
  1525.                 }
  1526.  
  1527.                 if (!(master->spawnMask & slave->spawnMask))  // they must have a possibility to meet (normal/heroic difficulty)
  1528.                 {
  1529.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1530.                     error = true;
  1531.                     break;
  1532.                 }
  1533.  
  1534.                 guid = ObjectGuid(HighGuid::GameObject, slave->id, guidLow);
  1535.                 linkedGuid = ObjectGuid(HighGuid::GameObject, master->id, linkedGuidLow);
  1536.                 break;
  1537.             }
  1538.             case LINKED_RESPAWN_GO_TO_CREATURE:
  1539.             {
  1540.                 GameObjectData const* slave = GetGameObjectData(guidLow);
  1541.                 if (!slave)
  1542.                 {
  1543.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
  1544.                     error = true;
  1545.                     break;
  1546.                 }
  1547.  
  1548.                 CreatureData const* master = GetCreatureData(linkedGuidLow);
  1549.                 if (!master)
  1550.                 {
  1551.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '%u' not found in creature table", linkedGuidLow);
  1552.                     error = true;
  1553.                     break;
  1554.                 }
  1555.  
  1556.                 MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
  1557.                 if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
  1558.                 {
  1559.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1560.                     error = true;
  1561.                     break;
  1562.                 }
  1563.  
  1564.                 if (!(master->spawnMask & slave->spawnMask))  // they must have a possibility to meet (normal/heroic difficulty)
  1565.                 {
  1566.                     TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1567.                     error = true;
  1568.                     break;
  1569.                 }
  1570.  
  1571.                 guid = ObjectGuid(HighGuid::GameObject, slave->id, guidLow);
  1572.                 linkedGuid = ObjectGuid(HighGuid::Unit, master->id, linkedGuidLow);
  1573.                 break;
  1574.             }
  1575.         }
  1576.  
  1577.         if (!error)
  1578.             _linkedRespawnStore[guid] = linkedGuid;
  1579.     }
  1580.     while (result->NextRow());
  1581.  
  1582.     TC_LOG_INFO("server.loading", ">> Loaded " UI64FMTD " linked respawns in %u ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
  1583. }
  1584.  
  1585. bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid::LowType linkedGuidLow)
  1586. {
  1587.     if (!guidLow)
  1588.         return false;
  1589.  
  1590.     CreatureData const* master = GetCreatureData(guidLow);
  1591.     ASSERT(master);
  1592.     ObjectGuid guid(HighGuid::Unit, master->id, guidLow);
  1593.  
  1594.     if (!linkedGuidLow) // we're removing the linking
  1595.     {
  1596.         _linkedRespawnStore.erase(guid);
  1597.         PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
  1598.         stmt->setUInt32(0, guidLow);
  1599.         stmt->setUInt32(1, LINKED_RESPAWN_CREATURE_TO_CREATURE);
  1600.         WorldDatabase.Execute(stmt);
  1601.         return true;
  1602.     }
  1603.  
  1604.     CreatureData const* slave = GetCreatureData(linkedGuidLow);
  1605.     if (!slave)
  1606.     {
  1607.         TC_LOG_ERROR("sql.sql", "Creature '%u' linking to non-existent creature '%u'.", guidLow, linkedGuidLow);
  1608.         return false;
  1609.     }
  1610.  
  1611.     MapEntry const* map = sMapStore.LookupEntry(master->spawnPoint.GetMapId());
  1612.     if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId()))
  1613.     {
  1614.         TC_LOG_ERROR("sql.sql", "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1615.         return false;
  1616.     }
  1617.  
  1618.     if (!(master->spawnMask & slave->spawnMask))  // they must have a possibility to meet (normal/heroic difficulty)
  1619.     {
  1620.         TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1621.         return false;
  1622.     }
  1623.  
  1624.     ObjectGuid linkedGuid(HighGuid::Unit, slave->id, linkedGuidLow);
  1625.  
  1626.     _linkedRespawnStore[guid] = linkedGuid;
  1627.     PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_LINKED_RESPAWN);
  1628.     stmt->setUInt32(0, guidLow);
  1629.     stmt->setUInt32(1, linkedGuidLow);
  1630.     stmt->setUInt32(2, LINKED_RESPAWN_CREATURE_TO_CREATURE);
  1631.     WorldDatabase.Execute(stmt);
  1632.     return true;
  1633. }
  1634.  
  1635. void ObjectMgr::LoadTempSummons()
  1636. {
  1637.     uint32 oldMSTime = getMSTime();
  1638.  
  1639.     _tempSummonDataStore.clear();   // needed for reload case
  1640.  
  1641.     //                                               0           1             2        3      4           5           6           7            8           9
  1642.     QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
  1643.  
  1644.     if (!result)
  1645.     {
  1646.         TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
  1647.         return;
  1648.     }
  1649.  
  1650.     uint32 count = 0;
  1651.     do
  1652.     {
  1653.         Field* fields = result->Fetch();
  1654.  
  1655.         uint32 summonerId               = fields[0].GetUInt32();
  1656.         SummonerType summonerType       = SummonerType(fields[1].GetUInt8());
  1657.         uint8 group                     = fields[2].GetUInt8();
  1658.  
  1659.         switch (summonerType)
  1660.         {
  1661.             case SUMMONER_TYPE_CREATURE:
  1662.                 if (!GetCreatureTemplate(summonerId))
  1663.                 {
  1664.                     TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for creature summoner type, skipped.", summonerId);
  1665.                     continue;
  1666.                 }
  1667.                 break;
  1668.             case SUMMONER_TYPE_GAMEOBJECT:
  1669.                 if (!GetGameObjectTemplate(summonerId))
  1670.                 {
  1671.                     TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for gameobject summoner type, skipped.", summonerId);
  1672.                     continue;
  1673.                 }
  1674.                 break;
  1675.             case SUMMONER_TYPE_MAP:
  1676.                 if (!sMapStore.LookupEntry(summonerId))
  1677.                 {
  1678.                     TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for map summoner type, skipped.", summonerId);
  1679.                     continue;
  1680.                 }
  1681.                 break;
  1682.             default:
  1683.                 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type %u for summoner %u, skipped.", summonerType, summonerId);
  1684.                 continue;
  1685.         }
  1686.  
  1687.         TempSummonData data;
  1688.         data.entry                      = fields[3].GetUInt32();
  1689.  
  1690.         if (!GetCreatureTemplate(data.entry))
  1691.         {
  1692.             TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has creature in group [Summoner ID: %u, Summoner Type: %u, Group ID: %u] with non existing creature entry %u, skipped.", summonerId, summonerType, group, data.entry);
  1693.             continue;
  1694.         }
  1695.  
  1696.         float posX                      = fields[4].GetFloat();
  1697.         float posY                      = fields[5].GetFloat();
  1698.         float posZ                      = fields[6].GetFloat();
  1699.         float orientation               = fields[7].GetFloat();
  1700.  
  1701.         data.pos.Relocate(posX, posY, posZ, orientation);
  1702.  
  1703.         data.type                       = TempSummonType(fields[8].GetUInt8());
  1704.  
  1705.         if (data.type > TEMPSUMMON_MANUAL_DESPAWN)
  1706.         {
  1707.             TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled temp summon type %u in group [Summoner ID: %u, Summoner Type: %u, Group ID: %u] for creature entry %u, skipped.", data.type, summonerId, summonerType, group, data.entry);
  1708.             continue;
  1709.         }
  1710.  
  1711.         data.time                       = fields[9].GetUInt32();
  1712.  
  1713.         TempSummonGroupKey key(summonerId, summonerType, group);
  1714.         _tempSummonDataStore[key].push_back(data);
  1715.  
  1716.         ++count;
  1717.  
  1718.     } while (result->NextRow());
  1719.  
  1720.     TC_LOG_INFO("server.loading", ">> Loaded %u temp summons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1721. }
  1722.  
  1723. void ObjectMgr::LoadCreatures()
  1724. {
  1725.     uint32 oldMSTime = getMSTime();
  1726.  
  1727.     //                                               0              1   2    3           4           5           6            7        8             9              10
  1728.     QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, spawndist, "
  1729.     //   11               12         13       14            15         16         17          18          19                20                   21
  1730.         "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
  1731.     //   22
  1732.         "creature.ScriptName "
  1733.         "FROM creature "
  1734.         "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
  1735.         "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
  1736.  
  1737.     if (!result)
  1738.     {
  1739.         TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
  1740.         return;
  1741.     }
  1742.  
  1743.     // Build single time for check spawnmask
  1744.     std::map<uint32, uint32> spawnMasks;
  1745.     for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
  1746.         if (sMapStore.LookupEntry(i))
  1747.             for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
  1748.                 if (GetMapDifficultyData(i, Difficulty(k)))
  1749.                     spawnMasks[i] |= (1 << k);
  1750.  
  1751.     _creatureDataStore.rehash(result->GetRowCount());
  1752.  
  1753.     do
  1754.     {
  1755.         Field* fields = result->Fetch();
  1756.  
  1757.         ObjectGuid::LowType guid = fields[0].GetUInt32();
  1758.         uint32 entry        = fields[1].GetUInt32();
  1759.  
  1760.         CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
  1761.         if (!cInfo)
  1762.         {
  1763.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
  1764.             continue;
  1765.         }
  1766.  
  1767.         CreatureData& data = _creatureDataStore[guid];
  1768.         data.spawnId        = guid;
  1769.         data.id             = entry;
  1770.         data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
  1771.         data.displayid      = fields[7].GetUInt32();
  1772.         data.equipmentId    = fields[8].GetInt8();
  1773.         data.spawntimesecs  = fields[9].GetUInt32();
  1774.         data.spawndist      = fields[10].GetFloat();
  1775.         data.currentwaypoint= fields[11].GetUInt32();
  1776.         data.curhealth      = fields[12].GetUInt32();
  1777.         data.curmana        = fields[13].GetUInt32();
  1778.         data.movementType   = fields[14].GetUInt8();
  1779.         data.spawnMask      = fields[15].GetUInt8();
  1780.         data.phaseMask      = fields[16].GetUInt32();
  1781.         int16 gameEvent     = fields[17].GetInt8();
  1782.         uint32 PoolId       = fields[18].GetUInt32();
  1783.         data.npcflag        = fields[19].GetUInt32();
  1784.         data.unit_flags     = fields[20].GetUInt32();
  1785.         data.dynamicflags   = fields[21].GetUInt32();
  1786.         data.scriptId       = GetScriptId(fields[22].GetString());
  1787.         data.spawnGroupData = &_spawnGroupDataStore[0];
  1788.  
  1789.         MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
  1790.         if (!mapEntry)
  1791.         {
  1792.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId());
  1793.             continue;
  1794.         }
  1795.  
  1796.         // Skip spawnMask check for transport maps
  1797.         if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
  1798.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.spawnPoint.GetMapId());
  1799.  
  1800.         bool ok = true;
  1801.         for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
  1802.         {
  1803.             if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
  1804.             {
  1805.                 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that is listed as difficulty %u template (entry: %u) in `creature_template`, skipped.",
  1806.                     guid, diff + 1, data.id);
  1807.                 ok = false;
  1808.             }
  1809.         }
  1810.         if (!ok)
  1811.             continue;
  1812.  
  1813.         // -1 random, 0 no equipment
  1814.         if (data.equipmentId != 0)
  1815.         {
  1816.             if (!GetEquipmentInfo(data.id, data.equipmentId))
  1817.             {
  1818.                 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
  1819.                 data.equipmentId = 0;
  1820.             }
  1821.         }
  1822.  
  1823.         if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
  1824.         {
  1825.             if (!mapEntry->IsDungeon())
  1826.                 TC_LOG_ERROR("sql.sql", "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);
  1827.         }
  1828.  
  1829.         if (data.movementType >= MAX_DB_MOTION_TYPE)
  1830.         {
  1831.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with wrong movement generator type (%u), ignored and set to IDLE.", guid, data.id, data.movementType);
  1832.             data.movementType = IDLE_MOTION_TYPE;
  1833.         }
  1834.  
  1835.         if (data.spawndist < 0.0f)
  1836.         {
  1837.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
  1838.             data.spawndist = 0.0f;
  1839.         }
  1840.         else if (data.movementType == RANDOM_MOTION_TYPE)
  1841.         {
  1842.             if (data.spawndist == 0.0f)
  1843.             {
  1844.                 TC_LOG_ERROR("sql.sql", "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);
  1845.                 data.movementType = IDLE_MOTION_TYPE;
  1846.             }
  1847.         }
  1848.         else if (data.movementType == IDLE_MOTION_TYPE)
  1849.         {
  1850.             if (data.spawndist != 0.0f)
  1851.             {
  1852.                 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
  1853.                 data.spawndist = 0.0f;
  1854.             }
  1855.         }
  1856.  
  1857.         if (data.phaseMask == 0)
  1858.         {
  1859.             TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
  1860.             data.phaseMask = 1;
  1861.         }
  1862.  
  1863.         if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA))
  1864.         {
  1865.             uint32 zoneId = 0;
  1866.             uint32 areaId = 0;
  1867.             sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
  1868.  
  1869.             PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
  1870.  
  1871.             stmt->setUInt32(0, zoneId);
  1872.             stmt->setUInt32(1, areaId);
  1873.             stmt->setUInt64(2, guid);
  1874.  
  1875.             WorldDatabase.Execute(stmt);
  1876.         }
  1877.  
  1878.         // Add to grid if not managed by the game event or pool system
  1879.         if (gameEvent == 0 && PoolId == 0)
  1880.             AddCreatureToGrid(guid, &data);
  1881.     }
  1882.     while (result->NextRow());
  1883.  
  1884.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " creatures in %u ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
  1885. }
  1886.  
  1887. void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* data)
  1888. {
  1889.     uint8 mask = data->spawnMask;
  1890.     for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1891.     {
  1892.         if (mask & 1)
  1893.         {
  1894.             CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
  1895.             CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
  1896.             cell_guids.creatures.insert(guid);
  1897.         }
  1898.     }
  1899. }
  1900.  
  1901. void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const* data)
  1902. {
  1903.     uint8 mask = data->spawnMask;
  1904.     for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1905.     {
  1906.         if (mask & 1)
  1907.         {
  1908.             CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
  1909.             CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
  1910.             cell_guids.creatures.erase(guid);
  1911.         }
  1912.     }
  1913. }
  1914.  
  1915. ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
  1916. {
  1917.     GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
  1918.     if (!goinfo)
  1919.         return 0;
  1920.  
  1921.     Map* map = sMapMgr->CreateBaseMap(mapId);
  1922.     if (!map)
  1923.         return 0;
  1924.  
  1925.     ObjectGuid::LowType spawnId = GenerateGameObjectSpawnId();
  1926.  
  1927.     GameObjectData& data = NewOrExistGameObjectData(spawnId);
  1928.     data.spawnId        = spawnId;
  1929.     data.id             = entry;
  1930.     data.spawnPoint.WorldRelocate(mapId,pos);
  1931.     data.rotation       = rot;
  1932.     data.spawntimesecs  = spawntimedelay;
  1933.     data.animprogress   = 100;
  1934.     data.spawnMask      = 1;
  1935.     data.goState        = GO_STATE_READY;
  1936.     data.phaseMask      = PHASEMASK_NORMAL;
  1937.     data.artKit         = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
  1938.     data.dbData         = false;
  1939.     data.spawnGroupData = GetLegacySpawnGroup();
  1940.  
  1941.     AddGameobjectToGrid(spawnId, &data);
  1942.  
  1943.     // Spawn if necessary (loaded grids only)
  1944.     // We use spawn coords to spawn
  1945.     if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint))
  1946.     {
  1947.         GameObject* go = new GameObject;
  1948.         if (!go->LoadFromDB(spawnId, map, true))
  1949.         {
  1950.             TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry %u to map", entry);
  1951.             delete go;
  1952.             return 0;
  1953.         }
  1954.     }
  1955.  
  1956.     TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid %u entry %u map %u pos %s", spawnId, entry, mapId, data.spawnPoint.ToString().c_str());
  1957.  
  1958.     return spawnId;
  1959. }
  1960.  
  1961.  
  1962. ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Position const& pos, uint32 spawntimedelay /*= 0*/)
  1963. {
  1964.     CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
  1965.     if (!cInfo)
  1966.         return 0;
  1967.  
  1968.     uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
  1969.     CreatureBaseStats const* stats = GetCreatureBaseStats(level, cInfo->unit_class);
  1970.     Map* map = sMapMgr->CreateBaseMap(mapId);
  1971.     if (!map)
  1972.         return 0;
  1973.  
  1974.     ObjectGuid::LowType spawnId = GenerateCreatureSpawnId();
  1975.     CreatureData& data = NewOrExistCreatureData(spawnId);
  1976.     data.spawnId = spawnId;
  1977.     data.id = entry;
  1978.     data.spawnPoint.WorldRelocate(mapId, pos);
  1979.     data.displayid = 0;
  1980.     data.equipmentId = 0;
  1981.     data.spawntimesecs = spawntimedelay;
  1982.     data.spawndist = 0;
  1983.     data.currentwaypoint = 0;
  1984.     data.curhealth = stats->GenerateHealth(cInfo);
  1985.     data.curmana = stats->GenerateMana(cInfo);
  1986.     data.movementType = cInfo->MovementType;
  1987.     data.spawnMask = 1;
  1988.     data.phaseMask = PHASEMASK_NORMAL;
  1989.     data.dbData = false;
  1990.     data.npcflag = cInfo->npcflag;
  1991.     data.unit_flags = cInfo->unit_flags;
  1992.     data.dynamicflags = cInfo->dynamicflags;
  1993.     data.spawnGroupData = GetLegacySpawnGroup();
  1994.  
  1995.     AddCreatureToGrid(spawnId, &data);
  1996.  
  1997.     // We use spawn coords to spawn
  1998.     if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint))
  1999.     {
  2000.         Creature* creature = new Creature();
  2001.         if (!creature->LoadFromDB(spawnId, map, true, true))
  2002.         {
  2003.             TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry);
  2004.             delete creature;
  2005.             return 0;
  2006.         }
  2007.     }
  2008.  
  2009.     return spawnId;
  2010. }
  2011.  
  2012. void ObjectMgr::LoadGameObjects()
  2013. {
  2014.     uint32 oldMSTime = getMSTime();
  2015.  
  2016.     //                                                0                1   2    3           4           5           6
  2017.     QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
  2018.     //   7          8          9          10         11             12            13     14         15         16          17
  2019.         "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, eventEntry, pool_entry, "
  2020.     //   18
  2021.         "ScriptName "
  2022.         "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
  2023.         "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");
  2024.  
  2025.     if (!result)
  2026.     {
  2027.         TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
  2028.         return;
  2029.     }
  2030.  
  2031.     // build single time for check spawnmask
  2032.     std::map<uint32, uint32> spawnMasks;
  2033.     for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
  2034.         if (sMapStore.LookupEntry(i))
  2035.             for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
  2036.                 if (GetMapDifficultyData(i, Difficulty(k)))
  2037.                     spawnMasks[i] |= (1 << k);
  2038.  
  2039.     _gameObjectDataStore.rehash(result->GetRowCount());
  2040.  
  2041.     do
  2042.     {
  2043.         Field* fields = result->Fetch();
  2044.  
  2045.         ObjectGuid::LowType guid = fields[0].GetUInt32();
  2046.         uint32 entry        = fields[1].GetUInt32();
  2047.  
  2048.         GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
  2049.         if (!gInfo)
  2050.         {
  2051.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
  2052.             continue;
  2053.         }
  2054.  
  2055.         if (!gInfo->displayId)
  2056.         {
  2057.             switch (gInfo->type)
  2058.             {
  2059.                 case GAMEOBJECT_TYPE_TRAP:
  2060.                 case GAMEOBJECT_TYPE_SPELL_FOCUS:
  2061.                     break;
  2062.                 default:
  2063.                     TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry %u GoType: %u) doesn't have a displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
  2064.                     break;
  2065.             }
  2066.         }
  2067.  
  2068.         if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
  2069.         {
  2070.             TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry %u GoType: %u) has an invalid displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
  2071.             continue;
  2072.         }
  2073.  
  2074.         GameObjectData& data = _gameObjectDataStore[guid];
  2075.  
  2076.         data.spawnId        = guid;
  2077.         data.id             = entry;
  2078.         data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
  2079.         data.rotation.x     = fields[7].GetFloat();
  2080.         data.rotation.y     = fields[8].GetFloat();
  2081.         data.rotation.z     = fields[9].GetFloat();
  2082.         data.rotation.w     = fields[10].GetFloat();
  2083.         data.spawntimesecs  = fields[11].GetInt32();
  2084.         data.spawnGroupData = &_spawnGroupDataStore[0];
  2085.  
  2086.         MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId());
  2087.         if (!mapEntry)
  2088.         {
  2089.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.spawnPoint.GetMapId());
  2090.             continue;
  2091.         }
  2092.  
  2093.         if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
  2094.         {
  2095.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but the gameobejct is marked as despawnable at action.", guid, data.id);
  2096.         }
  2097.  
  2098.         data.animprogress   = fields[12].GetUInt8();
  2099.         data.artKit         = 0;
  2100.  
  2101.         uint32 go_state     = fields[13].GetUInt8();
  2102.         if (go_state >= MAX_GO_STATE)
  2103.         {
  2104.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
  2105.             continue;
  2106.         }
  2107.         data.goState       = GOState(go_state);
  2108.  
  2109.         data.spawnMask      = fields[14].GetUInt8();
  2110.  
  2111.         if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()])
  2112.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.spawnPoint.GetMapId());
  2113.  
  2114.         data.phaseMask      = fields[15].GetUInt32();
  2115.         int16 gameEvent     = fields[16].GetInt8();
  2116.         uint32 PoolId        = fields[17].GetUInt32();
  2117.  
  2118.         data.scriptId = GetScriptId(fields[18].GetString());
  2119.  
  2120.         if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
  2121.         {
  2122.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationX (%f) value, skip", guid, data.id, data.rotation.x);
  2123.             continue;
  2124.         }
  2125.  
  2126.         if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
  2127.         {
  2128.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationY (%f) value, skip", guid, data.id, data.rotation.y);
  2129.             continue;
  2130.         }
  2131.  
  2132.         if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
  2133.         {
  2134.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationZ (%f) value, skip", guid, data.id, data.rotation.z);
  2135.             continue;
  2136.         }
  2137.  
  2138.         if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
  2139.         {
  2140.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationW (%f) value, skip", guid, data.id, data.rotation.w);
  2141.             continue;
  2142.         }
  2143.  
  2144.         if (!MapManager::IsValidMapCoord(data.spawnPoint))
  2145.         {
  2146.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
  2147.             continue;
  2148.         }
  2149.  
  2150.         if (data.phaseMask == 0)
  2151.         {
  2152.             TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
  2153.             data.phaseMask = 1;
  2154.         }
  2155.  
  2156.         if (sWorld->getBoolConfig(CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA))
  2157.         {
  2158.             uint32 zoneId = 0;
  2159.             uint32 areaId = 0;
  2160.             sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint);
  2161.  
  2162.             PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
  2163.  
  2164.             stmt->setUInt32(0, zoneId);
  2165.             stmt->setUInt32(1, areaId);
  2166.             stmt->setUInt64(2, guid);
  2167.  
  2168.             WorldDatabase.Execute(stmt);
  2169.         }
  2170.  
  2171.         if (gameEvent == 0 && PoolId == 0)                      // if not this is to be managed by GameEvent System or Pool system
  2172.             AddGameobjectToGrid(guid, &data);
  2173.     }
  2174.     while (result->NextRow());
  2175.  
  2176.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " gameobjects in %u ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
  2177. }
  2178.  
  2179. void ObjectMgr::LoadSpawnGroupTemplates()
  2180. {
  2181.     uint32 oldMSTime = getMSTime();
  2182.  
  2183.     //                                               0        1          2
  2184.     QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
  2185.  
  2186.     if (result)
  2187.     {
  2188.         do
  2189.         {
  2190.             Field* fields = result->Fetch();
  2191.             uint32 groupId = fields[0].GetUInt32();
  2192.             SpawnGroupTemplateData& group = _spawnGroupDataStore[groupId];
  2193.             group.groupId = groupId;
  2194.             group.name = fields[1].GetString();
  2195.             group.mapId = SPAWNGROUP_MAP_UNSET;
  2196.             uint32 flags = fields[2].GetUInt32();
  2197.             if (flags & ~SPAWNGROUP_FLAGS_ALL)
  2198.             {
  2199.                 flags &= SPAWNGROUP_FLAGS_ALL;
  2200.                 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag %u on group ID %u (%s), reduced to valid flag %u.", flags, groupId, group.name.c_str(), uint32(group.flags));
  2201.             }
  2202.             if (flags & SPAWNGROUP_FLAG_SYSTEM && flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)
  2203.             {
  2204.                 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
  2205.                 TC_LOG_ERROR("sql.sql", "System spawn group %u (%s) has invalid manual spawn flag. Ignored.", groupId, group.name.c_str());
  2206.             }
  2207.             group.flags = SpawnGroupFlags(flags);
  2208.         } while (result->NextRow());
  2209.     }
  2210.  
  2211.     if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
  2212.     {
  2213.         TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
  2214.         SpawnGroupTemplateData& data = _spawnGroupDataStore[0];
  2215.         data.groupId = 0;
  2216.         data.name = "Default Group";
  2217.         data.mapId = 0;
  2218.         data.flags = SPAWNGROUP_FLAG_SYSTEM;
  2219.     }
  2220.     if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
  2221.     {
  2222.         TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
  2223.         SpawnGroupTemplateData&data = _spawnGroupDataStore[1];
  2224.         data.groupId = 1;
  2225.         data.name = "Legacy Group";
  2226.         data.mapId = 0;
  2227.         data.flags = SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE);
  2228.     }
  2229.  
  2230.     if (result)
  2231.         TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " spawn group templates in %u ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
  2232.     else
  2233.         TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
  2234.  
  2235.     return;
  2236. }
  2237.  
  2238. void ObjectMgr::LoadSpawnGroups()
  2239. {
  2240.     uint32 oldMSTime = getMSTime();
  2241.  
  2242.     //                                               0        1          2
  2243.     QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
  2244.  
  2245.     if (!result)
  2246.     {
  2247.         TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
  2248.         return;
  2249.     }
  2250.  
  2251.     uint32 numMembers = 0;
  2252.     do
  2253.     {
  2254.         Field* fields = result->Fetch();
  2255.         uint32 groupId = fields[0].GetUInt32();
  2256.         SpawnObjectType spawnType;
  2257.         {
  2258.             uint32 type = fields[1].GetUInt8();
  2259.             if (type >= SPAWN_TYPE_MAX)
  2260.             {
  2261.                 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", type, groupId);
  2262.                 continue;
  2263.             }
  2264.             spawnType = SpawnObjectType(type);
  2265.         }
  2266.         ObjectGuid::LowType spawnId = fields[2].GetUInt32();
  2267.  
  2268.         SpawnData const* data = GetSpawnData(spawnType, spawnId);
  2269.         if (!data)
  2270.         {
  2271.             TC_LOG_ERROR("sql.sql", "Spawn data with ID (%u,%u) not found, but is listed as a member of spawn group %u!", uint32(spawnType), spawnId, groupId);
  2272.             continue;
  2273.         }
  2274.         else if (data->spawnGroupData->groupId)
  2275.         {
  2276.             TC_LOG_ERROR("sql.sql", "Spawn with ID (%u,%u) is listed as a member of spawn group %u, but is already a member of spawn group %u. Skipping.", uint32(spawnType), spawnId, groupId, data->spawnGroupData->groupId);
  2277.             continue;
  2278.         }
  2279.         auto it = _spawnGroupDataStore.find(groupId);
  2280.         if (it == _spawnGroupDataStore.end())
  2281.         {
  2282.             TC_LOG_ERROR("sql.sql", "Spawn group %u assigned to spawn ID (%u,%u), but group is found!", groupId, uint32(spawnType), spawnId);
  2283.             continue;
  2284.         }
  2285.         else
  2286.         {
  2287.             SpawnGroupTemplateData& groupTemplate = it->second;
  2288.             if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
  2289.                 groupTemplate.mapId = data->spawnPoint.GetMapId();
  2290.             else if (groupTemplate.mapId != data->spawnPoint.GetMapId() && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
  2291.             {
  2292.                 TC_LOG_ERROR("sql.sql", "Spawn group %u has map ID %u, but spawn (%u,%u) has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->spawnPoint.GetMapId());
  2293.                 continue;
  2294.             }
  2295.             const_cast<SpawnData*>(data)->spawnGroupData = &groupTemplate;
  2296.             if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
  2297.                 _spawnGroupMapStore.emplace(groupId, data);
  2298.             ++numMembers;
  2299.         }
  2300.     } while (result->NextRow());
  2301.  
  2302.     TC_LOG_INFO("server.loading", ">> Loaded %u spawn group members in %u ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
  2303. }
  2304.  
  2305. void ObjectMgr::LoadInstanceSpawnGroups()
  2306. {
  2307.     uint32 oldMSTime = getMSTime();
  2308.  
  2309.     //                                               0              1            2           3             4
  2310.     QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
  2311.  
  2312.     if (!result)
  2313.     {
  2314.         TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
  2315.         return;
  2316.     }
  2317.  
  2318.     uint32 n = 0;
  2319.     do
  2320.     {
  2321.         Field* fields = result->Fetch();
  2322.         uint32 const spawnGroupId = fields[3].GetUInt32();
  2323.         auto it = _spawnGroupDataStore.find(spawnGroupId);
  2324.         if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
  2325.         {
  2326.             TC_LOG_ERROR("sql.sql", "Invalid spawn group %u specified for instance %u. Skipped.", spawnGroupId, fields[0].GetUInt16());
  2327.             continue;
  2328.         }
  2329.  
  2330.         uint16 const instanceMapId = fields[0].GetUInt16();
  2331.         auto& vector = _instanceSpawnGroupStore[instanceMapId];
  2332.         vector.emplace_back();
  2333.         InstanceSpawnGroupInfo& info = vector.back();
  2334.         info.SpawnGroupId = spawnGroupId;
  2335.         info.BossStateId = fields[1].GetUInt8();
  2336.  
  2337.         uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
  2338.         uint8 const states = fields[2].GetUInt8();
  2339.         if (states & ~ALL_STATES)
  2340.         {
  2341.             info.BossStates = states & ALL_STATES;
  2342.             TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid boss state mask %u - truncated to %u.", instanceMapId, spawnGroupId, states, info.BossStates);
  2343.         }
  2344.         else
  2345.             info.BossStates = states;
  2346.  
  2347.         uint8 const flags = fields[4].GetUInt8();
  2348.         if (flags & ~InstanceSpawnGroupInfo::FLAG_ALL)
  2349.         {
  2350.             info.Flags = flags & InstanceSpawnGroupInfo::FLAG_ALL;
  2351.             TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid flags %u - truncated to %u.", instanceMapId, spawnGroupId, flags, info.Flags);
  2352.         }
  2353.         else
  2354.             info.Flags = flags;
  2355.  
  2356.         ++n;
  2357.     } while (result->NextRow());
  2358.  
  2359.     TC_LOG_INFO("server.loading", ">> Loaded %u instance spawn groups in %u ms", n, GetMSTimeDiffToNow(oldMSTime));
  2360. }
  2361.  
  2362. void ObjectMgr::OnDeleteSpawnData(SpawnData const* data)
  2363. {
  2364.     auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
  2365.     ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u,%u) is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
  2366.     if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
  2367.         return;
  2368.  
  2369.     auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
  2370.     for (auto it = pair.first; it != pair.second; ++it)
  2371.     {
  2372.         if (it->second != data)
  2373.             continue;
  2374.         _spawnGroupMapStore.erase(it);
  2375.         return;
  2376.     }
  2377.     ASSERT(false, "Spawn data (%u,%u) being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
  2378. }
  2379.  
  2380. void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data)
  2381. {
  2382.     uint8 mask = data->spawnMask;
  2383.     for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  2384.     {
  2385.         if (mask & 1)
  2386.         {
  2387.             CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
  2388.             CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
  2389.             cell_guids.gameobjects.insert(guid);
  2390.         }
  2391.     }
  2392. }
  2393.  
  2394. void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectData const* data)
  2395. {
  2396.     uint8 mask = data->spawnMask;
  2397.     for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  2398.     {
  2399.         if (mask & 1)
  2400.         {
  2401.             CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY());
  2402.             CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()];
  2403.             cell_guids.gameobjects.erase(guid);
  2404.         }
  2405.     }
  2406. }
  2407.  
  2408. void ObjectMgr::LoadItemLocales()
  2409. {
  2410.     uint32 oldMSTime = getMSTime();
  2411.  
  2412.     _itemLocaleStore.clear();                                 // need for reload case
  2413.  
  2414.     //                                               0   1       2     3
  2415.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name, Description FROM item_template_locale");
  2416.     if (!result)
  2417.         return;
  2418.  
  2419.     do
  2420.     {
  2421.         Field* fields = result->Fetch();
  2422.  
  2423.         uint32 id               = fields[0].GetUInt32();
  2424.         std::string localeName  = fields[1].GetString();
  2425.         std::string Name        = fields[2].GetString();
  2426.         std::string Description = fields[3].GetString();
  2427.  
  2428.         ItemLocale& data        = _itemLocaleStore[id];
  2429.         LocaleConstant locale   = GetLocaleByName(localeName);
  2430.         if (locale == LOCALE_enUS)
  2431.             continue;
  2432.  
  2433.         AddLocaleString(Name, locale, data.Name);
  2434.         AddLocaleString(Description, locale, data.Description);
  2435.     } while (result->NextRow());
  2436.  
  2437.     TC_LOG_INFO("server.loading", ">> Loaded %u Item locale strings in %u ms", uint32(_itemLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  2438. }
  2439.  
  2440. void ObjectMgr::LoadItemTemplates()
  2441. {
  2442.     uint32 oldMSTime = getMSTime();
  2443.  
  2444.     //                                                 0      1       2               3              4        5        6       7          8         9        10        11           12
  2445.     QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, "
  2446.     //                                              13              14           15          16             17               18                19              20
  2447.                                              "AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, "
  2448.     //                                              21                      22                       23               24        25          26             27           28
  2449.                                              "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, "
  2450.     //                                            29           30          31           32          33           34          35           36          37           38
  2451.                                              "stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, "
  2452.     //                                            39           40          41           42           43          44           45           46           47
  2453.                                              "stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, "
  2454.     //                                                   48                    49           50        51        52         53        54         55      56      57        58
  2455.                                              "ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, "
  2456.     //                                            59          60         61          62          63           64            65        66      67           68           69            70              71
  2457.                                              "nature_res, frost_res, shadow_res, arcane_res, customa_res, customb_res, customc_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, "
  2458.     //                                              72              73                74                 75                76           77               78
  2459.                                              "spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, "
  2460.     //                                              79               80              81                  82                 83           84               85
  2461.                                              "spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, "
  2462.     //                                              86               87              88                  89                 90           91               92
  2463.                                              "spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, "
  2464.     //                                              93               94              95                  96                  97          98               99
  2465.                                              "spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, "
  2466.     //                                             100              101             102                  103                 104        105         106       107          108
  2467.                                              "spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, "
  2468.     //                                            109       110     111      112          113            114       115     116        117        118   119     120
  2469.                                              "startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, "
  2470.     //                                            121             122             123             124             125            126              127            128
  2471.                                              "TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, "
  2472.     //                                            129                 130                    131             132            133            134         135         136
  2473.                                              "GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, "
  2474.     //                                           137       138           139           140
  2475.                                              "FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template");
  2476.  
  2477.     if (!result)
  2478.     {
  2479.         TC_LOG_INFO("server.loading", ">> Loaded 0 item templates. DB table `item_template` is empty.");
  2480.         return;
  2481.     }
  2482.  
  2483.     _itemTemplateStore.reserve(result->GetRowCount());
  2484.     bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES);
  2485.  
  2486.     do
  2487.     {
  2488.         Field* fields = result->Fetch();
  2489.  
  2490.         uint32 entry = fields[0].GetUInt32();
  2491.         ItemTemplate& itemTemplate = _itemTemplateStore[entry];
  2492.  
  2493.         itemTemplate.ItemId                    = entry;
  2494.         itemTemplate.Class                     = uint32(fields[1].GetUInt8());
  2495.         itemTemplate.SubClass                  = uint32(fields[2].GetUInt8());
  2496.         itemTemplate.SoundOverrideSubclass     = int32(fields[3].GetInt8());
  2497.         itemTemplate.Name1                     = fields[4].GetString();
  2498.         itemTemplate.DisplayInfoID             = fields[5].GetUInt32();
  2499.         itemTemplate.Quality                   = uint32(fields[6].GetUInt8());
  2500.         itemTemplate.Flags                     = fields[7].GetUInt32();
  2501.         itemTemplate.Flags2                    = fields[8].GetUInt32();
  2502.         itemTemplate.BuyCount                  = uint32(fields[9].GetUInt8());
  2503.         itemTemplate.BuyPrice                  = int32(fields[10].GetInt64());
  2504.         itemTemplate.SellPrice                 = fields[11].GetUInt32();
  2505.         itemTemplate.InventoryType             = uint32(fields[12].GetUInt8());
  2506.         itemTemplate.AllowableClass            = fields[13].GetInt32();
  2507.         itemTemplate.AllowableRace             = fields[14].GetInt32();
  2508.         itemTemplate.ItemLevel                 = uint32(fields[15].GetUInt16());
  2509.         itemTemplate.RequiredLevel             = uint32(fields[16].GetUInt8());
  2510.         itemTemplate.RequiredSkill             = uint32(fields[17].GetUInt16());
  2511.         itemTemplate.RequiredSkillRank         = uint32(fields[18].GetUInt16());
  2512.         itemTemplate.RequiredSpell             = fields[19].GetUInt32();
  2513.         itemTemplate.RequiredHonorRank         = fields[20].GetUInt32();
  2514.         itemTemplate.RequiredCityRank          = fields[21].GetUInt32();
  2515.         itemTemplate.RequiredReputationFaction = uint32(fields[22].GetUInt16());
  2516.         itemTemplate.RequiredReputationRank    = uint32(fields[23].GetUInt16());
  2517.         itemTemplate.MaxCount                  = fields[24].GetInt32();
  2518.         itemTemplate.Stackable                 = fields[25].GetInt32();
  2519.         itemTemplate.ContainerSlots            = uint32(fields[26].GetUInt8());
  2520.         itemTemplate.StatsCount                = uint32(fields[27].GetUInt8());
  2521.  
  2522.         if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
  2523.         {
  2524.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
  2525.             itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
  2526.         }
  2527.  
  2528.         for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
  2529.         {
  2530.             itemTemplate.ItemStat[i].ItemStatType  = uint32(fields[28 + i*2].GetUInt8());
  2531.             itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i*2].GetInt16());
  2532.         }
  2533.  
  2534.         itemTemplate.ScalingStatDistribution = uint32(fields[48].GetUInt16());
  2535.         itemTemplate.ScalingStatValue        = fields[49].GetInt32();
  2536.  
  2537.         for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
  2538.         {
  2539.             itemTemplate.Damage[i].DamageMin  = fields[50 + i*3].GetFloat();
  2540.             itemTemplate.Damage[i].DamageMax  = fields[51 + i*3].GetFloat();
  2541.             itemTemplate.Damage[i].DamageType = uint32(fields[52 + i*3].GetUInt8());
  2542.         }
  2543.  
  2544.         itemTemplate.Armor          = uint32(fields[56].GetUInt16());
  2545.         itemTemplate.HolyRes        = uint32(fields[57].GetUInt8());
  2546.         itemTemplate.FireRes        = uint32(fields[58].GetUInt8());
  2547.         itemTemplate.NatureRes      = uint32(fields[59].GetUInt8());
  2548.         itemTemplate.FrostRes       = uint32(fields[60].GetUInt8());
  2549.         itemTemplate.ShadowRes      = uint32(fields[61].GetUInt8());
  2550.         itemTemplate.ArcaneRes      = uint32(fields[62].GetUInt8());
  2551.         itemTemplate.CustomARes    = uint32(fields[63].GetUInt8());
  2552.         itemTemplate.CustomBRes    = uint32(fields[64].GetUInt8());
  2553.         itemTemplate.CustomCRes    = uint32(fields[65].GetUInt8());
  2554.         itemTemplate.Delay          = uint32(fields[66].GetUInt16());
  2555.         itemTemplate.AmmoType       = uint32(fields[67].GetUInt8());
  2556.         itemTemplate.RangedModRange = fields[68].GetFloat();
  2557.  
  2558.         for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  2559.         {
  2560.             itemTemplate.Spells[i].SpellId               = fields[69 + i*7  ].GetInt32();
  2561.             itemTemplate.Spells[i].SpellTrigger          = uint32(fields[70 + i*7].GetUInt8());
  2562.             itemTemplate.Spells[i].SpellCharges          = int32(fields[71 + i*7].GetInt16());
  2563.             itemTemplate.Spells[i].SpellPPMRate          = fields[72 + i*7].GetFloat();
  2564.             itemTemplate.Spells[i].SpellCooldown         = fields[73 + i*7].GetInt32();
  2565.             itemTemplate.Spells[i].SpellCategory         = uint32(fields[74 + i*7].GetUInt16());
  2566.             itemTemplate.Spells[i].SpellCategoryCooldown = fields[75 + i*7].GetInt32();
  2567.         }
  2568.  
  2569.         itemTemplate.Bonding        = uint32(fields[104].GetUInt8());
  2570.         itemTemplate.Description    = fields[105].GetString();
  2571.         itemTemplate.PageText       = fields[106].GetUInt32();
  2572.         itemTemplate.LanguageID     = uint32(fields[107].GetUInt8());
  2573.         itemTemplate.PageMaterial   = uint32(fields[108].GetUInt8());
  2574.         itemTemplate.StartQuest     = fields[109].GetUInt32();
  2575.         itemTemplate.LockID         = fields[110].GetUInt32();
  2576.         itemTemplate.Material       = int32(fields[111].GetInt8());
  2577.         itemTemplate.Sheath         = uint32(fields[112].GetUInt8());
  2578.         itemTemplate.RandomProperty = fields[113].GetUInt32();
  2579.         itemTemplate.RandomSuffix   = fields[114].GetInt32();
  2580.         itemTemplate.Block          = fields[115].GetUInt32();
  2581.         itemTemplate.ItemSet        = fields[116].GetUInt32();
  2582.         itemTemplate.MaxDurability  = uint32(fields[117].GetUInt16());
  2583.         itemTemplate.Area           = fields[118].GetUInt32();
  2584.         itemTemplate.Map            = uint32(fields[119].GetUInt16());
  2585.         itemTemplate.BagFamily      = fields[120].GetUInt32();
  2586.         itemTemplate.TotemCategory  = fields[121].GetUInt32();
  2587.  
  2588.         for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
  2589.         {
  2590.             itemTemplate.Socket[i].Color   = uint32(fields[122 + i*2].GetUInt8());
  2591.             itemTemplate.Socket[i].Content = fields[123 + i*2].GetUInt32();
  2592.         }
  2593.  
  2594.         itemTemplate.socketBonus             = fields[128].GetUInt32();
  2595.         itemTemplate.GemProperties           = fields[129].GetUInt32();
  2596.         itemTemplate.RequiredDisenchantSkill = uint32(fields[130].GetInt16());
  2597.         itemTemplate.ArmorDamageModifier     = fields[131].GetFloat();
  2598.         itemTemplate.Duration                = fields[132].GetUInt32();
  2599.         itemTemplate.ItemLimitCategory       = uint32(fields[133].GetInt16());
  2600.         itemTemplate.HolidayId               = fields[134].GetUInt32();
  2601.         itemTemplate.ScriptId                = sObjectMgr->GetScriptId(fields[135].GetString());
  2602.         itemTemplate.DisenchantID            = fields[136].GetUInt32();
  2603.         itemTemplate.FoodType                = uint32(fields[137].GetUInt8());
  2604.         itemTemplate.MinMoneyLoot            = fields[138].GetUInt32();
  2605.         itemTemplate.MaxMoneyLoot            = fields[139].GetUInt32();
  2606.         itemTemplate.FlagsCu                 = fields[140].GetUInt32();
  2607.  
  2608.         // Checks
  2609.  
  2610.         ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
  2611.  
  2612.         if (dbcitem)
  2613.         {
  2614.             if (itemTemplate.Class != dbcitem->Class)
  2615.             {
  2616.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct class %u, must be %u .", entry, itemTemplate.Class, dbcitem->Class);
  2617.                 if (enforceDBCAttributes)
  2618.                     itemTemplate.Class = dbcitem->Class;
  2619.             }
  2620.  
  2621.             if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclass)
  2622.             {
  2623.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct SoundOverrideSubclass (%i), must be %i .", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclass);
  2624.                 if (enforceDBCAttributes)
  2625.                     itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclass;
  2626.             }
  2627.             if (itemTemplate.Material != dbcitem->Material)
  2628.             {
  2629.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct material (%i), must be %i .", entry, itemTemplate.Material, dbcitem->Material);
  2630.                 if (enforceDBCAttributes)
  2631.                     itemTemplate.Material = dbcitem->Material;
  2632.             }
  2633.             if (itemTemplate.InventoryType != dbcitem->InventoryType)
  2634.             {
  2635.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct inventory type (%u), must be %u .", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
  2636.                 if (enforceDBCAttributes)
  2637.                     itemTemplate.InventoryType = dbcitem->InventoryType;
  2638.             }
  2639.             if (itemTemplate.DisplayInfoID != dbcitem->DisplayId)
  2640.             {
  2641.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct display id (%u), must be %u .", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayId);
  2642.                 if (enforceDBCAttributes)
  2643.                     itemTemplate.DisplayInfoID = dbcitem->DisplayId;
  2644.             }
  2645.             if (itemTemplate.Sheath != dbcitem->Sheath)
  2646.             {
  2647.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have a correct sheathid (%u), must be %u .", entry, itemTemplate.Sheath, dbcitem->Sheath);
  2648.                 if (enforceDBCAttributes)
  2649.                     itemTemplate.Sheath = dbcitem->Sheath;
  2650.             }
  2651.  
  2652.         }
  2653.         else
  2654.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not exist in item.dbc! (not correct id?).", entry);
  2655.  
  2656.         if (itemTemplate.Class >= MAX_ITEM_CLASS)
  2657.         {
  2658.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Class value (%u)", entry, itemTemplate.Class);
  2659.             itemTemplate.Class = ITEM_CLASS_MISC;
  2660.         }
  2661.  
  2662.         if (itemTemplate.SubClass >= MaxItemSubclassValues[itemTemplate.Class])
  2663.         {
  2664.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Subclass value (%u) for class %u", entry, itemTemplate.SubClass, itemTemplate.Class);
  2665.             itemTemplate.SubClass = 0;// exist for all item classes
  2666.         }
  2667.  
  2668.         if (itemTemplate.Quality >= MAX_ITEM_QUALITY)
  2669.         {
  2670.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Quality value (%u)", entry, itemTemplate.Quality);
  2671.             itemTemplate.Quality = ITEM_QUALITY_NORMAL;
  2672.         }
  2673.  
  2674.         if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_HORDE)
  2675.         {
  2676.             if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE))
  2677.                 if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0)
  2678.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_HORDE (%u) in Flags field, item cannot be equipped or used by these races.",
  2679.                         entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_HORDE);
  2680.  
  2681.             if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_ALLIANCE)
  2682.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `Flags2` flags (ITEM_FLAG2_FACTION_ALLIANCE) and ITEM_FLAG2_FACTION_HORDE (%u) in Flags field, this is a wrong combination.",
  2683.                     entry, ITEM_FLAG2_FACTION_ALLIANCE, ITEM_FLAG2_FACTION_HORDE);
  2684.         }
  2685.         else if (itemTemplate.Flags2 & ITEM_FLAG2_FACTION_ALLIANCE)
  2686.         {
  2687.             if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE))
  2688.                 if ((itemTemplate.AllowableRace & faction->BaseRepRaceMask[0]) == 0)
  2689.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has value (%u) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_ALLIANCE (%u) in Flags field, item cannot be equipped or used by these races.",
  2690.                         entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_ALLIANCE);
  2691.         }
  2692.  
  2693.         if (itemTemplate.BuyCount <= 0)
  2694.         {
  2695.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", entry, itemTemplate.BuyCount);
  2696.             itemTemplate.BuyCount = 1;
  2697.         }
  2698.  
  2699.         if (itemTemplate.InventoryType >= MAX_INVTYPE)
  2700.         {
  2701.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong InventoryType value (%u)", entry, itemTemplate.InventoryType);
  2702.             itemTemplate.InventoryType = INVTYPE_NON_EQUIP;
  2703.         }
  2704.  
  2705.         if (itemTemplate.RequiredSkill >= MAX_SKILL_TYPE)
  2706.         {
  2707.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong RequiredSkill value (%u)", entry, itemTemplate.RequiredSkill);
  2708.             itemTemplate.RequiredSkill = 0;
  2709.         }
  2710.  
  2711.         {
  2712.             // can be used in equip slot, as page read use in inventory, or spell casting at use
  2713.             bool req = itemTemplate.InventoryType != INVTYPE_NON_EQUIP || itemTemplate.PageText;
  2714.             if (!req)
  2715.                 for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
  2716.                 {
  2717.                     if (itemTemplate.Spells[j].SpellId > 0)
  2718.                     {
  2719.                         req = true;
  2720.                         break;
  2721.                     }
  2722.                 }
  2723.  
  2724.             if (req)
  2725.             {
  2726.                 if (!(itemTemplate.AllowableClass & CLASSMASK_ALL_PLAYABLE))
  2727.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have any playable classes (%u) in `AllowableClass` and can't be equipped or used.", entry, itemTemplate.AllowableClass);
  2728.  
  2729.                 if (!(itemTemplate.AllowableRace & RACEMASK_ALL_PLAYABLE))
  2730.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have any playable races (%u) in `AllowableRace` and can't be equipped or used.", entry, itemTemplate.AllowableRace);
  2731.             }
  2732.         }
  2733.  
  2734.         if (itemTemplate.RequiredSpell && !sSpellMgr->GetSpellInfo(itemTemplate.RequiredSpell))
  2735.         {
  2736.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has a wrong (non-existing) spell in RequiredSpell (%u)", entry, itemTemplate.RequiredSpell);
  2737.             itemTemplate.RequiredSpell = 0;
  2738.         }
  2739.  
  2740.         if (itemTemplate.RequiredReputationRank >= MAX_REPUTATION_RANK)
  2741.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", entry, itemTemplate.RequiredReputationRank);
  2742.  
  2743.         if (itemTemplate.RequiredReputationFaction)
  2744.         {
  2745.             if (!sFactionStore.LookupEntry(itemTemplate.RequiredReputationFaction))
  2746.             {
  2747.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", entry, itemTemplate.RequiredReputationFaction);
  2748.                 itemTemplate.RequiredReputationFaction = 0;
  2749.             }
  2750.  
  2751.             if (itemTemplate.RequiredReputationRank == MIN_REPUTATION_RANK)
  2752.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", entry);
  2753.         }
  2754.  
  2755.         if (itemTemplate.MaxCount < -1)
  2756.         {
  2757.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.", entry, itemTemplate.MaxCount);
  2758.             itemTemplate.MaxCount = -1;
  2759.         }
  2760.  
  2761.         if (itemTemplate.Stackable == 0)
  2762.         {
  2763.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.", entry, itemTemplate.Stackable);
  2764.             itemTemplate.Stackable = 1;
  2765.         }
  2766.         else if (itemTemplate.Stackable < -1)
  2767.         {
  2768.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.", entry, itemTemplate.Stackable);
  2769.             itemTemplate.Stackable = -1;
  2770.         }
  2771.  
  2772.         if (itemTemplate.ContainerSlots > MAX_BAG_SIZE)
  2773.         {
  2774.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", entry, itemTemplate.ContainerSlots, MAX_BAG_SIZE);
  2775.             itemTemplate.ContainerSlots = MAX_BAG_SIZE;
  2776.         }
  2777.  
  2778.         for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
  2779.         {
  2780.             // for ItemStatValue != 0
  2781.             if (itemTemplate.ItemStat[j].ItemStatValue && itemTemplate.ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
  2782.             {
  2783.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong (non-existing?) stat_type%d (%u)", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
  2784.                 itemTemplate.ItemStat[j].ItemStatType = 0;
  2785.             }
  2786.  
  2787.             switch (itemTemplate.ItemStat[j].ItemStatType)
  2788.             {
  2789.                 case ITEM_MOD_SPELL_HEALING_DONE:
  2790.                 case ITEM_MOD_SPELL_DAMAGE_DONE:
  2791.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has deprecated stat_type%d (%u)", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
  2792.                     break;
  2793.                 default:
  2794.                     break;
  2795.             }
  2796.         }
  2797.  
  2798.         for (uint8 j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
  2799.         {
  2800.             if (itemTemplate.Damage[j].DamageType >= MAX_SPELL_SCHOOL)
  2801.             {
  2802.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong dmg_type%d (%u)", entry, j+1, itemTemplate.Damage[j].DamageType);
  2803.                 itemTemplate.Damage[j].DamageType = 0;
  2804.             }
  2805.         }
  2806.  
  2807.         // special format
  2808.         if ((itemTemplate.Spells[0].SpellId == 483) || (itemTemplate.Spells[0].SpellId == 55884))
  2809.         {
  2810.             // spell_1
  2811.             if (itemTemplate.Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
  2812.             {
  2813.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format", entry, 0+1, itemTemplate.Spells[0].SpellTrigger);
  2814.                 itemTemplate.Spells[0].SpellId = 0;
  2815.                 itemTemplate.Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2816.                 itemTemplate.Spells[1].SpellId = 0;
  2817.                 itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2818.             }
  2819.  
  2820.             // spell_2 have learning spell
  2821.             if (itemTemplate.Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
  2822.             {
  2823.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.", entry, 1+1, itemTemplate.Spells[1].SpellTrigger);
  2824.                 itemTemplate.Spells[0].SpellId = 0;
  2825.                 itemTemplate.Spells[1].SpellId = 0;
  2826.                 itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2827.             }
  2828.             else if (!itemTemplate.Spells[1].SpellId)
  2829.             {
  2830.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not have an expected spell in spellid_%d in special learning format.", entry, 1+1);
  2831.                 itemTemplate.Spells[0].SpellId = 0;
  2832.                 itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2833.             }
  2834.             else if (itemTemplate.Spells[1].SpellId != -1)
  2835.             {
  2836.                 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[1].SpellId);
  2837.                 if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[1].SpellId, nullptr))
  2838.                 {
  2839.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", entry, 1+1, itemTemplate.Spells[1].SpellId);
  2840.                     itemTemplate.Spells[0].SpellId = 0;
  2841.                     itemTemplate.Spells[1].SpellId = 0;
  2842.                     itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2843.                 }
  2844.                 // allowed only in special format
  2845.                 else if ((itemTemplate.Spells[1].SpellId == 483) || (itemTemplate.Spells[1].SpellId == 55884))
  2846.                 {
  2847.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has broken spell in spellid_%d (%d)", entry, 1+1, itemTemplate.Spells[1].SpellId);
  2848.                     itemTemplate.Spells[0].SpellId = 0;
  2849.                     itemTemplate.Spells[1].SpellId = 0;
  2850.                     itemTemplate.Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2851.                 }
  2852.             }
  2853.  
  2854.             // spell_3*, spell_4*, spell_5* is empty
  2855.             for (uint8 j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j)
  2856.             {
  2857.                 if (itemTemplate.Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
  2858.                 {
  2859.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", entry, j+1, itemTemplate.Spells[j].SpellTrigger);
  2860.                     itemTemplate.Spells[j].SpellId = 0;
  2861.                     itemTemplate.Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2862.                 }
  2863.                 else if (itemTemplate.Spells[j].SpellId != 0)
  2864.                 {
  2865.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong spell in spellid_%d (%d) for learning special format", entry, j+1, itemTemplate.Spells[j].SpellId);
  2866.                     itemTemplate.Spells[j].SpellId = 0;
  2867.                 }
  2868.             }
  2869.         }
  2870.         // normal spell list
  2871.         else
  2872.         {
  2873.             for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
  2874.             {
  2875.                 if (itemTemplate.Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || itemTemplate.Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
  2876.                 {
  2877.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", entry, j+1, itemTemplate.Spells[j].SpellTrigger);
  2878.                     itemTemplate.Spells[j].SpellId = 0;
  2879.                     itemTemplate.Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
  2880.                 }
  2881.  
  2882.                 if (itemTemplate.Spells[j].SpellId && itemTemplate.Spells[j].SpellId != -1)
  2883.                 {
  2884.                     SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[j].SpellId);
  2885.                     if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[j].SpellId, nullptr))
  2886.                     {
  2887.                         TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", entry, j+1, itemTemplate.Spells[j].SpellId);
  2888.                         itemTemplate.Spells[j].SpellId = 0;
  2889.                     }
  2890.                     // allowed only in special format
  2891.                     else if ((itemTemplate.Spells[j].SpellId == 483) || (itemTemplate.Spells[j].SpellId == 55884))
  2892.                     {
  2893.                         TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has broken spell in spellid_%d (%d)", entry, j+1, itemTemplate.Spells[j].SpellId);
  2894.                         itemTemplate.Spells[j].SpellId = 0;
  2895.                     }
  2896.                 }
  2897.             }
  2898.         }
  2899.  
  2900.         if (itemTemplate.Bonding >= MAX_BIND_TYPE)
  2901.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Bonding value (%u)", entry, itemTemplate.Bonding);
  2902.  
  2903.         if (itemTemplate.PageText && !GetPageText(itemTemplate.PageText))
  2904.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has non existing first page (Id:%u)", entry, itemTemplate.PageText);
  2905.  
  2906.         if (itemTemplate.LockID && !sLockStore.LookupEntry(itemTemplate.LockID))
  2907.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong LockID (%u)", entry, itemTemplate.LockID);
  2908.  
  2909.         if (itemTemplate.Sheath >= MAX_SHEATHETYPE)
  2910.         {
  2911.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Sheath (%u)", entry, itemTemplate.Sheath);
  2912.             itemTemplate.Sheath = SHEATHETYPE_NONE;
  2913.         }
  2914.  
  2915.         if (itemTemplate.RandomProperty)
  2916.         {
  2917.             // To be implemented later
  2918.             if (itemTemplate.RandomProperty == -1)
  2919.                 itemTemplate.RandomProperty = 0;
  2920.  
  2921.             else if (!sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomProperty)))
  2922.             {
  2923.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", entry, itemTemplate.RandomProperty);
  2924.                 itemTemplate.RandomProperty = 0;
  2925.             }
  2926.         }
  2927.  
  2928.         if (itemTemplate.RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomSuffix)))
  2929.         {
  2930.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong RandomSuffix (%u)", entry, itemTemplate.RandomSuffix);
  2931.             itemTemplate.RandomSuffix = 0;
  2932.         }
  2933.  
  2934.         if (itemTemplate.ItemSet && !sItemSetStore.LookupEntry(itemTemplate.ItemSet))
  2935.         {
  2936.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) have wrong ItemSet (%u)", entry, itemTemplate.ItemSet);
  2937.             itemTemplate.ItemSet = 0;
  2938.         }
  2939.  
  2940.         if (itemTemplate.Area && !sAreaTableStore.LookupEntry(itemTemplate.Area))
  2941.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Area (%u)", entry, itemTemplate.Area);
  2942.  
  2943.         if (itemTemplate.Map && !sMapStore.LookupEntry(itemTemplate.Map))
  2944.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong Map (%u)", entry, itemTemplate.Map);
  2945.  
  2946.         if (itemTemplate.BagFamily)
  2947.         {
  2948.             // check bits
  2949.             for (uint32 j = 0; j < sizeof(itemTemplate.BagFamily)*8; ++j)
  2950.             {
  2951.                 uint32 mask = 1 << j;
  2952.                 if ((itemTemplate.BagFamily & mask) == 0)
  2953.                     continue;
  2954.  
  2955.                 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1);
  2956.                 if (!bf)
  2957.                 {
  2958.                     TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", entry);
  2959.                     itemTemplate.BagFamily &= ~mask;
  2960.                     continue;
  2961.                 }
  2962.  
  2963.                 if (BAG_FAMILY_MASK_CURRENCY_TOKENS & mask)
  2964.                 {
  2965.                     CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemTemplate.ItemId);
  2966.                     if (!ctEntry)
  2967.                     {
  2968.                         TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit", entry);
  2969.                         itemTemplate.BagFamily &= ~mask;
  2970.                     }
  2971.                 }
  2972.             }
  2973.         }
  2974.  
  2975.         if (itemTemplate.TotemCategory && !sTotemCategoryStore.LookupEntry(itemTemplate.TotemCategory))
  2976.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong TotemCategory (%u)", entry, itemTemplate.TotemCategory);
  2977.  
  2978.         for (uint8 j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
  2979.         {
  2980.             if (itemTemplate.Socket[j].Color && (itemTemplate.Socket[j].Color & SOCKET_COLOR_ALL) != itemTemplate.Socket[j].Color)
  2981.             {
  2982.                 TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong socketColor_%d (%u)", entry, j+1, itemTemplate.Socket[j].Color);
  2983.                 itemTemplate.Socket[j].Color = 0;
  2984.             }
  2985.         }
  2986.  
  2987.         if (itemTemplate.GemProperties && !sGemPropertiesStore.LookupEntry(itemTemplate.GemProperties))
  2988.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong GemProperties (%u)", entry, itemTemplate.GemProperties);
  2989.  
  2990.         if (itemTemplate.FoodType >= MAX_PET_DIET)
  2991.         {
  2992.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong FoodType value (%u)", entry, itemTemplate.FoodType);
  2993.             itemTemplate.FoodType = 0;
  2994.         }
  2995.  
  2996.         if (itemTemplate.ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(itemTemplate.ItemLimitCategory))
  2997.         {
  2998.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong LimitCategory value (%u)", entry, itemTemplate.ItemLimitCategory);
  2999.             itemTemplate.ItemLimitCategory = 0;
  3000.         }
  3001.  
  3002.         if (itemTemplate.HolidayId && !sHolidaysStore.LookupEntry(itemTemplate.HolidayId))
  3003.         {
  3004.             TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has wrong HolidayId value (%u)", entry, itemTemplate.HolidayId);
  3005.             itemTemplate.HolidayId = 0;
  3006.         }
  3007.  
  3008.         if (itemTemplate.FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME && !itemTemplate.Duration)
  3009.         {
  3010.             TC_LOG_ERROR("sql.sql", "Item (Entry %u) has flag ITEM_FLAGS_CU_DURATION_REAL_TIME but it does not have duration limit", entry);
  3011.             itemTemplate.FlagsCu &= ~ITEM_FLAGS_CU_DURATION_REAL_TIME;
  3012.         }
  3013.  
  3014.         // Load cached data
  3015.         itemTemplate._LoadTotalAP();
  3016.     } while (result->NextRow());
  3017.  
  3018.     // Check if item templates for DBC referenced character start outfit are present
  3019.     std::set<uint32> notFoundOutfit;
  3020.     for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
  3021.     {
  3022.         CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
  3023.         if (!entry)
  3024.             continue;
  3025.  
  3026.         for (uint8 j = 0; j < MAX_OUTFIT_ITEMS; ++j)
  3027.         {
  3028.             if (entry->ItemId[j] <= 0)
  3029.                 continue;
  3030.  
  3031.             uint32 item_id = entry->ItemId[j];
  3032.  
  3033.             if (!GetItemTemplate(item_id))
  3034.                 notFoundOutfit.insert(item_id);
  3035.         }
  3036.     }
  3037.  
  3038.     for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
  3039.         TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr);
  3040.  
  3041.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " item templates in %u ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
  3042. }
  3043.  
  3044. ItemTemplate const* ObjectMgr::GetItemTemplate(uint32 entry) const
  3045. {
  3046.     return Trinity::Containers::MapGetValuePtr(_itemTemplateStore, entry);
  3047. }
  3048.  
  3049. void ObjectMgr::LoadItemSetNameLocales()
  3050. {
  3051.     uint32 oldMSTime = getMSTime();
  3052.  
  3053.     _itemSetNameLocaleStore.clear();                                 // need for reload case
  3054.  
  3055.     //                                               0   1       2
  3056.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM item_set_names_locale");
  3057.     if (!result)
  3058.         return;
  3059.  
  3060.     do
  3061.     {
  3062.         Field* fields = result->Fetch();
  3063.  
  3064.         uint32 id               = fields[0].GetUInt32();
  3065.         std::string localeName  = fields[1].GetString();
  3066.         std::string Name        = fields[2].GetString();
  3067.  
  3068.         ItemSetNameLocale& data = _itemSetNameLocaleStore[id];
  3069.         LocaleConstant locale   = GetLocaleByName(localeName);
  3070.         if (locale == LOCALE_enUS)
  3071.             continue;
  3072.  
  3073.         AddLocaleString(Name, locale, data.Name);
  3074.     } while (result->NextRow());
  3075.  
  3076.     TC_LOG_INFO("server.loading", ">> Loaded " UI64FMTD " Item set name locale strings in %u ms", uint64(_itemSetNameLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  3077. }
  3078.  
  3079. void ObjectMgr::LoadItemSetNames()
  3080. {
  3081.     uint32 oldMSTime = getMSTime();
  3082.  
  3083.     _itemSetNameStore.clear();                               // needed for reload case
  3084.  
  3085.     std::set<uint32> itemSetItems;
  3086.  
  3087.     // fill item set member ids
  3088.     for (uint32 entryId = 0; entryId < sItemSetStore.GetNumRows(); ++entryId)
  3089.     {
  3090.         ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(entryId);
  3091.         if (!setEntry)
  3092.             continue;
  3093.  
  3094.         for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
  3095.             if (setEntry->itemId[i])
  3096.                 itemSetItems.insert(setEntry->itemId[i]);
  3097.     }
  3098.  
  3099.     //                                                  0        1            2
  3100.     QueryResult result = WorldDatabase.Query("SELECT `entry`, `name`, `InventoryType` FROM `item_set_names`");
  3101.  
  3102.     if (!result)
  3103.     {
  3104.         TC_LOG_INFO("server.loading", ">> Loaded 0 item set names. DB table `item_set_names` is empty.");
  3105.         return;
  3106.     }
  3107.  
  3108.     _itemSetNameStore.rehash(result->GetRowCount());
  3109.     uint32 count = 0;
  3110.  
  3111.     do
  3112.     {
  3113.         Field* fields = result->Fetch();
  3114.  
  3115.         uint32 entry = fields[0].GetUInt32();
  3116.         if (itemSetItems.find(entry) == itemSetItems.end())
  3117.         {
  3118.             TC_LOG_ERROR("sql.sql", "Item set name (Entry: %u) not found in ItemSet.dbc, data useless.", entry);
  3119.             continue;
  3120.         }
  3121.  
  3122.         ItemSetNameEntry &data = _itemSetNameStore[entry];
  3123.         data.name = fields[1].GetString();
  3124.  
  3125.         uint32 invType = fields[2].GetUInt8();
  3126.         if (invType >= MAX_INVTYPE)
  3127.         {
  3128.             TC_LOG_ERROR("sql.sql", "Item set name (Entry: %u) has wrong InventoryType value (%u)", entry, invType);
  3129.             invType = INVTYPE_NON_EQUIP;
  3130.         }
  3131.  
  3132.         data.InventoryType = invType;
  3133.         itemSetItems.erase(entry);
  3134.         ++count;
  3135.     } while (result->NextRow());
  3136.  
  3137.     if (!itemSetItems.empty())
  3138.     {
  3139.         ItemTemplate const* pProto;
  3140.         for (std::set<uint32>::iterator itr = itemSetItems.begin(); itr != itemSetItems.end(); ++itr)
  3141.         {
  3142.             uint32 entry = *itr;
  3143.             // add data from item_template if available
  3144.             pProto = sObjectMgr->GetItemTemplate(entry);
  3145.             if (pProto)
  3146.             {
  3147.                 TC_LOG_ERROR("sql.sql", "Item set part (Entry: %u) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
  3148.                 ItemSetNameEntry &data = _itemSetNameStore[entry];
  3149.                 data.name = pProto->Name1;
  3150.                 data.InventoryType = pProto->InventoryType;
  3151.                 ++count;
  3152.             }
  3153.             else
  3154.                 TC_LOG_ERROR("sql.sql", "Item set part (Entry: %u) does not have entry in `item_set_names`, set will not display properly.", entry);
  3155.         }
  3156.     }
  3157.  
  3158.     TC_LOG_INFO("server.loading", ">> Loaded %u item set names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3159. }
  3160.  
  3161. void ObjectMgr::LoadVehicleTemplateAccessories()
  3162. {
  3163.     uint32 oldMSTime = getMSTime();
  3164.  
  3165.     _vehicleTemplateAccessoryStore.clear();                           // needed for reload case
  3166.  
  3167.     uint32 count = 0;
  3168.  
  3169.     //                                                  0             1              2          3           4             5
  3170.     QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
  3171.  
  3172.     if (!result)
  3173.     {
  3174.         TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
  3175.         return;
  3176.     }
  3177.  
  3178.     do
  3179.     {
  3180.         Field* fields = result->Fetch();
  3181.  
  3182.         uint32 entry        = fields[0].GetUInt32();
  3183.         uint32 accessory    = fields[1].GetUInt32();
  3184.         int8   seatId       = fields[2].GetInt8();
  3185.         bool   isMinion     = fields[3].GetBool();
  3186.         uint8  summonType   = fields[4].GetUInt8();
  3187.         uint32 summonTimer  = fields[5].GetUInt32();
  3188.  
  3189.         if (!sObjectMgr->GetCreatureTemplate(entry))
  3190.         {
  3191.             TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u does not exist.", entry);
  3192.             continue;
  3193.         }
  3194.  
  3195.         if (!sObjectMgr->GetCreatureTemplate(accessory))
  3196.         {
  3197.             TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory %u does not exist.", accessory);
  3198.             continue;
  3199.         }
  3200.  
  3201.         if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
  3202.         {
  3203.             TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u has no data in npc_spellclick_spells", entry);
  3204.             continue;
  3205.         }
  3206.  
  3207.         _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
  3208.  
  3209.         ++count;
  3210.     }
  3211.     while (result->NextRow());
  3212.  
  3213.     TC_LOG_INFO("server.loading", ">> Loaded %u Vehicle Template Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3214. }
  3215.  
  3216. void ObjectMgr::LoadVehicleAccessories()
  3217. {
  3218.     uint32 oldMSTime = getMSTime();
  3219.  
  3220.     _vehicleAccessoryStore.clear();                           // needed for reload case
  3221.  
  3222.     uint32 count = 0;
  3223.  
  3224.     //                                                  0             1             2          3           4             5
  3225.     QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
  3226.  
  3227.     if (!result)
  3228.     {
  3229.         TC_LOG_INFO("server.loading", ">> Loaded 0 Vehicle Accessories in %u ms", GetMSTimeDiffToNow(oldMSTime));
  3230.         return;
  3231.     }
  3232.  
  3233.     do
  3234.     {
  3235.         Field* fields = result->Fetch();
  3236.  
  3237.         uint32 uiGUID       = fields[0].GetUInt32();
  3238.         uint32 uiAccessory  = fields[1].GetUInt32();
  3239.         int8   uiSeat       = int8(fields[2].GetInt16());
  3240.         bool   bMinion      = fields[3].GetBool();
  3241.         uint8  uiSummonType = fields[4].GetUInt8();
  3242.         uint32 uiSummonTimer= fields[5].GetUInt32();
  3243.  
  3244.         if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
  3245.         {
  3246.             TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory %u does not exist.", uiAccessory);
  3247.             continue;
  3248.         }
  3249.  
  3250.         _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
  3251.  
  3252.         ++count;
  3253.     }
  3254.     while (result->NextRow());
  3255.  
  3256.     TC_LOG_INFO("server.loading", ">> Loaded %u Vehicle Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3257. }
  3258.  
  3259. void ObjectMgr::LoadPetLevelInfo()
  3260. {
  3261.     uint32 oldMSTime = getMSTime();
  3262.  
  3263.     //                                                 0               1      2   3     4    5    6    7     8    9
  3264.     QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
  3265.  
  3266.     if (!result)
  3267.     {
  3268.         TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
  3269.         return;
  3270.     }
  3271.  
  3272.     uint32 count = 0;
  3273.  
  3274.     do
  3275.     {
  3276.         Field* fields = result->Fetch();
  3277.  
  3278.         uint32 creature_id = fields[0].GetUInt32();
  3279.         if (!sObjectMgr->GetCreatureTemplate(creature_id))
  3280.         {
  3281.             TC_LOG_ERROR("sql.sql", "Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
  3282.             continue;
  3283.         }
  3284.  
  3285.         uint32 current_level = fields[1].GetUInt8();
  3286.         if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3287.         {
  3288.             if (current_level > STRONG_MAX_LEVEL)        // hardcoded level maximum
  3289.                 TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  3290.             else
  3291.             {
  3292.                 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
  3293.                 ++count;                                // make result loading percent "expected" correct in case disabled detail mode for example.
  3294.             }
  3295.             continue;
  3296.         }
  3297.         else if (current_level < 1)
  3298.         {
  3299.             TC_LOG_ERROR("sql.sql", "Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
  3300.             continue;
  3301.         }
  3302.  
  3303.         auto& pInfoMapEntry = _petInfoStore[creature_id];
  3304.         if (!pInfoMapEntry)
  3305.             pInfoMapEntry = Trinity::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  3306.  
  3307.         // data for level 1 stored in [0] array element, ...
  3308.         PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
  3309.  
  3310.         pLevelInfo->health = fields[2].GetUInt16();
  3311.         pLevelInfo->mana   = fields[3].GetUInt16();
  3312.         pLevelInfo->armor  = fields[9].GetUInt32();
  3313.  
  3314.         for (uint8 i = 0; i < MAX_STATS; i++)
  3315.             pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
  3316.  
  3317.         ++count;
  3318.     }
  3319.     while (result->NextRow());
  3320.  
  3321.     // Fill gaps and check integrity
  3322.     for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
  3323.     {
  3324.         auto& pInfo = itr->second;
  3325.  
  3326.         // fatal error if no level 1 data
  3327.         if (!pInfo || pInfo[0].health == 0)
  3328.         {
  3329.             TC_LOG_ERROR("sql.sql", "Creature %u does not have pet stats data for Level 1!", itr->first);
  3330.             ABORT();
  3331.         }
  3332.  
  3333.         // fill level gaps
  3334.         for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3335.         {
  3336.             if (pInfo[level].health == 0)
  3337.             {
  3338.                 TC_LOG_ERROR("sql.sql", "Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
  3339.                 pInfo[level] = pInfo[level - 1];
  3340.             }
  3341.         }
  3342.     }
  3343.  
  3344.     TC_LOG_INFO("server.loading", ">> Loaded %u level pet stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3345. }
  3346.  
  3347. PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
  3348. {
  3349.     if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3350.         level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
  3351.  
  3352.     auto itr = _petInfoStore.find(creature_id);
  3353.     if (itr == _petInfoStore.end())
  3354.         return nullptr;
  3355.  
  3356.     return &itr->second[level - 1];                         // data for level 1 stored in [0] array element, ...
  3357. }
  3358.  
  3359. void ObjectMgr::PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
  3360. {
  3361.     if (!_playerInfo[race_][class_])
  3362.         return;
  3363.  
  3364.     if (count > 0)
  3365.         _playerInfo[race_][class_]->item.push_back(PlayerCreateInfoItem(itemId, count));
  3366.     else
  3367.     {
  3368.         if (count < -1)
  3369.             TC_LOG_ERROR("sql.sql", "Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId);
  3370.  
  3371.         for (uint32 gender = 0; gender < GENDER_NONE; ++gender)
  3372.         {
  3373.             if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender))
  3374.             {
  3375.                 bool found = false;
  3376.                 for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
  3377.                 {
  3378.                     if (entry->ItemId[x] > 0 && uint32(entry->ItemId[x]) == itemId)
  3379.                     {
  3380.                         found = true;
  3381.                         const_cast<CharStartOutfitEntry*>(entry)->ItemId[x] = 0;
  3382.                         break;
  3383.                     }
  3384.                 }
  3385.  
  3386.                 if (!found)
  3387.                     TC_LOG_ERROR("sql.sql", "Item %u specified to be removed from original create info not found in dbc!", itemId);
  3388.             }
  3389.         }
  3390.     }
  3391. }
  3392.  
  3393. void ObjectMgr::LoadPlayerInfo()
  3394. {
  3395.     // Load playercreate
  3396.     {
  3397.         uint32 oldMSTime = getMSTime();
  3398.         //                                                0     1      2    3        4          5           6
  3399.         QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
  3400.  
  3401.         if (!result)
  3402.         {
  3403.             TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
  3404.             ABORT();
  3405.         }
  3406.         else
  3407.         {
  3408.             uint32 count = 0;
  3409.  
  3410.             do
  3411.             {
  3412.                 Field* fields = result->Fetch();
  3413.  
  3414.                 uint32 current_race  = fields[0].GetUInt8();
  3415.                 uint32 current_class = fields[1].GetUInt8();
  3416.                 uint32 mapId         = fields[2].GetUInt16();
  3417.                 uint32 areaId        = fields[3].GetUInt32(); // zone
  3418.                 float  positionX     = fields[4].GetFloat();
  3419.                 float  positionY     = fields[5].GetFloat();
  3420.                 float  positionZ     = fields[6].GetFloat();
  3421.                 float  orientation   = fields[7].GetFloat();
  3422.  
  3423.                 if (current_race >= MAX_RACES)
  3424.                 {
  3425.                     TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
  3426.                     continue;
  3427.                 }
  3428.  
  3429.                 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
  3430.                 if (!rEntry)
  3431.                 {
  3432.                     TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
  3433.                     continue;
  3434.                 }
  3435.  
  3436.                 if (current_class >= MAX_CLASSES)
  3437.                 {
  3438.                     TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
  3439.                     continue;
  3440.                 }
  3441.  
  3442.                 if (!sChrClassesStore.LookupEntry(current_class))
  3443.                 {
  3444.                     TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
  3445.                     continue;
  3446.                 }
  3447.  
  3448.                 // accept DB data only for valid position (and non instanceable)
  3449.                 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
  3450.                 {
  3451.                     TC_LOG_ERROR("sql.sql", "Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
  3452.                     continue;
  3453.                 }
  3454.  
  3455.                 if (sMapStore.LookupEntry(mapId)->Instanceable())
  3456.                 {
  3457.                     TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
  3458.                     continue;
  3459.                 }
  3460.  
  3461.                 std::unique_ptr<PlayerInfo> info = Trinity::make_unique<PlayerInfo>();
  3462.                 info->mapId = mapId;
  3463.                 info->areaId = areaId;
  3464.                 info->positionX = positionX;
  3465.                 info->positionY = positionY;
  3466.                 info->positionZ = positionZ;
  3467.                 info->orientation = orientation;
  3468.                 info->displayId_m = rEntry->model_m;
  3469.                 info->displayId_f = rEntry->model_f;
  3470.                 _playerInfo[current_race][current_class] = std::move(info);
  3471.  
  3472.                 ++count;
  3473.             }
  3474.             while (result->NextRow());
  3475.  
  3476.             TC_LOG_INFO("server.loading", ">> Loaded %u player create definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3477.         }
  3478.     }
  3479.  
  3480.     // Load playercreate items
  3481.     TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
  3482.     {
  3483.         uint32 oldMSTime = getMSTime();
  3484.         //                                                0     1      2       3
  3485.         QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
  3486.  
  3487.         if (!result)
  3488.         {
  3489.             TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
  3490.         }
  3491.         else
  3492.         {
  3493.             uint32 count = 0;
  3494.  
  3495.             do
  3496.             {
  3497.                 Field* fields = result->Fetch();
  3498.  
  3499.                 uint32 current_race = fields[0].GetUInt8();
  3500.                 if (current_race >= MAX_RACES)
  3501.                 {
  3502.                     TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
  3503.                     continue;
  3504.                 }
  3505.  
  3506.                 uint32 current_class = fields[1].GetUInt8();
  3507.                 if (current_class >= MAX_CLASSES)
  3508.                 {
  3509.                     TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
  3510.                     continue;
  3511.                 }
  3512.  
  3513.                 uint32 item_id = fields[2].GetUInt32();
  3514.  
  3515.                 if (!GetItemTemplate(item_id))
  3516.                 {
  3517.                     TC_LOG_ERROR("sql.sql", "Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.", item_id, current_race, current_class);
  3518.                     continue;
  3519.                 }
  3520.  
  3521.                 int32 amount   = fields[3].GetInt8();
  3522.  
  3523.                 if (!amount)
  3524.                 {
  3525.                     TC_LOG_ERROR("sql.sql", "Item id %u (class %u race %u) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
  3526.                     continue;
  3527.                 }
  3528.  
  3529.                 if (!current_race || !current_class)
  3530.                 {
  3531.                     uint32 min_race = current_race ? current_race : 1;
  3532.                     uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
  3533.                     uint32 min_class = current_class ? current_class : 1;
  3534.                     uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
  3535.                     for (uint32 r = min_race; r < max_race; ++r)
  3536.                         for (uint32 c = min_class; c < max_class; ++c)
  3537.                             PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
  3538.                 }
  3539.                 else
  3540.                     PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
  3541.  
  3542.                 ++count;
  3543.             }
  3544.             while (result->NextRow());
  3545.  
  3546.             TC_LOG_INFO("server.loading", ">> Loaded %u custom player create items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3547.         }
  3548.     }
  3549.  
  3550.  
  3551.     // Load playercreate skills
  3552.     TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
  3553.     {
  3554.         uint32 oldMSTime = getMSTime();
  3555.  
  3556.         QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills");
  3557.  
  3558.         if (!result)
  3559.         {
  3560.             TC_LOG_INFO("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
  3561.         }
  3562.         else
  3563.         {
  3564.             uint32 count = 0;
  3565.  
  3566.             do
  3567.             {
  3568.                 Field* fields = result->Fetch();
  3569.                 uint32 raceMask = fields[0].GetUInt32();
  3570.                 uint32 classMask = fields[1].GetUInt32();
  3571.                 PlayerCreateInfoSkill skill;
  3572.                 skill.SkillId = fields[2].GetUInt16();
  3573.                 skill.Rank = fields[3].GetUInt16();
  3574.  
  3575.                 if (skill.Rank >= MAX_SKILL_STEP)
  3576.                 {
  3577.                     TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP);
  3578.                     continue;
  3579.                 }
  3580.  
  3581.                 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  3582.                 {
  3583.                     TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
  3584.                     continue;
  3585.                 }
  3586.  
  3587.                 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  3588.                 {
  3589.                     TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
  3590.                     continue;
  3591.                 }
  3592.  
  3593.                 if (!sSkillLineStore.LookupEntry(skill.SkillId))
  3594.                 {
  3595.                     TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
  3596.                     continue;
  3597.                 }
  3598.  
  3599.                 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  3600.                 {
  3601.                     if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  3602.                     {
  3603.                         for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  3604.                         {
  3605.                             if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  3606.                             {
  3607.                                 if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
  3608.                                     continue;
  3609.  
  3610.                                 if (auto& info = _playerInfo[raceIndex][classIndex])
  3611.                                 {
  3612.                                     info->skills.push_back(skill);
  3613.                                     ++count;
  3614.                                 }
  3615.                             }
  3616.                         }
  3617.                     }
  3618.                 }
  3619.             } while (result->NextRow());
  3620.  
  3621.             TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3622.         }
  3623.     }
  3624.  
  3625.     // Load playercreate spells
  3626.     TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
  3627.     {
  3628.         uint32 oldMSTime = getMSTime();
  3629.  
  3630.         QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
  3631.  
  3632.         if (!result)
  3633.         {
  3634.             TC_LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
  3635.         }
  3636.         else
  3637.         {
  3638.             uint32 count = 0;
  3639.  
  3640.             do
  3641.             {
  3642.                 Field* fields = result->Fetch();
  3643.                 uint32 raceMask = fields[0].GetUInt32();
  3644.                 uint32 classMask = fields[1].GetUInt32();
  3645.                 uint32 spellId = fields[2].GetUInt32();
  3646.  
  3647.                 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  3648.                 {
  3649.                     TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
  3650.                     continue;
  3651.                 }
  3652.  
  3653.                 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  3654.                 {
  3655.                     TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask);
  3656.                     continue;
  3657.                 }
  3658.  
  3659.                 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  3660.                 {
  3661.                     if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  3662.                     {
  3663.                         for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  3664.                         {
  3665.                             if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  3666.                             {
  3667.                                 if (auto& info = _playerInfo[raceIndex][classIndex])
  3668.                                 {
  3669.                                     info->customSpells.push_back(spellId);
  3670.                                     ++count;
  3671.                                 }
  3672.                                 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
  3673.                                 // Either split the masks per class, or per race, which kind of kills the point yet.
  3674.                                 // else if (raceMask != 0 && classMask != 0)
  3675.                                 //     TC_LOG_ERROR("sql.sql", "Racemask/classmask (%u/%u) combination was found containing an invalid race/class combination (%u/%u) in `%s` (Spell %u), ignoring.", raceMask, classMask, raceIndex, classIndex, tableName.c_str(), spellId);
  3676.                             }
  3677.                         }
  3678.                     }
  3679.                 }
  3680.             }
  3681.             while (result->NextRow());
  3682.  
  3683.             TC_LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3684.         }
  3685.     }
  3686.  
  3687.     // Load playercreate cast spell
  3688.     TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
  3689.     {
  3690.         uint32 oldMSTime = getMSTime();
  3691.  
  3692.         QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
  3693.  
  3694.         if (!result)
  3695.             TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
  3696.         else
  3697.         {
  3698.             uint32 count = 0;
  3699.  
  3700.             do
  3701.             {
  3702.                 Field* fields       = result->Fetch();
  3703.                 uint32 raceMask     = fields[0].GetUInt32();
  3704.                 uint32 classMask    = fields[1].GetUInt32();
  3705.                 uint32 spellId      = fields[2].GetUInt32();
  3706.  
  3707.                 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  3708.                 {
  3709.                     TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
  3710.                     continue;
  3711.                 }
  3712.  
  3713.                 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  3714.                 {
  3715.                     TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_cast_spell` table, ignoring.", classMask);
  3716.                     continue;
  3717.                 }
  3718.  
  3719.                 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  3720.                 {
  3721.                     if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  3722.                     {
  3723.                         for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  3724.                         {
  3725.                             if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  3726.                             {
  3727.                                 if (auto& info = _playerInfo[raceIndex][classIndex])
  3728.                                 {
  3729.                                     info->castSpells.push_back(spellId);
  3730.                                     ++count;
  3731.                                 }
  3732.                             }
  3733.                         }
  3734.                     }
  3735.                 }
  3736.             } while (result->NextRow());
  3737.  
  3738.             TC_LOG_INFO("server.loading", ">> Loaded %u player create cast spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3739.         }
  3740.     }
  3741.  
  3742.     // Load playercreate actions
  3743.     TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
  3744.     {
  3745.         uint32 oldMSTime = getMSTime();
  3746.  
  3747.         //                                                0     1      2       3       4
  3748.         QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
  3749.  
  3750.         if (!result)
  3751.         {
  3752.             TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
  3753.         }
  3754.         else
  3755.         {
  3756.             uint32 count = 0;
  3757.  
  3758.             do
  3759.             {
  3760.                 Field* fields = result->Fetch();
  3761.  
  3762.                 uint32 current_race = fields[0].GetUInt8();
  3763.                 if (current_race >= MAX_RACES)
  3764.                 {
  3765.                     TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
  3766.                     continue;
  3767.                 }
  3768.  
  3769.                 uint32 current_class = fields[1].GetUInt8();
  3770.                 if (current_class >= MAX_CLASSES)
  3771.                 {
  3772.                     TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
  3773.                     continue;
  3774.                 }
  3775.  
  3776.                 if (auto& info = _playerInfo[current_race][current_class])
  3777.                     info->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
  3778.  
  3779.                 ++count;
  3780.             }
  3781.             while (result->NextRow());
  3782.  
  3783.             TC_LOG_INFO("server.loading", ">> Loaded %u player create actions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3784.         }
  3785.     }
  3786.  
  3787.     // Loading levels data (class only dependent)
  3788.     TC_LOG_INFO("server.loading", "Loading Player Create Level HP/Mana Data...");
  3789.     {
  3790.         uint32 oldMSTime = getMSTime();
  3791.  
  3792.         //                                                0      1      2       3
  3793.         QueryResult result  = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
  3794.  
  3795.         if (!result)
  3796.         {
  3797.             TC_LOG_ERROR("server.loading", ">> Loaded 0 level health/mana definitions. DB table `player_classlevelstats` is empty.");
  3798.             ABORT();
  3799.         }
  3800.  
  3801.         uint32 count = 0;
  3802.  
  3803.         do
  3804.         {
  3805.             Field* fields = result->Fetch();
  3806.  
  3807.             uint32 current_class = fields[0].GetUInt8();
  3808.             if (current_class >= MAX_CLASSES)
  3809.             {
  3810.                 TC_LOG_ERROR("sql.sql", "Wrong class %u in `player_classlevelstats` table, ignoring.", current_class);
  3811.                 continue;
  3812.             }
  3813.  
  3814.             uint8 current_level = fields[1].GetUInt8();      // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type
  3815.             if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3816.             {
  3817.                 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_classlevelstats` table, ignoring.", current_level);
  3818.                 ++count;                                    // make result loading percent "expected" correct in case disabled detail mode for example.
  3819.                 continue;
  3820.             }
  3821.  
  3822.             auto& info = _playerClassInfo[current_class];
  3823.             if (!info)
  3824.             {
  3825.                 info = Trinity::make_unique<PlayerClassInfo>();
  3826.                 info->levelInfo = Trinity::make_unique<PlayerClassLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  3827.             }
  3828.  
  3829.             PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
  3830.             levelInfo.basehealth = fields[2].GetUInt16();
  3831.             levelInfo.basemana   = fields[3].GetUInt16();
  3832.  
  3833.             ++count;
  3834.         }
  3835.         while (result->NextRow());
  3836.  
  3837.         // Fill gaps and check integrity
  3838.         for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
  3839.         {
  3840.             // skip non existed classes
  3841.             if (!sChrClassesStore.LookupEntry(class_))
  3842.                 continue;
  3843.  
  3844.             auto& pClassInfo = _playerClassInfo[class_];
  3845.  
  3846.             // fatal error if no level 1 data
  3847.             if (!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
  3848.             {
  3849.                 TC_LOG_ERROR("sql.sql", "Class %i Level 1 does not have health/mana data!", class_);
  3850.                 ABORT();
  3851.             }
  3852.  
  3853.             // fill level gaps
  3854.             for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3855.             {
  3856.                 if (pClassInfo->levelInfo[level].basehealth == 0)
  3857.                 {
  3858.                     TC_LOG_ERROR("sql.sql", "Class %i Level %i does not have health/mana data. Using stats data of level %i.", class_, level + 1, level);
  3859.                     pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
  3860.                 }
  3861.             }
  3862.         }
  3863.  
  3864.         TC_LOG_INFO("server.loading", ">> Loaded %u level health/mana definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3865.     }
  3866.  
  3867.     // Loading levels data (class/race dependent)
  3868.     TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
  3869.     {
  3870.         uint32 oldMSTime = getMSTime();
  3871.  
  3872.         //                                                 0     1      2      3    4    5    6    7
  3873.         QueryResult result  = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
  3874.  
  3875.         if (!result)
  3876.         {
  3877.             TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
  3878.             ABORT();
  3879.         }
  3880.  
  3881.         uint32 count = 0;
  3882.  
  3883.         do
  3884.         {
  3885.             Field* fields = result->Fetch();
  3886.  
  3887.             uint32 current_race = fields[0].GetUInt8();
  3888.             if (current_race >= MAX_RACES)
  3889.             {
  3890.                 TC_LOG_ERROR("sql.sql", "Wrong race %u in `player_levelstats` table, ignoring.", current_race);
  3891.                 continue;
  3892.             }
  3893.  
  3894.             uint32 current_class = fields[1].GetUInt8();
  3895.             if (current_class >= MAX_CLASSES)
  3896.             {
  3897.                 TC_LOG_ERROR("sql.sql", "Wrong class %u in `player_levelstats` table, ignoring.", current_class);
  3898.                 continue;
  3899.             }
  3900.  
  3901.             uint32 current_level = fields[2].GetUInt8();
  3902.             if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3903.             {
  3904.                 if (current_level > STRONG_MAX_LEVEL)        // hardcoded level maximum
  3905.                     TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  3906.                 else
  3907.                 {
  3908.                     TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_levelstats` table, ignoring.", current_level);
  3909.                     ++count;                                // make result loading percent "expected" correct in case disabled detail mode for example.
  3910.                 }
  3911.                 continue;
  3912.             }
  3913.  
  3914.             if (auto& info = _playerInfo[current_race][current_class])
  3915.             {
  3916.                 if (!info->levelInfo)
  3917.                     info->levelInfo = Trinity::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  3918.  
  3919.                 PlayerLevelInfo& levelInfo = info->levelInfo[current_level - 1];
  3920.                 for (uint8 i = 0; i < MAX_STATS; ++i)
  3921.                     levelInfo.stats[i] = fields[i + 3].GetUInt8();
  3922.             }
  3923.  
  3924.             ++count;
  3925.         }
  3926.         while (result->NextRow());
  3927.  
  3928.         // Fill gaps and check integrity
  3929.         for (uint8 race = 0; race < MAX_RACES; ++race)
  3930.         {
  3931.             // skip non existed races
  3932.             if (!sChrRacesStore.LookupEntry(race))
  3933.                 continue;
  3934.  
  3935.             for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
  3936.             {
  3937.                 // skip non existed classes
  3938.                 if (!sChrClassesStore.LookupEntry(class_))
  3939.                     continue;
  3940.  
  3941.                 auto& info = _playerInfo[race][class_];
  3942.                 if (!info)
  3943.                     continue;
  3944.  
  3945.                 // skip expansion races if not playing with expansion
  3946.                 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
  3947.                     continue;
  3948.  
  3949.                 // skip expansion classes if not playing with expansion
  3950.                 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_WRATH_OF_THE_LICH_KING && class_ == CLASS_DEATH_KNIGHT)
  3951.                     continue;
  3952.  
  3953.                 // fatal error if no level 1 data
  3954.                 if (!info->levelInfo || info->levelInfo[0].stats[0] == 0)
  3955.                 {
  3956.                     TC_LOG_ERROR("sql.sql", "Race %i Class %i Level 1 does not have stats data!", race, class_);
  3957.                     ABORT();
  3958.                 }
  3959.  
  3960.                 // fill level gaps
  3961.                 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3962.                 {
  3963.                     if (info->levelInfo[level].stats[0] == 0)
  3964.                     {
  3965.                         TC_LOG_ERROR("sql.sql", "Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
  3966.                         info->levelInfo[level] = info->levelInfo[level - 1];
  3967.                     }
  3968.                 }
  3969.             }
  3970.         }
  3971.  
  3972.         TC_LOG_INFO("server.loading", ">> Loaded %u level stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3973.     }
  3974.  
  3975.     // Loading xp per level data
  3976.     TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
  3977.     {
  3978.         uint32 oldMSTime = getMSTime();
  3979.  
  3980.         _playerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  3981.         for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3982.             _playerXPperLevel[level] = 0;
  3983.  
  3984.         //                                                  0         1
  3985.         QueryResult result  = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
  3986.  
  3987.         if (!result)
  3988.         {
  3989.             TC_LOG_ERROR("server.loading", ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
  3990.             ABORT();
  3991.         }
  3992.  
  3993.         uint32 count = 0;
  3994.  
  3995.         do
  3996.         {
  3997.             Field* fields = result->Fetch();
  3998.  
  3999.             uint32 current_level = fields[0].GetUInt8();
  4000.             uint32 current_xp    = fields[1].GetUInt32();
  4001.  
  4002.             if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  4003.             {
  4004.                 if (current_level > STRONG_MAX_LEVEL)        // hardcoded level maximum
  4005.                     TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  4006.                 else
  4007.                 {
  4008.                     TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
  4009.                     ++count;                                // make result loading percent "expected" correct in case disabled detail mode for example.
  4010.                 }
  4011.                 continue;
  4012.             }
  4013.             //PlayerXPperLevel
  4014.             _playerXPperLevel[current_level] = current_xp;
  4015.             ++count;
  4016.         }
  4017.         while (result->NextRow());
  4018.  
  4019.         // fill level gaps
  4020.         for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  4021.         {
  4022.             if (_playerXPperLevel[level] == 0)
  4023.             {
  4024.                 TC_LOG_ERROR("sql.sql", "Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
  4025.                 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 100;
  4026.             }
  4027.         }
  4028.  
  4029.         TC_LOG_INFO("server.loading", ">> Loaded %u xp for level definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4030.     }
  4031. }
  4032.  
  4033. void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, PlayerClassLevelInfo* info) const
  4034. {
  4035.     if (level < 1 || class_ >= MAX_CLASSES)
  4036.         return;
  4037.  
  4038.     auto const& pInfo = _playerClassInfo[class_];
  4039.  
  4040.     if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  4041.         level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
  4042.  
  4043.     *info = pInfo->levelInfo[level - 1];
  4044. }
  4045.  
  4046. void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const
  4047. {
  4048.     if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
  4049.         return;
  4050.  
  4051.     auto const& pInfo = _playerInfo[race][class_];
  4052.     if (!pInfo)
  4053.         return;
  4054.  
  4055.     if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  4056.         *info = pInfo->levelInfo[level - 1];
  4057.     else
  4058.         BuildPlayerLevelInfo(race, class_, level, info);
  4059. }
  4060.  
  4061. void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
  4062. {
  4063.     // base data (last known level)
  4064.     *info = _playerInfo[race][_class]->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
  4065.  
  4066.     // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
  4067.     for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
  4068.     {
  4069.         switch (_class)
  4070.         {
  4071.             case CLASS_WARRIOR:
  4072.                 info->stats[STAT_STRENGTH]  += (lvl > 23 ? 2: (lvl > 1  ? 1: 0));
  4073.                 info->stats[STAT_STAMINA]   += (lvl > 23 ? 2: (lvl > 1  ? 1: 0));
  4074.                 info->stats[STAT_AGILITY]   += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
  4075.                 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
  4076.                 info->stats[STAT_SPIRIT]    += (lvl > 9 && !(lvl%2) ? 1: 0);
  4077.                 break;
  4078.             case CLASS_PALADIN:
  4079.                 info->stats[STAT_STRENGTH]  += (lvl > 3  ? 1: 0);
  4080.                 info->stats[STAT_STAMINA]   += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
  4081.                 info->stats[STAT_AGILITY]   += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
  4082.                 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
  4083.                 info->stats[STAT_SPIRIT]    += (lvl > 7 ? 1: 0);
  4084.                 break;
  4085.             case CLASS_HUNTER:
  4086.                 info->stats[STAT_STRENGTH]  += (lvl > 4  ? 1: 0);
  4087.                 info->stats[STAT_STAMINA]   += (lvl > 4  ? 1: 0);
  4088.                 info->stats[STAT_AGILITY]   += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
  4089.                 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
  4090.                 info->stats[STAT_SPIRIT]    += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
  4091.                 break;
  4092.             case CLASS_ROGUE:
  4093.                 info->stats[STAT_STRENGTH]  += (lvl > 5  ? 1: 0);
  4094.                 info->stats[STAT_STAMINA]   += (lvl > 4  ? 1: 0);
  4095.                 info->stats[STAT_AGILITY]   += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
  4096.                 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
  4097.                 info->stats[STAT_SPIRIT]    += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
  4098.                 break;
  4099.             case CLASS_PRIEST:
  4100.                 info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
  4101.                 info->stats[STAT_STAMINA]   += (lvl > 5  ? 1: 0);
  4102.                 info->stats[STAT_AGILITY]   += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
  4103.                 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
  4104.                 info->stats[STAT_SPIRIT]    += (lvl > 3  ? 1: 0);
  4105.                 break;
  4106.             case CLASS_SHAMAN:
  4107.                 info->stats[STAT_STRENGTH]  += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
  4108.                 info->stats[STAT_STAMINA]   += (lvl > 4 ? 1: 0);
  4109.                 info->stats[STAT_AGILITY]   += (lvl > 7 && !(lvl%2) ? 1: 0);
  4110.                 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
  4111.                 info->stats[STAT_SPIRIT]    += (lvl > 4 ? 1: 0);
  4112.                 break;
  4113.             case CLASS_MAGE:
  4114.                 info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
  4115.                 info->stats[STAT_STAMINA]   += (lvl > 5  ? 1: 0);
  4116.                 info->stats[STAT_AGILITY]   += (lvl > 9 && !(lvl%2) ? 1: 0);
  4117.                 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
  4118.                 info->stats[STAT_SPIRIT]    += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
  4119.                 break;
  4120.             case CLASS_WARLOCK:
  4121.                 info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
  4122.                 info->stats[STAT_STAMINA]   += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
  4123.                 info->stats[STAT_AGILITY]   += (lvl > 9 && !(lvl%2) ? 1: 0);
  4124.                 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
  4125.                 info->stats[STAT_SPIRIT]    += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
  4126.                 break;
  4127.             case CLASS_DRUID:
  4128.                 info->stats[STAT_STRENGTH]  += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
  4129.                 info->stats[STAT_STAMINA]   += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
  4130.                 info->stats[STAT_AGILITY]   += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
  4131.                 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
  4132.                 info->stats[STAT_SPIRIT]    += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
  4133.         }
  4134.     }
  4135. }
  4136.  
  4137. void ObjectMgr::LoadQuests()
  4138. {
  4139.     uint32 oldMSTime = getMSTime();
  4140.  
  4141.     _questTemplates.clear();
  4142.  
  4143.     _exclusiveQuestGroups.clear();
  4144.  
  4145.     QueryResult result = WorldDatabase.Query("SELECT "
  4146.         //0      1           2         3           4            5                6              7             8
  4147.         "ID, QuestType, QuestLevel, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, TimeAllowed, AllowableRaces,"
  4148.         //      9                     10                   11                    12
  4149.         "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, "
  4150.         //     13                 14               15             16                17               18            19            20
  4151.         "RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardBonusMoney, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
  4152.         //   21       22        23              24                25               26
  4153.         "StartItem, Flags, RewardTitle, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
  4154.         //    27            28            29           30             31            32            33            34
  4155.         "RewardItem1, RewardAmount1, RewardItem2, RewardAmount2, RewardItem3, RewardAmount3, RewardItem4, RewardAmount4, "
  4156.         //        35                      36                      37                      38                      39                      40                      41                      42                      43                      44                     45                      46
  4157.         "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemID6, RewardChoiceItemQuantity6, "
  4158.         //       47                 48                     49                  50                  51                     52                 53                  54                     55                  56                  57                    58                   59                 60                      61
  4159.         "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionID5, RewardFactionValue5,  RewardFactionOverride5,"
  4160.         //    62        63    64       65
  4161.         "POIContinent, POIx, POIy, POIPriority, "
  4162.         //   66          67               68                69                70
  4163.         "LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, "
  4164.         //      71                72                73                74                   75                     76                    77                      78
  4165.         "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
  4166.         //   79         80         81         82            83                 84                  85                86
  4167.         "ItemDrop1, ItemDrop2, ItemDrop3, ItemDrop4, ItemDropQuantity1, ItemDropQuantity2, ItemDropQuantity3, ItemDropQuantity4, "
  4168.         //      87               88               89               90               91               92                93                  94                  95                  96                  97                  98
  4169.         "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
  4170.         //  99          100             101             102             103
  4171.         "Unknown0, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4"
  4172.         " FROM quest_template");
  4173.     if (!result)
  4174.     {
  4175.         TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
  4176.         return;
  4177.     }
  4178.  
  4179.     _questTemplates.reserve(result->GetRowCount());
  4180.  
  4181.     // create multimap previous quest for each existed quest
  4182.     // some quests can have many previous maps set by NextQuestId in previous quest
  4183.     // for example set of race quests can lead to single not race specific quest
  4184.     do
  4185.     {
  4186.         Field* fields = result->Fetch();
  4187.  
  4188.         uint32 questId = fields[0].GetUInt32();
  4189.         _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(fields));
  4190.     } while (result->NextRow());
  4191.  
  4192.     std::unordered_map<uint32, uint32> usedMailTemplates;
  4193.  
  4194.     struct QuestLoaderHelper
  4195.     {
  4196.         typedef void(Quest::*QuestLoaderFunction)(Field* fields);
  4197.  
  4198.         char const* QueryFields;
  4199.         char const* TableName;
  4200.         char const* TableDesc;
  4201.         QuestLoaderFunction LoaderFunction;
  4202.     };
  4203.  
  4204.     static std::vector<QuestLoaderHelper> const QuestLoaderHelpers =
  4205.     {
  4206.         // 0   1       2       3       4       5            6            7            8
  4207.         { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4",                                                                       "quest_details",        "details",             &Quest::LoadQuestDetails       },
  4208.  
  4209.         // 0   1                2                  3
  4210.         { "ID, EmoteOnComplete, EmoteOnIncomplete, CompletionText",                                                                                                       "quest_request_items",  "request items",       &Quest::LoadQuestRequestItems  },
  4211.  
  4212.         // 0   1       2       3       4       5            6            7            8            9
  4213.         { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText",                                                           "quest_offer_reward",   "reward emotes",       &Quest::LoadQuestOfferReward   },
  4214.  
  4215.         // 0   1         2                 3              4            5            6               7                     8
  4216.         { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay,"
  4217.         // 9               10                   11                     12                     13                   14                   15                 16
  4218.         " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags", "quest_template_addon", "template addons",     &Quest::LoadQuestTemplateAddon },
  4219.  
  4220.         // 0        1
  4221.         { "QuestId, RewardMailSenderEntry",                                                                                                                               "quest_mail_sender",    "mail sender entries", &Quest::LoadQuestMailSender    }
  4222.     };
  4223.  
  4224.     for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
  4225.     {
  4226.         result = WorldDatabase.PQuery("SELECT %s FROM %s", loader.QueryFields, loader.TableName);
  4227.         if (!result)
  4228.             TC_LOG_INFO("server.loading", ">> Loaded 0 quest %s. DB table `%s` is empty.", loader.TableDesc, loader.TableName);
  4229.         else
  4230.         {
  4231.             do
  4232.             {
  4233.                 Field* fields = result->Fetch();
  4234.                 uint32 questId = fields[0].GetUInt32();
  4235.  
  4236.                 auto itr = _questTemplates.find(questId);
  4237.                 if (itr != _questTemplates.end())
  4238.                     (itr->second.*loader.LoaderFunction)(fields);
  4239.                 else
  4240.                     TC_LOG_ERROR("server.loading", "Table `%s` has data for quest %u but such quest does not exist", loader.TableName, questId);
  4241.             } while (result->NextRow());
  4242.         }
  4243.     }
  4244.  
  4245.     // Post processing
  4246.     for (auto& questPair : _questTemplates)
  4247.     {
  4248.         // skip post-loading checks for disabled quests
  4249.         if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
  4250.             continue;
  4251.  
  4252.         Quest* qinfo = &questPair.second;
  4253.  
  4254.         // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
  4255.  
  4256.         if (qinfo->GetQuestMethod() >= 3)
  4257.             TC_LOG_ERROR("sql.sql", "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
  4258.  
  4259.         if (qinfo->_specialFlags & ~QUEST_SPECIAL_FLAGS_DB_ALLOWED)
  4260.         {
  4261.             TC_LOG_ERROR("sql.sql", "Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
  4262.                 qinfo->GetQuestId(), qinfo->_specialFlags, QUEST_SPECIAL_FLAGS_DB_ALLOWED);
  4263.             qinfo->_specialFlags &= QUEST_SPECIAL_FLAGS_DB_ALLOWED;
  4264.         }
  4265.  
  4266.         if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
  4267.         {
  4268.             TC_LOG_ERROR("sql.sql", "Weekly Quest %u is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
  4269.             qinfo->_flags &= ~QUEST_FLAGS_DAILY;
  4270.         }
  4271.  
  4272.         if (qinfo->_flags & QUEST_FLAGS_DAILY)
  4273.         {
  4274.             if (!(qinfo->_specialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  4275.             {
  4276.                 TC_LOG_ERROR("sql.sql", "Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  4277.                 qinfo->_specialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  4278.             }
  4279.         }
  4280.  
  4281.         if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
  4282.         {
  4283.             if (!(qinfo->_specialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  4284.             {
  4285.                 TC_LOG_ERROR("sql.sql", "Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  4286.                 qinfo->_specialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  4287.             }
  4288.         }
  4289.  
  4290.         if (qinfo->_specialFlags & QUEST_SPECIAL_FLAGS_MONTHLY)
  4291.         {
  4292.             if (!(qinfo->_specialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  4293.             {
  4294.                 TC_LOG_ERROR("sql.sql", "Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  4295.                 qinfo->_specialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  4296.             }
  4297.         }
  4298.  
  4299.         if (qinfo->_flags & QUEST_FLAGS_TRACKING)
  4300.         {
  4301.             // at auto-reward can be rewarded only RewardChoiceItemId[0]
  4302.             for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
  4303.             {
  4304.                 if (uint32 id = qinfo->RewardChoiceItemId[j])
  4305.                 {
  4306.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = %u but item from `RewardChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
  4307.                         qinfo->GetQuestId(), j + 1, id, j + 1);
  4308.                     // no changes, quest ignore this data
  4309.                 }
  4310.             }
  4311.         }
  4312.  
  4313.         // client quest log visual (area case)
  4314.         if (qinfo->_zoneOrSort > 0)
  4315.         {
  4316.             if (!sAreaTableStore.LookupEntry(qinfo->_zoneOrSort))
  4317.             {
  4318.                 TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
  4319.                     qinfo->GetQuestId(), qinfo->_zoneOrSort);
  4320.                 // no changes, quest not dependent from this value but can have problems at client
  4321.             }
  4322.         }
  4323.         // client quest log visual (sort case)
  4324.         if (qinfo->_zoneOrSort < 0)
  4325.         {
  4326.             QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_zoneOrSort));
  4327.             if (!qSort)
  4328.             {
  4329.                 TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
  4330.                     qinfo->GetQuestId(), qinfo->_zoneOrSort);
  4331.                 // 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)
  4332.             }
  4333.             //check for proper RequiredSkillId value (skill case)
  4334.             if (uint32 skill_id = SkillByQuestSort(-qinfo->_zoneOrSort))
  4335.             {
  4336.                 if (qinfo->_requiredSkillId != skill_id)
  4337.                 {
  4338.                     TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %i but `RequiredSkillId` does not have a corresponding value (%d).",
  4339.                         qinfo->GetQuestId(), qinfo->_zoneOrSort, skill_id);
  4340.                     //override, and force proper value here?
  4341.                 }
  4342.             }
  4343.         }
  4344.  
  4345.         // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
  4346.         if (qinfo->_requiredClasses)
  4347.         {
  4348.             if (!(qinfo->_requiredClasses & CLASSMASK_ALL_PLAYABLE))
  4349.             {
  4350.                 TC_LOG_ERROR("sql.sql", "Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_requiredClasses);
  4351.                     qinfo->_requiredClasses = 0;
  4352.             }
  4353.         }
  4354.  
  4355.         // AllowableRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
  4356.         if (qinfo->_allowableRaces)
  4357.         {
  4358.             if (!(qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE))
  4359.             {
  4360.                 TC_LOG_ERROR("sql.sql", "Quest %u does not contain any playable races in `AllowableRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->_allowableRaces);
  4361.                 qinfo->_allowableRaces = 0;
  4362.             }
  4363.         }
  4364.  
  4365.         // RequiredSkillId, can be 0
  4366.         if (qinfo->_requiredSkillId)
  4367.         {
  4368.             if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
  4369.             {
  4370.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSkillId` = %u but this skill does not exist",
  4371.                     qinfo->GetQuestId(), qinfo->_requiredSkillId);
  4372.             }
  4373.         }
  4374.  
  4375.         if (qinfo->_requiredSkillPoints)
  4376.         {
  4377.             if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
  4378.             {
  4379.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSkillPoints` = %u but max possible skill is %u, quest can't be done.",
  4380.                     qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
  4381.                 // no changes, quest can't be done for this requirement
  4382.             }
  4383.         }
  4384.         // else Skill quests can have 0 skill level, this is ok
  4385.  
  4386.         if (qinfo->_requiredFactionId2 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId2))
  4387.         {
  4388.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionId2` = %u but faction template %u does not exist, quest can't be done.",
  4389.                 qinfo->GetQuestId(), qinfo->_requiredFactionId2, qinfo->_requiredFactionId2);
  4390.             // no changes, quest can't be done for this requirement
  4391.         }
  4392.  
  4393.         if (qinfo->_requiredFactionId1 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId1))
  4394.         {
  4395.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionId1` = %u but faction template %u does not exist, quest can't be done.",
  4396.                 qinfo->GetQuestId(), qinfo->_requiredFactionId1, qinfo->_requiredFactionId1);
  4397.             // no changes, quest can't be done for this requirement
  4398.         }
  4399.  
  4400.         if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
  4401.         {
  4402.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
  4403.                 qinfo->GetQuestId(), qinfo->_requiredMinRepFaction, qinfo->_requiredMinRepFaction);
  4404.             // no changes, quest can't be done for this requirement
  4405.         }
  4406.  
  4407.         if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
  4408.         {
  4409.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
  4410.                 qinfo->GetQuestId(), qinfo->_requiredMaxRepFaction, qinfo->_requiredMaxRepFaction);
  4411.             // no changes, quest can't be done for this requirement
  4412.         }
  4413.  
  4414.         if (qinfo->_requiredMinRepValue && qinfo->_requiredMinRepValue > ReputationMgr::Reputation_Cap)
  4415.         {
  4416.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
  4417.                 qinfo->GetQuestId(), qinfo->_requiredMinRepValue, ReputationMgr::Reputation_Cap);
  4418.             // no changes, quest can't be done for this requirement
  4419.         }
  4420.  
  4421.         if (qinfo->_requiredMinRepValue && qinfo->_requiredMaxRepValue && qinfo->_requiredMaxRepValue <= qinfo->_requiredMinRepValue)
  4422.         {
  4423.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
  4424.                 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
  4425.             // no changes, quest can't be done for this requirement
  4426.         }
  4427.  
  4428.         if (!qinfo->_requiredFactionId1 && qinfo->_requiredFactionValue1 != 0)
  4429.         {
  4430.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionValue1` = %d but `RequiredFactionId1` is 0, value has no effect",
  4431.                 qinfo->GetQuestId(), qinfo->_requiredFactionValue1);
  4432.             // warning
  4433.         }
  4434.  
  4435.         if (!qinfo->_requiredFactionId2 && qinfo->_requiredFactionValue2 != 0)
  4436.         {
  4437.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionValue2` = %d but `RequiredFactionId2` is 0, value has no effect",
  4438.                 qinfo->GetQuestId(), qinfo->_requiredFactionValue2);
  4439.             // warning
  4440.         }
  4441.  
  4442.         if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
  4443.         {
  4444.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
  4445.                 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
  4446.             // warning
  4447.         }
  4448.  
  4449.         if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
  4450.         {
  4451.             TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
  4452.                 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
  4453.             // warning
  4454.         }
  4455.  
  4456.         if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
  4457.         {
  4458.             TC_LOG_ERROR("sql.sql", "Quest %u has `RewardTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
  4459.                 qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
  4460.             qinfo->_rewardTitleId = 0;
  4461.             // quest can't reward this title
  4462.         }
  4463.  
  4464.         if (qinfo->_startItem)
  4465.         {
  4466.             if (!sObjectMgr->GetItemTemplate(qinfo->_startItem))
  4467.             {
  4468.                 TC_LOG_ERROR("sql.sql", "Quest %u has `StartItem` = %u but item with entry %u does not exist, quest can't be done.",
  4469.                     qinfo->GetQuestId(), qinfo->_startItem, qinfo->_startItem);
  4470.                 qinfo->_startItem = 0;                       // quest can't be done for this requirement
  4471.             }
  4472.             else if (qinfo->_startItemCount == 0)
  4473.             {
  4474.                 TC_LOG_ERROR("sql.sql", "Quest %u has `StartItem` = %u but `StartItemCount` = 0, set to 1 but need fix in DB.",
  4475.                     qinfo->GetQuestId(), qinfo->_startItem);
  4476.                 qinfo->_startItemCount = 1;                    // update to 1 for allow quest work for backward compatibility with DB
  4477.             }
  4478.         }
  4479.         else if (qinfo->_startItemCount > 0)
  4480.         {
  4481.             TC_LOG_ERROR("sql.sql", "Quest %u has `StartItem` = 0 but `StartItemCount` = %u, useless value.",
  4482.                 qinfo->GetQuestId(), qinfo->_startItemCount);
  4483.             qinfo->_startItemCount = 0;                          // no quest work changes in fact
  4484.         }
  4485.  
  4486.         if (qinfo->_sourceSpellid)
  4487.         {
  4488.             SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellid);
  4489.             if (!spellInfo)
  4490.             {
  4491.                 TC_LOG_ERROR("sql.sql", "Quest %u has `SourceSpellid` = %u but spell %u doesn't exist, quest can't be done.",
  4492.                     qinfo->GetQuestId(), qinfo->_sourceSpellid, qinfo->_sourceSpellid);
  4493.                 qinfo->_sourceSpellid = 0;                        // quest can't be done for this requirement
  4494.             }
  4495.             else if (!SpellMgr::IsSpellValid(spellInfo))
  4496.             {
  4497.                 TC_LOG_ERROR("sql.sql", "Quest %u has `SourceSpellid` = %u but spell %u is broken, quest can't be done.",
  4498.                     qinfo->GetQuestId(), qinfo->_sourceSpellid, qinfo->_sourceSpellid);
  4499.                 qinfo->_sourceSpellid = 0;                        // quest can't be done for this requirement
  4500.             }
  4501.         }
  4502.  
  4503.         for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  4504.         {
  4505.             uint32 id = qinfo->RequiredItemId[j];
  4506.             if (id)
  4507.             {
  4508.                 if (qinfo->RequiredItemCount[j] == 0)
  4509.                 {
  4510.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = %u but `RequiredItemCount%d` = 0, quest can't be done.",
  4511.                         qinfo->GetQuestId(), j+1, id, j+1);
  4512.                     // no changes, quest can't be done for this requirement
  4513.                 }
  4514.  
  4515.                 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER);
  4516.  
  4517.                 if (!sObjectMgr->GetItemTemplate(id))
  4518.                 {
  4519.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
  4520.                         qinfo->GetQuestId(), j+1, id, id);
  4521.                     qinfo->RequiredItemCount[j] = 0;             // prevent incorrect work of quest
  4522.                 }
  4523.             }
  4524.             else if (qinfo->RequiredItemCount[j]>0)
  4525.             {
  4526.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = 0 but `RequiredItemCount%d` = %u, quest can't be done.",
  4527.                     qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredItemCount[j]);
  4528.                 qinfo->RequiredItemCount[j] = 0;                 // prevent incorrect work of quest
  4529.             }
  4530.         }
  4531.  
  4532.         for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
  4533.         {
  4534.             uint32 id = qinfo->ItemDrop[j];
  4535.             if (id)
  4536.             {
  4537.                 if (!sObjectMgr->GetItemTemplate(id))
  4538.                 {
  4539.                     TC_LOG_ERROR("sql.sql", "Quest %u has `ItemDrop%d` = %u but item with entry %u does not exist, quest can't be done.",
  4540.                         qinfo->GetQuestId(), j+1, id, id);
  4541.                     // no changes, quest can't be done for this requirement
  4542.                 }
  4543.             }
  4544.             else
  4545.             {
  4546.                 if (qinfo->ItemDropQuantity[j]>0)
  4547.                 {
  4548.                     TC_LOG_ERROR("sql.sql", "Quest %u has `ItemDrop%d` = 0 but `ItemDropQuantity%d` = %u.",
  4549.                         qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
  4550.                     // no changes, quest ignore this data
  4551.                 }
  4552.             }
  4553.         }
  4554.  
  4555.         for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  4556.         {
  4557.             int32 id = qinfo->RequiredNpcOrGo[j];
  4558.             if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
  4559.             {
  4560.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %i but gameobject %u does not exist, quest can't be done.",
  4561.                     qinfo->GetQuestId(), j+1, id, uint32(-id));
  4562.                 qinfo->RequiredNpcOrGo[j] = 0;            // quest can't be done for this requirement
  4563.             }
  4564.  
  4565.             if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
  4566.             {
  4567.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %i but creature with entry %u does not exist, quest can't be done.",
  4568.                     qinfo->GetQuestId(), j+1, id, uint32(id));
  4569.                 qinfo->RequiredNpcOrGo[j] = 0;            // quest can't be done for this requirement
  4570.             }
  4571.  
  4572.             if (id)
  4573.             {
  4574.                 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
  4575.  
  4576.                 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO);
  4577.  
  4578.                 if (!qinfo->RequiredNpcOrGoCount[j])
  4579.                 {
  4580.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %u but `RequiredNpcOrGoCount%d` = 0, quest can't be done.",
  4581.                         qinfo->GetQuestId(), j+1, id, j+1);
  4582.                     // no changes, quest can be incorrectly done, but we already report this
  4583.                 }
  4584.             }
  4585.             else if (qinfo->RequiredNpcOrGoCount[j]>0)
  4586.             {
  4587.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = 0 but `RequiredNpcOrGoCount%d` = %u.",
  4588.                     qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredNpcOrGoCount[j]);
  4589.                 // no changes, quest ignore this data
  4590.             }
  4591.         }
  4592.  
  4593.         for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
  4594.         {
  4595.             uint32 id = qinfo->RewardChoiceItemId[j];
  4596.             if (id)
  4597.             {
  4598.                 if (!sObjectMgr->GetItemTemplate(id))
  4599.                 {
  4600.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
  4601.                         qinfo->GetQuestId(), j+1, id, id);
  4602.                     qinfo->RewardChoiceItemId[j] = 0;          // no changes, quest will not reward this
  4603.                 }
  4604.  
  4605.                 if (!qinfo->RewardChoiceItemCount[j])
  4606.                 {
  4607.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = %u but `RewardChoiceItemCount%d` = 0, quest can't be done.",
  4608.                         qinfo->GetQuestId(), j+1, id, j+1);
  4609.                     // no changes, quest can't be done
  4610.                 }
  4611.             }
  4612.             else if (qinfo->RewardChoiceItemCount[j]>0)
  4613.             {
  4614.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = 0 but `RewardChoiceItemCount%d` = %u.",
  4615.                     qinfo->GetQuestId(), j+1, j+1, qinfo->RewardChoiceItemCount[j]);
  4616.                 // no changes, quest ignore this data
  4617.             }
  4618.         }
  4619.  
  4620.         for (uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j)
  4621.         {
  4622.             uint32 id = qinfo->RewardItemId[j];
  4623.             if (id)
  4624.             {
  4625.                 if (!sObjectMgr->GetItemTemplate(id))
  4626.                 {
  4627.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
  4628.                         qinfo->GetQuestId(), j+1, id, id);
  4629.                     qinfo->RewardItemId[j] = 0;                // no changes, quest will not reward this item
  4630.                 }
  4631.  
  4632.                 if (!qinfo->RewardItemIdCount[j])
  4633.                 {
  4634.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardItemId%d` = %u but `RewardItemIdCount%d` = 0, quest will not reward this item.",
  4635.                         qinfo->GetQuestId(), j+1, id, j+1);
  4636.                     // no changes
  4637.                 }
  4638.             }
  4639.             else if (qinfo->RewardItemIdCount[j]>0)
  4640.             {
  4641.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardItemId%d` = 0 but `RewardItemIdCount%d` = %u.",
  4642.                     qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemIdCount[j]);
  4643.                 // no changes, quest ignore this data
  4644.             }
  4645.         }
  4646.  
  4647.         for (uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
  4648.         {
  4649.             if (qinfo->RewardFactionId[j])
  4650.             {
  4651.                 if (std::abs(qinfo->RewardFactionValueId[j]) > 9)
  4652.                 {
  4653.                TC_LOG_ERROR("sql.sql", "Quest %u has RewardFactionValueId%d = %i. That is outside the range of valid values (-9 to 9).", qinfo->GetQuestId(), j+1, qinfo->RewardFactionValueId[j]);
  4654.                 }
  4655.                 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
  4656.                 {
  4657.                     TC_LOG_ERROR("sql.sql", "Quest %u has `RewardFactionId%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.", qinfo->GetQuestId(), j+1, qinfo->RewardFactionId[j], qinfo->RewardFactionId[j]);
  4658.                     qinfo->RewardFactionId[j] = 0;            // quest will not reward this
  4659.                 }
  4660.             }
  4661.  
  4662.             else if (qinfo->RewardFactionValueIdOverride[j] != 0)
  4663.             {
  4664.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardFactionId%d` = 0 but `RewardFactionValueIdOverride%d` = %i.",
  4665.                     qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionValueIdOverride[j]);
  4666.                 // no changes, quest ignore this data
  4667.             }
  4668.         }
  4669.  
  4670.         if (qinfo->_rewardDisplaySpell)
  4671.         {
  4672.             SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardDisplaySpell);
  4673.             if (!spellInfo)
  4674.             {
  4675.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardDisplaySpell` = %u but spell %u does not exist, spell removed as display reward.",
  4676.                     qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
  4677.                 qinfo->_rewardDisplaySpell = 0;                        // no spell reward will display for this quest
  4678.             }
  4679.  
  4680.             else if (!SpellMgr::IsSpellValid(spellInfo))
  4681.             {
  4682.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardDisplaySpell` = %u but spell %u is broken, quest will not have a spell reward.",
  4683.                     qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
  4684.                 qinfo->_rewardDisplaySpell = 0;                        // no spell reward will display for this quest
  4685.             }
  4686.  
  4687.             else if (GetTalentSpellCost(qinfo->_rewardDisplaySpell))
  4688.             {
  4689.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardDisplaySpell` = %u but spell %u is talent, quest will not have a spell reward.",
  4690.                     qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
  4691.                 qinfo->_rewardDisplaySpell = 0;                        // no spell reward will display for this quest
  4692.             }
  4693.         }
  4694.  
  4695.         if (qinfo->_rewardSpell > 0)
  4696.         {
  4697.             SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell);
  4698.             if (!spellInfo)
  4699.             {
  4700.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u does not exist, quest will not have a spell reward.",
  4701.                     qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
  4702.                 qinfo->_rewardSpell = 0;                    // no spell will be cast on player
  4703.             }
  4704.  
  4705.             else if (!SpellMgr::IsSpellValid(spellInfo))
  4706.             {
  4707.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u is broken, quest will not have a spell reward.",
  4708.                     qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
  4709.                 qinfo->_rewardSpell = 0;                    // no spell will be cast on player
  4710.             }
  4711.  
  4712.             else if (GetTalentSpellCost(qinfo->_rewardSpell))
  4713.             {
  4714.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardDisplaySpell` = %u but spell %u is talent, quest will not have a spell reward.",
  4715.                     qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
  4716.                 qinfo->_rewardSpell = 0;                    // no spell will be cast on player
  4717.             }
  4718.         }
  4719.  
  4720.         if (qinfo->_rewardMailTemplateId)
  4721.         {
  4722.             if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
  4723.             {
  4724.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardMailTemplateId` = %u but mail template  %u does not exist, quest will not have a mail reward.",
  4725.                     qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
  4726.                 qinfo->_rewardMailTemplateId = 0;               // no mail will send to player
  4727.                 qinfo->_rewardMailDelay = 0;                // no mail will send to player
  4728.                 qinfo->_rewardMailSenderEntry = 0;
  4729.             }
  4730.             else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
  4731.             {
  4732.                 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
  4733.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardMailTemplateId` = %u but mail template  %u already used for quest %u, quest will not have a mail reward.",
  4734.                     qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
  4735.                 qinfo->_rewardMailTemplateId = 0;               // no mail will send to player
  4736.                 qinfo->_rewardMailDelay = 0;                // no mail will send to player
  4737.                 qinfo->_rewardMailSenderEntry = 0;
  4738.             }
  4739.             else
  4740.                 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
  4741.         }
  4742.  
  4743.         if (uint32 rewardNextQuest = qinfo->_rewardNextQuest)
  4744.         {
  4745.             if (!_questTemplates.count(rewardNextQuest))
  4746.             {
  4747.                 TC_LOG_ERROR("sql.sql", "Quest %u has `RewardNextQuest` = %u but quest %u does not exist, quest chain will not work.",
  4748.                     qinfo->GetQuestId(), qinfo->_rewardNextQuest, qinfo->_rewardNextQuest);
  4749.                 qinfo->_rewardNextQuest = 0;
  4750.             }
  4751.         }
  4752.  
  4753.         // fill additional data stores
  4754.         if (uint32 prevQuestId = std::abs(qinfo->_prevQuestId))
  4755.         {
  4756.             if (!_questTemplates.count(prevQuestId))
  4757.                 TC_LOG_ERROR("sql.sql", "Quest %u has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->_prevQuestId);
  4758.         }
  4759.  
  4760.         if (uint32 nextQuestId = qinfo->_nextQuestId)
  4761.         {
  4762.             auto nextQuestItr = _questTemplates.find(nextQuestId);
  4763.             if (nextQuestItr == _questTemplates.end())
  4764.                 TC_LOG_ERROR("sql.sql", "Quest %u has NextQuestId %u, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestId);
  4765.             else
  4766.                 nextQuestItr->second.DependentPreviousQuests.push_back(qinfo->GetQuestId());
  4767.         }
  4768.  
  4769.         if (qinfo->_exclusiveGroup)
  4770.             _exclusiveQuestGroups.emplace(qinfo->_exclusiveGroup, qinfo->GetQuestId());
  4771.         if (qinfo->_timeAllowed)
  4772.             qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED);
  4773.         if (qinfo->_requiredPlayerKills)
  4774.             qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL);
  4775.  
  4776.         // Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed
  4777.         if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO | QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  4778.         {
  4779.             bool addFlag = true;
  4780.             if (qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
  4781.             {
  4782.                 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  4783.                 {
  4784.                     if (qinfo->RequiredItemId[j] != 0 && (qinfo->RequiredItemId[j] != qinfo->GetSrcItemId() || qinfo->RequiredItemCount[j] > qinfo->GetSrcItemCount()))
  4785.                     {
  4786.                         addFlag = false;
  4787.                         break;
  4788.                     }
  4789.                 }
  4790.             }
  4791.  
  4792.             if (addFlag)
  4793.                 qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_COMPLETED_AT_START);
  4794.         }
  4795.     }
  4796.  
  4797.     // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
  4798.     for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
  4799.     {
  4800.         SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
  4801.         if (!spellInfo)
  4802.             continue;
  4803.  
  4804.         for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
  4805.         {
  4806.             if (spellInfo->Effects[j].Effect != SPELL_EFFECT_QUEST_COMPLETE)
  4807.                 continue;
  4808.  
  4809.             uint32 quest_id = spellInfo->Effects[j].MiscValue;
  4810.  
  4811.             Quest const* quest = GetQuestTemplate(quest_id);
  4812.  
  4813.             // some quest referenced in spells not exist (outdated spells)
  4814.             if (!quest)
  4815.                 continue;
  4816.  
  4817.             if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  4818.             {
  4819.                 TC_LOG_ERROR("sql.sql", "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id);
  4820.  
  4821.                 // this will prevent quest completing without objective
  4822.                 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  4823.             }
  4824.         }
  4825.     }
  4826.  
  4827.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " quests definitions in %u ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
  4828. }
  4829.  
  4830. void ObjectMgr::LoadQuestStartersAndEnders()
  4831. {
  4832.     TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
  4833.     LoadGameobjectQuestStarters();
  4834.     TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
  4835.     LoadGameobjectQuestEnders();
  4836.     TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
  4837.     LoadCreatureQuestStarters();
  4838.     TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
  4839.     LoadCreatureQuestEnders();
  4840. }
  4841.  
  4842. void ObjectMgr::LoadQuestLocales()
  4843. {
  4844.     uint32 oldMSTime = getMSTime();
  4845.  
  4846.     _questLocaleStore.clear();                                // need for reload case
  4847.  
  4848.     //                                               0   1       2      3        4           5                6                 7        8              9               10              11              12
  4849.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, CompletedText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4 FROM quest_template_locale");
  4850.     if (!result)
  4851.         return;
  4852.  
  4853.     do
  4854.     {
  4855.         Field* fields = result->Fetch();
  4856.  
  4857.         uint32 id               = fields[0].GetUInt32();
  4858.         std::string localeName  = fields[1].GetString();
  4859.  
  4860.         QuestLocale& data = _questLocaleStore[id];
  4861.  
  4862.         LocaleConstant locale = GetLocaleByName(localeName);
  4863.         if (locale == LOCALE_enUS)
  4864.             continue;
  4865.  
  4866.         AddLocaleString(fields[2].GetString(), locale, data.Title);
  4867.         AddLocaleString(fields[3].GetString(), locale, data.Details);
  4868.         AddLocaleString(fields[4].GetString(), locale, data.Objectives);
  4869.         AddLocaleString(fields[5].GetString(), locale, data.OfferRewardText);
  4870.         AddLocaleString(fields[6].GetString(), locale, data.RequestItemsText);
  4871.         AddLocaleString(fields[7].GetString(), locale, data.AreaDescription);
  4872.         AddLocaleString(fields[8].GetString(), locale, data.CompletedText);
  4873.  
  4874.         for (uint8 i = 0; i < 4; ++i)
  4875.             AddLocaleString(fields[i + 9].GetString(), locale, data.ObjectiveText[i]);
  4876.     } while (result->NextRow());
  4877.  
  4878.     TC_LOG_INFO("server.loading", ">> Loaded %u Quest locale strings in %u ms", uint32(_questLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  4879. }
  4880.  
  4881. void ObjectMgr::LoadScripts(ScriptsType type)
  4882. {
  4883.     uint32 oldMSTime = getMSTime();
  4884.  
  4885.     ScriptMapMap* scripts = GetScriptsMapByType(type);
  4886.     if (!scripts)
  4887.         return;
  4888.  
  4889.     std::string tableName = GetScriptsTableNameByType(type);
  4890.     if (tableName.empty())
  4891.         return;
  4892.  
  4893.     if (sMapMgr->IsScriptScheduled())                    // function cannot be called when scripts are in use.
  4894.         return;
  4895.  
  4896.     TC_LOG_INFO("server.loading", "Loading %s...", tableName.c_str());
  4897.  
  4898.     scripts->clear();                                       // need for reload support
  4899.  
  4900.     bool isSpellScriptTable = (type == SCRIPTS_SPELL);
  4901.     //                                                 0    1       2         3         4          5    6  7  8  9
  4902.     QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o%s FROM %s", isSpellScriptTable ? ", effIndex" : "", tableName.c_str());
  4903.  
  4904.     if (!result)
  4905.     {
  4906.         TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `%s` is empty!", tableName.c_str());
  4907.         return;
  4908.     }
  4909.  
  4910.     uint32 count = 0;
  4911.  
  4912.     do
  4913.     {
  4914.         Field* fields = result->Fetch();
  4915.         ScriptInfo tmp;
  4916.         tmp.type      = type;
  4917.         tmp.id           = fields[0].GetUInt32();
  4918.         if (isSpellScriptTable)
  4919.             tmp.id      |= fields[10].GetUInt8() << 24;
  4920.         tmp.delay        = fields[1].GetUInt32();
  4921.         tmp.command      = ScriptCommands(fields[2].GetUInt32());
  4922.         tmp.Raw.nData[0] = fields[3].GetUInt32();
  4923.         tmp.Raw.nData[1] = fields[4].GetUInt32();
  4924.         tmp.Raw.nData[2] = fields[5].GetInt32();
  4925.         tmp.Raw.fData[0] = fields[6].GetFloat();
  4926.         tmp.Raw.fData[1] = fields[7].GetFloat();
  4927.         tmp.Raw.fData[2] = fields[8].GetFloat();
  4928.         tmp.Raw.fData[3] = fields[9].GetFloat();
  4929.  
  4930.         // generic command args check
  4931.         switch (tmp.command)
  4932.         {
  4933.             case SCRIPT_COMMAND_TALK:
  4934.             {
  4935.                 if (tmp.Talk.ChatType > CHAT_TYPE_WHISPER && tmp.Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER)
  4936.                 {
  4937.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",
  4938.                         tableName.c_str(), tmp.Talk.ChatType, tmp.id);
  4939.                     continue;
  4940.                 }
  4941.                 if (!sObjectMgr->GetBroadcastText(uint32(tmp.Talk.TextID)))
  4942.                 {
  4943.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u",
  4944.                         tableName.c_str(), tmp.Talk.TextID, tmp.id);
  4945.                     continue;
  4946.                 }
  4947.  
  4948.                 break;
  4949.             }
  4950.  
  4951.             case SCRIPT_COMMAND_EMOTE:
  4952.             {
  4953.                 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
  4954.                 {
  4955.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",
  4956.                         tableName.c_str(), tmp.Emote.EmoteID, tmp.id);
  4957.                     continue;
  4958.                 }
  4959.                 break;
  4960.             }
  4961.  
  4962.             case SCRIPT_COMMAND_TELEPORT_TO:
  4963.             {
  4964.                 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
  4965.                 {
  4966.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
  4967.                         tableName.c_str(), tmp.TeleportTo.MapID, tmp.id);
  4968.                     continue;
  4969.                 }
  4970.  
  4971.                 if (!Trinity::IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation))
  4972.                 {
  4973.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
  4974.                         tableName.c_str(), tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
  4975.                     continue;
  4976.                 }
  4977.                 break;
  4978.             }
  4979.  
  4980.             case SCRIPT_COMMAND_QUEST_EXPLORED:
  4981.             {
  4982.                 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
  4983.                 if (!quest)
  4984.                 {
  4985.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
  4986.                         tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
  4987.                     continue;
  4988.                 }
  4989.  
  4990.                 if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  4991.                 {
  4992.                     TC_LOG_ERROR("sql.sql", "Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",
  4993.                         tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
  4994.  
  4995.                     // this will prevent quest completing without objective
  4996.                     const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  4997.  
  4998.                     // continue; - quest objective requirement set and command can be allowed
  4999.                 }
  5000.  
  5001.                 if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
  5002.                 {
  5003.                     TC_LOG_ERROR("sql.sql", "Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
  5004.                         tableName.c_str(), tmp.QuestExplored.Distance, tmp.id);
  5005.                     continue;
  5006.                 }
  5007.  
  5008.                 if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
  5009.                 {
  5010.                     TC_LOG_ERROR("sql.sql", "Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check",
  5011.                         tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE);
  5012.                     continue;
  5013.                 }
  5014.  
  5015.                 if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) < INTERACTION_DISTANCE)
  5016.                 {
  5017.                     TC_LOG_ERROR("sql.sql", "Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check",
  5018.                         tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
  5019.                     continue;
  5020.                 }
  5021.  
  5022.                 break;
  5023.             }
  5024.  
  5025.             case SCRIPT_COMMAND_KILL_CREDIT:
  5026.             {
  5027.                 if (!GetCreatureTemplate(tmp.KillCredit.CreatureEntry))
  5028.                 {
  5029.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u",
  5030.                         tableName.c_str(), tmp.KillCredit.CreatureEntry, tmp.id);
  5031.                     continue;
  5032.                 }
  5033.                 break;
  5034.             }
  5035.  
  5036.             case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
  5037.             {
  5038.                 GameObjectData const* data = GetGameObjectData(tmp.RespawnGameobject.GOGuid);
  5039.                 if (!data)
  5040.                 {
  5041.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
  5042.                         tableName.c_str(), tmp.RespawnGameobject.GOGuid, tmp.id);
  5043.                     continue;
  5044.                 }
  5045.  
  5046.                 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
  5047.                 if (!info)
  5048.                 {
  5049.                     TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
  5050.                         tableName.c_str(), tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
  5051.                     continue;
  5052.                 }
  5053.  
  5054.                 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
  5055.                     info->type == GAMEOBJECT_TYPE_FISHINGHOLE ||
  5056.                     info->type == GAMEOBJECT_TYPE_DOOR        ||
  5057.                     info->type == GAMEOBJECT_TYPE_BUTTON      ||
  5058.                     info->type == GAMEOBJECT_TYPE_TRAP)
  5059.                 {
  5060.                     TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
  5061.                         tableName.c_str(), info->entry, tmp.id);
  5062.                     continue;
  5063.                 }
  5064.                 break;
  5065.             }
  5066.  
  5067.             case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
  5068.             {
  5069.                 if (!Trinity::IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation))
  5070.                 {
  5071.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
  5072.                         tableName.c_str(), tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id);
  5073.                     continue;
  5074.                 }
  5075.  
  5076.                 if (!GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry))
  5077.                 {
  5078.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
  5079.                         tableName.c_str(), tmp.TempSummonCreature.CreatureEntry, tmp.id);
  5080.                     continue;
  5081.                 }
  5082.                 break;
  5083.             }
  5084.  
  5085.             case SCRIPT_COMMAND_OPEN_DOOR:
  5086.             case SCRIPT_COMMAND_CLOSE_DOOR:
  5087.             {
  5088.                 GameObjectData const* data = GetGameObjectData(tmp.ToggleDoor.GOGuid);
  5089.                 if (!data)
  5090.                 {
  5091.                     TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
  5092.                         tableName.c_str(), tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  5093.                     continue;
  5094.                 }
  5095.  
  5096.                 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
  5097.                 if (!info)
  5098.                 {
  5099.                     TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",
  5100.                         tableName.c_str(), tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  5101.                     continue;
  5102.                 }
  5103.  
  5104.                 if (info->type != GAMEOBJECT_TYPE_DOOR)
  5105.                 {
  5106.                     TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject type (%u) unsupported by command %s for script id %u",
  5107.                         tableName.c_str(), info->entry, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  5108.                     continue;
  5109.                 }
  5110.  
  5111.                 break;
  5112.             }
  5113.  
  5114.             case SCRIPT_COMMAND_REMOVE_AURA:
  5115.             {
  5116.                 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID))
  5117.                 {
  5118.                     TC_LOG_ERROR("sql.sql", "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
  5119.                         tableName.c_str(), tmp.RemoveAura.SpellID, tmp.id);
  5120.                     continue;
  5121.                 }
  5122.                 if (tmp.RemoveAura.Flags & ~0x1)                    // 1 bits (0, 1)
  5123.                 {
  5124.                     TC_LOG_ERROR("sql.sql", "Table `%s` using unknown flags in datalong2 (%u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
  5125.                         tableName.c_str(), tmp.RemoveAura.Flags, tmp.id);
  5126.                     continue;
  5127.                 }
  5128.                 break;
  5129.             }
  5130.  
  5131.             case SCRIPT_COMMAND_CAST_SPELL:
  5132.             {
  5133.                 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID))
  5134.                 {
  5135.                     TC_LOG_ERROR("sql.sql", "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  5136.                         tableName.c_str(), tmp.CastSpell.SpellID, tmp.id);
  5137.                     continue;
  5138.                 }
  5139.                 if (tmp.CastSpell.Flags > 4)                      // targeting type
  5140.                 {
  5141.                     TC_LOG_ERROR("sql.sql", "Table `%s` using unknown target in datalong2 (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  5142.                         tableName.c_str(), tmp.CastSpell.Flags, tmp.id);
  5143.                     continue;
  5144.                 }
  5145.                 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1)                      // 1 bit (0, 1)
  5146.                 {
  5147.                     TC_LOG_ERROR("sql.sql", "Table `%s` using unknown flags in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  5148.                         tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
  5149.                     continue;
  5150.                 }
  5151.                 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
  5152.                 {
  5153.                     TC_LOG_ERROR("sql.sql", "Table `%s` using invalid creature entry in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  5154.                         tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
  5155.                     continue;
  5156.                 }
  5157.                 break;
  5158.             }
  5159.  
  5160.             case SCRIPT_COMMAND_CREATE_ITEM:
  5161.             {
  5162.                 if (!GetItemTemplate(tmp.CreateItem.ItemEntry))
  5163.                 {
  5164.                     TC_LOG_ERROR("sql.sql", "Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u",
  5165.                         tableName.c_str(), tmp.CreateItem.ItemEntry, tmp.id);
  5166.                     continue;
  5167.                 }
  5168.                 if (!tmp.CreateItem.Amount)
  5169.                 {
  5170.                     TC_LOG_ERROR("sql.sql", "Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u",
  5171.                         tableName.c_str(), tmp.CreateItem.Amount, tmp.id);
  5172.                     continue;
  5173.                 }
  5174.                 break;
  5175.             }
  5176.             default:
  5177.                 break;
  5178.         }
  5179.  
  5180.         if (scripts->find(tmp.id) == scripts->end())
  5181.         {
  5182.             ScriptMap emptyMap;
  5183.             (*scripts)[tmp.id] = emptyMap;
  5184.         }
  5185.         (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
  5186.  
  5187.         ++count;
  5188.     }
  5189.     while (result->NextRow());
  5190.  
  5191.     TC_LOG_INFO("server.loading", ">> Loaded %u script definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5192. }
  5193.  
  5194. void ObjectMgr::LoadSpellScripts()
  5195. {
  5196.     LoadScripts(SCRIPTS_SPELL);
  5197.  
  5198.     // check ids
  5199.     for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
  5200.     {
  5201.         uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
  5202.         SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  5203.  
  5204.         if (!spellInfo)
  5205.         {
  5206.             TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has not existing spell (Id: %u) as script id", spellId);
  5207.             continue;
  5208.         }
  5209.  
  5210.         uint8 i = (uint8)((uint32(itr->first) >> 24) & 0x000000FF);
  5211.         //check for correct spellEffect
  5212.         if (!spellInfo->Effects[i].Effect || (spellInfo->Effects[i].Effect != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->Effects[i].Effect != SPELL_EFFECT_DUMMY))
  5213.             TC_LOG_ERROR("sql.sql", "Table `spell_scripts` - spell %u effect %u is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, i);
  5214.     }
  5215. }
  5216.  
  5217. void ObjectMgr::LoadEventScripts()
  5218. {
  5219.     LoadScripts(SCRIPTS_EVENT);
  5220.  
  5221.     std::set<uint32> evt_scripts;
  5222.     // Load all possible script entries from gameobjects
  5223.     for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
  5224.         if (uint32 eventId = gameObjectTemplatePair.second.GetEventScriptId())
  5225.             evt_scripts.insert(eventId);
  5226.  
  5227.     // Load all possible script entries from spells
  5228.     for (uint32 i = 1; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
  5229.         if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(i))
  5230.             for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
  5231.                 if (spell->Effects[j].Effect == SPELL_EFFECT_SEND_EVENT)
  5232.                     if (spell->Effects[j].MiscValue)
  5233.                         evt_scripts.insert(spell->Effects[j].MiscValue);
  5234.  
  5235.     for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
  5236.     {
  5237.         for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
  5238.         {
  5239.             TaxiPathNodeEntry const* node = sTaxiPathNodesByPath[path_idx][node_idx];
  5240.  
  5241.             if (node->ArrivalEventID)
  5242.                 evt_scripts.insert(node->ArrivalEventID);
  5243.  
  5244.             if (node->DepartureEventID)
  5245.                 evt_scripts.insert(node->DepartureEventID);
  5246.         }
  5247.     }
  5248.  
  5249.     // Then check if all scripts are in above list of possible script entries
  5250.     for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
  5251.     {
  5252.         std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
  5253.         if (itr2 == evt_scripts.end())
  5254.             TC_LOG_ERROR("sql.sql", "Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect %u",
  5255.                 itr->first, SPELL_EFFECT_SEND_EVENT);
  5256.     }
  5257. }
  5258.  
  5259. //Load WP Scripts
  5260. void ObjectMgr::LoadWaypointScripts()
  5261. {
  5262.     LoadScripts(SCRIPTS_WAYPOINT);
  5263.  
  5264.     std::set<uint32> actionSet;
  5265.  
  5266.     for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
  5267.         actionSet.insert(itr->first);
  5268.  
  5269.     PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
  5270.     PreparedQueryResult result = WorldDatabase.Query(stmt);
  5271.  
  5272.     if (result)
  5273.     {
  5274.         do
  5275.         {
  5276.             Field* fields = result->Fetch();
  5277.             uint32 action = fields[0].GetUInt32();
  5278.  
  5279.             actionSet.erase(action);
  5280.         }
  5281.         while (result->NextRow());
  5282.     }
  5283.  
  5284.     for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
  5285.         TC_LOG_ERROR("sql.sql", "There is no waypoint which links to the waypoint script %u", *itr);
  5286. }
  5287.  
  5288. void ObjectMgr::LoadSpellScriptNames()
  5289. {
  5290.     uint32 oldMSTime = getMSTime();
  5291.  
  5292.     _spellScriptsStore.clear();                            // need for reload case
  5293.  
  5294.     QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
  5295.  
  5296.     if (!result)
  5297.     {
  5298.         TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
  5299.         return;
  5300.     }
  5301.  
  5302.     uint32 count = 0;
  5303.  
  5304.     do
  5305.     {
  5306.  
  5307.         Field* fields = result->Fetch();
  5308.  
  5309.         int32 spellId                = fields[0].GetInt32();
  5310.         std::string const scriptName = fields[1].GetString();
  5311.  
  5312.         bool allRanks = false;
  5313.         if (spellId < 0)
  5314.         {
  5315.             allRanks = true;
  5316.             spellId = -spellId;
  5317.         }
  5318.  
  5319.         SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  5320.         if (!spellInfo)
  5321.         {
  5322.             TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) does not exist.", scriptName.c_str(), spellId);
  5323.             continue;
  5324.         }
  5325.  
  5326.         if (allRanks)
  5327.         {
  5328.             if (!spellInfo->IsRanked())
  5329.                 TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) has no ranks of spell.", scriptName.c_str(), fields[0].GetInt32());
  5330.  
  5331.             if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
  5332.             {
  5333.                 TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is not first rank of spell.", scriptName.c_str(), fields[0].GetInt32());
  5334.                 continue;
  5335.             }
  5336.  
  5337.             while (spellInfo)
  5338.             {
  5339.                 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
  5340.                 spellInfo = spellInfo->GetNextRankSpell();
  5341.             }
  5342.         }
  5343.         else
  5344.         {
  5345.             if (spellInfo->IsRanked())
  5346.                 TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId);
  5347.  
  5348.             _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
  5349.         }
  5350.  
  5351.         ++count;
  5352.     }
  5353.     while (result->NextRow());
  5354.  
  5355.     TC_LOG_INFO("server.loading", ">> Loaded %u spell script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5356. }
  5357.  
  5358. void ObjectMgr::ValidateSpellScripts()
  5359. {
  5360.     uint32 oldMSTime = getMSTime();
  5361.  
  5362.     if (_spellScriptsStore.empty())
  5363.     {
  5364.         TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
  5365.         return;
  5366.     }
  5367.  
  5368.     uint32 count = 0;
  5369.  
  5370.     for (auto spell : _spellScriptsStore)
  5371.     {
  5372.         SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell.first);
  5373.  
  5374.         auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
  5375.  
  5376.         for (auto itr = bounds.first; itr != bounds.second; ++itr)
  5377.         {
  5378.             if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
  5379.             {
  5380.                 ++count;
  5381.  
  5382.                 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
  5383.                 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
  5384.  
  5385.                 if (!spellScript && !auraScript)
  5386.                 {
  5387.                     TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(itr->second.first).c_str());
  5388.  
  5389.                     itr->second.second = false;
  5390.                     continue;
  5391.                 }
  5392.  
  5393.                 if (spellScript)
  5394.                 {
  5395.                     spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
  5396.                     spellScript->_Register();
  5397.  
  5398.                     if (!spellScript->_Validate(spellEntry))
  5399.                     {
  5400.                         itr->second.second = false;
  5401.                         continue;
  5402.                     }
  5403.                 }
  5404.  
  5405.                 if (auraScript)
  5406.                 {
  5407.                     auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
  5408.                     auraScript->_Register();
  5409.  
  5410.                     if (!auraScript->_Validate(spellEntry))
  5411.                     {
  5412.                         itr->second.second = false;
  5413.                         continue;
  5414.                     }
  5415.                 }
  5416.  
  5417.                 // Enable the script when all checks passed
  5418.                 itr->second.second = true;
  5419.             }
  5420.             else
  5421.                 itr->second.second = false;
  5422.         }
  5423.     }
  5424.  
  5425.     TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5426. }
  5427.  
  5428. void ObjectMgr::LoadPageTexts()
  5429. {
  5430.     uint32 oldMSTime = getMSTime();
  5431.  
  5432.     //                                               0     1       2
  5433.     QueryResult result = WorldDatabase.Query("SELECT ID, Text, NextPageID FROM page_text");
  5434.  
  5435.     if (!result)
  5436.     {
  5437.         TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
  5438.         return;
  5439.     }
  5440.  
  5441.     uint32 count = 0;
  5442.     do
  5443.     {
  5444.         Field* fields = result->Fetch();
  5445.  
  5446.         PageText& pageText  = _pageTextStore[fields[0].GetUInt32()];
  5447.  
  5448.         pageText.Text       = fields[1].GetString();
  5449.         pageText.NextPageID = fields[2].GetUInt32();
  5450.  
  5451.         ++count;
  5452.     }
  5453.     while (result->NextRow());
  5454.  
  5455.     for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
  5456.     {
  5457.         if (itr->second.NextPageID)
  5458.         {
  5459.             PageTextContainer::const_iterator itr2 = _pageTextStore.find(itr->second.NextPageID);
  5460.             if (itr2 == _pageTextStore.end())
  5461.                 TC_LOG_ERROR("sql.sql", "Page text (ID: %u) has non-existing `NextPageID` (%u)", itr->first, itr->second.NextPageID);
  5462.  
  5463.         }
  5464.     }
  5465.  
  5466.     TC_LOG_INFO("server.loading", ">> Loaded %u page texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5467. }
  5468.  
  5469. PageText const* ObjectMgr::GetPageText(uint32 pageEntry)
  5470. {
  5471.     PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
  5472.     if (itr != _pageTextStore.end())
  5473.         return &(itr->second);
  5474.  
  5475.     return nullptr;
  5476. }
  5477.  
  5478. void ObjectMgr::LoadPageTextLocales()
  5479. {
  5480.     uint32 oldMSTime = getMSTime();
  5481.  
  5482.     _pageTextLocaleStore.clear();                             // need for reload case
  5483.  
  5484.     //                                               0   1       2
  5485.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, Text FROM page_text_locale");
  5486.  
  5487.     if (!result)
  5488.         return;
  5489.  
  5490.     do
  5491.     {
  5492.         Field* fields = result->Fetch();
  5493.  
  5494.         uint32 id                   = fields[0].GetUInt32();
  5495.         std::string localeName      = fields[1].GetString();
  5496.         std::string text            = fields[2].GetString();
  5497.  
  5498.         PageTextLocale& data = _pageTextLocaleStore[id];
  5499.         LocaleConstant locale = GetLocaleByName(localeName);
  5500.  
  5501.         AddLocaleString(text, locale, data.Text);
  5502.     } while (result->NextRow());
  5503.  
  5504.     TC_LOG_INFO("server.loading", ">> Loaded %u PageText locale strings in %u ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  5505. }
  5506.  
  5507. void ObjectMgr::LoadInstanceTemplate()
  5508. {
  5509.     uint32 oldMSTime = getMSTime();
  5510.  
  5511.     //                                                0     1       2        4
  5512.     QueryResult result = WorldDatabase.Query("SELECT map, parent, script, allowMount FROM instance_template");
  5513.  
  5514.     if (!result)
  5515.     {
  5516.         TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
  5517.         return;
  5518.     }
  5519.  
  5520.     uint32 count = 0;
  5521.     do
  5522.     {
  5523.         Field* fields = result->Fetch();
  5524.  
  5525.         uint16 mapID = fields[0].GetUInt16();
  5526.  
  5527.         if (!MapManager::IsValidMAP(mapID, true))
  5528.         {
  5529.             TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", mapID);
  5530.             continue;
  5531.         }
  5532.  
  5533.         InstanceTemplate instanceTemplate;
  5534.  
  5535.         instanceTemplate.AllowMount = fields[3].GetBool();
  5536.         instanceTemplate.Parent     = uint32(fields[1].GetUInt16());
  5537.         instanceTemplate.ScriptId   = sObjectMgr->GetScriptId(fields[2].GetString());
  5538.  
  5539.         _instanceTemplateStore[mapID] = instanceTemplate;
  5540.  
  5541.         ++count;
  5542.     }
  5543.     while (result->NextRow());
  5544.  
  5545.     TC_LOG_INFO("server.loading", ">> Loaded %u instance templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5546. }
  5547.  
  5548. InstanceTemplate const* ObjectMgr::GetInstanceTemplate(uint32 mapID) const
  5549. {
  5550.     InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
  5551.     if (itr != _instanceTemplateStore.end())
  5552.         return &(itr->second);
  5553.  
  5554.     return nullptr;
  5555. }
  5556.  
  5557. void ObjectMgr::LoadInstanceEncounters()
  5558. {
  5559.     uint32 oldMSTime = getMSTime();
  5560.  
  5561.     //                                                 0         1            2                3
  5562.     QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
  5563.     if (!result)
  5564.     {
  5565.         TC_LOG_INFO("server.loading", ">> Loaded 0 instance encounters, table is empty!");
  5566.         return;
  5567.     }
  5568.  
  5569.     uint32 count = 0;
  5570.     std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
  5571.     do
  5572.     {
  5573.         Field* fields = result->Fetch();
  5574.         uint32 entry = fields[0].GetUInt32();
  5575.         uint8 creditType = fields[1].GetUInt8();
  5576.         uint32 creditEntry = fields[2].GetUInt32();
  5577.         uint32 lastEncounterDungeon = fields[3].GetUInt16();
  5578.         DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
  5579.         if (!dungeonEncounter)
  5580.         {
  5581.             TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
  5582.             continue;
  5583.         }
  5584.  
  5585.         if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon))
  5586.         {
  5587.             TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName[0], lastEncounterDungeon);
  5588.             continue;
  5589.         }
  5590.  
  5591.         auto itr = dungeonLastBosses.find(lastEncounterDungeon);
  5592.         if (lastEncounterDungeon)
  5593.         {
  5594.             if (itr != dungeonLastBosses.end())
  5595.             {
  5596.                 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounter->encounterName[0], itr->second->id, itr->second->encounterName[0]);
  5597.                 continue;
  5598.             }
  5599.  
  5600.             dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
  5601.         }
  5602.  
  5603.         switch (creditType)
  5604.         {
  5605.             case ENCOUNTER_CREDIT_KILL_CREATURE:
  5606.             {
  5607.                 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry);
  5608.                 if (!creatureInfo)
  5609.                 {
  5610.                     TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]);
  5611.                     continue;
  5612.                 }
  5613.                 const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
  5614.                 for (uint8 diff = 1; diff < MAX_DIFFICULTY; ++diff)
  5615.                 {
  5616.                     if (uint32 diffEntry = creatureInfo->DifficultyEntry[diff - 1])
  5617.                     {
  5618.                         if (CreatureTemplate const* diffInfo = GetCreatureTemplate(diffEntry))
  5619.                             const_cast<CreatureTemplate*>(diffInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
  5620.                     }
  5621.                 }
  5622.                 break;
  5623.             }
  5624.             case ENCOUNTER_CREDIT_CAST_SPELL:
  5625.                 if (!sSpellMgr->GetSpellInfo(creditEntry))
  5626.                 {
  5627.                     TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]);
  5628.                     continue;
  5629.                 }
  5630.                 break;
  5631.             default:
  5632.                 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName[0]);
  5633.                 continue;
  5634.         }
  5635.  
  5636.         DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, dungeonEncounter->difficulty)];
  5637.         encounters.emplace_back(Trinity::make_unique<DungeonEncounter>(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
  5638.         ++count;
  5639.     } while (result->NextRow());
  5640.  
  5641.     TC_LOG_INFO("server.loading", ">> Loaded %u instance encounters in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5642. }
  5643.  
  5644. GossipText const* ObjectMgr::GetGossipText(uint32 Text_ID) const
  5645. {
  5646.     GossipTextContainer::const_iterator itr = _gossipTextStore.find(Text_ID);
  5647.     if (itr != _gossipTextStore.end())
  5648.         return &itr->second;
  5649.     return nullptr;
  5650. }
  5651.  
  5652. void ObjectMgr::LoadGossipText()
  5653. {
  5654.     uint32 oldMSTime = getMSTime();
  5655.  
  5656.     QueryResult result = WorldDatabase.Query("SELECT ID, "
  5657.         "text0_0, text0_1, BroadcastTextID0, lang0, Probability0, em0_0, em0_1, em0_2, em0_3, em0_4, em0_5, "
  5658.         "text1_0, text1_1, BroadcastTextID1, lang1, Probability1, em1_0, em1_1, em1_2, em1_3, em1_4, em1_5, "
  5659.         "text2_0, text2_1, BroadcastTextID2, lang2, Probability2, em2_0, em2_1, em2_2, em2_3, em2_4, em2_5, "
  5660.         "text3_0, text3_1, BroadcastTextID3, lang3, Probability3, em3_0, em3_1, em3_2, em3_3, em3_4, em3_5, "
  5661.         "text4_0, text4_1, BroadcastTextID4, lang4, Probability4, em4_0, em4_1, em4_2, em4_3, em4_4, em4_5, "
  5662.         "text5_0, text5_1, BroadcastTextID5, lang5, Probability5, em5_0, em5_1, em5_2, em5_3, em5_4, em5_5, "
  5663.         "text6_0, text6_1, BroadcastTextID6, lang6, Probability6, em6_0, em6_1, em6_2, em6_3, em6_4, em6_5, "
  5664.         "text7_0, text7_1, BroadcastTextID7, lang7, Probability7, em7_0, em7_1, em7_2, em7_3, em7_4, em7_5 "
  5665.         "FROM npc_text");
  5666.  
  5667.  
  5668.     if (!result)
  5669.     {
  5670.         TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
  5671.         return;
  5672.     }
  5673.  
  5674.     _gossipTextStore.rehash(result->GetRowCount());
  5675.  
  5676.     uint32 count = 0;
  5677.     uint8 cic;
  5678.  
  5679.     do
  5680.     {
  5681.         ++count;
  5682.         cic = 0;
  5683.  
  5684.         Field* fields = result->Fetch();
  5685.  
  5686.         uint32 id = fields[cic++].GetUInt32();
  5687.         if (!id)
  5688.         {
  5689.             TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
  5690.             continue;
  5691.         }
  5692.  
  5693.         GossipText& gText = _gossipTextStore[id];
  5694.  
  5695.         for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
  5696.         {
  5697.             gText.Options[i].Text_0           = fields[cic++].GetString();
  5698.             gText.Options[i].Text_1           = fields[cic++].GetString();
  5699.             gText.Options[i].BroadcastTextID  = fields[cic++].GetUInt32();
  5700.             gText.Options[i].Language         = fields[cic++].GetUInt8();
  5701.             gText.Options[i].Probability      = fields[cic++].GetFloat();
  5702.  
  5703.             for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
  5704.             {
  5705.                 gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt16();
  5706.                 gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt16();
  5707.             }
  5708.         }
  5709.  
  5710.         for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; i++)
  5711.         {
  5712.             if (gText.Options[i].BroadcastTextID)
  5713.             {
  5714.                 if (!sObjectMgr->GetBroadcastText(gText.Options[i].BroadcastTextID))
  5715.                 {
  5716.                     TC_LOG_ERROR("sql.sql", "GossipText (Id: %u) in table `npc_text` has non-existing or incompatible BroadcastTextID%u %u.", id, i, gText.Options[i].BroadcastTextID);
  5717.                     gText.Options[i].BroadcastTextID = 0;
  5718.                 }
  5719.             }
  5720.         }
  5721.  
  5722.     }
  5723.     while (result->NextRow());
  5724.  
  5725.     TC_LOG_INFO("server.loading", ">> Loaded %u npc texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5726. }
  5727.  
  5728. void ObjectMgr::LoadNpcTextLocales()
  5729. {
  5730.     uint32 oldMSTime = getMSTime();
  5731.  
  5732.     _npcTextLocaleStore.clear();                              // need for reload case
  5733.  
  5734.     QueryResult result = WorldDatabase.Query("SELECT ID, Locale, "
  5735.     //   2        3        4        5        6        7        8        9        10       11       12       13       14       15       16       17
  5736.         "Text0_0, Text0_1, Text1_0, Text1_1, Text2_0, Text2_1, Text3_0, Text3_1, Text4_0, Text4_1, Text5_0, Text5_1, Text6_0, Text6_1, Text7_0, Text7_1 "
  5737.         "FROM npc_text_locale");
  5738.  
  5739.     if (!result)
  5740.         return;
  5741.  
  5742.     do
  5743.     {
  5744.         Field* fields = result->Fetch();
  5745.  
  5746.         uint32 id               = fields[0].GetUInt32();
  5747.         std::string localeName  = fields[1].GetString();
  5748.  
  5749.         NpcTextLocale& data     = _npcTextLocaleStore[id];
  5750.         LocaleConstant locale   = GetLocaleByName(localeName);
  5751.         if (locale == LOCALE_enUS)
  5752.             continue;
  5753.  
  5754.         for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
  5755.         {
  5756.             AddLocaleString(fields[2 + i * 2].GetString(), locale, data.Text_0[i]);
  5757.             AddLocaleString(fields[3 + i * 2].GetString(), locale, data.Text_1[i]);
  5758.         }
  5759.     } while (result->NextRow());
  5760.  
  5761.     TC_LOG_INFO("server.loading", ">> Loaded %u NpcText locale strings in %u ms", uint32(_npcTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  5762. }
  5763.  
  5764. //not very fast function but it is called only once a day, or on starting-up
  5765. void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
  5766. {
  5767.     uint32 oldMSTime = getMSTime();
  5768.  
  5769.     time_t curTime = GameTime::GetGameTime();
  5770.     tm lt;
  5771.     localtime_r(&curTime, &lt);
  5772.     uint64 basetime(curTime);
  5773.     TC_LOG_INFO("misc", "Returning mails current time: hour: %d, minute: %d, second: %d ", lt.tm_hour, lt.tm_min, lt.tm_sec);
  5774.  
  5775.     // Delete all old mails without item and without body immediately, if starting server
  5776.     if (!serverUp)
  5777.     {
  5778.         PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EMPTY_EXPIRED_MAIL);
  5779.         stmt->setUInt64(0, basetime);
  5780.         CharacterDatabase.Execute(stmt);
  5781.     }
  5782.     PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
  5783.     stmt->setUInt64(0, basetime);
  5784.     PreparedQueryResult result = CharacterDatabase.Query(stmt);
  5785.     if (!result)
  5786.     {
  5787.         TC_LOG_INFO("server.loading", ">> No expired mails found.");
  5788.         return;                                             // any mails need to be returned or deleted
  5789.     }
  5790.  
  5791.     std::map<uint32 /*messageId*/, MailItemInfoVec> itemsCache;
  5792.     stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
  5793.     stmt->setUInt32(0, (uint32)basetime);
  5794.     if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
  5795.     {
  5796.         MailItemInfo item;
  5797.         do
  5798.         {
  5799.             Field* fields = items->Fetch();
  5800.             item.item_guid = fields[0].GetUInt32();
  5801.             item.item_template = fields[1].GetUInt32();
  5802.             uint32 mailId = fields[2].GetUInt32();
  5803.             itemsCache[mailId].push_back(item);
  5804.         } while (items->NextRow());
  5805.     }
  5806.  
  5807.     uint32 deletedCount = 0;
  5808.     uint32 returnedCount = 0;
  5809.     do
  5810.     {
  5811.         Field* fields = result->Fetch();
  5812.         Mail* m = new Mail;
  5813.         m->messageID      = fields[0].GetUInt32();
  5814.         m->messageType    = fields[1].GetUInt8();
  5815.         m->sender         = fields[2].GetUInt32();
  5816.         m->receiver       = fields[3].GetUInt32();
  5817.         bool has_items    = fields[4].GetBool();
  5818.         m->expire_time    = time_t(fields[5].GetUInt32());
  5819.         m->deliver_time   = 0;
  5820.         m->COD            = fields[6].GetUInt32();
  5821.         m->checked        = fields[7].GetUInt8();
  5822.         m->mailTemplateId = fields[8].GetInt16();
  5823.  
  5824.         Player* player = nullptr;
  5825.         if (serverUp)
  5826.             player = ObjectAccessor::FindConnectedPlayer(ObjectGuid(HighGuid::Player, m->receiver));
  5827.  
  5828.         if (player && player->m_mailsLoaded)
  5829.         {                                                   // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
  5830.             // his in mailbox and he has already listed his mails)
  5831.             delete m;
  5832.             continue;
  5833.         }
  5834.  
  5835.         // Delete or return mail
  5836.         if (has_items)
  5837.         {
  5838.             // read items from cache
  5839.             m->items.swap(itemsCache[m->messageID]);
  5840.  
  5841.             // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
  5842.             if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
  5843.             {
  5844.                 // mail open and then not returned
  5845.                 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  5846.                 {
  5847.                     stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  5848.                     stmt->setUInt32(0, itr2->item_guid);
  5849.                     CharacterDatabase.Execute(stmt);
  5850.                 }
  5851.  
  5852.                 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
  5853.                 stmt->setUInt32(0, m->messageID);
  5854.                 CharacterDatabase.Execute(stmt);
  5855.             }
  5856.             else
  5857.             {
  5858.                 // Mail will be returned
  5859.                 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
  5860.                 stmt->setUInt32(0, m->receiver);
  5861.                 stmt->setUInt32(1, m->sender);
  5862.                 stmt->setUInt32(2, basetime + 30 * DAY);
  5863.                 stmt->setUInt32(3, basetime);
  5864.                 stmt->setUInt8 (4, uint8(MAIL_CHECK_MASK_RETURNED));
  5865.                 stmt->setUInt32(5, m->messageID);
  5866.                 CharacterDatabase.Execute(stmt);
  5867.                 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  5868.                 {
  5869.                     // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
  5870.                     stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
  5871.                     stmt->setUInt32(0, m->sender);
  5872.                     stmt->setUInt32(1, itr2->item_guid);
  5873.                     CharacterDatabase.Execute(stmt);
  5874.  
  5875.                     stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
  5876.                     stmt->setUInt32(0, m->sender);
  5877.                     stmt->setUInt32(1, itr2->item_guid);
  5878.                     CharacterDatabase.Execute(stmt);
  5879.                 }
  5880.                 delete m;
  5881.                 ++returnedCount;
  5882.                 continue;
  5883.             }
  5884.         }
  5885.  
  5886.         stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
  5887.         stmt->setUInt32(0, m->messageID);
  5888.         CharacterDatabase.Execute(stmt);
  5889.         delete m;
  5890.         ++deletedCount;
  5891.     }
  5892.     while (result->NextRow());
  5893.  
  5894.     TC_LOG_INFO("server.loading", ">> Processed %u expired mails: %u deleted and %u returned in %u ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
  5895. }
  5896.  
  5897. void ObjectMgr::LoadQuestAreaTriggers()
  5898. {
  5899.     uint32 oldMSTime = getMSTime();
  5900.  
  5901.     _questAreaTriggerStore.clear();                           // need for reload case
  5902.  
  5903.     QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
  5904.  
  5905.     if (!result)
  5906.     {
  5907.         TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
  5908.         return;
  5909.     }
  5910.  
  5911.     uint32 count = 0;
  5912.  
  5913.     do
  5914.     {
  5915.         ++count;
  5916.  
  5917.         Field* fields = result->Fetch();
  5918.  
  5919.         uint32 trigger_ID = fields[0].GetUInt32();
  5920.         uint32 quest_ID   = fields[1].GetUInt32();
  5921.  
  5922.         AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
  5923.         if (!atEntry)
  5924.         {
  5925.             TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", trigger_ID);
  5926.             continue;
  5927.         }
  5928.  
  5929.         Quest const* quest = GetQuestTemplate(quest_ID);
  5930.  
  5931.         if (!quest)
  5932.         {
  5933.             TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
  5934.             continue;
  5935.         }
  5936.  
  5937.         if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  5938.         {
  5939.             TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
  5940.  
  5941.             // this will prevent quest completing without objective
  5942.             const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  5943.  
  5944.             // continue; - quest modified to required objective and trigger can be allowed.
  5945.         }
  5946.  
  5947.         _questAreaTriggerStore[trigger_ID] = quest_ID;
  5948.  
  5949.     } while (result->NextRow());
  5950.  
  5951.     TC_LOG_INFO("server.loading", ">> Loaded %u quest trigger points in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5952. }
  5953.  
  5954. QuestGreeting const* ObjectMgr::GetQuestGreeting(ObjectGuid guid) const
  5955. {
  5956.     auto itr = _questGreetingStore.find(guid.GetTypeId());
  5957.     if (itr == _questGreetingStore.end())
  5958.         return nullptr;
  5959.  
  5960.     auto questItr = itr->second.find(guid.GetEntry());
  5961.     if (questItr == itr->second.end())
  5962.         return nullptr;
  5963.  
  5964.     return &questItr->second;
  5965. }
  5966.  
  5967. void ObjectMgr::LoadQuestGreetings()
  5968. {
  5969.     uint32 oldMSTime = getMSTime();
  5970.  
  5971.     _questGreetingStore.clear(); // need for reload case
  5972.  
  5973.     //                                                0   1          2                3             4
  5974.     QueryResult result = WorldDatabase.Query("SELECT ID, Type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
  5975.     if (!result)
  5976.     {
  5977.         TC_LOG_INFO("server.loading", ">> Loaded 0 quest greetings. DB table `quest_greeting` is empty.");
  5978.         return;
  5979.     }
  5980.  
  5981.     _questGreetingStore.rehash(result->GetRowCount());
  5982.  
  5983.     uint32 count = 0;
  5984.  
  5985.     do
  5986.     {
  5987.         Field* fields = result->Fetch();
  5988.  
  5989.         uint32 id                   = fields[0].GetUInt32();
  5990.         uint8 type                  = fields[1].GetUInt8();
  5991.         // overwrite
  5992.         switch (type)
  5993.         {
  5994.             case 0: // Creature
  5995.                 type = TYPEID_UNIT;
  5996.                 if (!sObjectMgr->GetCreatureTemplate(id))
  5997.                 {
  5998.                     TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template (entry: %u) does not exist.", id);
  5999.                     continue;
  6000.                 }
  6001.                 break;
  6002.             case 1: // GameObject
  6003.                 type = TYPEID_GAMEOBJECT;
  6004.                 if (!sObjectMgr->GetGameObjectTemplate(id))
  6005.                 {
  6006.                     TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template (entry: %u) does not exist.", id);
  6007.                     continue;
  6008.                 }
  6009.                 break;
  6010.             default:
  6011.                 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: unknown type = %u for entry = %u. Skipped.", type, id);
  6012.                 continue;
  6013.         }
  6014.  
  6015.         uint16 greetEmoteType       = fields[2].GetUInt16();
  6016.  
  6017.         if (greetEmoteType > 0 && !sEmotesStore.LookupEntry(greetEmoteType))
  6018.         {
  6019.             TC_LOG_DEBUG("sql.sql", "Table `quest_greeting`: entry %u has greetEmoteType = %u but emote does not exist. Set to 0.", id, greetEmoteType);
  6020.             greetEmoteType = 0;
  6021.         }
  6022.  
  6023.         uint32 greetEmoteDelay      = fields[3].GetUInt32();
  6024.         std::string greeting        = fields[4].GetString();
  6025.  
  6026.         _questGreetingStore[type][id] = QuestGreeting(greetEmoteType, greetEmoteDelay, greeting);
  6027.  
  6028.         ++count;
  6029.     }
  6030.     while (result->NextRow());
  6031.  
  6032.     TC_LOG_INFO("server.loading", ">> Loaded %u quest_greeting in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6033. }
  6034.  
  6035. void ObjectMgr::LoadQuestGreetingsLocales()
  6036. {
  6037.     uint32 oldMSTime = getMSTime();
  6038.  
  6039.     _questGreetingLocaleStore.clear();                              // need for reload case
  6040.  
  6041.     //                                               0     1      2       3
  6042.     QueryResult result = WorldDatabase.Query("SELECT ID, Type, Locale, Greeting FROM quest_greeting_locale");
  6043.     if (!result)
  6044.     {
  6045.         TC_LOG_INFO("server.loading", ">> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty.");
  6046.         return;
  6047.     }
  6048.  
  6049.     do
  6050.     {
  6051.         Field* fields = result->Fetch();
  6052.  
  6053.         uint32 id                   = fields[0].GetUInt32();
  6054.         uint8 type                  = fields[1].GetUInt8();
  6055.         // overwrite
  6056.         switch (type)
  6057.         {
  6058.             case 0: // Creature
  6059.                 type = TYPEID_UNIT;
  6060.                 break;
  6061.             case 1: // GameObject
  6062.                 type = TYPEID_GAMEOBJECT;
  6063.                 break;
  6064.             default:
  6065.                 break;
  6066.         }
  6067.  
  6068.         std::string localeName      = fields[2].GetString();
  6069.         std::string greeting        = fields[3].GetString();
  6070.  
  6071.         QuestGreetingLocale& data   = _questGreetingLocaleStore[MAKE_PAIR32(id, type)];
  6072.         LocaleConstant locale       = GetLocaleByName(localeName);
  6073.         if (locale == LOCALE_enUS)
  6074.             continue;
  6075.  
  6076.         AddLocaleString(greeting, locale, data.greeting);
  6077.     } while (result->NextRow());
  6078.  
  6079.     TC_LOG_INFO("server.loading", ">> Loaded %u quest greeting locale strings in %u ms", uint32(_questGreetingLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  6080. }
  6081.  
  6082. void ObjectMgr::LoadTavernAreaTriggers()
  6083. {
  6084.     uint32 oldMSTime = getMSTime();
  6085.  
  6086.     _tavernAreaTriggerStore.clear();                          // need for reload case
  6087.  
  6088.     QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
  6089.  
  6090.     if (!result)
  6091.     {
  6092.         TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
  6093.         return;
  6094.     }
  6095.  
  6096.     uint32 count = 0;
  6097.  
  6098.     do
  6099.     {
  6100.         ++count;
  6101.  
  6102.         Field* fields = result->Fetch();
  6103.  
  6104.         uint32 Trigger_ID      = fields[0].GetUInt32();
  6105.  
  6106.         AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
  6107.         if (!atEntry)
  6108.         {
  6109.             TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
  6110.             continue;
  6111.         }
  6112.  
  6113.         _tavernAreaTriggerStore.insert(Trigger_ID);
  6114.     } while (result->NextRow());
  6115.  
  6116.     TC_LOG_INFO("server.loading", ">> Loaded %u tavern triggers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6117. }
  6118.  
  6119. void ObjectMgr::LoadAreaTriggerScripts()
  6120. {
  6121.     uint32 oldMSTime = getMSTime();
  6122.  
  6123.     _areaTriggerScriptStore.clear();                            // need for reload case
  6124.  
  6125.     QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
  6126.     if (!result)
  6127.     {
  6128.         TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
  6129.         return;
  6130.     }
  6131.  
  6132.     do
  6133.     {
  6134.         Field* fields = result->Fetch();
  6135.  
  6136.         uint32 triggerId             = fields[0].GetUInt32();
  6137.         std::string const scriptName = fields[1].GetString();
  6138.  
  6139.         AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
  6140.         if (!atEntry)
  6141.         {
  6142.             TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: %u) does not exist in `AreaTrigger.dbc`.", triggerId);
  6143.             continue;
  6144.         }
  6145.         _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
  6146.     }
  6147.     while (result->NextRow());
  6148.  
  6149.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " areatrigger scripts in %u ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
  6150. }
  6151.  
  6152. uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
  6153. {
  6154.     bool found = false;
  6155.     float dist = 10000;
  6156.     uint32 id = 0;
  6157.  
  6158.     for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
  6159.     {
  6160.         TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
  6161.  
  6162.         if (!node || node->map_id != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight
  6163.             continue;
  6164.  
  6165.         uint8  field   = (uint8)((i - 1) / 32);
  6166.         uint32 submask = 1<<((i-1)%32);
  6167.  
  6168.         // skip not taxi network nodes
  6169.         if ((sTaxiNodesMask[field] & submask) == 0)
  6170.             continue;
  6171.  
  6172.         float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
  6173.         if (found)
  6174.         {
  6175.             if (dist2 < dist)
  6176.             {
  6177.                 dist = dist2;
  6178.                 id = i;
  6179.             }
  6180.         }
  6181.         else
  6182.         {
  6183.             found = true;
  6184.             dist = dist2;
  6185.             id = i;
  6186.         }
  6187.     }
  6188.  
  6189.     return id;
  6190. }
  6191.  
  6192. void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
  6193. {
  6194.     TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
  6195.     if (src_i == sTaxiPathSetBySource.end())
  6196.     {
  6197.         path = 0;
  6198.         cost = 0;
  6199.         return;
  6200.     }
  6201.  
  6202.     TaxiPathSetForSource& pathSet = src_i->second;
  6203.  
  6204.     TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
  6205.     if (dest_i == pathSet.end())
  6206.     {
  6207.         path = 0;
  6208.         cost = 0;
  6209.         return;
  6210.     }
  6211.  
  6212.     cost = dest_i->second.price;
  6213.     path = dest_i->second.ID;
  6214. }
  6215.  
  6216. uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
  6217. {
  6218.     uint32 mount_id = 0;
  6219.  
  6220.     // select mount creature id
  6221.     TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
  6222.     if (node)
  6223.     {
  6224.         uint32 mount_entry = 0;
  6225.         if (team == ALLIANCE)
  6226.             mount_entry = node->MountCreatureID[1];
  6227.         else
  6228.             mount_entry = node->MountCreatureID[0];
  6229.  
  6230.         // Fix for Alliance not being able to use Acherus taxi
  6231.         // only one mount type for both sides
  6232.         if (mount_entry == 0 && allowed_alt_team)
  6233.         {
  6234.             // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
  6235.             mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
  6236.         }
  6237.  
  6238.         CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
  6239.         if (mount_info)
  6240.         {
  6241.             mount_id = mount_info->GetRandomValidModelId();
  6242.             if (!mount_id)
  6243.             {
  6244.                 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry);
  6245.                 return 0;
  6246.             }
  6247.         }
  6248.     }
  6249.  
  6250.     // minfo is not actually used but the mount_id was updated
  6251.     GetCreatureModelRandomGender(&mount_id);
  6252.  
  6253.     return mount_id;
  6254. }
  6255.  
  6256. Quest const* ObjectMgr::GetQuestTemplate(uint32 quest_id) const
  6257. {
  6258.     return Trinity::Containers::MapGetValuePtr(_questTemplates, quest_id);
  6259. }
  6260.  
  6261. void ObjectMgr::LoadGraveyardZones()
  6262. {
  6263.     uint32 oldMSTime = getMSTime();
  6264.  
  6265.     GraveyardStore.clear(); // need for reload case
  6266.  
  6267.     //                                               0   1          2
  6268.     QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
  6269.  
  6270.     if (!result)
  6271.     {
  6272.         TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
  6273.         return;
  6274.     }
  6275.  
  6276.     uint32 count = 0;
  6277.  
  6278.     do
  6279.     {
  6280.         ++count;
  6281.  
  6282.         Field* fields = result->Fetch();
  6283.  
  6284.         uint32 safeLocId = fields[0].GetUInt32();
  6285.         uint32 zoneId = fields[1].GetUInt32();
  6286.         uint32 team   = fields[2].GetUInt16();
  6287.  
  6288.         WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
  6289.         if (!entry)
  6290.         {
  6291.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: %u), skipped.", safeLocId);
  6292.             continue;
  6293.         }
  6294.  
  6295.         AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
  6296.         if (!areaEntry)
  6297.         {
  6298.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId);
  6299.             continue;
  6300.         }
  6301.  
  6302.         if (areaEntry->zone != 0)
  6303.         {
  6304.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: %u) instead of zone, skipped.", zoneId);
  6305.             continue;
  6306.         }
  6307.  
  6308.         if (team != 0 && team != HORDE && team != ALLIANCE)
  6309.         {
  6310.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction (%u), skipped.", team);
  6311.             continue;
  6312.         }
  6313.  
  6314.         if (!AddGraveyardLink(safeLocId, zoneId, team, false))
  6315.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
  6316.     } while (result->NextRow());
  6317.  
  6318.     TC_LOG_INFO("server.loading", ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6319. }
  6320.  
  6321. WorldSafeLocsEntry const* ObjectMgr::GetDefaultGraveyard(uint32 team) const
  6322. {
  6323.     enum DefaultGraveyard
  6324.     {
  6325.         HORDE_GRAVEYARD    = 10, // Crossroads
  6326.         ALLIANCE_GRAVEYARD = 4   // Westfall
  6327.     };
  6328.  
  6329.     if (team == HORDE)
  6330.         return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
  6331.     else if (team == ALLIANCE)
  6332.         return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
  6333.     else return nullptr;
  6334. }
  6335.  
  6336. WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team) const
  6337. {
  6338.     // search for zone associated closest graveyard
  6339.     uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z);
  6340.  
  6341.     if (!zoneId)
  6342.     {
  6343.         if (z > -500)
  6344.         {
  6345.             TC_LOG_ERROR("misc", "ZoneId not found for map %u coords (%f, %f, %f)", MapId, x, y, z);
  6346.             return GetDefaultGraveyard(team);
  6347.         }
  6348.     }
  6349.  
  6350.     // Simulate std. algorithm:
  6351.     //   found some graveyard associated to (ghost_zone, ghost_map)
  6352.     //
  6353.     //   if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
  6354.     //     then check faction
  6355.     //   if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
  6356.     //     then check faction
  6357.     GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
  6358.     MapEntry const* map = sMapStore.LookupEntry(MapId);
  6359.  
  6360.     // not need to check validity of map object; MapId _MUST_ be valid here
  6361.     if (range.first == range.second && !map->IsBattlegroundOrArena())
  6362.     {
  6363.         if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones
  6364.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
  6365.         return GetDefaultGraveyard(team);
  6366.     }
  6367.  
  6368.     // at corpse map
  6369.     bool foundNear = false;
  6370.     float distNear = 10000;
  6371.     WorldSafeLocsEntry const* entryNear = nullptr;
  6372.  
  6373.     // at entrance map for corpse map
  6374.     bool foundEntr = false;
  6375.     float distEntr = 10000;
  6376.     WorldSafeLocsEntry const* entryEntr = nullptr;
  6377.  
  6378.     // some where other
  6379.     WorldSafeLocsEntry const* entryFar = nullptr;
  6380.  
  6381.     MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
  6382.  
  6383.     for (; range.first != range.second; ++range.first)
  6384.     {
  6385.         GraveyardData const& data = range.first->second;
  6386.  
  6387.         WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
  6388.         if (!entry)
  6389.         {
  6390.             TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID %u), skipped.", data.safeLocId);
  6391.             continue;
  6392.         }
  6393.  
  6394.         // skip enemy faction graveyard
  6395.         // team == 0 case can be at call from .neargrave
  6396.         if (data.team != 0 && team != 0 && data.team != team)
  6397.             continue;
  6398.  
  6399.         // find now nearest graveyard at other map
  6400.         if (MapId != entry->map_id)
  6401.         {
  6402.             // if find graveyard at different map from where entrance placed (or no entrance data), use any first
  6403.             if (!mapEntry
  6404.                 || mapEntry->entrance_map < 0
  6405.                 || uint32(mapEntry->entrance_map) != entry->map_id
  6406.                 || (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
  6407.             {
  6408.                 // not have any corrdinates for check distance anyway
  6409.                 entryFar = entry;
  6410.                 continue;
  6411.             }
  6412.  
  6413.             // at entrance map calculate distance (2D);
  6414.             float dist2 = (entry->x - mapEntry->entrance_x)*(entry->x - mapEntry->entrance_x)
  6415.                 +(entry->y - mapEntry->entrance_y)*(entry->y - mapEntry->entrance_y);
  6416.             if (foundEntr)
  6417.             {
  6418.                 if (dist2 < distEntr)
  6419.                 {
  6420.                     distEntr = dist2;
  6421.                     entryEntr = entry;
  6422.                 }
  6423.             }
  6424.             else
  6425.             {
  6426.                 foundEntr = true;
  6427.                 distEntr = dist2;
  6428.                 entryEntr = entry;
  6429.             }
  6430.         }
  6431.         // find now nearest graveyard at same map
  6432.         else
  6433.         {
  6434.             float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
  6435.             if (foundNear)
  6436.             {
  6437.                 if (dist2 < distNear)
  6438.                 {
  6439.                     distNear = dist2;
  6440.                     entryNear = entry;
  6441.                 }
  6442.             }
  6443.             else
  6444.             {
  6445.                 foundNear = true;
  6446.                 distNear = dist2;
  6447.                 entryNear = entry;
  6448.             }
  6449.         }
  6450.     }
  6451.  
  6452.     if (entryNear)
  6453.         return entryNear;
  6454.  
  6455.     if (entryEntr)
  6456.         return entryEntr;
  6457.  
  6458.     return entryFar;
  6459. }
  6460.  
  6461. GraveyardData const* ObjectMgr::FindGraveyardData(uint32 id, uint32 zoneId) const
  6462. {
  6463.     GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
  6464.     for (; range.first != range.second; ++range.first)
  6465.     {
  6466.         GraveyardData const& data = range.first->second;
  6467.         if (data.safeLocId == id)
  6468.             return &data;
  6469.     }
  6470.     return nullptr;
  6471. }
  6472.  
  6473. AreaTrigger const* ObjectMgr::GetAreaTrigger(uint32 trigger) const
  6474. {
  6475.     AreaTriggerContainer::const_iterator itr = _areaTriggerStore.find(trigger);
  6476.     if (itr != _areaTriggerStore.end())
  6477.         return &itr->second;
  6478.     return nullptr;
  6479. }
  6480.  
  6481. AccessRequirement const* ObjectMgr::GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
  6482. {
  6483.     AccessRequirementContainer::const_iterator itr = _accessRequirementStore.find(MAKE_PAIR32(mapid, difficulty));
  6484.     if (itr != _accessRequirementStore.end())
  6485.         return itr->second.get();
  6486.     return nullptr;
  6487. }
  6488.  
  6489. bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
  6490. {
  6491.     if (FindGraveyardData(id, zoneId))
  6492.         return false;
  6493.  
  6494.     // add link to loaded data
  6495.     GraveyardData data;
  6496.     data.safeLocId = id;
  6497.     data.team = team;
  6498.  
  6499.     GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
  6500.  
  6501.     // add link to DB
  6502.     if (persist)
  6503.     {
  6504.         PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE);
  6505.  
  6506.         stmt->setUInt32(0, id);
  6507.         stmt->setUInt32(1, zoneId);
  6508.         stmt->setUInt16(2, uint16(team));
  6509.  
  6510.         WorldDatabase.Execute(stmt);
  6511.     }
  6512.  
  6513.     return true;
  6514. }
  6515.  
  6516. void ObjectMgr::RemoveGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= false*/)
  6517. {
  6518.     GraveyardMapBoundsNonConst range = GraveyardStore.equal_range(zoneId);
  6519.     if (range.first == range.second)
  6520.     {
  6521.         //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
  6522.         return;
  6523.     }
  6524.  
  6525.     bool found = false;
  6526.  
  6527.  
  6528.     for (; range.first != range.second; ++range.first)
  6529.     {
  6530.         GraveyardData & data = range.first->second;
  6531.  
  6532.         // skip not matching safezone id
  6533.         if (data.safeLocId != id)
  6534.             continue;
  6535.  
  6536.         // skip enemy faction graveyard at same map (normal area, city, or battleground)
  6537.         // team == 0 case can be at call from .neargrave
  6538.         if (data.team != 0 && team != 0 && data.team != team)
  6539.             continue;
  6540.  
  6541.         found = true;
  6542.         break;
  6543.     }
  6544.  
  6545.     // no match, return
  6546.     if (!found)
  6547.         return;
  6548.  
  6549.     // remove from links
  6550.     GraveyardStore.erase(range.first);
  6551.  
  6552.     // remove link from DB
  6553.     if (persist)
  6554.     {
  6555.         PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE);
  6556.  
  6557.         stmt->setUInt32(0, id);
  6558.         stmt->setUInt32(1, zoneId);
  6559.         stmt->setUInt16(2, uint16(team));
  6560.  
  6561.         WorldDatabase.Execute(stmt);
  6562.     }
  6563. }
  6564.  
  6565. void ObjectMgr::LoadAreaTriggerTeleports()
  6566. {
  6567.     uint32 oldMSTime = getMSTime();
  6568.  
  6569.     _areaTriggerStore.clear();                                  // need for reload case
  6570.  
  6571.     //                                               0        1              2                  3                  4                   5
  6572.     QueryResult result = WorldDatabase.Query("SELECT ID,  target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
  6573.     if (!result)
  6574.     {
  6575.         TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
  6576.         return;
  6577.     }
  6578.  
  6579.     uint32 count = 0;
  6580.  
  6581.     do
  6582.     {
  6583.         Field* fields = result->Fetch();
  6584.  
  6585.         ++count;
  6586.  
  6587.         uint32 Trigger_ID = fields[0].GetUInt32();
  6588.  
  6589.         AreaTrigger at;
  6590.  
  6591.         at.target_mapId             = fields[1].GetUInt16();
  6592.         at.target_X                 = fields[2].GetFloat();
  6593.         at.target_Y                 = fields[3].GetFloat();
  6594.         at.target_Z                 = fields[4].GetFloat();
  6595.         at.target_Orientation       = fields[5].GetFloat();
  6596.  
  6597.         AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
  6598.         if (!atEntry)
  6599.         {
  6600.             TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
  6601.             continue;
  6602.         }
  6603.  
  6604.         MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
  6605.         if (!mapEntry)
  6606.         {
  6607.             TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
  6608.             continue;
  6609.         }
  6610.  
  6611.         if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
  6612.         {
  6613.             TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) target coordinates not provided.", Trigger_ID);
  6614.             continue;
  6615.         }
  6616.  
  6617.         _areaTriggerStore[Trigger_ID] = at;
  6618.  
  6619.     } while (result->NextRow());
  6620.  
  6621.     TC_LOG_INFO("server.loading", ">> Loaded %u area trigger teleport definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6622. }
  6623.  
  6624. void ObjectMgr::LoadAccessRequirements()
  6625. {
  6626.     uint32 oldMSTime = getMSTime();
  6627.  
  6628.     _accessRequirementStore.clear();                                  // need for reload case
  6629.  
  6630.     //                                               0      1           2          3          4           5      6             7             8                      9     10
  6631.     QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item_level, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text FROM access_requirement");
  6632.  
  6633.     if (!result)
  6634.     {
  6635.         TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
  6636.         return;
  6637.     }
  6638.  
  6639.     uint32 count = 0;
  6640.  
  6641.     do
  6642.     {
  6643.         Field* fields = result->Fetch();
  6644.  
  6645.         ++count;
  6646.  
  6647.         uint32 mapid = fields[0].GetUInt32();
  6648.         uint8 difficulty = fields[1].GetUInt8();
  6649.         uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
  6650.  
  6651.         auto& ar = _accessRequirementStore[requirement_ID];
  6652.         ar = Trinity::make_unique<AccessRequirement>();
  6653.  
  6654.         ar->levelMin = fields[2].GetUInt8();
  6655.         ar->levelMax = fields[3].GetUInt8();
  6656.         ar->item_level = fields[4].GetUInt16();
  6657.         ar->item = fields[5].GetUInt32();
  6658.         ar->item2 = fields[6].GetUInt32();
  6659.         ar->quest_A = fields[7].GetUInt32();
  6660.         ar->quest_H = fields[8].GetUInt32();
  6661.         ar->achievement = fields[9].GetUInt32();
  6662.         ar->questFailedText = fields[10].GetString();
  6663.  
  6664.         if (ar->item)
  6665.         {
  6666.             ItemTemplate const* pProto = GetItemTemplate(ar->item);
  6667.             if (!pProto)
  6668.             {
  6669.                 TC_LOG_ERROR("misc", "Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item, mapid, difficulty);
  6670.                 ar->item = 0;
  6671.             }
  6672.         }
  6673.  
  6674.         if (ar->item2)
  6675.         {
  6676.             ItemTemplate const* pProto = GetItemTemplate(ar->item2);
  6677.             if (!pProto)
  6678.             {
  6679.                 TC_LOG_ERROR("misc", "Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item2, mapid, difficulty);
  6680.                 ar->item2 = 0;
  6681.             }
  6682.         }
  6683.  
  6684.         if (ar->quest_A)
  6685.         {
  6686.             if (!GetQuestTemplate(ar->quest_A))
  6687.             {
  6688.                 TC_LOG_ERROR("sql.sql", "Required Alliance Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar->quest_A, mapid, difficulty);
  6689.                 ar->quest_A = 0;
  6690.             }
  6691.         }
  6692.  
  6693.         if (ar->quest_H)
  6694.         {
  6695.             if (!GetQuestTemplate(ar->quest_H))
  6696.             {
  6697.                 TC_LOG_ERROR("sql.sql", "Required Horde Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar->quest_H, mapid, difficulty);
  6698.                 ar->quest_H = 0;
  6699.             }
  6700.         }
  6701.  
  6702.         if (ar->achievement)
  6703.         {
  6704.             if (!sAchievementMgr->GetAchievement(ar->achievement))
  6705.             {
  6706.                 TC_LOG_ERROR("sql.sql", "Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar->achievement, mapid, difficulty);
  6707.                 ar->achievement = 0;
  6708.             }
  6709.         }
  6710.     } while (result->NextRow());
  6711.  
  6712.     TC_LOG_INFO("server.loading", ">> Loaded %u access requirement definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6713. }
  6714.  
  6715. /*
  6716.  * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
  6717.  */
  6718. AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
  6719. {
  6720.     bool useParentDbValue = false;
  6721.     uint32 parentId = 0;
  6722.     MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
  6723.     if (!mapEntry || mapEntry->entrance_map < 0)
  6724.         return nullptr;
  6725.  
  6726.     if (mapEntry->IsDungeon())
  6727.     {
  6728.         InstanceTemplate const* iTemplate = sObjectMgr->GetInstanceTemplate(Map);
  6729.  
  6730.         if (!iTemplate)
  6731.             return nullptr;
  6732.  
  6733.         parentId = iTemplate->Parent;
  6734.         useParentDbValue = true;
  6735.     }
  6736.  
  6737.     uint32 entrance_map = uint32(mapEntry->entrance_map);
  6738.     for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
  6739.         if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
  6740.         {
  6741.             AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
  6742.             if (atEntry && atEntry->mapid == Map)
  6743.                 return &itr->second;
  6744.         }
  6745.     return nullptr;
  6746. }
  6747.  
  6748. /**
  6749.  * Searches for the areatrigger which teleports players to the given map
  6750.  */
  6751. AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
  6752. {
  6753.     for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
  6754.     {
  6755.         if (itr->second.target_mapId == Map)
  6756.         {
  6757.             AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
  6758.             if (atEntry)
  6759.                 return &itr->second;
  6760.         }
  6761.     }
  6762.     return nullptr;
  6763. }
  6764.  
  6765. void ObjectMgr::SetHighestGuids()
  6766. {
  6767.     QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
  6768.     if (result)
  6769.         GetGuidSequenceGenerator<HighGuid::Player>().Set((*result)[0].GetUInt32()+1);
  6770.  
  6771.     result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
  6772.     if (result)
  6773.         GetGuidSequenceGenerator<HighGuid::Item>().Set((*result)[0].GetUInt32()+1);
  6774.  
  6775.     // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
  6776.     CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed());      // One-time query
  6777.     CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed());          // One-time query
  6778.     CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed());         // One-time query
  6779.     CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed());     // One-time query
  6780.  
  6781.     result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
  6782.     if (result)
  6783.         GetGuidSequenceGenerator<HighGuid::Mo_Transport>().Set((*result)[0].GetUInt32()+1);
  6784.  
  6785.     result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
  6786.     if (result)
  6787.         _auctionId = (*result)[0].GetUInt32()+1;
  6788.  
  6789.     result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
  6790.     if (result)
  6791.         _mailId = (*result)[0].GetUInt32()+1;
  6792.  
  6793.     result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
  6794.     if (result)
  6795.         sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
  6796.  
  6797.     result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
  6798.     if (result)
  6799.         _equipmentSetGuid = (*result)[0].GetUInt64()+1;
  6800.  
  6801.     result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
  6802.     if (result)
  6803.         sGuildMgr->SetNextGuildId((*result)[0].GetUInt32()+1);
  6804.  
  6805.     result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups");
  6806.     if (result)
  6807.         sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
  6808.  
  6809.     result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
  6810.     if (result)
  6811.         _creatureSpawnId = (*result)[0].GetUInt32() + 1;
  6812.  
  6813.     result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
  6814.     if (result)
  6815.         _gameObjectSpawnId = (*result)[0].GetUInt32() + 1;
  6816. }
  6817.  
  6818. uint32 ObjectMgr::GenerateAuctionID()
  6819. {
  6820.     if (_auctionId >= 0xFFFFFFFE)
  6821.     {
  6822.         TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
  6823.         World::StopNow(ERROR_EXIT_CODE);
  6824.     }
  6825.     return _auctionId++;
  6826. }
  6827.  
  6828. uint64 ObjectMgr::GenerateEquipmentSetGuid()
  6829. {
  6830.     if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
  6831.     {
  6832.         TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
  6833.         World::StopNow(ERROR_EXIT_CODE);
  6834.     }
  6835.     return _equipmentSetGuid++;
  6836. }
  6837.  
  6838. uint32 ObjectMgr::GenerateMailID()
  6839. {
  6840.     if (_mailId >= 0xFFFFFFFE)
  6841.     {
  6842.         TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
  6843.         World::StopNow(ERROR_EXIT_CODE);
  6844.     }
  6845.     return _mailId++;
  6846. }
  6847.  
  6848. uint32 ObjectMgr::GeneratePetNumber()
  6849. {
  6850.     if (_hiPetNumber >= 0xFFFFFFFE)
  6851.     {
  6852.         TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
  6853.         World::StopNow(ERROR_EXIT_CODE);
  6854.     }
  6855.     return _hiPetNumber++;
  6856. }
  6857.  
  6858. uint32 ObjectMgr::GenerateCreatureSpawnId()
  6859. {
  6860.     if (_creatureSpawnId >= uint32(0xFFFFFF))
  6861.     {
  6862.         TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
  6863.         World::StopNow(ERROR_EXIT_CODE);
  6864.     }
  6865.     return _creatureSpawnId++;
  6866. }
  6867.  
  6868. uint32 ObjectMgr::GenerateGameObjectSpawnId()
  6869. {
  6870.     if (_gameObjectSpawnId >= uint32(0xFFFFFF))
  6871.     {
  6872.         TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
  6873.         World::StopNow(ERROR_EXIT_CODE);
  6874.     }
  6875.     return _gameObjectSpawnId++;
  6876. }
  6877.  
  6878. void ObjectMgr::LoadGameObjectLocales()
  6879. {
  6880.     uint32 oldMSTime = getMSTime();
  6881.  
  6882.     _gameObjectLocaleStore.clear(); // need for reload case
  6883.  
  6884.     //                                               0      1       2     3
  6885.     QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption FROM gameobject_template_locale");
  6886.     if (!result)
  6887.         return;
  6888.  
  6889.     do
  6890.     {
  6891.         Field* fields = result->Fetch();
  6892.  
  6893.         uint32 id                   = fields[0].GetUInt32();
  6894.         std::string localeName      = fields[1].GetString();
  6895.  
  6896.         std::string name            = fields[2].GetString();
  6897.         std::string castBarCaption  = fields[3].GetString();
  6898.  
  6899.         GameObjectLocale& data = _gameObjectLocaleStore[id];
  6900.         LocaleConstant locale = GetLocaleByName(localeName);
  6901.         if (locale == LOCALE_enUS)
  6902.             continue;
  6903.  
  6904.         AddLocaleString(name, locale, data.Name);
  6905.         AddLocaleString(castBarCaption, locale, data.CastBarCaption);
  6906.  
  6907.     } while (result->NextRow());
  6908.  
  6909.     TC_LOG_INFO("server.loading", ">> Loaded %u gameobject_template_locale strings in %u ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  6910. }
  6911.  
  6912. inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  6913. {
  6914.     if (sLockStore.LookupEntry(dataN))
  6915.         return;
  6916.  
  6917.     TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
  6918.         goInfo->entry, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
  6919. }
  6920.  
  6921. inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  6922. {
  6923.     if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
  6924.     {
  6925.         if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
  6926.             TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
  6927.             goInfo->entry, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
  6928.     }
  6929. }
  6930.  
  6931. inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  6932. {
  6933.     if (sSpellMgr->GetSpellInfo(dataN))
  6934.         return;
  6935.  
  6936.     TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
  6937.         goInfo->entry, goInfo->type, N, dataN, dataN);
  6938. }
  6939.  
  6940. inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32 const& dataN, uint32 N)
  6941. {
  6942.     if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR))
  6943.         return;
  6944.  
  6945.     TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
  6946.         goInfo->entry, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR);
  6947.  
  6948.     // prevent client and server unexpected work
  6949.     const_cast<uint32&>(dataN) = 0;
  6950. }
  6951.  
  6952. inline void CheckGONoDamageImmuneId(GameObjectTemplate* goTemplate, uint32 dataN, uint32 N)
  6953. {
  6954.     // 0/1 correct values
  6955.     if (dataN <= 1)
  6956.         return;
  6957.  
  6958.     TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
  6959. }
  6960.  
  6961. inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  6962. {
  6963.     // 0/1 correct values
  6964.     if (dataN <= 1)
  6965.         return;
  6966.  
  6967.     TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
  6968.         goInfo->entry, goInfo->type, N, dataN);
  6969. }
  6970.  
  6971. void ObjectMgr::LoadGameObjectTemplate()
  6972. {
  6973.     uint32 oldMSTime = getMSTime();
  6974.  
  6975.     //                                                 0      1      2        3       4             5          6     7
  6976.     QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
  6977.     //                                         8      9      10     11     12     13     14     15     16     17     18      19      20
  6978.                                              "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
  6979.     //                                         21      22      23      24      25      26      27      28      29      30      31      32      33
  6980.                                              "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, AIName, ScriptName "
  6981.                                              "FROM gameobject_template");
  6982.  
  6983.     if (!result)
  6984.     {
  6985.         TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
  6986.         return;
  6987.     }
  6988.  
  6989.     _gameObjectTemplateStore.reserve(result->GetRowCount());
  6990.     do
  6991.     {
  6992.         Field* fields = result->Fetch();
  6993.  
  6994.         uint32 entry = fields[0].GetUInt32();
  6995.  
  6996.         GameObjectTemplate& got = _gameObjectTemplateStore[entry];
  6997.         got.entry          = entry;
  6998.         got.type           = uint32(fields[1].GetUInt8());
  6999.         got.displayId      = fields[2].GetUInt32();
  7000.         got.name           = fields[3].GetString();
  7001.         got.IconName       = fields[4].GetString();
  7002.         got.castBarCaption = fields[5].GetString();
  7003.         got.unk1           = fields[6].GetString();
  7004.         got.size           = fields[7].GetFloat();
  7005.  
  7006.         for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
  7007.             got.raw.data[i] = fields[8 + i].GetInt32(); // data1 and data6 can be -1
  7008.  
  7009.         got.AIName = fields[32].GetString();
  7010.         got.ScriptId = GetScriptId(fields[33].GetString());
  7011.  
  7012.         // Checks
  7013.         if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
  7014.         {
  7015.             TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u) has non-registered `AIName` '%s' set, removing", got.entry, got.AIName.c_str());
  7016.             got.AIName.clear();
  7017.         }
  7018.  
  7019.         switch (got.type)
  7020.         {
  7021.             case GAMEOBJECT_TYPE_DOOR:                      //0
  7022.             {
  7023.                 if (got.door.lockId)
  7024.                     CheckGOLockId(&got, got.door.lockId, 1);
  7025.                 CheckGONoDamageImmuneId(&got, got.door.noDamageImmune, 3);
  7026.                 break;
  7027.             }
  7028.             case GAMEOBJECT_TYPE_BUTTON:                    //1
  7029.             {
  7030.                 if (got.button.lockId)
  7031.                     CheckGOLockId(&got, got.button.lockId, 1);
  7032.                 CheckGONoDamageImmuneId(&got, got.button.noDamageImmune, 4);
  7033.                 break;
  7034.             }
  7035.             case GAMEOBJECT_TYPE_QUESTGIVER:                //2
  7036.             {
  7037.                 if (got.questgiver.lockId)
  7038.                     CheckGOLockId(&got, got.questgiver.lockId, 0);
  7039.                 CheckGONoDamageImmuneId(&got, got.questgiver.noDamageImmune, 5);
  7040.                 break;
  7041.             }
  7042.             case GAMEOBJECT_TYPE_CHEST:                     //3
  7043.             {
  7044.                 if (got.chest.lockId)
  7045.                     CheckGOLockId(&got, got.chest.lockId, 0);
  7046.  
  7047.                 CheckGOConsumable(&got, got.chest.consumable, 3);
  7048.  
  7049.                 if (got.chest.linkedTrapId)              // linked trap
  7050.                     CheckGOLinkedTrapId(&got, got.chest.linkedTrapId, 7);
  7051.                 break;
  7052.             }
  7053.             case GAMEOBJECT_TYPE_TRAP:                      //6
  7054.             {
  7055.                 if (got.trap.lockId)
  7056.                     CheckGOLockId(&got, got.trap.lockId, 0);
  7057.                 break;
  7058.             }
  7059.             case GAMEOBJECT_TYPE_CHAIR:                     //7
  7060.                 CheckAndFixGOChairHeightId(&got, got.chair.height, 1);
  7061.                 break;
  7062.             case GAMEOBJECT_TYPE_SPELL_FOCUS:               //8
  7063.             {
  7064.                 if (got.spellFocus.focusId)
  7065.                 {
  7066.                     if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.focusId))
  7067.                         TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
  7068.                         entry, got.type, got.spellFocus.focusId, got.spellFocus.focusId);
  7069.                 }
  7070.  
  7071.                 if (got.spellFocus.linkedTrapId)        // linked trap
  7072.                     CheckGOLinkedTrapId(&got, got.spellFocus.linkedTrapId, 2);
  7073.                 break;
  7074.             }
  7075.             case GAMEOBJECT_TYPE_GOOBER:                    //10
  7076.             {
  7077.                 if (got.goober.lockId)
  7078.                     CheckGOLockId(&got, got.goober.lockId, 0);
  7079.  
  7080.                 CheckGOConsumable(&got, got.goober.consumable, 3);
  7081.  
  7082.                 if (got.goober.pageId)                  // pageId
  7083.                 {
  7084.                     if (!GetPageText(got.goober.pageId))
  7085.                         TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
  7086.                         entry, got.type, got.goober.pageId, got.goober.pageId);
  7087.                 }
  7088.                 CheckGONoDamageImmuneId(&got, got.goober.noDamageImmune, 11);
  7089.                 if (got.goober.linkedTrapId)            // linked trap
  7090.                     CheckGOLinkedTrapId(&got, got.goober.linkedTrapId, 12);
  7091.                 break;
  7092.             }
  7093.             case GAMEOBJECT_TYPE_AREADAMAGE:                //12
  7094.             {
  7095.                 if (got.areadamage.lockId)
  7096.                     CheckGOLockId(&got, got.areadamage.lockId, 0);
  7097.                 break;
  7098.             }
  7099.             case GAMEOBJECT_TYPE_CAMERA:                    //13
  7100.             {
  7101.                 if (got.camera.lockId)
  7102.                     CheckGOLockId(&got, got.camera.lockId, 0);
  7103.                 break;
  7104.             }
  7105.             case GAMEOBJECT_TYPE_MO_TRANSPORT:              //15
  7106.             {
  7107.                 if (got.moTransport.taxiPathId)
  7108.                 {
  7109.                     if (got.moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[got.moTransport.taxiPathId].empty())
  7110.                         TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
  7111.                             entry, got.type, got.moTransport.taxiPathId, got.moTransport.taxiPathId);
  7112.                 }
  7113.                 if (uint32 transportMap = got.moTransport.mapID)
  7114.                     _transportMaps.insert(transportMap);
  7115.                 break;
  7116.             }
  7117.             case GAMEOBJECT_TYPE_SUMMONING_RITUAL:          //18
  7118.                 break;
  7119.             case GAMEOBJECT_TYPE_SPELLCASTER:               //22
  7120.             {
  7121.                 // always must have spell
  7122.                 CheckGOSpellId(&got, got.spellcaster.spellId, 0);
  7123.                 break;
  7124.             }
  7125.             case GAMEOBJECT_TYPE_FLAGSTAND:                 //24
  7126.             {
  7127.                 if (got.flagstand.lockId)
  7128.                     CheckGOLockId(&got, got.flagstand.lockId, 0);
  7129.                 CheckGONoDamageImmuneId(&got, got.flagstand.noDamageImmune, 5);
  7130.                 break;
  7131.             }
  7132.             case GAMEOBJECT_TYPE_FISHINGHOLE:               //25
  7133.             {
  7134.                 if (got.fishinghole.lockId)
  7135.                     CheckGOLockId(&got, got.fishinghole.lockId, 4);
  7136.                 break;
  7137.             }
  7138.             case GAMEOBJECT_TYPE_FLAGDROP:                  //26
  7139.             {
  7140.                 if (got.flagdrop.lockId)
  7141.                     CheckGOLockId(&got, got.flagdrop.lockId, 0);
  7142.                 CheckGONoDamageImmuneId(&got, got.flagdrop.noDamageImmune, 3);
  7143.                 break;
  7144.             }
  7145.             case GAMEOBJECT_TYPE_BARBER_CHAIR:              //32
  7146.                 CheckAndFixGOChairHeightId(&got, got.barberChair.chairheight, 0);
  7147.                 break;
  7148.         }
  7149.     } while (result->NextRow());
  7150.  
  7151.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " game object templates in %u ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
  7152. }
  7153.  
  7154. void ObjectMgr::LoadGameObjectTemplateAddons()
  7155. {
  7156.     uint32 oldMSTime = getMSTime();
  7157.  
  7158.     //                                                0       1       2      3        4
  7159.     QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold FROM gameobject_template_addon");
  7160.  
  7161.     if (!result)
  7162.     {
  7163.         TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
  7164.         return;
  7165.     }
  7166.  
  7167.     uint32 count = 0;
  7168.     do
  7169.     {
  7170.         Field* fields = result->Fetch();
  7171.  
  7172.         uint32 entry = fields[0].GetUInt32();
  7173.  
  7174.         GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry);
  7175.         if (!got)
  7176.         {
  7177.             TC_LOG_ERROR("sql.sql", "GameObject template (Entry: %u) does not exist but has a record in `gameobject_template_addon`", entry);
  7178.             continue;
  7179.         }
  7180.  
  7181.         GameObjectTemplateAddon& gameObjectAddon = _gameObjectTemplateAddonStore[entry];
  7182.         gameObjectAddon.faction = uint32(fields[1].GetUInt16());
  7183.         gameObjectAddon.flags   = fields[2].GetUInt32();
  7184.         gameObjectAddon.mingold = fields[3].GetUInt32();
  7185.         gameObjectAddon.maxgold = fields[4].GetUInt32();
  7186.  
  7187.         // checks
  7188.         if (gameObjectAddon.faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.faction))
  7189.             TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u) has invalid faction (%u) defined in `gameobject_template_addon`.", entry, gameObjectAddon.faction);
  7190.  
  7191.         if (gameObjectAddon.maxgold > 0)
  7192.         {
  7193.             switch (got->type)
  7194.             {
  7195.                 case GAMEOBJECT_TYPE_CHEST:
  7196.                 case GAMEOBJECT_TYPE_FISHINGHOLE:
  7197.                     break;
  7198.                 default:
  7199.                     TC_LOG_ERROR("sql.sql", "GameObject (Entry %u GoType: %u) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
  7200.                     break;
  7201.             }
  7202.         }
  7203.  
  7204.         ++count;
  7205.     }
  7206.     while (result->NextRow());
  7207.  
  7208.     TC_LOG_INFO("server.loading", ">> Loaded %u game object template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7209. }
  7210.  
  7211. void ObjectMgr::LoadExplorationBaseXP()
  7212. {
  7213.     uint32 oldMSTime = getMSTime();
  7214.  
  7215.     QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
  7216.  
  7217.     if (!result)
  7218.     {
  7219.         TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
  7220.         return;
  7221.     }
  7222.  
  7223.     uint32 count = 0;
  7224.  
  7225.     do
  7226.     {
  7227.         Field* fields = result->Fetch();
  7228.         uint8 level  = fields[0].GetUInt8();
  7229.         uint32 basexp = fields[1].GetInt32();
  7230.         _baseXPTable[level] = basexp;
  7231.         ++count;
  7232.     }
  7233.     while (result->NextRow());
  7234.  
  7235.     TC_LOG_INFO("server.loading", ">> Loaded %u BaseXP definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7236. }
  7237.  
  7238. uint32 ObjectMgr::GetBaseXP(uint8 level)
  7239. {
  7240.     return _baseXPTable[level] ? _baseXPTable[level] : 0;
  7241. }
  7242.  
  7243. uint32 ObjectMgr::GetXPForLevel(uint8 level) const
  7244. {
  7245.     if (level < _playerXPperLevel.size())
  7246.         return _playerXPperLevel[level];
  7247.     return 0;
  7248. }
  7249.  
  7250. void ObjectMgr::LoadPetNames()
  7251. {
  7252.     uint32 oldMSTime = getMSTime();
  7253.     //                                                0     1      2
  7254.     QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
  7255.  
  7256.     if (!result)
  7257.     {
  7258.         TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
  7259.         return;
  7260.     }
  7261.  
  7262.     uint32 count = 0;
  7263.  
  7264.     do
  7265.     {
  7266.         Field* fields = result->Fetch();
  7267.         std::string word = fields[0].GetString();
  7268.         uint32 entry     = fields[1].GetUInt32();
  7269.         bool   half      = fields[2].GetBool();
  7270.         if (half)
  7271.             _petHalfName1[entry].push_back(word);
  7272.         else
  7273.             _petHalfName0[entry].push_back(word);
  7274.         ++count;
  7275.     }
  7276.     while (result->NextRow());
  7277.  
  7278.     TC_LOG_INFO("server.loading", ">> Loaded %u pet name parts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7279. }
  7280.  
  7281. void ObjectMgr::LoadPetNumber()
  7282. {
  7283.     uint32 oldMSTime = getMSTime();
  7284.  
  7285.     QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
  7286.     if (result)
  7287.     {
  7288.         Field* fields = result->Fetch();
  7289.         _hiPetNumber = fields[0].GetUInt32()+1;
  7290.     }
  7291.  
  7292.     TC_LOG_INFO("server.loading", ">> Loaded the max pet number: %d in %u ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
  7293. }
  7294.  
  7295. std::string ObjectMgr::GeneratePetName(uint32 entry)
  7296. {
  7297.     std::vector<std::string>& list0 = _petHalfName0[entry];
  7298.     std::vector<std::string>& list1 = _petHalfName1[entry];
  7299.  
  7300.     if (list0.empty() || list1.empty())
  7301.     {
  7302.         CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
  7303.         if (!cinfo)
  7304.             return std::string();
  7305.  
  7306.         char* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
  7307.         if (petname)
  7308.             return std::string(petname);
  7309.         else
  7310.             return cinfo->Name;
  7311.     }
  7312.  
  7313.     return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
  7314. }
  7315.  
  7316. void ObjectMgr::LoadReputationRewardRate()
  7317. {
  7318.     uint32 oldMSTime = getMSTime();
  7319.  
  7320.     _repRewardRateStore.clear();                             // for reload case
  7321.  
  7322.     uint32 count = 0; //                                0          1             2                  3                  4                 5                      6             7
  7323.     QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, quest_daily_rate, quest_weekly_rate, quest_monthly_rate, quest_repeatable_rate, creature_rate, spell_rate FROM reputation_reward_rate");
  7324.     if (!result)
  7325.     {
  7326.         TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
  7327.         return;
  7328.     }
  7329.  
  7330.     do
  7331.     {
  7332.         Field* fields = result->Fetch();
  7333.  
  7334.         uint32 factionId            = fields[0].GetUInt32();
  7335.  
  7336.         RepRewardRate repRate;
  7337.  
  7338.         repRate.questRate           = fields[1].GetFloat();
  7339.         repRate.questDailyRate      = fields[2].GetFloat();
  7340.         repRate.questWeeklyRate     = fields[3].GetFloat();
  7341.         repRate.questMonthlyRate    = fields[4].GetFloat();
  7342.         repRate.questRepeatableRate = fields[5].GetFloat();
  7343.         repRate.creatureRate        = fields[6].GetFloat();
  7344.         repRate.spellRate           = fields[7].GetFloat();
  7345.  
  7346.         FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
  7347.         if (!factionEntry)
  7348.         {
  7349.             TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
  7350.             continue;
  7351.         }
  7352.  
  7353.         if (repRate.questRate < 0.0f)
  7354.         {
  7355.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.questRate, factionId);
  7356.             continue;
  7357.         }
  7358.  
  7359.         if (repRate.questDailyRate < 0.0f)
  7360.         {
  7361.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate %f, skipping data for faction %u", repRate.questDailyRate, factionId);
  7362.             continue;
  7363.         }
  7364.  
  7365.         if (repRate.questWeeklyRate < 0.0f)
  7366.         {
  7367.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate %f, skipping data for faction %u", repRate.questWeeklyRate, factionId);
  7368.             continue;
  7369.         }
  7370.  
  7371.         if (repRate.questMonthlyRate < 0.0f)
  7372.         {
  7373.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate %f, skipping data for faction %u", repRate.questMonthlyRate, factionId);
  7374.             continue;
  7375.         }
  7376.  
  7377.         if (repRate.questRepeatableRate < 0.0f)
  7378.         {
  7379.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate %f, skipping data for faction %u", repRate.questRepeatableRate, factionId);
  7380.             continue;
  7381.         }
  7382.  
  7383.         if (repRate.creatureRate < 0.0f)
  7384.         {
  7385.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creatureRate, factionId);
  7386.             continue;
  7387.         }
  7388.  
  7389.         if (repRate.spellRate < 0.0f)
  7390.         {
  7391.             TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spellRate, factionId);
  7392.             continue;
  7393.         }
  7394.  
  7395.         _repRewardRateStore[factionId] = repRate;
  7396.  
  7397.         ++count;
  7398.     }
  7399.     while (result->NextRow());
  7400.  
  7401.     TC_LOG_INFO("server.loading", ">> Loaded %u reputation_reward_rate in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7402. }
  7403.  
  7404. void ObjectMgr::LoadReputationOnKill()
  7405. {
  7406.     uint32 oldMSTime = getMSTime();
  7407.  
  7408.     // For reload case
  7409.     _repOnKillStore.clear();
  7410.  
  7411.     uint32 count = 0;
  7412.  
  7413.     //                                                0            1                     2
  7414.     QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
  7415.     //   3             4             5                   6             7             8                   9
  7416.         "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
  7417.         "FROM creature_onkill_reputation");
  7418.  
  7419.     if (!result)
  7420.     {
  7421.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
  7422.         return;
  7423.     }
  7424.  
  7425.     do
  7426.     {
  7427.         Field* fields = result->Fetch();
  7428.  
  7429.         uint32 creature_id = fields[0].GetUInt32();
  7430.  
  7431.         ReputationOnKillEntry repOnKill;
  7432.         repOnKill.RepFaction1          = fields[1].GetInt16();
  7433.         repOnKill.RepFaction2          = fields[2].GetInt16();
  7434.         repOnKill.IsTeamAward1        = fields[3].GetBool();
  7435.         repOnKill.ReputationMaxCap1  = fields[4].GetUInt8();
  7436.         repOnKill.RepValue1            = fields[5].GetInt32();
  7437.         repOnKill.IsTeamAward2        = fields[6].GetBool();
  7438.         repOnKill.ReputationMaxCap2  = fields[7].GetUInt8();
  7439.         repOnKill.RepValue2            = fields[8].GetInt32();
  7440.         repOnKill.TeamDependent       = fields[9].GetBool();
  7441.  
  7442.         if (!GetCreatureTemplate(creature_id))
  7443.         {
  7444.             TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry (%u), skipped", creature_id);
  7445.             continue;
  7446.         }
  7447.  
  7448.         if (repOnKill.RepFaction1)
  7449.         {
  7450.             FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
  7451.             if (!factionEntry1)
  7452.             {
  7453.                 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
  7454.                 continue;
  7455.             }
  7456.         }
  7457.  
  7458.         if (repOnKill.RepFaction2)
  7459.         {
  7460.             FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
  7461.             if (!factionEntry2)
  7462.             {
  7463.                 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
  7464.                 continue;
  7465.             }
  7466.         }
  7467.  
  7468.         _repOnKillStore[creature_id] = repOnKill;
  7469.  
  7470.         ++count;
  7471.     } while (result->NextRow());
  7472.  
  7473.     TC_LOG_INFO("server.loading", ">> Loaded %u creature award reputation definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7474. }
  7475.  
  7476. void ObjectMgr::LoadReputationSpilloverTemplate()
  7477. {
  7478.     uint32 oldMSTime = getMSTime();
  7479.  
  7480.     _repSpilloverTemplateStore.clear();                      // for reload case
  7481.  
  7482.     uint32 count = 0; //                                0         1        2       3        4       5       6         7        8      9        10       11     12
  7483.     QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4 FROM reputation_spillover_template");
  7484.  
  7485.     if (!result)
  7486.     {
  7487.         TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
  7488.         return;
  7489.     }
  7490.  
  7491.     do
  7492.     {
  7493.         Field* fields = result->Fetch();
  7494.  
  7495.         uint32 factionId                = fields[0].GetUInt16();
  7496.  
  7497.         RepSpilloverTemplate repTemplate;
  7498.  
  7499.         repTemplate.faction[0]          = fields[1].GetUInt16();
  7500.         repTemplate.faction_rate[0]     = fields[2].GetFloat();
  7501.         repTemplate.faction_rank[0]     = fields[3].GetUInt8();
  7502.         repTemplate.faction[1]          = fields[4].GetUInt16();
  7503.         repTemplate.faction_rate[1]     = fields[5].GetFloat();
  7504.         repTemplate.faction_rank[1]     = fields[6].GetUInt8();
  7505.         repTemplate.faction[2]          = fields[7].GetUInt16();
  7506.         repTemplate.faction_rate[2]     = fields[8].GetFloat();
  7507.         repTemplate.faction_rank[2]     = fields[9].GetUInt8();
  7508.         repTemplate.faction[3]          = fields[10].GetUInt16();
  7509.         repTemplate.faction_rate[3]     = fields[11].GetFloat();
  7510.         repTemplate.faction_rank[3]     = fields[12].GetUInt8();
  7511.  
  7512.         FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
  7513.  
  7514.         if (!factionEntry)
  7515.         {
  7516.             TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
  7517.             continue;
  7518.         }
  7519.  
  7520.         if (factionEntry->team == 0)
  7521.         {
  7522.             TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId);
  7523.             continue;
  7524.         }
  7525.  
  7526.         bool invalidSpilloverFaction = false;
  7527.         for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
  7528.         {
  7529.             if (repTemplate.faction[i])
  7530.             {
  7531.                 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
  7532.  
  7533.                 if (!factionSpillover)
  7534.                 {
  7535.                     TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate.faction[i], factionId);
  7536.                     invalidSpilloverFaction = true;
  7537.                     break;
  7538.                 }
  7539.  
  7540.                 if (factionSpillover->reputationListID < 0)
  7541.                 {
  7542.                     TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) %u for faction %u in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate.faction[i], factionId);
  7543.                     invalidSpilloverFaction = true;
  7544.                     break;
  7545.                 }
  7546.  
  7547.                 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
  7548.                 {
  7549.                     TC_LOG_ERROR("sql.sql", "Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
  7550.                     invalidSpilloverFaction = true;
  7551.                     break;
  7552.                 }
  7553.             }
  7554.         }
  7555.  
  7556.         if (invalidSpilloverFaction)
  7557.             continue;
  7558.  
  7559.         _repSpilloverTemplateStore[factionId] = repTemplate;
  7560.  
  7561.         ++count;
  7562.     }
  7563.     while (result->NextRow());
  7564.  
  7565.     TC_LOG_INFO("server.loading", ">> Loaded %u reputation_spillover_template in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7566. }
  7567.  
  7568. void ObjectMgr::LoadPointsOfInterest()
  7569. {
  7570.     uint32 oldMSTime = getMSTime();
  7571.  
  7572.     _pointsOfInterestStore.clear();                              // need for reload case
  7573.  
  7574.     uint32 count = 0;
  7575.  
  7576.     //                                               0       1          2        3     4      5    6
  7577.     QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, Icon, Flags, Importance, Name FROM points_of_interest");
  7578.  
  7579.     if (!result)
  7580.     {
  7581.         TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
  7582.         return;
  7583.     }
  7584.  
  7585.     do
  7586.     {
  7587.         Field* fields = result->Fetch();
  7588.  
  7589.         uint32 id = fields[0].GetUInt32();
  7590.  
  7591.         PointOfInterest pointOfInterest;
  7592.         pointOfInterest.ID         = id;
  7593.         pointOfInterest.PositionX  = fields[1].GetFloat();
  7594.         pointOfInterest.PositionY  = fields[2].GetFloat();
  7595.         pointOfInterest.Icon       = fields[3].GetUInt32();
  7596.         pointOfInterest.Flags      = fields[4].GetUInt32();
  7597.         pointOfInterest.Importance = fields[5].GetUInt32();
  7598.         pointOfInterest.Name       = fields[6].GetString();
  7599.  
  7600.         if (!Trinity::IsValidMapCoord(pointOfInterest.PositionX, pointOfInterest.PositionY))
  7601.         {
  7602.             TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: %u) have invalid coordinates (X: %f Y: %f), ignored.", id, pointOfInterest.PositionX, pointOfInterest.PositionY);
  7603.             continue;
  7604.         }
  7605.  
  7606.         _pointsOfInterestStore[id] = pointOfInterest;
  7607.  
  7608.         ++count;
  7609.     } while (result->NextRow());
  7610.  
  7611.     TC_LOG_INFO("server.loading", ">> Loaded %u Points of Interest definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7612. }
  7613.  
  7614. void ObjectMgr::LoadQuestPOI()
  7615. {
  7616.     uint32 oldMSTime = getMSTime();
  7617.  
  7618.     _questPOIStore.clear();                              // need for reload case
  7619.  
  7620.     //                                               0        1          2          3           4          5       6        7
  7621.     QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi");
  7622.     if (!result)
  7623.     {
  7624.         TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
  7625.         return;
  7626.     }
  7627.  
  7628.     _questPOIStore.reserve(result->GetRowCount());
  7629.  
  7630.     //                                                  0       1   2  3
  7631.     QueryResult points = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx2");
  7632.  
  7633.     std::vector<std::vector<std::vector<QuestPOIBlobPoint>>> POIs;
  7634.     if (points)
  7635.     {
  7636.         // The first result should have the highest questId
  7637.         Field* fields = points->Fetch();
  7638.         uint32 const maxQuestPOIId = fields[0].GetUInt32();
  7639.         POIs.resize(maxQuestPOIId + 1);
  7640.  
  7641.         do
  7642.         {
  7643.             fields = points->Fetch();
  7644.  
  7645.             uint32 questId            = fields[0].GetUInt32();
  7646.             uint32 id                 = fields[1].GetUInt32();
  7647.             int32  x                  = fields[2].GetInt32();
  7648.             int32  y                  = fields[3].GetInt32();
  7649.  
  7650.             if (POIs[questId].size() <= id + 1)
  7651.                 POIs[questId].resize(id + 10);
  7652.  
  7653.             QuestPOIBlobPoint point;
  7654.             point.X = x;
  7655.             point.Y = y;
  7656.  
  7657.             POIs[questId][id].push_back(point);
  7658.         } while (points->NextRow());
  7659.     }
  7660.  
  7661.     do
  7662.     {
  7663.         Field* fields = result->Fetch();
  7664.  
  7665.         uint32 questId            = fields[0].GetUInt32();
  7666.         uint32 id                 = fields[1].GetUInt32();
  7667.         int32 objIndex            = fields[2].GetInt32();
  7668.         uint32 mapId              = fields[3].GetUInt32();
  7669.         uint32 WorldMapAreaId     = fields[4].GetUInt32();
  7670.         uint32 FloorId            = fields[5].GetUInt32();
  7671.         uint32 unk3               = fields[6].GetUInt32();
  7672.         uint32 unk4               = fields[7].GetUInt32();
  7673.  
  7674.         QuestPOIBlobData POI;
  7675.         POI.BlobIndex = id;
  7676.         POI.ObjectiveIndex = objIndex;
  7677.         POI.MapID = mapId;
  7678.         POI.WorldMapAreaID = WorldMapAreaId;
  7679.         POI.Floor = FloorId;
  7680.         POI.Unk3 = unk3;
  7681.         POI.Unk4 = unk4;
  7682.  
  7683.         if (questId < POIs.size() && id < POIs[questId].size())
  7684.         {
  7685.             POI.QuestPOIBlobPointStats = POIs[questId][id];
  7686.  
  7687.             auto itr = _questPOIStore.find(questId);
  7688.             if (itr == _questPOIStore.end())
  7689.             {
  7690.                 QuestPOIWrapper wrapper;
  7691.                 QuestPOIData data;
  7692.                 data.QuestID = questId;
  7693.                 wrapper.POIData = data;
  7694.  
  7695.                 std::tie(itr, std::ignore) = _questPOIStore.emplace(questId, std::move(wrapper));
  7696.             }
  7697.  
  7698.             itr->second.POIData.QuestPOIBlobDataStats.push_back(POI);
  7699.         }
  7700.         else
  7701.             TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest %u POI id %u", questId, id);
  7702.     } while (result->NextRow());
  7703.  
  7704.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " quest POI definitions in %u ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
  7705. }
  7706.  
  7707. void ObjectMgr::LoadNPCSpellClickSpells()
  7708. {
  7709.     uint32 oldMSTime = getMSTime();
  7710.  
  7711.     _spellClickInfoStore.clear();
  7712.     //                                                0          1         2            3
  7713.     QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
  7714.  
  7715.     if (!result)
  7716.     {
  7717.         TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
  7718.         return;
  7719.     }
  7720.  
  7721.     uint32 count = 0;
  7722.  
  7723.     do
  7724.     {
  7725.         Field* fields = result->Fetch();
  7726.  
  7727.         uint32 npc_entry = fields[0].GetUInt32();
  7728.         CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
  7729.         if (!cInfo)
  7730.         {
  7731.             TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
  7732.             continue;
  7733.         }
  7734.  
  7735.         uint32 spellid = fields[1].GetUInt32();
  7736.         SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid);
  7737.         if (!spellinfo)
  7738.         {
  7739.             TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: %u references unknown spellid %u. Skipping entry.", npc_entry, spellid);
  7740.             continue;
  7741.         }
  7742.  
  7743.         uint8 userType = fields[3].GetUInt16();
  7744.         if (userType >= SPELL_CLICK_USER_MAX)
  7745.             TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: %u  references unknown user type %u. Skipping entry.", npc_entry, uint32(userType));
  7746.  
  7747.         uint8 castFlags = fields[2].GetUInt8();
  7748.         SpellClickInfo info;
  7749.         info.spellId = spellid;
  7750.         info.castFlags = castFlags;
  7751.         info.userType = SpellClickUserTypes(userType);
  7752.         _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
  7753.  
  7754.         ++count;
  7755.     }
  7756.     while (result->NextRow());
  7757.  
  7758.     // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
  7759.     // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
  7760.     for (auto& creatureTemplatePair : _creatureTemplateStore)
  7761.     {
  7762.         if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
  7763.         {
  7764.             TC_LOG_ERROR("sql.sql", "npc_spellclick_spells: Creature template %u has UNIT_NPC_FLAG_SPELLCLICK but no data in spellclick table! Removing flag", creatureTemplatePair.first);
  7765.             creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
  7766.         }
  7767.     }
  7768.  
  7769.     TC_LOG_INFO("server.loading", ">> Loaded %u spellclick definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7770. }
  7771.  
  7772. void ObjectMgr::DeleteCreatureData(ObjectGuid::LowType guid)
  7773. {
  7774.     // remove mapid*cellid -> guid_set map
  7775.     CreatureData const* data = GetCreatureData(guid);
  7776.     if (data)
  7777.     {
  7778.         RemoveCreatureFromGrid(guid, data);
  7779.         OnDeleteSpawnData(data);
  7780.     }
  7781.  
  7782.     _creatureDataStore.erase(guid);
  7783. }
  7784.  
  7785. void ObjectMgr::DeleteGameObjectData(ObjectGuid::LowType guid)
  7786. {
  7787.     // remove mapid*cellid -> guid_set map
  7788.     GameObjectData const* data = GetGameObjectData(guid);
  7789.     if (data)
  7790.     {
  7791.         RemoveGameobjectFromGrid(guid, data);
  7792.         OnDeleteSpawnData(data);
  7793.     }
  7794.  
  7795.     _gameObjectDataStore.erase(guid);
  7796. }
  7797.  
  7798. void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go)
  7799. {
  7800.     uint32 oldMSTime = getMSTime();
  7801.  
  7802.     map.clear();                                            // need for reload case
  7803.  
  7804.     uint32 count = 0;
  7805.  
  7806.     QueryResult result = WorldDatabase.PQuery("SELECT id, quest, pool_entry FROM %s qr LEFT JOIN pool_quest pq ON qr.quest = pq.entry", table.c_str());
  7807.  
  7808.     if (!result)
  7809.     {
  7810.         TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str());
  7811.         return;
  7812.     }
  7813.  
  7814.     PooledQuestRelation* poolRelationMap = go ? &sPoolMgr->mQuestGORelation : &sPoolMgr->mQuestCreatureRelation;
  7815.     if (starter)
  7816.         poolRelationMap->clear();
  7817.  
  7818.     do
  7819.     {
  7820.         uint32 id     = result->Fetch()[0].GetUInt32();
  7821.         uint32 quest  = result->Fetch()[1].GetUInt32();
  7822.         uint32 poolId = result->Fetch()[2].GetUInt32();
  7823.  
  7824.         if (!_questTemplates.count(quest))
  7825.         {
  7826.             TC_LOG_ERROR("sql.sql", "Table `%s`: Quest %u listed for entry %u does not exist.", table.c_str(), quest, id);
  7827.             continue;
  7828.         }
  7829.  
  7830.         if (!poolId || !starter)
  7831.         {
  7832.             map.insert(QuestRelations::value_type(id, quest));
  7833.             if (reverseMap)
  7834.                 reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
  7835.         }
  7836.         else
  7837.             poolRelationMap->insert(PooledQuestRelation::value_type(quest, id));
  7838.  
  7839.         ++count;
  7840.     } while (result->NextRow());
  7841.  
  7842.     TC_LOG_INFO("server.loading", ">> Loaded %u quest relations from %s in %u ms", count, table.c_str(), GetMSTimeDiffToNow(oldMSTime));
  7843. }
  7844.  
  7845. void ObjectMgr::LoadGameobjectQuestStarters()
  7846. {
  7847.     LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter", true, true);
  7848.  
  7849.     for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
  7850.     {
  7851.         GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
  7852.         if (!goInfo)
  7853.             TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry (%u) and existed quest %u", itr->first, itr->second);
  7854.         else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
  7855.             TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
  7856.     }
  7857. }
  7858.  
  7859. void ObjectMgr::LoadGameobjectQuestEnders()
  7860. {
  7861.     LoadQuestRelationsHelper(_goQuestInvolvedRelations, &_goQuestInvolvedRelationsReverse, "gameobject_questender", false, true);
  7862.  
  7863.     for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
  7864.     {
  7865.         GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
  7866.         if (!goInfo)
  7867.             TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry (%u) and existed quest %u", itr->first, itr->second);
  7868.         else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
  7869.             TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
  7870.     }
  7871. }
  7872.  
  7873. void ObjectMgr::LoadCreatureQuestStarters()
  7874. {
  7875.     LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter", true, false);
  7876.  
  7877.     for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
  7878.     {
  7879.         CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
  7880.         if (!cInfo)
  7881.             TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry (%u) and existed quest %u", itr->first, itr->second);
  7882.         else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
  7883.             TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
  7884.     }
  7885. }
  7886.  
  7887. void ObjectMgr::LoadCreatureQuestEnders()
  7888. {
  7889.     LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, &_creatureQuestInvolvedRelationsReverse, "creature_questender", false, false);
  7890.  
  7891.     for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
  7892.     {
  7893.         CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
  7894.         if (!cInfo)
  7895.             TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry (%u) and existed quest %u", itr->first, itr->second);
  7896.         else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
  7897.             TC_LOG_ERROR("sql.sql", "Table `creature_questender` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
  7898.     }
  7899. }
  7900.  
  7901. void ObjectMgr::LoadReservedPlayersNames()
  7902. {
  7903.     uint32 oldMSTime = getMSTime();
  7904.  
  7905.     _reservedNamesStore.clear();                                // need for reload case
  7906.  
  7907.     QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
  7908.  
  7909.     if (!result)
  7910.     {
  7911.         TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
  7912.         return;
  7913.     }
  7914.  
  7915.     uint32 count = 0;
  7916.  
  7917.     Field* fields;
  7918.     do
  7919.     {
  7920.         fields = result->Fetch();
  7921.         std::string name= fields[0].GetString();
  7922.  
  7923.         std::wstring wstr;
  7924.         if (!Utf8toWStr (name, wstr))
  7925.         {
  7926.             TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: %s", name.c_str());
  7927.             continue;
  7928.         }
  7929.  
  7930.         wstrToLower(wstr);
  7931.  
  7932.         _reservedNamesStore.insert(wstr);
  7933.         ++count;
  7934.     }
  7935.     while (result->NextRow());
  7936.  
  7937.     TC_LOG_INFO("server.loading", ">> Loaded %u reserved player names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7938. }
  7939.  
  7940. bool ObjectMgr::IsReservedName(const std::string& name) const
  7941. {
  7942.     std::wstring wstr;
  7943.     if (!Utf8toWStr (name, wstr))
  7944.         return false;
  7945.  
  7946.     wstrToLower(wstr);
  7947.  
  7948.     return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
  7949. }
  7950.  
  7951. enum LanguageType
  7952. {
  7953.     LT_BASIC_LATIN    = 0x0000,
  7954.     LT_EXTENDEN_LATIN = 0x0001,
  7955.     LT_CYRILLIC       = 0x0002,
  7956.     LT_EAST_ASIA      = 0x0004,
  7957.     LT_ANY            = 0xFFFF
  7958. };
  7959.  
  7960. static LanguageType GetRealmLanguageType(bool create)
  7961. {
  7962.     switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
  7963.     {
  7964.         case REALM_ZONE_UNKNOWN:                            // any language
  7965.         case REALM_ZONE_DEVELOPMENT:
  7966.         case REALM_ZONE_TEST_SERVER:
  7967.         case REALM_ZONE_QA_SERVER:
  7968.             return LT_ANY;
  7969.         case REALM_ZONE_UNITED_STATES:                      // extended-Latin
  7970.         case REALM_ZONE_OCEANIC:
  7971.         case REALM_ZONE_LATIN_AMERICA:
  7972.         case REALM_ZONE_ENGLISH:
  7973.         case REALM_ZONE_GERMAN:
  7974.         case REALM_ZONE_FRENCH:
  7975.         case REALM_ZONE_SPANISH:
  7976.             return LT_EXTENDEN_LATIN;
  7977.         case REALM_ZONE_KOREA:                              // East-Asian
  7978.         case REALM_ZONE_TAIWAN:
  7979.         case REALM_ZONE_CHINA:
  7980.             return LT_EAST_ASIA;
  7981.         case REALM_ZONE_RUSSIAN:                            // Cyrillic
  7982.             return LT_CYRILLIC;
  7983.         default:
  7984.             return create ? LT_BASIC_LATIN : LT_ANY;        // basic-Latin at create, any at login
  7985.     }
  7986. }
  7987.  
  7988. bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
  7989. {
  7990.     if (strictMask == 0)                                       // any language, ignore realm
  7991.     {
  7992.         if (isExtendedLatinString(wstr, numericOrSpace))
  7993.             return true;
  7994.         if (isCyrillicString(wstr, numericOrSpace))
  7995.             return true;
  7996.         if (isEastAsianString(wstr, numericOrSpace))
  7997.             return true;
  7998.         return false;
  7999.     }
  8000.  
  8001.     if (strictMask & 0x2)                                    // realm zone specific
  8002.     {
  8003.         LanguageType lt = GetRealmLanguageType(create);
  8004.         if (lt & LT_EXTENDEN_LATIN)
  8005.             if (isExtendedLatinString(wstr, numericOrSpace))
  8006.                 return true;
  8007.         if (lt & LT_CYRILLIC)
  8008.             if (isCyrillicString(wstr, numericOrSpace))
  8009.                 return true;
  8010.         if (lt & LT_EAST_ASIA)
  8011.             if (isEastAsianString(wstr, numericOrSpace))
  8012.                 return true;
  8013.     }
  8014.  
  8015.     if (strictMask & 0x1)                                    // basic Latin
  8016.     {
  8017.         if (isBasicLatinString(wstr, numericOrSpace))
  8018.             return true;
  8019.     }
  8020.  
  8021.     return false;
  8022. }
  8023.  
  8024. ResponseCodes ObjectMgr::CheckPlayerName(std::string const& name, LocaleConstant locale, bool create /*= false*/)
  8025. {
  8026.     std::wstring wname;
  8027.     if (!Utf8toWStr(name, wname))
  8028.         return CHAR_NAME_INVALID_CHARACTER;
  8029.  
  8030.     if (wname.size() > MAX_PLAYER_NAME)
  8031.         return CHAR_NAME_TOO_LONG;
  8032.  
  8033.     uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
  8034.     if (wname.size() < minName)
  8035.         return CHAR_NAME_TOO_SHORT;
  8036.  
  8037.     uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
  8038.     if (!isValidString(wname, strictMask, false, create))
  8039.         return CHAR_NAME_MIXED_LANGUAGES;
  8040.  
  8041.     wstrToLower(wname);
  8042.     for (size_t i = 2; i < wname.size(); ++i)
  8043.         if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
  8044.             return CHAR_NAME_THREE_CONSECUTIVE;
  8045.  
  8046.     return ValidateName(wname, locale);
  8047. }
  8048.  
  8049. bool ObjectMgr::IsValidCharterName(const std::string& name)
  8050. {
  8051.     std::wstring wname;
  8052.     if (!Utf8toWStr(name, wname))
  8053.         return false;
  8054.  
  8055.     if (wname.size() > MAX_CHARTER_NAME)
  8056.         return false;
  8057.  
  8058.     uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
  8059.     if (wname.size() < minName)
  8060.         return false;
  8061.  
  8062.     uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
  8063.  
  8064.     return isValidString(wname, strictMask, true);
  8065. }
  8066.  
  8067. PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name, LocaleConstant locale)
  8068. {
  8069.     std::wstring wname;
  8070.     if (!Utf8toWStr(name, wname))
  8071.         return PET_NAME_INVALID;
  8072.  
  8073.     if (wname.size() > MAX_PET_NAME)
  8074.         return PET_NAME_TOO_LONG;
  8075.  
  8076.     uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
  8077.     if (wname.size() < minName)
  8078.         return PET_NAME_TOO_SHORT;
  8079.  
  8080.     uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
  8081.     if (!isValidString(wname, strictMask, false))
  8082.         return PET_NAME_MIXED_LANGUAGES;
  8083.  
  8084.     switch (ValidateName(wname, locale))
  8085.     {
  8086.         case CHAR_NAME_PROFANE:
  8087.             return PET_NAME_PROFANE;
  8088.         case CHAR_NAME_RESERVED:
  8089.             return PET_NAME_RESERVED;
  8090.         case RESPONSE_FAILURE:
  8091.             return PET_NAME_INVALID;
  8092.         default:
  8093.             break;
  8094.     }
  8095.     return PET_NAME_SUCCESS;
  8096. }
  8097.  
  8098. void ObjectMgr::LoadGameObjectForQuests()
  8099. {
  8100.     uint32 oldMSTime = getMSTime();
  8101.  
  8102.     _gameObjectForQuestStore.clear();                         // need for reload case
  8103.  
  8104.     if (_gameObjectTemplateStore.empty())
  8105.     {
  8106.         TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
  8107.         return;
  8108.     }
  8109.  
  8110.     uint32 count = 0;
  8111.  
  8112.     // collect GO entries for GO that must activated
  8113.     for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
  8114.     {
  8115.         switch (gameObjectTemplatePair.second.type)
  8116.         {
  8117.             case GAMEOBJECT_TYPE_QUESTGIVER:
  8118.                 break;
  8119.             case GAMEOBJECT_TYPE_CHEST:
  8120.             {
  8121.                 // scan GO chest with loot including quest items
  8122.                 uint32 lootId = gameObjectTemplatePair.second.GetLootId();
  8123.                 // find quest loot for GO
  8124.                 if (gameObjectTemplatePair.second.chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(lootId))
  8125.                     break;
  8126.                 continue;
  8127.             }
  8128.             case GAMEOBJECT_TYPE_GENERIC:
  8129.             {
  8130.                 if (gameObjectTemplatePair.second._generic.questID > 0)            //quests objects
  8131.                     break;
  8132.                 continue;
  8133.             }
  8134.             case GAMEOBJECT_TYPE_GOOBER:
  8135.             {
  8136.                 if (gameObjectTemplatePair.second.goober.questId > 0)              //quests objects
  8137.                     break;
  8138.                 continue;
  8139.             }
  8140.             default:
  8141.                 continue;
  8142.         }
  8143.  
  8144.         _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
  8145.         ++count;
  8146.     }
  8147.  
  8148.     TC_LOG_INFO("server.loading", ">> Loaded %u GameObjects for quests in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8149. }
  8150.  
  8151. bool ObjectMgr::LoadTrinityStrings()
  8152. {
  8153.     uint32 oldMSTime = getMSTime();
  8154.  
  8155.     _trinityStringStore.clear(); // for reload case
  8156.  
  8157.     QueryResult result = WorldDatabase.Query("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM trinity_string");
  8158.     if (!result)
  8159.     {
  8160.         TC_LOG_INFO("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum.");
  8161.         return false;
  8162.     }
  8163.  
  8164.     do
  8165.     {
  8166.         Field* fields = result->Fetch();
  8167.  
  8168.         uint32 entry = fields[0].GetUInt32();
  8169.  
  8170.         TrinityString& data = _trinityStringStore[entry];
  8171.  
  8172.         data.Content.resize(DEFAULT_LOCALE + 1);
  8173.  
  8174.         for (int8 i = TOTAL_LOCALES - 1; i >= 0; --i)
  8175.             AddLocaleString(fields[i + 1].GetString(), LocaleConstant(i), data.Content);
  8176.     }
  8177.     while (result->NextRow());
  8178.  
  8179.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " trinity strings in %u ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
  8180.     return true;
  8181. }
  8182.  
  8183. char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
  8184. {
  8185.     if (TrinityString const* ts = GetTrinityString(entry))
  8186.     {
  8187.         if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
  8188.             return ts->Content[locale].c_str();
  8189.         return ts->Content[DEFAULT_LOCALE].c_str();
  8190.     }
  8191.  
  8192.     TC_LOG_ERROR("sql.sql", "Trinity string entry %u not found in DB.", entry);
  8193.     return "<error>";
  8194. }
  8195.  
  8196. void ObjectMgr::LoadFishingBaseSkillLevel()
  8197. {
  8198.     uint32 oldMSTime = getMSTime();
  8199.  
  8200.     _fishingBaseForAreaStore.clear();                            // for reload case
  8201.  
  8202.     QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
  8203.  
  8204.     if (!result)
  8205.     {
  8206.         TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
  8207.         return;
  8208.     }
  8209.  
  8210.     uint32 count = 0;
  8211.  
  8212.     do
  8213.     {
  8214.         Field* fields = result->Fetch();
  8215.         uint32 entry  = fields[0].GetUInt32();
  8216.         int32 skill   = fields[1].GetInt16();
  8217.  
  8218.         AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
  8219.         if (!fArea)
  8220.         {
  8221.             TC_LOG_ERROR("sql.sql", "AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
  8222.             continue;
  8223.         }
  8224.  
  8225.         _fishingBaseForAreaStore[entry] = skill;
  8226.         ++count;
  8227.     }
  8228.     while (result->NextRow());
  8229.  
  8230.     TC_LOG_INFO("server.loading", ">> Loaded %u areas for fishing base skill level in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8231. }
  8232.  
  8233. bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
  8234. {
  8235.     // get main part of the name
  8236.     std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
  8237.     // prepare flags
  8238.     bool x = true;
  8239.     bool y = true;
  8240.  
  8241.     // check declined names
  8242.     for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
  8243.     {
  8244.         std::wstring wname;
  8245.         if (!Utf8toWStr(names.name[i], wname))
  8246.             return false;
  8247.  
  8248.         if (mainpart != GetMainPartOfName(wname, i+1))
  8249.             x = false;
  8250.  
  8251.         if (w_ownname != wname)
  8252.             y = false;
  8253.     }
  8254.     return (x || y);
  8255. }
  8256.  
  8257. uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id) const
  8258. {
  8259.     AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
  8260.     if (i!= _areaTriggerScriptStore.end())
  8261.         return i->second;
  8262.     return 0;
  8263. }
  8264.  
  8265. SpellScriptsBounds ObjectMgr::GetSpellScriptsBounds(uint32 spellId)
  8266. {
  8267.     return _spellScriptsStore.equal_range(spellId);
  8268. }
  8269.  
  8270. // this allows calculating base reputations to offline players, just by race and class
  8271. int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
  8272. {
  8273.     if (!factionEntry)
  8274.         return 0;
  8275.  
  8276.     uint32 raceMask = (1 << (race - 1));
  8277.     uint32 classMask = (1 << (playerClass-1));
  8278.  
  8279.     for (uint8 i = 0; i < 4; ++i)
  8280.     {
  8281.         if ((!factionEntry->BaseRepClassMask[i] ||
  8282.             factionEntry->BaseRepClassMask[i] & classMask) &&
  8283.             (!factionEntry->BaseRepRaceMask[i] ||
  8284.             factionEntry->BaseRepRaceMask[i] & raceMask))
  8285.             return factionEntry->BaseRepValue[i];
  8286.     }
  8287.  
  8288.     return 0;
  8289. }
  8290.  
  8291. SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
  8292. {
  8293.     SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillId);
  8294.     if (!skill)
  8295.         return SKILL_RANGE_NONE;
  8296.  
  8297.     if (sSkillTiersStore.LookupEntry(rcEntry->SkillTier))
  8298.         return SKILL_RANGE_RANK;
  8299.  
  8300.     if (rcEntry->SkillId == SKILL_RUNEFORGING)
  8301.         return SKILL_RANGE_MONO;
  8302.  
  8303.     switch (skill->categoryId)
  8304.     {
  8305.         case SKILL_CATEGORY_ARMOR:
  8306.             return SKILL_RANGE_MONO;
  8307.         case SKILL_CATEGORY_LANGUAGES:
  8308.             return SKILL_RANGE_LANGUAGE;
  8309.     }
  8310.  
  8311.     return SKILL_RANGE_LEVEL;
  8312. }
  8313.  
  8314. void ObjectMgr::LoadGameTele()
  8315. {
  8316.     uint32 oldMSTime = getMSTime();
  8317.  
  8318.     _gameTeleStore.clear();                                  // for reload case
  8319.  
  8320.     //                                                0       1           2           3           4        5     6
  8321.     QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
  8322.  
  8323.     if (!result)
  8324.     {
  8325.         TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
  8326.         return;
  8327.     }
  8328.  
  8329.     uint32 count = 0;
  8330.  
  8331.     do
  8332.     {
  8333.         Field* fields = result->Fetch();
  8334.  
  8335.         uint32 id         = fields[0].GetUInt32();
  8336.  
  8337.         GameTele gt;
  8338.  
  8339.         gt.position_x     = fields[1].GetFloat();
  8340.         gt.position_y     = fields[2].GetFloat();
  8341.         gt.position_z     = fields[3].GetFloat();
  8342.         gt.orientation    = fields[4].GetFloat();
  8343.         gt.mapId          = fields[5].GetUInt16();
  8344.         gt.name           = fields[6].GetString();
  8345.  
  8346.         if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
  8347.         {
  8348.             TC_LOG_ERROR("sql.sql", "Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
  8349.             continue;
  8350.         }
  8351.  
  8352.         if (!Utf8toWStr(gt.name, gt.wnameLow))
  8353.         {
  8354.             TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
  8355.             continue;
  8356.         }
  8357.  
  8358.         wstrToLower(gt.wnameLow);
  8359.  
  8360.         _gameTeleStore[id] = gt;
  8361.  
  8362.         ++count;
  8363.     }
  8364.     while (result->NextRow());
  8365.  
  8366.     TC_LOG_INFO("server.loading", ">> Loaded %u GameTeleports in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8367. }
  8368.  
  8369. GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
  8370. {
  8371.     // explicit name case
  8372.     std::wstring wname;
  8373.     if (!Utf8toWStr(name, wname))
  8374.         return nullptr;
  8375.  
  8376.     // converting string that we try to find to lower case
  8377.     wstrToLower(wname);
  8378.  
  8379.     // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
  8380.     GameTele const* alt = nullptr;
  8381.     for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  8382.     {
  8383.         if (itr->second.wnameLow == wname)
  8384.             return &itr->second;
  8385.         else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
  8386.             alt = &itr->second;
  8387.     }
  8388.  
  8389.     return alt;
  8390. }
  8391.  
  8392. GameTele const* ObjectMgr::GetGameTeleExactName(const std::string& name) const
  8393. {
  8394.     // explicit name case
  8395.     std::wstring wname;
  8396.     if (!Utf8toWStr(name, wname))
  8397.         return nullptr;
  8398.  
  8399.     // converting string that we try to find to lower case
  8400.     wstrToLower(wname);
  8401.  
  8402.     for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  8403.     {
  8404.         if (itr->second.wnameLow == wname)
  8405.             return &itr->second;
  8406.     }
  8407.  
  8408.     return nullptr;
  8409. }
  8410.  
  8411. bool ObjectMgr::AddGameTele(GameTele& tele)
  8412. {
  8413.     // find max id
  8414.     uint32 new_id = 0;
  8415.     for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  8416.         if (itr->first > new_id)
  8417.             new_id = itr->first;
  8418.  
  8419.     // use next
  8420.     ++new_id;
  8421.  
  8422.     if (!Utf8toWStr(tele.name, tele.wnameLow))
  8423.         return false;
  8424.  
  8425.     wstrToLower(tele.wnameLow);
  8426.  
  8427.     _gameTeleStore[new_id] = tele;
  8428.  
  8429.     PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE);
  8430.  
  8431.     stmt->setUInt32(0, new_id);
  8432.     stmt->setFloat(1, tele.position_x);
  8433.     stmt->setFloat(2, tele.position_y);
  8434.     stmt->setFloat(3, tele.position_z);
  8435.     stmt->setFloat(4, tele.orientation);
  8436.     stmt->setUInt16(5, uint16(tele.mapId));
  8437.     stmt->setString(6, tele.name);
  8438.  
  8439.     WorldDatabase.Execute(stmt);
  8440.  
  8441.     return true;
  8442. }
  8443.  
  8444. bool ObjectMgr::DeleteGameTele(const std::string& name)
  8445. {
  8446.     // explicit name case
  8447.     std::wstring wname;
  8448.     if (!Utf8toWStr(name, wname))
  8449.         return false;
  8450.  
  8451.     // converting string that we try to find to lower case
  8452.     wstrToLower(wname);
  8453.  
  8454.     for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  8455.     {
  8456.         if (itr->second.wnameLow == wname)
  8457.         {
  8458.             PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE);
  8459.  
  8460.             stmt->setString(0, itr->second.name);
  8461.  
  8462.             WorldDatabase.Execute(stmt);
  8463.  
  8464.             _gameTeleStore.erase(itr);
  8465.             return true;
  8466.         }
  8467.     }
  8468.  
  8469.     return false;
  8470. }
  8471.  
  8472. void ObjectMgr::LoadMailLevelRewards()
  8473. {
  8474.     uint32 oldMSTime = getMSTime();
  8475.  
  8476.     _mailLevelRewardStore.clear();                           // for reload case
  8477.  
  8478.     //                                                 0        1             2            3
  8479.     QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
  8480.  
  8481.     if (!result)
  8482.     {
  8483.         TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
  8484.         return;
  8485.     }
  8486.  
  8487.     uint32 count = 0;
  8488.  
  8489.     do
  8490.     {
  8491.         Field* fields = result->Fetch();
  8492.  
  8493.         uint8 level           = fields[0].GetUInt8();
  8494.         uint32 raceMask       = fields[1].GetUInt32();
  8495.         uint32 mailTemplateId = fields[2].GetUInt32();
  8496.         uint32 senderEntry    = fields[3].GetUInt32();
  8497.  
  8498.         if (level > MAX_LEVEL)
  8499.         {
  8500.             TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level %u that more supported by client (%u), ignoring.", level, MAX_LEVEL);
  8501.             continue;
  8502.         }
  8503.  
  8504.         if (!(raceMask & RACEMASK_ALL_PLAYABLE))
  8505.         {
  8506.             TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask (%u) for level %u that not include any player races, ignoring.", raceMask, level);
  8507.             continue;
  8508.         }
  8509.  
  8510.         if (!sMailTemplateStore.LookupEntry(mailTemplateId))
  8511.         {
  8512.             TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.", mailTemplateId, level);
  8513.             continue;
  8514.         }
  8515.  
  8516.         if (!GetCreatureTemplate(senderEntry))
  8517.         {
  8518.             TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has nonexistent sender creature entry (%u) for level %u that invalid not include any player races, ignoring.", senderEntry, level);
  8519.             continue;
  8520.         }
  8521.  
  8522.         _mailLevelRewardStore[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
  8523.  
  8524.         ++count;
  8525.     }
  8526.     while (result->NextRow());
  8527.  
  8528.     TC_LOG_INFO("server.loading", ">> Loaded %u level dependent mail rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8529. }
  8530.  
  8531. void ObjectMgr::AddSpellToTrainer(uint32 ID, uint32 SpellID, uint32 MoneyCost, uint32 ReqSkillLine, uint32 ReqSkillRank, uint32 ReqLevel)
  8532. {
  8533.     if (ID >= TRINITY_TRAINER_START_REF)
  8534.         return;
  8535.  
  8536.     CreatureTemplate const* cInfo = GetCreatureTemplate(ID);
  8537.     if (!cInfo)
  8538.     {
  8539.         TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains entries for a non-existing creature template (ID: %u), ignoring", ID);
  8540.         return;
  8541.     }
  8542.  
  8543.     if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
  8544.     {
  8545.         TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains entries for a creature template (ID: %u) without trainer flag, ignoring", ID);
  8546.         return;
  8547.     }
  8548.  
  8549.     SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(SpellID);
  8550.     if (!spellinfo)
  8551.     {
  8552.         TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an ID (%u) for a non-existing SpellID (%u), ignoring", ID, SpellID);
  8553.         return;
  8554.     }
  8555.  
  8556.     if (!SpellMgr::IsSpellValid(spellinfo))
  8557.     {
  8558.         TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an ID (%u) for a broken SpellID (%u), ignoring", ID, SpellID);
  8559.         return;
  8560.     }
  8561.  
  8562.     if (GetTalentSpellCost(SpellID))
  8563.     {
  8564.         TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an ID (%u) for a non-existing SpellID (%u) which is a talent, ignoring", ID, SpellID);
  8565.         return;
  8566.     }
  8567.  
  8568.     TrainerSpellData& data = _cacheTrainerSpellStore[ID];
  8569.  
  8570.     TrainerSpell& trainerSpell = data.spellList[SpellID];
  8571.     trainerSpell.SpellID       = SpellID;
  8572.     trainerSpell.MoneyCost     = MoneyCost;
  8573.     trainerSpell.ReqSkillLine  = ReqSkillLine;
  8574.     trainerSpell.ReqSkillRank = ReqSkillRank;
  8575.     trainerSpell.ReqLevel      = ReqLevel;
  8576.  
  8577.     if (!trainerSpell.ReqLevel)
  8578.         trainerSpell.ReqLevel = spellinfo->SpellLevel;
  8579.  
  8580.     // calculate learned spell for profession case when stored cast-spell
  8581.     trainerSpell.ReqAbility[0] = SpellID;
  8582.     for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  8583.     {
  8584.         if (spellinfo->Effects[i].Effect != SPELL_EFFECT_LEARN_SPELL)
  8585.             continue;
  8586.         if (trainerSpell.ReqAbility[0] == SpellID)
  8587.             trainerSpell.ReqAbility[0] = 0;
  8588.         // player must be able to cast spell on himself
  8589.         if (spellinfo->Effects[i].TargetA.GetTarget() != 0 && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY
  8590.             && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ANY && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_CASTER)
  8591.         {
  8592.             TC_LOG_ERROR("sql.sql", "Table `npc_trainer` has spell %u for trainer entry %u with learn effect which has incorrect target type, ignoring learn effect!", SpellID, ID);
  8593.             continue;
  8594.         }
  8595.  
  8596.         trainerSpell.ReqAbility[i] = spellinfo->Effects[i].TriggerSpell;
  8597.  
  8598.         if (trainerSpell.ReqAbility[i])
  8599.         {
  8600.             SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(trainerSpell.ReqAbility[i]);
  8601.             if (learnedSpellInfo && learnedSpellInfo->IsProfession())
  8602.                 data.trainerType = 2;
  8603.         }
  8604.     }
  8605.  
  8606.     return;
  8607. }
  8608.  
  8609. void ObjectMgr::LoadTrainerSpell()
  8610. {
  8611.     uint32 oldMSTime = getMSTime();
  8612.  
  8613.     // For reload case
  8614.     _cacheTrainerSpellStore.clear();
  8615.  
  8616.     QueryResult result = WorldDatabase.Query("SELECT b.ID, a.SpellID, a.MoneyCost, a.ReqSkillLine, a.ReqSkillRank, a.ReqLevel FROM npc_trainer AS a "
  8617.                                              "INNER JOIN npc_trainer AS b ON a.ID = -(b.SpellID) "
  8618.                                              "UNION SELECT * FROM npc_trainer WHERE SpellID > 0");
  8619.  
  8620.     if (!result)
  8621.     {
  8622.         TC_LOG_ERROR("sql.sql", ">>  Loaded 0 Trainers. DB table `npc_trainer` is empty!");
  8623.  
  8624.         return;
  8625.     }
  8626.  
  8627.     uint32 count = 0;
  8628.  
  8629.     do
  8630.     {
  8631.         Field* fields = result->Fetch();
  8632.  
  8633.         uint32 ID           = fields[0].GetUInt32();
  8634.         uint32 SpellID      = fields[1].GetUInt32();
  8635.         uint32 MoneyCost    = fields[2].GetUInt32();
  8636.         uint32 ReqSkillLine = fields[3].GetUInt16();
  8637.         uint32 ReqSkillRank = fields[4].GetUInt16();
  8638.         uint32 ReqLevel     = fields[5].GetUInt8();
  8639.  
  8640.         AddSpellToTrainer(ID, SpellID, MoneyCost, ReqSkillLine, ReqSkillRank, ReqLevel);
  8641.  
  8642.         ++count;
  8643.     }
  8644.     while (result->NextRow());
  8645.  
  8646.     TC_LOG_INFO("server.loading", ">> Loaded %d Trainers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8647. }
  8648.  
  8649. uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
  8650. {
  8651.     // find all items from the reference vendor
  8652.     PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
  8653.     stmt->setUInt32(0, uint32(item));
  8654.     PreparedQueryResult result = WorldDatabase.Query(stmt);
  8655.  
  8656.     if (!result)
  8657.         return 0;
  8658.  
  8659.     uint32 count = 0;
  8660.     do
  8661.     {
  8662.         Field* fields = result->Fetch();
  8663.  
  8664.         int32 item_id = fields[0].GetInt32();
  8665.  
  8666.         // if item is a negative, its a reference
  8667.         if (item_id < 0)
  8668.             count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
  8669.         else
  8670.         {
  8671.             int32  maxcount     = fields[1].GetUInt8();
  8672.             uint32 incrtime     = fields[2].GetUInt32();
  8673.             uint32 ExtendedCost = fields[3].GetUInt32();
  8674.  
  8675.             if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, nullptr, skip_vendors))
  8676.                 continue;
  8677.  
  8678.             VendorItemData& vList = _cacheVendorItemStore[vendor];
  8679.  
  8680.             vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
  8681.             ++count;
  8682.         }
  8683.     } while (result->NextRow());
  8684.  
  8685.     return count;
  8686. }
  8687.  
  8688. void ObjectMgr::LoadVendors()
  8689. {
  8690.     uint32 oldMSTime = getMSTime();
  8691.  
  8692.     // For reload case
  8693.     for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
  8694.         itr->second.Clear();
  8695.     _cacheVendorItemStore.clear();
  8696.  
  8697.     std::set<uint32> skip_vendors;
  8698.  
  8699.     QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor ORDER BY entry, slot ASC");
  8700.     if (!result)
  8701.     {
  8702.  
  8703.         TC_LOG_ERROR("sql.sql", ">>  Loaded 0 Vendors. DB table `npc_vendor` is empty!");
  8704.         return;
  8705.     }
  8706.  
  8707.     uint32 count = 0;
  8708.  
  8709.     do
  8710.     {
  8711.         Field* fields = result->Fetch();
  8712.  
  8713.         uint32 entry        = fields[0].GetUInt32();
  8714.         int32 item_id      = fields[1].GetInt32();
  8715.  
  8716.         // if item is a negative, its a reference
  8717.         if (item_id < 0)
  8718.             count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
  8719.         else
  8720.         {
  8721.             uint32 maxcount     = fields[2].GetUInt8();
  8722.             uint32 incrtime     = fields[3].GetUInt32();
  8723.             uint32 ExtendedCost = fields[4].GetUInt32();
  8724.  
  8725.             if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, nullptr, &skip_vendors))
  8726.                 continue;
  8727.  
  8728.             VendorItemData& vList = _cacheVendorItemStore[entry];
  8729.  
  8730.             vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
  8731.             ++count;
  8732.         }
  8733.     }
  8734.     while (result->NextRow());
  8735.  
  8736.     TC_LOG_INFO("server.loading", ">> Loaded %d Vendors in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  8737. }
  8738.  
  8739. void ObjectMgr::LoadGossipMenu()
  8740. {
  8741.     uint32 oldMSTime = getMSTime();
  8742.  
  8743.     _gossipMenusStore.clear();
  8744.  
  8745.     //                                               0       1
  8746.     QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
  8747.  
  8748.     if (!result)
  8749.     {
  8750.         TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
  8751.         return;
  8752.     }
  8753.  
  8754.     do
  8755.     {
  8756.         Field* fields = result->Fetch();
  8757.  
  8758.         GossipMenus gMenu;
  8759.  
  8760.         gMenu.MenuID = fields[0].GetUInt16();
  8761.         gMenu.TextID = fields[1].GetUInt32();
  8762.  
  8763.         if (!GetGossipText(gMenu.TextID))
  8764.         {
  8765.             TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID %u is using non-existing TextID %u", gMenu.MenuID, gMenu.TextID);
  8766.             continue;
  8767.         }
  8768.  
  8769.         _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
  8770.     } while (result->NextRow());
  8771.  
  8772.     TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu IDs in %u ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
  8773. }
  8774.  
  8775. void ObjectMgr::LoadGossipMenuItems()
  8776. {
  8777.     uint32 oldMSTime = getMSTime();
  8778.  
  8779.     _gossipMenuItemsStore.clear();
  8780.  
  8781.     QueryResult result = WorldDatabase.Query(
  8782.         //      0       1       2           3           4                      5           6              7             8            9         10        11       12
  8783.         "SELECT MenuID, OptionID, OptionIcon, OptionText, OptionBroadcastTextID, OptionType, OptionNpcFlag, ActionMenuID, ActionPoiID, BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID "
  8784.         "FROM gossip_menu_option ORDER BY MenuID, OptionID");
  8785.  
  8786.     if (!result)
  8787.     {
  8788.         TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
  8789.         return;
  8790.     }
  8791.  
  8792.     do
  8793.     {
  8794.         Field* fields = result->Fetch();
  8795.  
  8796.         GossipMenuItems gMenuItem;
  8797.  
  8798.         gMenuItem.MenuID                = fields[0].GetUInt16();
  8799.         gMenuItem.OptionID              = fields[1].GetUInt16();
  8800.         gMenuItem.OptionIcon            = fields[2].GetUInt32();
  8801.         gMenuItem.OptionText            = fields[3].GetString();
  8802.         gMenuItem.OptionBroadcastTextID = fields[4].GetUInt32();
  8803.         gMenuItem.OptionType            = fields[5].GetUInt8();
  8804.         gMenuItem.OptionNpcFlag         = fields[6].GetUInt32();
  8805.         gMenuItem.ActionMenuID          = fields[7].GetUInt32();
  8806.         gMenuItem.ActionPoiID           = fields[8].GetUInt32();
  8807.         gMenuItem.BoxCoded              = fields[9].GetBool();
  8808.         gMenuItem.BoxMoney              = fields[10].GetUInt32();
  8809.         gMenuItem.BoxText               = fields[11].GetString();
  8810.         gMenuItem.BoxBroadcastTextID    = fields[12].GetUInt32();
  8811.  
  8812.         if (gMenuItem.OptionIcon >= GOSSIP_ICON_MAX)
  8813.         {
  8814.             TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionIcon);
  8815.             gMenuItem.OptionIcon = GOSSIP_ICON_CHAT;
  8816.         }
  8817.  
  8818.         if (gMenuItem.OptionBroadcastTextID)
  8819.         {
  8820.             if (!GetBroadcastText(gMenuItem.OptionBroadcastTextID))
  8821.             {
  8822.                 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u has non-existing or incompatible OptionBroadcastTextID %u, ignoring.", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionBroadcastTextID);
  8823.                 gMenuItem.OptionBroadcastTextID = 0;
  8824.             }
  8825.         }
  8826.  
  8827.         if (gMenuItem.OptionType >= GOSSIP_OPTION_MAX)
  8828.             TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionType);
  8829.  
  8830.         if (gMenuItem.ActionPoiID && !GetPointOfInterest(gMenuItem.ActionPoiID))
  8831.         {
  8832.             TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u use non-existing ActionPoiID %u, ignoring", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.ActionPoiID);
  8833.             gMenuItem.ActionPoiID = 0;
  8834.         }
  8835.  
  8836.         if (gMenuItem.BoxBroadcastTextID)
  8837.         {
  8838.             if (!GetBroadcastText(gMenuItem.BoxBroadcastTextID))
  8839.             {
  8840.                 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u has non-existing or incompatible BoxBroadcastTextID %u, ignoring.", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.BoxBroadcastTextID);
  8841.                 gMenuItem.BoxBroadcastTextID = 0;
  8842.             }
  8843.         }
  8844.  
  8845.         _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
  8846.     } while (result->NextRow());
  8847.  
  8848.     TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu_option entries in %u ms", uint32(_gossipMenuItemsStore.size()), GetMSTimeDiffToNow(oldMSTime));
  8849. }
  8850.  
  8851. void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, bool persist /*= true*/)
  8852. {
  8853.     VendorItemData& vList = _cacheVendorItemStore[entry];
  8854.     vList.AddItem(item, maxcount, incrtime, extendedCost);
  8855.  
  8856.     if (persist)
  8857.     {
  8858.         PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
  8859.  
  8860.         stmt->setUInt32(0, entry);
  8861.         stmt->setUInt32(1, item);
  8862.         stmt->setUInt8(2, maxcount);
  8863.         stmt->setUInt32(3, incrtime);
  8864.         stmt->setUInt32(4, extendedCost);
  8865.  
  8866.         WorldDatabase.Execute(stmt);
  8867.     }
  8868. }
  8869.  
  8870. bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= true*/)
  8871. {
  8872.     CacheVendorItemContainer::iterator  iter = _cacheVendorItemStore.find(entry);
  8873.     if (iter == _cacheVendorItemStore.end())
  8874.         return false;
  8875.  
  8876.     if (!iter->second.RemoveItem(item))
  8877.         return false;
  8878.  
  8879.     if (persist)
  8880.     {
  8881.         PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR);
  8882.  
  8883.         stmt->setUInt32(0, entry);
  8884.         stmt->setUInt32(1, item);
  8885.  
  8886.         WorldDatabase.Execute(stmt);
  8887.     }
  8888.  
  8889.     return true;
  8890. }
  8891.  
  8892. bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
  8893. {
  8894.     CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
  8895.     if (!cInfo)
  8896.     {
  8897.         if (player)
  8898.             ChatHandler(player->GetSession()).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
  8899.         else
  8900.             TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: %u), ignore", vendor_entry);
  8901.         return false;
  8902.     }
  8903.  
  8904.     if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
  8905.     {
  8906.         if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
  8907.         {
  8908.             if (player)
  8909.                 ChatHandler(player->GetSession()).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
  8910.             else
  8911.                 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
  8912.  
  8913.             if (skip_vendors)
  8914.                 skip_vendors->insert(vendor_entry);
  8915.         }
  8916.         return false;
  8917.     }
  8918.  
  8919.     if (!sObjectMgr->GetItemTemplate(item_id))
  8920.     {
  8921.         if (player)
  8922.             ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
  8923.         else
  8924.             TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore", vendor_entry, item_id);
  8925.         return false;
  8926.     }
  8927.  
  8928.     if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
  8929.     {
  8930.         if (player)
  8931.             ChatHandler(player->GetSession()).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
  8932.         else
  8933.             TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", item_id, ExtendedCost, vendor_entry);
  8934.         return false;
  8935.     }
  8936.  
  8937.     if (maxcount > 0 && incrtime == 0)
  8938.     {
  8939.         if (player)
  8940.             ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
  8941.         else
  8942.             TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
  8943.         return false;
  8944.     }
  8945.     else if (maxcount == 0 && incrtime > 0)
  8946.     {
  8947.         if (player)
  8948.             ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
  8949.         else
  8950.             TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
  8951.         return false;
  8952.     }
  8953.  
  8954.     VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
  8955.     if (!vItems)
  8956.         return true;                                        // later checks for non-empty lists
  8957.  
  8958.     if (vItems->FindItemCostPair(item_id, ExtendedCost))
  8959.     {
  8960.         if (player)
  8961.             ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, ExtendedCost);
  8962.         else
  8963.             TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items %u (with extended cost %u) for vendor (Entry: %u), ignoring", item_id, ExtendedCost, vendor_entry);
  8964.         return false;
  8965.     }
  8966.  
  8967.     return true;
  8968. }
  8969.  
  8970. void ObjectMgr::LoadScriptNames()
  8971. {
  8972.     uint32 oldMSTime = getMSTime();
  8973.  
  8974.     // We insert an empty placeholder here so we can use the
  8975.     // script id 0 as dummy for "no script found".
  8976.     _scriptNamesStore.emplace_back("");
  8977.  
  8978.     QueryResult result = WorldDatabase.Query(
  8979.         "SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
  8980.         "UNION "
  8981.         "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
  8982.         "UNION "
  8983.         "SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' "
  8984.         "UNION "
  8985.         "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
  8986.         "UNION "
  8987.         "SELECT DISTINCT(ScriptName) FROM gameobject WHERE ScriptName <> '' "
  8988.         "UNION "
  8989.         "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
  8990.         "UNION "
  8991.         "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' "
  8992.         "UNION "
  8993.         "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
  8994.         "UNION "
  8995.         "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
  8996.         "UNION "
  8997.         "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
  8998.         "UNION "
  8999.         "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
  9000.         "UNION "
  9001.         "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
  9002.         "UNION "
  9003.         "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
  9004.         "UNION "
  9005.         "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
  9006.  
  9007.     if (!result)
  9008.     {
  9009.         TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!");
  9010.         return;
  9011.     }
  9012.  
  9013.     _scriptNamesStore.reserve(result->GetRowCount() + 1);
  9014.  
  9015.     do
  9016.     {
  9017.         _scriptNamesStore.push_back((*result)[0].GetString());
  9018.     }
  9019.     while (result->NextRow());
  9020.  
  9021.     std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
  9022.  
  9023.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
  9024. }
  9025.  
  9026. ObjectMgr::ScriptNameContainer const& ObjectMgr::GetAllScriptNames() const
  9027. {
  9028.     return _scriptNamesStore;
  9029. }
  9030.  
  9031. std::string const& ObjectMgr::GetScriptName(uint32 id) const
  9032. {
  9033.     static std::string const empty = "";
  9034.     return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
  9035. }
  9036.  
  9037.  
  9038. uint32 ObjectMgr::GetScriptId(std::string const& name)
  9039. {
  9040.     // use binary search to find the script name in the sorted vector
  9041.     // assume "" is the first element
  9042.     if (name.empty())
  9043.         return 0;
  9044.  
  9045.     ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name);
  9046.     if (itr == _scriptNamesStore.end() || *itr != name)
  9047.         return 0;
  9048.  
  9049.     return uint32(itr - _scriptNamesStore.begin());
  9050. }
  9051.  
  9052. void ObjectMgr::LoadBroadcastTexts()
  9053. {
  9054.     uint32 oldMSTime = getMSTime();
  9055.  
  9056.     _broadcastTextStore.clear(); // for reload case
  9057.  
  9058.     //                                               0   1         2         3           4         5         6         7            8            9            10       11    12
  9059.     QueryResult result = WorldDatabase.Query("SELECT ID, Language, MaleText, FemaleText, EmoteID0, EmoteID1, EmoteID2, EmoteDelay0, EmoteDelay1, EmoteDelay2, SoundId, Unk1, Unk2 FROM broadcast_text");
  9060.     if (!result)
  9061.     {
  9062.         TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast texts. DB table `broadcast_text` is empty.");
  9063.         return;
  9064.     }
  9065.  
  9066.     _broadcastTextStore.rehash(result->GetRowCount());
  9067.  
  9068.     do
  9069.     {
  9070.         Field* fields = result->Fetch();
  9071.  
  9072.         BroadcastText bct;
  9073.  
  9074.         bct.Id = fields[0].GetUInt32();
  9075.         bct.Language = fields[1].GetUInt32();
  9076.         bct.MaleText[DEFAULT_LOCALE] = fields[2].GetString();
  9077.         bct.FemaleText[DEFAULT_LOCALE] = fields[3].GetString();
  9078.         bct.EmoteId0 = fields[4].GetUInt32();
  9079.         bct.EmoteId1 = fields[5].GetUInt32();
  9080.         bct.EmoteId2 = fields[6].GetUInt32();
  9081.         bct.EmoteDelay0 = fields[7].GetUInt32();
  9082.         bct.EmoteDelay1 = fields[8].GetUInt32();
  9083.         bct.EmoteDelay2 = fields[9].GetUInt32();
  9084.         bct.SoundId = fields[10].GetUInt32();
  9085.         bct.Unk1 = fields[11].GetUInt32();
  9086.         bct.Unk2 = fields[12].GetUInt32();
  9087.  
  9088.         if (bct.SoundId)
  9089.         {
  9090.             if (!sSoundEntriesStore.LookupEntry(bct.SoundId))
  9091.             {
  9092.                 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has SoundId %u but sound does not exist.", bct.Id, bct.SoundId);
  9093.                 bct.SoundId = 0;
  9094.             }
  9095.         }
  9096.  
  9097.         if (!GetLanguageDescByID(bct.Language))
  9098.         {
  9099.             TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` using Language %u but Language does not exist.", bct.Id, bct.Language);
  9100.             bct.Language = LANG_UNIVERSAL;
  9101.         }
  9102.  
  9103.         if (bct.EmoteId0)
  9104.         {
  9105.             if (!sEmotesStore.LookupEntry(bct.EmoteId0))
  9106.             {
  9107.                 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId0 %u but emote does not exist.", bct.Id, bct.EmoteId0);
  9108.                 bct.EmoteId0 = 0;
  9109.             }
  9110.         }
  9111.  
  9112.         if (bct.EmoteId1)
  9113.         {
  9114.             if (!sEmotesStore.LookupEntry(bct.EmoteId1))
  9115.             {
  9116.                 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId1 %u but emote does not exist.", bct.Id, bct.EmoteId1);
  9117.                 bct.EmoteId1 = 0;
  9118.             }
  9119.         }
  9120.  
  9121.         if (bct.EmoteId2)
  9122.         {
  9123.             if (!sEmotesStore.LookupEntry(bct.EmoteId2))
  9124.             {
  9125.                 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId2 %u but emote does not exist.", bct.Id, bct.EmoteId2);
  9126.                 bct.EmoteId2 = 0;
  9127.             }
  9128.         }
  9129.  
  9130.         _broadcastTextStore[bct.Id] = bct;
  9131.     }
  9132.     while (result->NextRow());
  9133.  
  9134.     TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " broadcast texts in %u ms", _broadcastTextStore.size(), GetMSTimeDiffToNow(oldMSTime));
  9135. }
  9136.  
  9137. void ObjectMgr::LoadBroadcastTextLocales()
  9138. {
  9139.     uint32 oldMSTime = getMSTime();
  9140.  
  9141.     //                                               0   1       2         3
  9142.     QueryResult result = WorldDatabase.Query("SELECT ID, locale, MaleText, FemaleText FROM broadcast_text_locale");
  9143.     if (!result)
  9144.     {
  9145.         TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast text locales. DB table `broadcast_text_locale` is empty.");
  9146.         return;
  9147.     }
  9148.  
  9149.     do
  9150.     {
  9151.         Field* fields = result->Fetch();
  9152.  
  9153.         uint32 id               = fields[0].GetUInt32();
  9154.         std::string localeName  = fields[1].GetString();
  9155.         std::string MaleText    = fields[2].GetString();
  9156.         std::string FemaleText  = fields[3].GetString();
  9157.  
  9158.         BroadcastTextContainer::iterator bct = _broadcastTextStore.find(id);
  9159.         if (bct == _broadcastTextStore.end())
  9160.         {
  9161.             TC_LOG_ERROR("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text_locale` does not exist. Skipped!", id);
  9162.             continue;
  9163.         }
  9164.  
  9165.         LocaleConstant locale = GetLocaleByName(localeName);
  9166.         if (locale == LOCALE_enUS)
  9167.             continue;
  9168.  
  9169.         AddLocaleString(MaleText, locale, bct->second.MaleText);
  9170.         AddLocaleString(FemaleText, locale, bct->second.FemaleText);
  9171.     } while (result->NextRow());
  9172.  
  9173.     TC_LOG_INFO("server.loading", ">> Loaded %u broadcast text locales in %u ms", uint32(_broadcastTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
  9174. }
  9175.  
  9176. CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unitClass)
  9177. {
  9178.     CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
  9179.  
  9180.     if (it != _creatureBaseStatsStore.end())
  9181.         return &(it->second);
  9182.  
  9183.     struct DefaultCreatureBaseStats : public CreatureBaseStats
  9184.     {
  9185.         DefaultCreatureBaseStats()
  9186.         {
  9187.             BaseArmor = 1;
  9188.             for (uint8 j = 0; j < MAX_EXPANSIONS; ++j)
  9189.             {
  9190.                 BaseHealth[j] = 1;
  9191.                 BaseDamage[j] = 0.0f;
  9192.             }
  9193.             BaseMana = 0;
  9194.             AttackPower = 0;
  9195.             RangedAttackPower = 0;
  9196.         }
  9197.     };
  9198.     static const DefaultCreatureBaseStats defStats;
  9199.     return &defStats;
  9200. }
  9201.  
  9202. void ObjectMgr::LoadCreatureClassLevelStats()
  9203. {
  9204.     uint32 oldMSTime = getMSTime();
  9205.  
  9206.     QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2 FROM creature_classlevelstats");
  9207.  
  9208.     if (!result)
  9209.     {
  9210.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
  9211.         return;
  9212.     }
  9213.  
  9214.     uint32 count = 0;
  9215.     do
  9216.     {
  9217.         Field* fields = result->Fetch();
  9218.  
  9219.         uint8 Level = fields[0].GetUInt8();
  9220.         uint8 Class = fields[1].GetUInt8();
  9221.  
  9222.         if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
  9223.             TC_LOG_ERROR("sql.sql", "Creature base stats for level %u has invalid class %u", Level, Class);
  9224.  
  9225.         CreatureBaseStats stats;
  9226.  
  9227.         for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
  9228.         {
  9229.             stats.BaseHealth[i] = fields[2 + i].GetUInt16();
  9230.  
  9231.             if (stats.BaseHealth[i] == 0)
  9232.             {
  9233.                 TC_LOG_ERROR("sql.sql", "Creature base stats for class %u, level %u has invalid zero base HP[%u] - set to 1", Class, Level, i);
  9234.                 stats.BaseHealth[i] = 1;
  9235.             }
  9236.  
  9237.             stats.BaseDamage[i] = fields[9 + i].GetFloat();
  9238.             if (stats.BaseDamage[i] < 0.0f)
  9239.             {
  9240.                 TC_LOG_ERROR("sql.sql", "Creature base stats for class %u, level %u has invalid negative base damage[%u] - set to 0.0", Class, Level, i);
  9241.                 stats.BaseDamage[i] = 0.0f;
  9242.             }
  9243.         }
  9244.  
  9245.         stats.BaseMana = fields[5].GetUInt16();
  9246.         stats.BaseArmor = fields[6].GetUInt16();
  9247.  
  9248.         stats.AttackPower = fields[7].GetUInt16();
  9249.         stats.RangedAttackPower = fields[8].GetUInt16();
  9250.  
  9251.         _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
  9252.  
  9253.         ++count;
  9254.     }
  9255.     while (result->NextRow());
  9256.  
  9257.     for (auto const& creatureTemplatePair : _creatureTemplateStore)
  9258.     {
  9259.         for (uint16 lvl = creatureTemplatePair.second.minlevel; lvl <= creatureTemplatePair.second.maxlevel; ++lvl)
  9260.         {
  9261.             if (!_creatureBaseStatsStore.count(MAKE_PAIR16(lvl, creatureTemplatePair.second.unit_class)))
  9262.                 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class %u level %u", creatureTemplatePair.second.unit_class, lvl);
  9263.         }
  9264.     }
  9265.  
  9266.     TC_LOG_INFO("server.loading", ">> Loaded %u creature base stats in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9267. }
  9268.  
  9269. void ObjectMgr::LoadFactionChangeAchievements()
  9270. {
  9271.     uint32 oldMSTime = getMSTime();
  9272.  
  9273.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
  9274.  
  9275.     if (!result)
  9276.     {
  9277.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
  9278.         return;
  9279.     }
  9280.  
  9281.     uint32 count = 0;
  9282.  
  9283.     do
  9284.     {
  9285.         Field* fields = result->Fetch();
  9286.  
  9287.         uint32 alliance = fields[0].GetUInt32();
  9288.         uint32 horde = fields[1].GetUInt32();
  9289.  
  9290.         if (!sAchievementMgr->GetAchievement(alliance))
  9291.             TC_LOG_ERROR("sql.sql", "Achievement %u (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
  9292.         else if (!sAchievementMgr->GetAchievement(horde))
  9293.             TC_LOG_ERROR("sql.sql", "Achievement %u (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
  9294.         else
  9295.             FactionChangeAchievements[alliance] = horde;
  9296.  
  9297.         ++count;
  9298.     }
  9299.     while (result->NextRow());
  9300.  
  9301.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change achievement pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9302. }
  9303.  
  9304. void ObjectMgr::LoadFactionChangeItems()
  9305. {
  9306.     uint32 oldMSTime = getMSTime();
  9307.  
  9308.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
  9309.  
  9310.     if (!result)
  9311.     {
  9312.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
  9313.         return;
  9314.     }
  9315.  
  9316.     uint32 count = 0;
  9317.  
  9318.     do
  9319.     {
  9320.         Field* fields = result->Fetch();
  9321.  
  9322.         uint32 alliance = fields[0].GetUInt32();
  9323.         uint32 horde = fields[1].GetUInt32();
  9324.  
  9325.         if (!GetItemTemplate(alliance))
  9326.             TC_LOG_ERROR("sql.sql", "Item %u (alliance_id) referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
  9327.         else if (!GetItemTemplate(horde))
  9328.             TC_LOG_ERROR("sql.sql", "Item %u (horde_id) referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
  9329.         else
  9330.             FactionChangeItems[alliance] = horde;
  9331.  
  9332.         ++count;
  9333.     }
  9334.     while (result->NextRow());
  9335.  
  9336.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change item pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9337. }
  9338.  
  9339. void ObjectMgr::LoadFactionChangeQuests()
  9340. {
  9341.     uint32 oldMSTime = getMSTime();
  9342.  
  9343.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
  9344.  
  9345.     if (!result)
  9346.     {
  9347.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
  9348.         return;
  9349.     }
  9350.  
  9351.     uint32 count = 0;
  9352.  
  9353.     do
  9354.     {
  9355.         Field* fields = result->Fetch();
  9356.  
  9357.         uint32 alliance = fields[0].GetUInt32();
  9358.         uint32 horde = fields[1].GetUInt32();
  9359.  
  9360.         if (!sObjectMgr->GetQuestTemplate(alliance))
  9361.             TC_LOG_ERROR("sql.sql", "Quest %u (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
  9362.         else if (!sObjectMgr->GetQuestTemplate(horde))
  9363.             TC_LOG_ERROR("sql.sql", "Quest %u (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
  9364.         else
  9365.             FactionChangeQuests[alliance] = horde;
  9366.  
  9367.         ++count;
  9368.     }
  9369.     while (result->NextRow());
  9370.  
  9371.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change quest pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9372. }
  9373.  
  9374. void ObjectMgr::LoadFactionChangeReputations()
  9375. {
  9376.     uint32 oldMSTime = getMSTime();
  9377.  
  9378.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
  9379.  
  9380.     if (!result)
  9381.     {
  9382.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
  9383.         return;
  9384.     }
  9385.  
  9386.     uint32 count = 0;
  9387.  
  9388.     do
  9389.     {
  9390.         Field* fields = result->Fetch();
  9391.  
  9392.         uint32 alliance = fields[0].GetUInt32();
  9393.         uint32 horde = fields[1].GetUInt32();
  9394.  
  9395.         if (!sFactionStore.LookupEntry(alliance))
  9396.             TC_LOG_ERROR("sql.sql", "Reputation %u (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
  9397.         else if (!sFactionStore.LookupEntry(horde))
  9398.             TC_LOG_ERROR("sql.sql", "Reputation %u (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
  9399.         else
  9400.             FactionChangeReputation[alliance] = horde;
  9401.  
  9402.         ++count;
  9403.     }
  9404.     while (result->NextRow());
  9405.  
  9406.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change reputation pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9407. }
  9408.  
  9409. void ObjectMgr::LoadFactionChangeSpells()
  9410. {
  9411.     uint32 oldMSTime = getMSTime();
  9412.  
  9413.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
  9414.  
  9415.     if (!result)
  9416.     {
  9417.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
  9418.         return;
  9419.     }
  9420.  
  9421.     uint32 count = 0;
  9422.  
  9423.     do
  9424.     {
  9425.         Field* fields = result->Fetch();
  9426.  
  9427.         uint32 alliance = fields[0].GetUInt32();
  9428.         uint32 horde = fields[1].GetUInt32();
  9429.  
  9430.         if (!sSpellMgr->GetSpellInfo(alliance))
  9431.             TC_LOG_ERROR("sql.sql", "Spell %u (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
  9432.         else if (!sSpellMgr->GetSpellInfo(horde))
  9433.             TC_LOG_ERROR("sql.sql", "Spell %u (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
  9434.         else
  9435.             FactionChangeSpells[alliance] = horde;
  9436.  
  9437.         ++count;
  9438.     }
  9439.     while (result->NextRow());
  9440.  
  9441.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change spell pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9442. }
  9443.  
  9444. void ObjectMgr::LoadFactionChangeTitles()
  9445. {
  9446.     uint32 oldMSTime = getMSTime();
  9447.  
  9448.     QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
  9449.  
  9450.     if (!result)
  9451.     {
  9452.         TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
  9453.         return;
  9454.     }
  9455.  
  9456.     uint32 count = 0;
  9457.  
  9458.     do
  9459.     {
  9460.         Field* fields = result->Fetch();
  9461.  
  9462.         uint32 alliance = fields[0].GetUInt32();
  9463.         uint32 horde = fields[1].GetUInt32();
  9464.  
  9465.         if (!sCharTitlesStore.LookupEntry(alliance))
  9466.             TC_LOG_ERROR("sql.sql", "Title %u (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
  9467.         else if (!sCharTitlesStore.LookupEntry(horde))
  9468.             TC_LOG_ERROR("sql.sql", "Title %u (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
  9469.         else
  9470.             FactionChangeTitles[alliance] = horde;
  9471.  
  9472.         ++count;
  9473.     }
  9474.     while (result->NextRow());
  9475.  
  9476.     TC_LOG_INFO("server.loading", ">> Loaded %u faction change title pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9477. }
  9478.  
  9479. GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry) const
  9480. {
  9481.     return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
  9482. }
  9483.  
  9484. GameObjectTemplateAddon const* ObjectMgr::GetGameObjectTemplateAddon(uint32 entry) const
  9485. {
  9486.     auto itr = _gameObjectTemplateAddonStore.find(entry);
  9487.     if (itr != _gameObjectTemplateAddonStore.end())
  9488.         return &itr->second;
  9489.  
  9490.     return nullptr;
  9491. }
  9492.  
  9493. CreatureTemplate const* ObjectMgr::GetCreatureTemplate(uint32 entry) const
  9494. {
  9495.     return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
  9496. }
  9497.  
  9498. QuestPOIWrapper const* ObjectMgr::GetQuestPOIWrapper(uint32 questId) const
  9499. {
  9500.     return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
  9501. }
  9502.  
  9503. VehicleAccessoryList const* ObjectMgr::GetVehicleAccessoryList(Vehicle* veh) const
  9504. {
  9505.     if (Creature* cre = veh->GetBase()->ToCreature())
  9506.     {
  9507.         // Give preference to GUID-based accessories
  9508.         VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
  9509.         if (itr != _vehicleAccessoryStore.end())
  9510.             return &itr->second;
  9511.     }
  9512.  
  9513.     // Otherwise return entry-based
  9514.     VehicleAccessoryContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
  9515.     if (itr != _vehicleTemplateAccessoryStore.end())
  9516.         return &itr->second;
  9517.     return nullptr;
  9518. }
  9519.  
  9520. DungeonEncounterList const* ObjectMgr::GetDungeonEncounterList(uint32 mapId, Difficulty difficulty) const
  9521. {
  9522.     auto itr = _dungeonEncounterStore.find(MAKE_PAIR32(mapId, difficulty));
  9523.     if (itr != _dungeonEncounterStore.end())
  9524.         return &itr->second;
  9525.     return nullptr;
  9526. }
  9527.  
  9528. PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const
  9529. {
  9530.     if (race >= MAX_RACES)
  9531.         return nullptr;
  9532.     if (class_ >= MAX_CLASSES)
  9533.         return nullptr;
  9534.     auto const& info = _playerInfo[race][class_];
  9535.     if (!info)
  9536.         return nullptr;
  9537.     return info.get();
  9538. }
  9539.  
  9540. void ObjectMgr::LoadGameObjectQuestItems()
  9541. {
  9542.     uint32 oldMSTime = getMSTime();
  9543.  
  9544.     //                                               0                1       2
  9545.     QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
  9546.  
  9547.     if (!result)
  9548.     {
  9549.         TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
  9550.         return;
  9551.     }
  9552.  
  9553.     uint32 count = 0;
  9554.     do
  9555.     {
  9556.         Field* fields = result->Fetch();
  9557.  
  9558.         uint32 entry = fields[0].GetUInt32();
  9559.         uint32 item  = fields[1].GetUInt32();
  9560.         uint32 idx   = fields[2].GetUInt32();
  9561.  
  9562.         GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
  9563.         if (!goInfo)
  9564.         {
  9565.             TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: %u, idx: %u), skipped", entry, idx);
  9566.             continue;
  9567.         };
  9568.  
  9569.         ItemEntry const* db2Data = sItemStore.LookupEntry(item);
  9570.         if (!db2Data)
  9571.         {
  9572.             TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: %u) in gameobject (entry: %u, idx: %u), skipped", item, entry, idx);
  9573.             continue;
  9574.         };
  9575.  
  9576.         _gameObjectQuestItemStore[entry].push_back(item);
  9577.  
  9578.         ++count;
  9579.     }
  9580.     while (result->NextRow());
  9581.  
  9582.     TC_LOG_INFO("server.loading", ">> Loaded %u gameobject quest items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9583. }
  9584.  
  9585. void ObjectMgr::LoadCreatureQuestItems()
  9586. {
  9587.     uint32 oldMSTime = getMSTime();
  9588.  
  9589.     //                                               0              1       2
  9590.     QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
  9591.  
  9592.     if (!result)
  9593.     {
  9594.         TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
  9595.         return;
  9596.     }
  9597.  
  9598.     uint32 count = 0;
  9599.     do
  9600.     {
  9601.         Field* fields = result->Fetch();
  9602.  
  9603.         uint32 entry = fields[0].GetUInt32();
  9604.         uint32 item  = fields[1].GetUInt32();
  9605.         uint32 idx   = fields[2].GetUInt32();
  9606.  
  9607.         CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
  9608.         if (!creatureInfo)
  9609.         {
  9610.             TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: %u, idx: %u), skipped", entry, idx);
  9611.             continue;
  9612.         };
  9613.  
  9614.         ItemEntry const* db2Data = sItemStore.LookupEntry(item);
  9615.         if (!db2Data)
  9616.         {
  9617.             TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: %u) in creature (entry: %u, idx: %u), skipped", item, entry, idx);
  9618.             continue;
  9619.         };
  9620.  
  9621.         _creatureQuestItemStore[entry].push_back(item);
  9622.  
  9623.         ++count;
  9624.     }
  9625.     while (result->NextRow());
  9626.  
  9627.     TC_LOG_INFO("server.loading", ">> Loaded %u creature quest items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  9628. }
  9629.  
  9630. void ObjectMgr::InitializeQueriesData(QueryDataGroup mask)
  9631. {
  9632.     // cache disabled
  9633.     if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
  9634.         return;
  9635.  
  9636.     // Initialize Query data for creatures
  9637.     if (mask & QUERY_DATA_CREATURES)
  9638.         for (auto& creatureTemplatePair : _creatureTemplateStore)
  9639.             creatureTemplatePair.second.InitializeQueryData();
  9640.  
  9641.     // Initialize Query Data for gameobjects
  9642.     if (mask & QUERY_DATA_GAMEOBJECTS)
  9643.         for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
  9644.             gameObjectTemplatePair.second.InitializeQueryData();
  9645.  
  9646.     // Initialize Query Data for items
  9647.     if (mask & QUERY_DATA_ITEMS)
  9648.         for (auto& itemTemplatePair : _itemTemplateStore)
  9649.             itemTemplatePair.second.InitializeQueryData();
  9650.  
  9651.     // Initialize Query Data for quests
  9652.     if (mask & QUERY_DATA_QUESTS)
  9653.         for (auto& questTemplatePair : _questTemplates)
  9654.             questTemplatePair.second.InitializeQueryData();
  9655.  
  9656.     // Initialize Quest POI data
  9657.     if (mask & QUERY_DATA_POIS)
  9658.         for (auto& poiWrapperPair : _questPOIStore)
  9659.             poiWrapperPair.second.InitializeQueryData();
  9660. }
  9661.  
  9662. void QuestPOIWrapper::InitializeQueryData()
  9663. {
  9664.     QueryDataBuffer = BuildQueryData();
  9665. }
  9666.  
  9667. ByteBuffer QuestPOIWrapper::BuildQueryData() const
  9668. {
  9669.     ByteBuffer tempBuffer;
  9670.     tempBuffer << uint32(POIData.QuestID);                                      // quest ID
  9671.     tempBuffer << uint32(POIData.QuestPOIBlobDataStats.size());                 // POI count
  9672.  
  9673.     for (QuestPOIBlobData const& questPOIBlobData : POIData.QuestPOIBlobDataStats)
  9674.     {
  9675.         tempBuffer << uint32(questPOIBlobData.BlobIndex);                       // POI index
  9676.         tempBuffer << int32(questPOIBlobData.ObjectiveIndex);                   // objective index
  9677.         tempBuffer << uint32(questPOIBlobData.MapID);                           // mapid
  9678.         tempBuffer << uint32(questPOIBlobData.WorldMapAreaID);                  // areaid
  9679.         tempBuffer << uint32(questPOIBlobData.Floor);                           // floorid
  9680.         tempBuffer << uint32(questPOIBlobData.Unk3);                            // unknown
  9681.         tempBuffer << uint32(questPOIBlobData.Unk4);                            // unknown
  9682.         tempBuffer << uint32(questPOIBlobData.QuestPOIBlobPointStats.size());   // POI points count
  9683.  
  9684.         for (QuestPOIBlobPoint const& questPOIBlobPoint : questPOIBlobData.QuestPOIBlobPointStats)
  9685.         {
  9686.             tempBuffer << int32(questPOIBlobPoint.X); // POI point x
  9687.             tempBuffer << int32(questPOIBlobPoint.Y); // POI point y
  9688.         }
  9689.     }
  9690.  
  9691.     return tempBuffer;
  9692. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement