PageRenderTime 53ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 2ms

/src/server/game/Globals/ObjectMgr.cpp

https://gitlab.com/IlluminatiCore/IlluminatiCore
C++ | 9357 lines | 7458 code | 1565 blank | 334 comment | 1633 complexity | edca2f07d7e750a1406a24f1de8e96b8 MD5 | raw file
Possible License(s): GPL-2.0, BSD-2-Clause
  1. /*
  2. * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
  3. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the
  7. * Free Software Foundation; either version 2 of the License, or (at your
  8. * option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "AccountMgr.h"
  19. #include "AchievementMgr.h"
  20. #include "ArenaTeam.h"
  21. #include "ArenaTeamMgr.h"
  22. #include "BattlegroundMgr.h"
  23. #include "Chat.h"
  24. #include "Common.h"
  25. #include "DatabaseEnv.h"
  26. #include "DB2Structure.h"
  27. #include "DB2Stores.h"
  28. #include "DisableMgr.h"
  29. #include "GameEventMgr.h"
  30. #include "GossipDef.h"
  31. #include "GroupMgr.h"
  32. #include "GuildMgr.h"
  33. #include "InstanceSaveMgr.h"
  34. #include "Language.h"
  35. #include "LFGMgr.h"
  36. #include "Log.h"
  37. #include "MapManager.h"
  38. #include "Object.h"
  39. #include "ObjectMgr.h"
  40. #include "Pet.h"
  41. #include "PoolMgr.h"
  42. #include "ReputationMgr.h"
  43. #include "ScriptMgr.h"
  44. #include "SpellAuras.h"
  45. #include "Spell.h"
  46. #include "SpellMgr.h"
  47. #include "SpellScript.h"
  48. #include "Transport.h"
  49. #include "UpdateMask.h"
  50. #include "Util.h"
  51. #include "Vehicle.h"
  52. #include "WaypointManager.h"
  53. #include "World.h"
  54. ScriptMapMap sSpellScripts;
  55. ScriptMapMap sEventScripts;
  56. ScriptMapMap sWaypointScripts;
  57. std::string GetScriptsTableNameByType(ScriptsType type)
  58. {
  59. std::string res = "";
  60. switch (type)
  61. {
  62. case SCRIPTS_SPELL: res = "spell_scripts"; break;
  63. case SCRIPTS_EVENT: res = "event_scripts"; break;
  64. case SCRIPTS_WAYPOINT: res = "waypoint_scripts"; break;
  65. default: break;
  66. }
  67. return res;
  68. }
  69. ScriptMapMap* GetScriptsMapByType(ScriptsType type)
  70. {
  71. ScriptMapMap* res = NULL;
  72. switch (type)
  73. {
  74. case SCRIPTS_SPELL: res = &sSpellScripts; break;
  75. case SCRIPTS_EVENT: res = &sEventScripts; break;
  76. case SCRIPTS_WAYPOINT: res = &sWaypointScripts; break;
  77. default: break;
  78. }
  79. return res;
  80. }
  81. std::string GetScriptCommandName(ScriptCommands command)
  82. {
  83. std::string res = "";
  84. switch (command)
  85. {
  86. case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
  87. case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
  88. case SCRIPT_COMMAND_FIELD_SET: res = "SCRIPT_COMMAND_FIELD_SET"; break;
  89. case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
  90. case SCRIPT_COMMAND_FLAG_SET: res = "SCRIPT_COMMAND_FLAG_SET"; break;
  91. case SCRIPT_COMMAND_FLAG_REMOVE: res = "SCRIPT_COMMAND_FLAG_REMOVE"; break;
  92. case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
  93. case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
  94. case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
  95. case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
  96. case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
  97. case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
  98. case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
  99. case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
  100. case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
  101. case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
  102. case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
  103. case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
  104. case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
  105. case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
  106. case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
  107. case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
  108. // TrinityCore only
  109. case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
  110. case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
  111. case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
  112. case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
  113. case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
  114. default:
  115. {
  116. char sz[32];
  117. sprintf(sz, "Unknown command: %d", command);
  118. res = sz;
  119. break;
  120. }
  121. }
  122. return res;
  123. }
  124. std::string ScriptInfo::GetDebugInfo() const
  125. {
  126. char sz[256];
  127. sprintf(sz, "%s ('%s' script id: %u)", GetScriptCommandName(command).c_str(), GetScriptsTableNameByType(type).c_str(), id);
  128. return std::string(sz);
  129. }
  130. bool normalizePlayerName(std::string& name)
  131. {
  132. if (name.empty())
  133. return false;
  134. wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
  135. size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
  136. if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
  137. return false;
  138. wstr_buf[0] = wcharToUpper(wstr_buf[0]);
  139. for (size_t i = 1; i < wstr_len; ++i)
  140. wstr_buf[i] = wcharToLower(wstr_buf[i]);
  141. if (!WStrToUtf8(wstr_buf, wstr_len, name))
  142. return false;
  143. return true;
  144. }
  145. LanguageDesc lang_description[LANGUAGES_COUNT] =
  146. {
  147. { LANG_ADDON, 0, 0 },
  148. { LANG_UNIVERSAL, 0, 0 },
  149. { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
  150. { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
  151. { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
  152. { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
  153. { LANG_COMMON, 668, SKILL_LANG_COMMON },
  154. { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
  155. { LANG_TITAN, 816, SKILL_LANG_TITAN },
  156. { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
  157. { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
  158. { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
  159. { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
  160. { LANG_TROLL, 7341, SKILL_LANG_TROLL },
  161. { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
  162. { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
  163. { LANG_ZOMBIE, 0, 0 },
  164. { LANG_GNOMISH_BINARY, 0, 0 },
  165. { LANG_GOBLIN_BINARY, 0, 0 },
  166. { LANG_WORGEN, 69270, SKILL_LANG_WORGEN },
  167. { LANG_GOBLIN, 69269, SKILL_LANG_GOBLIN }
  168. };
  169. LanguageDesc const* GetLanguageDescByID(uint32 lang)
  170. {
  171. for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
  172. {
  173. if (uint32(lang_description[i].lang_id) == lang)
  174. return &lang_description[i];
  175. }
  176. return NULL;
  177. }
  178. bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
  179. {
  180. Player const* playerClicker = clicker->ToPlayer();
  181. if (!playerClicker)
  182. return true;
  183. Unit const* summoner = NULL;
  184. // Check summoners for party
  185. if (clickee->IsSummon())
  186. summoner = clickee->ToTempSummon()->GetSummoner();
  187. if (!summoner)
  188. summoner = clickee;
  189. // This only applies to players
  190. switch (userType)
  191. {
  192. case SPELL_CLICK_USER_FRIEND:
  193. if (!playerClicker->IsFriendlyTo(summoner))
  194. return false;
  195. break;
  196. case SPELL_CLICK_USER_RAID:
  197. if (!playerClicker->IsInRaidWith(summoner))
  198. return false;
  199. break;
  200. case SPELL_CLICK_USER_PARTY:
  201. if (!playerClicker->IsInPartyWith(summoner))
  202. return false;
  203. break;
  204. default:
  205. break;
  206. }
  207. return true;
  208. }
  209. ObjectMgr::ObjectMgr():
  210. _auctionId(1),
  211. _equipmentSetGuid(1),
  212. _itemTextId(1),
  213. _mailId(1),
  214. _hiPetNumber(1),
  215. _voidItemId(1),
  216. _hiCharGuid(1),
  217. _hiCreatureGuid(1),
  218. _hiPetGuid(1),
  219. _hiVehicleGuid(1),
  220. _hiItemGuid(1),
  221. _hiGoGuid(1),
  222. _hiDoGuid(1),
  223. _hiCorpseGuid(1),
  224. _hiAreaTriggerGuid(1),
  225. _hiMoTransGuid(1),
  226. DBCLocaleIndex(LOCALE_enUS)
  227. {
  228. for (uint8 i = 0; i < MAX_CLASSES; ++i)
  229. for (uint8 j = 0; j < MAX_RACES; ++j)
  230. _playerInfo[j][i] = NULL;
  231. }
  232. ObjectMgr::~ObjectMgr()
  233. {
  234. for (QuestMap::iterator i = _questTemplates.begin(); i != _questTemplates.end(); ++i)
  235. delete i->second;
  236. for (PetLevelInfoContainer::iterator i = _petInfoStore.begin(); i != _petInfoStore.end(); ++i)
  237. delete[] i->second;
  238. for (int race = 0; race < MAX_RACES; ++race)
  239. {
  240. for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
  241. {
  242. if (_playerInfo[race][class_])
  243. delete[] _playerInfo[race][class_]->levelInfo;
  244. delete _playerInfo[race][class_];
  245. }
  246. }
  247. for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
  248. itr->second.Clear();
  249. _cacheTrainerSpellStore.clear();
  250. _graveyardOrientations.clear();
  251. for (DungeonEncounterContainer::iterator itr =_dungeonEncounterStore.begin(); itr != _dungeonEncounterStore.end(); ++itr)
  252. for (DungeonEncounterList::iterator encounterItr = itr->second.begin(); encounterItr != itr->second.end(); ++encounterItr)
  253. delete *encounterItr;
  254. for (AccessRequirementContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
  255. delete itr->second;
  256. }
  257. void ObjectMgr::AddLocaleString(std::string const& s, LocaleConstant locale, StringVector& data)
  258. {
  259. if (!s.empty())
  260. {
  261. if (data.size() <= size_t(locale))
  262. data.resize(locale + 1);
  263. data[locale] = s;
  264. }
  265. }
  266. void ObjectMgr::LoadGraveyardOrientations()
  267. {
  268. uint32 oldMSTime = getMSTime();
  269. _graveyardOrientations.clear();
  270. QueryResult result = WorldDatabase.Query("SELECT id, orientation FROM graveyard_orientation");
  271. if (!result)
  272. return;
  273. do
  274. {
  275. Field* fields = result->Fetch();
  276. uint32 id = fields[0].GetUInt32();
  277. if (!sWorldSafeLocsStore.LookupEntry(id))
  278. {
  279. TC_LOG_ERROR("server.loading", "Graveyard %u referenced in graveyard_orientation doesn't exist.", id);
  280. continue;
  281. }
  282. _graveyardOrientations[id] = fields[1].GetFloat();
  283. } while (result->NextRow());
  284. TC_LOG_INFO("server.loading", ">> Loaded %lu graveyard orientations in %u ms", (unsigned long)_graveyardOrientations.size(), GetMSTimeDiffToNow(oldMSTime));
  285. }
  286. void ObjectMgr::LoadCreatureLocales()
  287. {
  288. uint32 oldMSTime = getMSTime();
  289. _creatureLocaleStore.clear(); // need for reload case
  290. QueryResult result = WorldDatabase.Query("SELECT entry, "
  291. "name_loc1, femaleName_loc1, subname_loc1, "
  292. "name_loc2, femaleName_loc2, subname_loc2, "
  293. "name_loc3, femaleName_loc3, subname_loc3, "
  294. "name_loc4, femaleName_loc4, subname_loc4, "
  295. "name_loc5, femaleName_loc5, subname_loc5, "
  296. "name_loc6, femaleName_loc6, subname_loc6, "
  297. "name_loc7, femaleName_loc7, subname_loc7, "
  298. "name_loc8, femaleName_loc8, subname_loc8 "
  299. "FROM locales_creature");
  300. if (!result)
  301. return;
  302. do
  303. {
  304. Field* fields = result->Fetch();
  305. uint32 entry = fields[0].GetUInt32();
  306. CreatureLocale& data = _creatureLocaleStore[entry];
  307. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  308. {
  309. LocaleConstant locale = (LocaleConstant) i;
  310. AddLocaleString(fields[1 + 3 * (i - 1)].GetString(), locale, data.Name);
  311. AddLocaleString(fields[1 + 3 * (i - 1) + 1].GetString(), locale, data.FemaleName);
  312. AddLocaleString(fields[1 + 3 * (i - 1) + 2].GetString(), locale, data.SubName);
  313. }
  314. } while (result->NextRow());
  315. TC_LOG_INFO("server.loading", ">> Loaded %u creature locale strings in %u ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  316. }
  317. void ObjectMgr::LoadGossipMenuItemsLocales()
  318. {
  319. uint32 oldMSTime = getMSTime();
  320. _gossipMenuItemsLocaleStore.clear(); // need for reload case
  321. QueryResult result = WorldDatabase.Query("SELECT menu_id, id, "
  322. "option_text_loc1, box_text_loc1, option_text_loc2, box_text_loc2, "
  323. "option_text_loc3, box_text_loc3, option_text_loc4, box_text_loc4, "
  324. "option_text_loc5, box_text_loc5, option_text_loc6, box_text_loc6, "
  325. "option_text_loc7, box_text_loc7, option_text_loc8, box_text_loc8 "
  326. "FROM locales_gossip_menu_option");
  327. if (!result)
  328. return;
  329. do
  330. {
  331. Field* fields = result->Fetch();
  332. uint16 menuId = fields[0].GetUInt16();
  333. uint16 id = fields[1].GetUInt16();
  334. GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[MAKE_PAIR32(menuId, id)];
  335. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  336. {
  337. LocaleConstant locale = (LocaleConstant) i;
  338. AddLocaleString(fields[2 + 2 * (i - 1)].GetString(), locale, data.OptionText);
  339. AddLocaleString(fields[2 + 2 * (i - 1) + 1].GetString(), locale, data.BoxText);
  340. }
  341. }
  342. while (result->NextRow());
  343. TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu_option locale strings in %u ms", uint32(_gossipMenuItemsLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  344. }
  345. void ObjectMgr::LoadPointOfInterestLocales()
  346. {
  347. uint32 oldMSTime = getMSTime();
  348. _pointOfInterestLocaleStore.clear(); // need for reload case
  349. QueryResult result = WorldDatabase.Query("SELECT entry, icon_name_loc1, icon_name_loc2, icon_name_loc3, icon_name_loc4, icon_name_loc5, icon_name_loc6, icon_name_loc7, icon_name_loc8 FROM locales_points_of_interest");
  350. if (!result)
  351. return;
  352. do
  353. {
  354. Field* fields = result->Fetch();
  355. uint32 entry = fields[0].GetUInt32();
  356. PointOfInterestLocale& data = _pointOfInterestLocaleStore[entry];
  357. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  358. AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.IconName);
  359. } while (result->NextRow());
  360. TC_LOG_INFO("server.loading", ">> Loaded %u points_of_interest locale strings in %u ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  361. }
  362. void ObjectMgr::LoadCreatureTemplates()
  363. {
  364. uint32 oldMSTime = getMSTime();
  365. // 0 1 2 3 4 5 6 7 8
  366. QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, "
  367. // 9 10 11 12 13 14 15 16 17 18 19 20 21 22
  368. "modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, "
  369. // 23 24 25 26 27 28 29 30 31 32
  370. "scale, rank, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, "
  371. // 33 34 35 36 37 38
  372. "dynamicflags, family, trainer_type, trainer_class, trainer_race, type, "
  373. // 39 40 41 42 43 44 45 46 47 48 49
  374. "type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, "
  375. // 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  376. "spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
  377. // 64 65 66 67 68 69 70 71 72
  378. "InhabitType, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, "
  379. // 73 74 75 76 77 78 79
  380. "RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, "
  381. // 80 81 82 83 84
  382. "movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName "
  383. "FROM creature_template");
  384. if (!result)
  385. {
  386. TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
  387. return;
  388. }
  389. _creatureTemplateStore.rehash(result->GetRowCount());
  390. uint32 count = 0;
  391. do
  392. {
  393. Field* fields = result->Fetch();
  394. LoadCreatureTemplate(fields);
  395. ++count;
  396. }
  397. while (result->NextRow());
  398. // Checking needs to be done after loading because of the difficulty self referencing
  399. for (CreatureTemplateContainer::const_iterator itr = _creatureTemplateStore.begin(); itr != _creatureTemplateStore.end(); ++itr)
  400. CheckCreatureTemplate(&itr->second);
  401. TC_LOG_INFO("server.loading", ">> Loaded %u creature definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  402. }
  403. void ObjectMgr::LoadCreatureTemplate(Field* fields)
  404. {
  405. uint32 entry = fields[0].GetUInt32();
  406. CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
  407. creatureTemplate.Entry = entry;
  408. for (uint8 i = 0; i < MAX_DIFFICULTY - 1; ++i)
  409. creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32();
  410. for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
  411. creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
  412. creatureTemplate.Modelid1 = fields[6].GetUInt32();
  413. creatureTemplate.Modelid2 = fields[7].GetUInt32();
  414. creatureTemplate.Modelid3 = fields[8].GetUInt32();
  415. creatureTemplate.Modelid4 = fields[9].GetUInt32();
  416. creatureTemplate.Name = fields[10].GetString();
  417. creatureTemplate.FemaleName = fields[11].GetString();
  418. creatureTemplate.SubName = fields[12].GetString();
  419. creatureTemplate.IconName = fields[13].GetString();
  420. creatureTemplate.GossipMenuId = fields[14].GetUInt32();
  421. creatureTemplate.minlevel = fields[15].GetUInt8();
  422. creatureTemplate.maxlevel = fields[16].GetUInt8();
  423. creatureTemplate.expansion = uint32(fields[17].GetInt16());
  424. creatureTemplate.expansionUnknown = uint32(fields[18].GetUInt16());
  425. creatureTemplate.faction = uint32(fields[19].GetUInt16());
  426. creatureTemplate.npcflag = fields[20].GetUInt32();
  427. creatureTemplate.speed_walk = fields[21].GetFloat();
  428. creatureTemplate.speed_run = fields[22].GetFloat();
  429. creatureTemplate.scale = fields[23].GetFloat();
  430. creatureTemplate.rank = uint32(fields[24].GetUInt8());
  431. creatureTemplate.dmgschool = uint32(fields[25].GetInt8());
  432. creatureTemplate.BaseAttackTime = fields[26].GetUInt32();
  433. creatureTemplate.RangeAttackTime = fields[27].GetUInt32();
  434. creatureTemplate.BaseVariance = fields[28].GetFloat();
  435. creatureTemplate.RangeVariance = fields[29].GetFloat();
  436. creatureTemplate.unit_class = uint32(fields[30].GetUInt8());
  437. creatureTemplate.unit_flags = fields[31].GetUInt32();
  438. creatureTemplate.unit_flags2 = fields[32].GetUInt32();
  439. creatureTemplate.dynamicflags = fields[33].GetUInt32();
  440. creatureTemplate.family = uint32(fields[34].GetUInt8());
  441. creatureTemplate.trainer_type = uint32(fields[35].GetUInt8());
  442. creatureTemplate.trainer_class = uint32(fields[36].GetUInt8());
  443. creatureTemplate.trainer_race = uint32(fields[37].GetUInt8());
  444. creatureTemplate.type = uint32(fields[38].GetUInt8());
  445. creatureTemplate.type_flags = fields[39].GetUInt32();
  446. creatureTemplate.type_flags2 = fields[40].GetUInt32();
  447. creatureTemplate.lootid = fields[41].GetUInt32();
  448. creatureTemplate.pickpocketLootId = fields[42].GetUInt32();
  449. creatureTemplate.SkinLootId = fields[43].GetUInt32();
  450. for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
  451. creatureTemplate.resistance[i] = fields[44 + i - 1].GetInt16();
  452. for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
  453. creatureTemplate.spells[i] = fields[50 + i].GetUInt32();
  454. creatureTemplate.PetSpellDataId = fields[58].GetUInt32();
  455. creatureTemplate.VehicleId = fields[59].GetUInt32();
  456. creatureTemplate.mingold = fields[60].GetUInt32();
  457. creatureTemplate.maxgold = fields[61].GetUInt32();
  458. creatureTemplate.AIName = fields[62].GetString();
  459. creatureTemplate.MovementType = uint32(fields[63].GetUInt8());
  460. creatureTemplate.InhabitType = uint32(fields[64].GetUInt8());
  461. creatureTemplate.HoverHeight = fields[65].GetFloat();
  462. creatureTemplate.ModHealth = fields[66].GetFloat();
  463. creatureTemplate.ModHealthExtra = fields[67].GetFloat();
  464. creatureTemplate.ModMana = fields[68].GetFloat();
  465. creatureTemplate.ModManaExtra = fields[69].GetFloat();
  466. creatureTemplate.ModArmor = fields[70].GetFloat();
  467. creatureTemplate.ModDamage = fields[71].GetFloat();
  468. creatureTemplate.ModExperience = fields[72].GetFloat();
  469. creatureTemplate.RacialLeader = fields[73].GetBool();
  470. for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
  471. creatureTemplate.questItems[i] = fields[74 + i].GetUInt32();
  472. creatureTemplate.movementId = fields[80].GetUInt32();
  473. creatureTemplate.RegenHealth = fields[81].GetBool();
  474. creatureTemplate.MechanicImmuneMask = fields[82].GetUInt32();
  475. creatureTemplate.flags_extra = fields[83].GetUInt32();
  476. creatureTemplate.ScriptID = GetScriptId(fields[84].GetCString());
  477. }
  478. void ObjectMgr::LoadCreatureTemplateAddons()
  479. {
  480. uint32 oldMSTime = getMSTime();
  481. // 0 1 2 3 4 5 6
  482. QueryResult result = WorldDatabase.Query("SELECT entry, path_id, mount, bytes1, bytes2, emote, auras FROM creature_template_addon");
  483. if (!result)
  484. {
  485. TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
  486. return;
  487. }
  488. uint32 count = 0;
  489. do
  490. {
  491. Field* fields = result->Fetch();
  492. uint32 entry = fields[0].GetUInt32();
  493. if (!sObjectMgr->GetCreatureTemplate(entry))
  494. {
  495. TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_template_addon`", entry);
  496. continue;
  497. }
  498. CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
  499. creatureAddon.path_id = fields[1].GetUInt32();
  500. creatureAddon.mount = fields[2].GetUInt32();
  501. creatureAddon.bytes1 = fields[3].GetUInt32();
  502. creatureAddon.bytes2 = fields[4].GetUInt32();
  503. creatureAddon.emote = fields[5].GetUInt32();
  504. Tokenizer tokens(fields[6].GetString(), ' ');
  505. uint8 i = 0;
  506. creatureAddon.auras.resize(tokens.size());
  507. for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
  508. {
  509. SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr)));
  510. if (!AdditionalSpellInfo)
  511. {
  512. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong spell %u defined in `auras` field in `creature_template_addon`.", entry, uint32(atol(*itr)));
  513. continue;
  514. }
  515. if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
  516. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has SPELL_AURA_CONTROL_VEHICLE aura %u defined in `auras` field in `creature_template_addon`.", entry, uint32(atol(*itr)));
  517. creatureAddon.auras[i++] = uint32(atol(*itr));
  518. }
  519. if (creatureAddon.mount)
  520. {
  521. if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
  522. {
  523. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid displayInfoId (%u) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
  524. creatureAddon.mount = 0;
  525. }
  526. }
  527. if (!sEmotesStore.LookupEntry(creatureAddon.emote))
  528. {
  529. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid emote (%u) defined in `creature_template_addon`.", entry, creatureAddon.emote);
  530. creatureAddon.emote = 0;
  531. }
  532. ++count;
  533. }
  534. while (result->NextRow());
  535. TC_LOG_INFO("server.loading", ">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  536. }
  537. void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
  538. {
  539. if (!cInfo)
  540. return;
  541. bool ok = true; // bool to allow continue outside this loop
  542. for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
  543. {
  544. if (!cInfo->DifficultyEntry[diff])
  545. continue;
  546. ok = false; // will be set to true at the end of this loop again
  547. CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
  548. if (!difficultyInfo)
  549. {
  550. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u does not exist.",
  551. cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
  552. continue;
  553. }
  554. bool ok2 = true;
  555. for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
  556. {
  557. ok2 = false;
  558. if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end())
  559. {
  560. 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`.",
  561. cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
  562. continue;
  563. }
  564. if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end())
  565. {
  566. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) already listed as `difficulty_entry_%u` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
  567. continue;
  568. }
  569. if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end())
  570. {
  571. 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`.",
  572. cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
  573. continue;
  574. }
  575. ok2 = true;
  576. }
  577. if (!ok2)
  578. continue;
  579. if (cInfo->expansion > difficultyInfo->expansion)
  580. {
  581. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, exp %u) has different `exp` in difficulty %u mode (Entry: %u, exp %u).",
  582. cInfo->Entry, cInfo->expansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->expansion);
  583. }
  584. if (cInfo->minlevel > difficultyInfo->minlevel)
  585. {
  586. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, minlevel %u) has lower `minlevel` in difficulty %u mode (Entry: %u, minlevel %u).",
  587. cInfo->Entry, cInfo->minlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->minlevel);
  588. }
  589. if (cInfo->maxlevel > difficultyInfo->maxlevel)
  590. {
  591. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, maxlevel %u) has lower `maxlevel` in difficulty %u mode (Entry: %u, maxlevel %u).",
  592. cInfo->Entry, cInfo->maxlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->maxlevel);
  593. }
  594. if (cInfo->faction != difficultyInfo->faction)
  595. {
  596. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, faction %u) has different `faction` in difficulty %u mode (Entry: %u, faction %u).",
  597. cInfo->Entry, cInfo->faction, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->faction);
  598. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `faction`=%u WHERE `entry`=%u;",
  599. cInfo->faction, cInfo->DifficultyEntry[diff]);
  600. }
  601. if (cInfo->unit_class != difficultyInfo->unit_class)
  602. {
  603. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, class %u) has different `unit_class` in difficulty %u mode (Entry: %u, class %u).",
  604. cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
  605. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_class`=%u WHERE `entry`=%u;",
  606. cInfo->unit_class, cInfo->DifficultyEntry[diff]);
  607. continue;
  608. }
  609. if (cInfo->npcflag != difficultyInfo->npcflag)
  610. {
  611. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, `npcflag`: %u) has different `npcflag` in difficulty %u mode (Entry: %u, `npcflag`: %u).",
  612. cInfo->Entry, cInfo->npcflag, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->npcflag);
  613. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `npcflag`=%u WHERE `entry`=%u;",
  614. cInfo->npcflag, cInfo->DifficultyEntry[diff]);
  615. continue;
  616. }
  617. if (cInfo->family != difficultyInfo->family)
  618. {
  619. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, family %u) has different `family` in difficulty %u mode (Entry: %u, family %u).",
  620. cInfo->Entry, cInfo->family, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->family);
  621. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `family`=%u WHERE `entry`=%u;",
  622. cInfo->family, cInfo->DifficultyEntry[diff]);
  623. }
  624. if (cInfo->trainer_class != difficultyInfo->trainer_class)
  625. {
  626. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_class: %u) has different `trainer_class` in difficulty %u mode (Entry: %u, trainer_class: %u).",
  627. cInfo->Entry, cInfo->trainer_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_class);
  628. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_class`=%u WHERE `entry`=%u;",
  629. cInfo->trainer_class, cInfo->DifficultyEntry[diff]);
  630. continue;
  631. }
  632. if (cInfo->trainer_race != difficultyInfo->trainer_race)
  633. {
  634. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_race: %u) has different `trainer_race` in difficulty %u mode (Entry: %u, trainer_race: %u).",
  635. cInfo->Entry, cInfo->trainer_race, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_race);
  636. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_race`=%u WHERE `entry`=%u;",
  637. cInfo->trainer_race, cInfo->DifficultyEntry[diff]);
  638. continue;
  639. }
  640. if (cInfo->trainer_type != difficultyInfo->trainer_type)
  641. {
  642. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, trainer_type: %u) has different `trainer_type` in difficulty %u mode (Entry: %u, trainer_type: %u).",
  643. cInfo->Entry, cInfo->trainer_type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_type);
  644. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_type`=%u WHERE `entry`=%u;",
  645. cInfo->trainer_type, cInfo->DifficultyEntry[diff]);
  646. continue;
  647. }
  648. if (cInfo->type != difficultyInfo->type)
  649. {
  650. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, type %u) has different `type` in difficulty %u mode (Entry: %u, type %u).",
  651. cInfo->Entry, cInfo->type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->type);
  652. TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `type`=%u WHERE `entry`=%u;",
  653. cInfo->type, cInfo->DifficultyEntry[diff]);
  654. }
  655. if (!cInfo->VehicleId && difficultyInfo->VehicleId)
  656. {
  657. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, VehicleId %u) has different `VehicleId` in difficulty %u mode (Entry: %u, VehicleId %u).",
  658. cInfo->Entry, cInfo->VehicleId, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->VehicleId);
  659. }
  660. if (!difficultyInfo->AIName.empty())
  661. {
  662. 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.",
  663. cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
  664. continue;
  665. }
  666. if (difficultyInfo->ScriptID)
  667. {
  668. 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.",
  669. cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
  670. continue;
  671. }
  672. _hasDifficultyEntries[diff].insert(cInfo->Entry);
  673. _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
  674. ok = true;
  675. }
  676. FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
  677. if (!factionTemplate)
  678. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing faction template (%u).", cInfo->Entry, cInfo->faction);
  679. // used later for scale
  680. CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
  681. if (cInfo->Modelid1)
  682. {
  683. CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
  684. if (!displayEntry)
  685. {
  686. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
  687. const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
  688. }
  689. else if (!displayScaleEntry)
  690. displayScaleEntry = displayEntry;
  691. CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
  692. if (!modelInfo)
  693. TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry);
  694. }
  695. if (cInfo->Modelid2)
  696. {
  697. CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
  698. if (!displayEntry)
  699. {
  700. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
  701. const_cast<CreatureTemplate*>(cInfo)->Modelid2 = 0;
  702. }
  703. else if (!displayScaleEntry)
  704. displayScaleEntry = displayEntry;
  705. CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);
  706. if (!modelInfo)
  707. TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry);
  708. }
  709. if (cInfo->Modelid3)
  710. {
  711. CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
  712. if (!displayEntry)
  713. {
  714. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
  715. const_cast<CreatureTemplate*>(cInfo)->Modelid3 = 0;
  716. }
  717. else if (!displayScaleEntry)
  718. displayScaleEntry = displayEntry;
  719. CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
  720. if (!modelInfo)
  721. TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry);
  722. }
  723. if (cInfo->Modelid4)
  724. {
  725. CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
  726. if (!displayEntry)
  727. {
  728. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
  729. const_cast<CreatureTemplate*>(cInfo)->Modelid4 = 0;
  730. }
  731. else if (!displayScaleEntry)
  732. displayScaleEntry = displayEntry;
  733. CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);
  734. if (!modelInfo)
  735. TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry);
  736. }
  737. if (!displayScaleEntry)
  738. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
  739. for (int k = 0; k < MAX_KILL_CREDIT; ++k)
  740. {
  741. if (cInfo->KillCredit[k])
  742. {
  743. if (!GetCreatureTemplate(cInfo->KillCredit[k]))
  744. {
  745. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing creature entry %u in `KillCredit%d`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
  746. const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
  747. }
  748. }
  749. }
  750. if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
  751. {
  752. 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);
  753. const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
  754. }
  755. if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
  756. {
  757. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
  758. const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
  759. }
  760. if (cInfo->BaseAttackTime == 0)
  761. const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime = BASE_ATTACK_TIME;
  762. if (cInfo->RangeAttackTime == 0)
  763. const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
  764. if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
  765. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong trainer type %u.", cInfo->Entry, cInfo->trainer_type);
  766. if (cInfo->speed_walk == 0.0f)
  767. {
  768. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
  769. const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
  770. }
  771. if (cInfo->speed_run == 0.0f)
  772. {
  773. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
  774. const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
  775. }
  776. if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
  777. {
  778. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature type (%u) in `type`.", cInfo->Entry, cInfo->type);
  779. const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
  780. }
  781. // must exist or used hidden but used in data horse case
  782. if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
  783. {
  784. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family);
  785. const_cast<CreatureTemplate*>(cInfo)->family = 0;
  786. }
  787. if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
  788. {
  789. 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);
  790. const_cast<CreatureTemplate*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
  791. }
  792. if (cInfo->HoverHeight < 0.0f)
  793. {
  794. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%f) in `HoverHeight`", cInfo->Entry, cInfo->HoverHeight);
  795. const_cast<CreatureTemplate*>(cInfo)->HoverHeight = 1.0f;
  796. }
  797. if (cInfo->VehicleId)
  798. {
  799. VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
  800. if (!vehId)
  801. {
  802. 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);
  803. const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
  804. }
  805. }
  806. if (cInfo->PetSpellDataId)
  807. {
  808. CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
  809. if (!spellDataId)
  810. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId);
  811. }
  812. for (uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j)
  813. {
  814. if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j]))
  815. {
  816. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing Spell%d (%u), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
  817. const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
  818. }
  819. }
  820. if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
  821. {
  822. TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong movement generator type (%u), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
  823. const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
  824. }
  825. /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
  826. if (cInfo->scale <= 0.0f)
  827. {
  828. if (displayScaleEntry)
  829. const_cast<CreatureTemplate*>(cInfo)->scale = displayScaleEntry->scale;
  830. else
  831. const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f;
  832. }
  833. if (cInfo->expansion > (MAX_EXPANSIONS - 1))
  834. {
  835. TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with `exp` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
  836. const_cast<CreatureTemplate*>(cInfo)->expansion = 0;
  837. }
  838. if (cInfo->expansionUnknown > MAX_EXPANSIONS)
  839. {
  840. TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with `exp_unk` %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansionUnknown);
  841. const_cast<CreatureTemplate*>(cInfo)->expansionUnknown = 0;
  842. }
  843. if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
  844. {
  845. TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with disallowed `flags_extra` %u, removing incorrect flag.", cInfo->Entry, badFlags);
  846. const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
  847. }
  848. const_cast<CreatureTemplate*>(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank);
  849. }
  850. void ObjectMgr::LoadCreatureAddons()
  851. {
  852. uint32 oldMSTime = getMSTime();
  853. // 0 1 2 3 4 5 6
  854. QueryResult result = WorldDatabase.Query("SELECT guid, path_id, mount, bytes1, bytes2, emote, auras FROM creature_addon");
  855. if (!result)
  856. {
  857. TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
  858. return;
  859. }
  860. uint32 count = 0;
  861. do
  862. {
  863. Field* fields = result->Fetch();
  864. uint32 guid = fields[0].GetUInt32();
  865. CreatureData const* creData = GetCreatureData(guid);
  866. if (!creData)
  867. {
  868. TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) does not exist but has a record in `creature_addon`", guid);
  869. continue;
  870. }
  871. CreatureAddon& creatureAddon = _creatureAddonStore[guid];
  872. creatureAddon.path_id = fields[1].GetUInt32();
  873. if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.path_id)
  874. {
  875. const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
  876. TC_LOG_ERROR("sql.sql", "Creature (GUID %u) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
  877. }
  878. creatureAddon.mount = fields[2].GetUInt32();
  879. creatureAddon.bytes1 = fields[3].GetUInt32();
  880. creatureAddon.bytes2 = fields[4].GetUInt32();
  881. creatureAddon.emote = fields[5].GetUInt32();
  882. Tokenizer tokens(fields[6].GetString(), ' ');
  883. uint8 i = 0;
  884. creatureAddon.auras.resize(tokens.size());
  885. for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
  886. {
  887. SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr)));
  888. if (!AdditionalSpellInfo)
  889. {
  890. TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has wrong spell %u defined in `auras` field in `creature_addon`.", guid, uint32(atol(*itr)));
  891. continue;
  892. }
  893. if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
  894. TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has SPELL_AURA_CONTROL_VEHICLE aura %u defined in `auras` field in `creature_addon`.", guid, uint32(atol(*itr)));
  895. creatureAddon.auras[i++] = uint32(atol(*itr));
  896. }
  897. if (creatureAddon.mount)
  898. {
  899. if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
  900. {
  901. TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid displayInfoId (%u) for mount defined in `creature_addon`", guid, creatureAddon.mount);
  902. creatureAddon.mount = 0;
  903. }
  904. }
  905. if (!sEmotesStore.LookupEntry(creatureAddon.emote))
  906. {
  907. TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);
  908. creatureAddon.emote = 0;
  909. }
  910. ++count;
  911. }
  912. while (result->NextRow());
  913. TC_LOG_INFO("server.loading", ">> Loaded %u creature addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  914. }
  915. CreatureAddon const* ObjectMgr::GetCreatureAddon(uint32 lowguid)
  916. {
  917. CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
  918. if (itr != _creatureAddonStore.end())
  919. return &(itr->second);
  920. return NULL;
  921. }
  922. CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry)
  923. {
  924. CreatureAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
  925. if (itr != _creatureTemplateAddonStore.end())
  926. return &(itr->second);
  927. return NULL;
  928. }
  929. EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry, int8& id)
  930. {
  931. EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
  932. if (itr == _equipmentInfoStore.end())
  933. return NULL;
  934. if (itr->second.empty())
  935. return NULL;
  936. if (id == -1) // select a random element
  937. {
  938. EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
  939. std::advance(ritr, urand(0u, itr->second.size() - 1));
  940. id = std::distance(itr->second.begin(), ritr) + 1;
  941. return &ritr->second;
  942. }
  943. else
  944. {
  945. EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
  946. if (itr2 != itr->second.end())
  947. return &itr2->second;
  948. }
  949. return NULL;
  950. }
  951. void ObjectMgr::LoadEquipmentTemplates()
  952. {
  953. uint32 oldMSTime = getMSTime();
  954. // 0 1 2 3 4
  955. QueryResult result = WorldDatabase.Query("SELECT entry, id, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template");
  956. if (!result)
  957. {
  958. TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
  959. return;
  960. }
  961. uint32 count = 0;
  962. do
  963. {
  964. Field* fields = result->Fetch();
  965. uint32 entry = fields[0].GetUInt32();
  966. if (!sObjectMgr->GetCreatureTemplate(entry))
  967. {
  968. TC_LOG_ERROR("sql.sql", "Creature template (Entry: %u) does not exist but has a record in `creature_equip_template`", entry);
  969. continue;
  970. }
  971. uint8 id = fields[1].GetUInt8();
  972. if (!id)
  973. {
  974. TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature %u, skipped.", entry);
  975. continue;
  976. }
  977. EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
  978. equipmentInfo.ItemEntry[0] = fields[2].GetUInt32();
  979. equipmentInfo.ItemEntry[1] = fields[3].GetUInt32();
  980. equipmentInfo.ItemEntry[2] = fields[4].GetUInt32();
  981. for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
  982. {
  983. if (!equipmentInfo.ItemEntry[i])
  984. continue;
  985. ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
  986. if (!dbcItem)
  987. {
  988. TC_LOG_ERROR("sql.sql", "Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u and id=%u, forced to 0.",
  989. equipmentInfo.ItemEntry[i], i+1, entry, id);
  990. equipmentInfo.ItemEntry[i] = 0;
  991. continue;
  992. }
  993. if (dbcItem->InventoryType != INVTYPE_WEAPON &&
  994. dbcItem->InventoryType != INVTYPE_SHIELD &&
  995. dbcItem->InventoryType != INVTYPE_RANGED &&
  996. dbcItem->InventoryType != INVTYPE_2HWEAPON &&
  997. dbcItem->InventoryType != INVTYPE_WEAPONMAINHAND &&
  998. dbcItem->InventoryType != INVTYPE_WEAPONOFFHAND &&
  999. dbcItem->InventoryType != INVTYPE_HOLDABLE &&
  1000. dbcItem->InventoryType != INVTYPE_THROWN &&
  1001. dbcItem->InventoryType != INVTYPE_RANGEDRIGHT)
  1002. {
  1003. 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.",
  1004. equipmentInfo.ItemEntry[i], i+1, entry, id);
  1005. equipmentInfo.ItemEntry[i] = 0;
  1006. }
  1007. }
  1008. ++count;
  1009. }
  1010. while (result->NextRow());
  1011. TC_LOG_INFO("server.loading", ">> Loaded %u equipment templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1012. }
  1013. CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId)
  1014. {
  1015. CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
  1016. if (itr != _creatureModelStore.end())
  1017. return &(itr->second);
  1018. return NULL;
  1019. }
  1020. uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= NULL*/)
  1021. {
  1022. // Load creature model (display id)
  1023. if (data && data->displayid)
  1024. return data->displayid;
  1025. return cinfo->GetRandomValidModelId();
  1026. }
  1027. void ObjectMgr::ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData* data /*= NULL*/)
  1028. {
  1029. npcflag = cinfo->npcflag;
  1030. unit_flags = cinfo->unit_flags;
  1031. dynamicflags = cinfo->dynamicflags;
  1032. if (data)
  1033. {
  1034. if (data->npcflag)
  1035. npcflag = data->npcflag;
  1036. if (data->unit_flags)
  1037. unit_flags = data->unit_flags;
  1038. if (data->dynamicflags)
  1039. dynamicflags = data->dynamicflags;
  1040. }
  1041. }
  1042. CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* displayID)
  1043. {
  1044. CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID);
  1045. if (!modelInfo)
  1046. return NULL;
  1047. // If a model for another gender exists, 50% chance to use it
  1048. if (modelInfo->modelid_other_gender != 0 && urand(0, 1) == 0)
  1049. {
  1050. CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->modelid_other_gender);
  1051. if (!minfo_tmp)
  1052. 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);
  1053. else
  1054. {
  1055. // Model ID changed
  1056. *displayID = modelInfo->modelid_other_gender;
  1057. return minfo_tmp;
  1058. }
  1059. }
  1060. return modelInfo;
  1061. }
  1062. void ObjectMgr::LoadCreatureModelInfo()
  1063. {
  1064. uint32 oldMSTime = getMSTime();
  1065. QueryResult result = WorldDatabase.Query("SELECT modelid, bounding_radius, combat_reach, gender, modelid_other_gender FROM creature_model_info");
  1066. if (!result)
  1067. {
  1068. TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
  1069. return;
  1070. }
  1071. _creatureModelStore.rehash(result->GetRowCount());
  1072. uint32 count = 0;
  1073. do
  1074. {
  1075. Field* fields = result->Fetch();
  1076. uint32 modelId = fields[0].GetUInt32();
  1077. CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
  1078. modelInfo.bounding_radius = fields[1].GetFloat();
  1079. modelInfo.combat_reach = fields[2].GetFloat();
  1080. modelInfo.gender = fields[3].GetUInt8();
  1081. modelInfo.modelid_other_gender = fields[4].GetUInt32();
  1082. // Checks
  1083. if (!sCreatureDisplayInfoStore.LookupEntry(modelId))
  1084. TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has model for nonexistent display id (%u).", modelId);
  1085. if (modelInfo.gender > GENDER_NONE)
  1086. {
  1087. TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(modelInfo.gender), modelId);
  1088. modelInfo.gender = GENDER_MALE;
  1089. }
  1090. if (modelInfo.modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.modelid_other_gender))
  1091. {
  1092. 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);
  1093. modelInfo.modelid_other_gender = 0;
  1094. }
  1095. if (modelInfo.combat_reach < 0.1f)
  1096. modelInfo.combat_reach = DEFAULT_COMBAT_REACH;
  1097. ++count;
  1098. }
  1099. while (result->NextRow());
  1100. TC_LOG_INFO("server.loading", ">> Loaded %u creature model based info in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1101. }
  1102. void ObjectMgr::LoadLinkedRespawn()
  1103. {
  1104. uint32 oldMSTime = getMSTime();
  1105. _linkedRespawnStore.clear();
  1106. // 0 1 2
  1107. QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
  1108. if (!result)
  1109. {
  1110. TC_LOG_ERROR("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
  1111. return;
  1112. }
  1113. do
  1114. {
  1115. Field* fields = result->Fetch();
  1116. uint32 guidLow = fields[0].GetUInt32();
  1117. uint32 linkedGuidLow = fields[1].GetUInt32();
  1118. uint8 linkType = fields[2].GetUInt8();
  1119. ObjectGuid guid, linkedGuid;
  1120. bool error = false;
  1121. switch (linkType)
  1122. {
  1123. case CREATURE_TO_CREATURE:
  1124. {
  1125. const CreatureData* slave = GetCreatureData(guidLow);
  1126. if (!slave)
  1127. {
  1128. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '%u' not found in creature table", guidLow);
  1129. error = true;
  1130. break;
  1131. }
  1132. const CreatureData* master = GetCreatureData(linkedGuidLow);
  1133. if (!master)
  1134. {
  1135. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '%u' not found in creature table", linkedGuidLow);
  1136. error = true;
  1137. break;
  1138. }
  1139. const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
  1140. if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
  1141. {
  1142. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1143. error = true;
  1144. break;
  1145. }
  1146. if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
  1147. {
  1148. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1149. error = true;
  1150. break;
  1151. }
  1152. guid = ObjectGuid(HIGHGUID_UNIT, slave->id, guidLow);
  1153. linkedGuid = ObjectGuid(HIGHGUID_UNIT, master->id, linkedGuidLow);
  1154. break;
  1155. }
  1156. case CREATURE_TO_GO:
  1157. {
  1158. const CreatureData* slave = GetCreatureData(guidLow);
  1159. if (!slave)
  1160. {
  1161. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '%u' not found in creature table", guidLow);
  1162. error = true;
  1163. break;
  1164. }
  1165. const GameObjectData* master = GetGOData(linkedGuidLow);
  1166. if (!master)
  1167. {
  1168. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
  1169. error = true;
  1170. break;
  1171. }
  1172. const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
  1173. if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
  1174. {
  1175. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1176. error = true;
  1177. break;
  1178. }
  1179. if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
  1180. {
  1181. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1182. error = true;
  1183. break;
  1184. }
  1185. guid = ObjectGuid(HIGHGUID_UNIT, slave->id, guidLow);
  1186. linkedGuid = ObjectGuid(HIGHGUID_GAMEOBJECT, master->id, linkedGuidLow);
  1187. break;
  1188. }
  1189. case GO_TO_GO:
  1190. {
  1191. const GameObjectData* slave = GetGOData(guidLow);
  1192. if (!slave)
  1193. {
  1194. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
  1195. error = true;
  1196. break;
  1197. }
  1198. const GameObjectData* master = GetGOData(linkedGuidLow);
  1199. if (!master)
  1200. {
  1201. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow);
  1202. error = true;
  1203. break;
  1204. }
  1205. const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
  1206. if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
  1207. {
  1208. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1209. error = true;
  1210. break;
  1211. }
  1212. if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
  1213. {
  1214. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1215. error = true;
  1216. break;
  1217. }
  1218. guid = ObjectGuid(HIGHGUID_GAMEOBJECT, slave->id, guidLow);
  1219. linkedGuid = ObjectGuid(HIGHGUID_GAMEOBJECT, master->id, linkedGuidLow);
  1220. break;
  1221. }
  1222. case GO_TO_CREATURE:
  1223. {
  1224. const GameObjectData* slave = GetGOData(guidLow);
  1225. if (!slave)
  1226. {
  1227. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow);
  1228. error = true;
  1229. break;
  1230. }
  1231. const CreatureData* master = GetCreatureData(linkedGuidLow);
  1232. if (!master)
  1233. {
  1234. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '%u' not found in creature table", linkedGuidLow);
  1235. error = true;
  1236. break;
  1237. }
  1238. const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
  1239. if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
  1240. {
  1241. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1242. error = true;
  1243. break;
  1244. }
  1245. if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
  1246. {
  1247. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1248. error = true;
  1249. break;
  1250. }
  1251. guid = ObjectGuid(HIGHGUID_GAMEOBJECT, slave->id, guidLow);
  1252. linkedGuid = ObjectGuid(HIGHGUID_UNIT, master->id, linkedGuidLow);
  1253. break;
  1254. }
  1255. }
  1256. if (!error)
  1257. _linkedRespawnStore[guid] = linkedGuid;
  1258. }
  1259. while (result->NextRow());
  1260. TC_LOG_INFO("server.loading", ">> Loaded " UI64FMTD " linked respawns in %u ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
  1261. }
  1262. bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guidLow, uint32 linkedGuidLow)
  1263. {
  1264. if (!guidLow)
  1265. return false;
  1266. CreatureData const* master = GetCreatureData(guidLow);
  1267. ASSERT(master);
  1268. ObjectGuid guid(HIGHGUID_UNIT, master->id, guidLow);
  1269. if (!linkedGuidLow) // we're removing the linking
  1270. {
  1271. _linkedRespawnStore.erase(guid);
  1272. PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN);
  1273. stmt->setUInt32(0, guidLow);
  1274. WorldDatabase.Execute(stmt);
  1275. return true;
  1276. }
  1277. CreatureData const* slave = GetCreatureData(linkedGuidLow);
  1278. if (!slave)
  1279. {
  1280. TC_LOG_ERROR("sql.sql", "Creature '%u' linking to non-existent creature '%u'.", guidLow, linkedGuidLow);
  1281. return false;
  1282. }
  1283. MapEntry const* map = sMapStore.LookupEntry(master->mapid);
  1284. if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
  1285. {
  1286. TC_LOG_ERROR("sql.sql", "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
  1287. return false;
  1288. }
  1289. if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
  1290. {
  1291. TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
  1292. return false;
  1293. }
  1294. ObjectGuid linkedGuid(HIGHGUID_UNIT, slave->id, linkedGuidLow);
  1295. _linkedRespawnStore[guid] = linkedGuid;
  1296. PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
  1297. stmt->setUInt32(0, guidLow);
  1298. stmt->setUInt32(1, linkedGuidLow);
  1299. WorldDatabase.Execute(stmt);
  1300. return true;
  1301. }
  1302. void ObjectMgr::LoadTempSummons()
  1303. {
  1304. uint32 oldMSTime = getMSTime();
  1305. _tempSummonDataStore.clear(); // needed for reload case
  1306. // 0 1 2 3 4 5 6 7 8 9
  1307. QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
  1308. if (!result)
  1309. {
  1310. TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
  1311. return;
  1312. }
  1313. uint32 count = 0;
  1314. do
  1315. {
  1316. Field* fields = result->Fetch();
  1317. uint32 summonerId = fields[0].GetUInt32();
  1318. SummonerType summonerType = SummonerType(fields[1].GetUInt8());
  1319. uint8 group = fields[2].GetUInt8();
  1320. switch (summonerType)
  1321. {
  1322. case SUMMONER_TYPE_CREATURE:
  1323. if (!GetCreatureTemplate(summonerId))
  1324. {
  1325. TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for creature summoner type, skipped.", summonerId);
  1326. continue;
  1327. }
  1328. break;
  1329. case SUMMONER_TYPE_GAMEOBJECT:
  1330. if (!GetGameObjectTemplate(summonerId))
  1331. {
  1332. TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for gameobject summoner type, skipped.", summonerId);
  1333. continue;
  1334. }
  1335. break;
  1336. case SUMMONER_TYPE_MAP:
  1337. if (!sMapStore.LookupEntry(summonerId))
  1338. {
  1339. TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry %u for map summoner type, skipped.", summonerId);
  1340. continue;
  1341. }
  1342. break;
  1343. default:
  1344. TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type %u for summoner %u, skipped.", summonerType, summonerId);
  1345. continue;
  1346. }
  1347. TempSummonData data;
  1348. data.entry = fields[3].GetUInt32();
  1349. if (!GetCreatureTemplate(data.entry))
  1350. {
  1351. 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);
  1352. continue;
  1353. }
  1354. float posX = fields[4].GetFloat();
  1355. float posY = fields[5].GetFloat();
  1356. float posZ = fields[6].GetFloat();
  1357. float orientation = fields[7].GetFloat();
  1358. data.pos.Relocate(posX, posY, posZ, orientation);
  1359. data.type = TempSummonType(fields[8].GetUInt8());
  1360. if (data.type > TEMPSUMMON_MANUAL_DESPAWN)
  1361. {
  1362. 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);
  1363. continue;
  1364. }
  1365. data.time = fields[9].GetUInt32();
  1366. TempSummonGroupKey key(summonerId, summonerType, group);
  1367. _tempSummonDataStore[key].push_back(data);
  1368. ++count;
  1369. } while (result->NextRow());
  1370. TC_LOG_INFO("server.loading", ">> Loaded %u temp summons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1371. }
  1372. void ObjectMgr::LoadCreatures()
  1373. {
  1374. uint32 oldMSTime = getMSTime();
  1375. // 0 1 2 3 4 5 6 7 8 9 10
  1376. QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, "
  1377. // 11 12 13 14 15 16 17 18 19 20 21 22 23
  1378. "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, creature.phaseid, creature.phasegroup "
  1379. "FROM creature "
  1380. "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
  1381. "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
  1382. if (!result)
  1383. {
  1384. TC_LOG_ERROR("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
  1385. return;
  1386. }
  1387. // Build single time for check spawnmask
  1388. std::map<uint32, uint32> spawnMasks;
  1389. for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
  1390. if (sMapStore.LookupEntry(i))
  1391. for (int k = 0; k < MAX_DIFFICULTY; ++k)
  1392. if (GetMapDifficultyData(i, Difficulty(k)))
  1393. spawnMasks[i] |= (1 << k);
  1394. _creatureDataStore.rehash(result->GetRowCount());
  1395. uint32 count = 0;
  1396. do
  1397. {
  1398. Field* fields = result->Fetch();
  1399. uint32 guid = fields[0].GetUInt32();
  1400. uint32 entry = fields[1].GetUInt32();
  1401. CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
  1402. if (!cInfo)
  1403. {
  1404. TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
  1405. continue;
  1406. }
  1407. CreatureData& data = _creatureDataStore[guid];
  1408. data.id = entry;
  1409. data.mapid = fields[2].GetUInt16();
  1410. data.displayid = fields[3].GetUInt32();
  1411. data.equipmentId = fields[4].GetInt8();
  1412. data.posX = fields[5].GetFloat();
  1413. data.posY = fields[6].GetFloat();
  1414. data.posZ = fields[7].GetFloat();
  1415. data.orientation = fields[8].GetFloat();
  1416. data.spawntimesecs = fields[9].GetUInt32();
  1417. data.spawndist = fields[10].GetFloat();
  1418. data.currentwaypoint= fields[11].GetUInt32();
  1419. data.curhealth = fields[12].GetUInt32();
  1420. data.curmana = fields[13].GetUInt32();
  1421. data.movementType = fields[14].GetUInt8();
  1422. data.spawnMask = fields[15].GetUInt8();
  1423. data.phaseMask = fields[16].GetUInt32();
  1424. int16 gameEvent = fields[17].GetInt8();
  1425. uint32 PoolId = fields[18].GetUInt32();
  1426. data.npcflag = fields[19].GetUInt32();
  1427. data.unit_flags = fields[20].GetUInt32();
  1428. data.dynamicflags = fields[21].GetUInt32();
  1429. data.phaseid = fields[22].GetUInt32();
  1430. data.phaseGroup = fields[23].GetUInt32();
  1431. MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
  1432. if (!mapEntry)
  1433. {
  1434. TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid);
  1435. continue;
  1436. }
  1437. // Skip spawnMask check for transport maps
  1438. if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
  1439. 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.mapid);
  1440. bool ok = true;
  1441. for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
  1442. {
  1443. if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
  1444. {
  1445. TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that is listed as difficulty %u template (entry: %u) in `creature_template`, skipped.",
  1446. guid, diff + 1, data.id);
  1447. ok = false;
  1448. }
  1449. }
  1450. if (!ok)
  1451. continue;
  1452. // -1 random, 0 no equipment,
  1453. if (data.equipmentId != 0)
  1454. {
  1455. if (!GetEquipmentInfo(data.id, data.equipmentId))
  1456. {
  1457. 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);
  1458. data.equipmentId = 0;
  1459. }
  1460. }
  1461. if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
  1462. {
  1463. if (!mapEntry || !mapEntry->IsDungeon())
  1464. 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);
  1465. }
  1466. if (data.spawndist < 0.0f)
  1467. {
  1468. TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
  1469. data.spawndist = 0.0f;
  1470. }
  1471. else if (data.movementType == RANDOM_MOTION_TYPE)
  1472. {
  1473. if (data.spawndist == 0.0f)
  1474. {
  1475. 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);
  1476. data.movementType = IDLE_MOTION_TYPE;
  1477. }
  1478. }
  1479. else if (data.movementType == IDLE_MOTION_TYPE)
  1480. {
  1481. if (data.spawndist != 0.0f)
  1482. {
  1483. 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);
  1484. data.spawndist = 0.0f;
  1485. }
  1486. }
  1487. if (std::abs(data.orientation) > 2 * float(M_PI))
  1488. {
  1489. TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
  1490. data.orientation = Position::NormalizeOrientation(data.orientation);
  1491. }
  1492. if (data.phaseMask == 0)
  1493. {
  1494. 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);
  1495. data.phaseMask = 1;
  1496. }
  1497. if (data.phaseGroup && data.phaseid)
  1498. {
  1499. TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: %u Entry: %u) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
  1500. data.phaseGroup = 0;
  1501. }
  1502. if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA))
  1503. {
  1504. uint32 zoneId = 0;
  1505. uint32 areaId = 0;
  1506. sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
  1507. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
  1508. stmt->setUInt32(0, zoneId);
  1509. stmt->setUInt32(1, areaId);
  1510. stmt->setUInt64(2, guid);
  1511. WorldDatabase.Execute(stmt);
  1512. }
  1513. // Add to grid if not managed by the game event or pool system
  1514. if (gameEvent == 0 && PoolId == 0)
  1515. AddCreatureToGrid(guid, &data);
  1516. ++count;
  1517. } while (result->NextRow());
  1518. TC_LOG_INFO("server.loading", ">> Loaded %u creatures in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  1519. }
  1520. void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
  1521. {
  1522. uint8 mask = data->spawnMask;
  1523. for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1524. {
  1525. if (mask & 1)
  1526. {
  1527. CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
  1528. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
  1529. cell_guids.creatures.insert(guid);
  1530. }
  1531. }
  1532. }
  1533. void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
  1534. {
  1535. uint8 mask = data->spawnMask;
  1536. for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1537. {
  1538. if (mask & 1)
  1539. {
  1540. CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
  1541. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
  1542. cell_guids.creatures.erase(guid);
  1543. }
  1544. }
  1545. }
  1546. uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
  1547. {
  1548. GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
  1549. if (!goinfo)
  1550. return 0;
  1551. Map* map = sMapMgr->CreateBaseMap(mapId);
  1552. if (!map)
  1553. return 0;
  1554. uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT);
  1555. GameObjectData& data = NewGOData(guid);
  1556. data.id = entry;
  1557. data.mapid = mapId;
  1558. data.posX = x;
  1559. data.posY = y;
  1560. data.posZ = z;
  1561. data.orientation = o;
  1562. data.rotation0 = rotation0;
  1563. data.rotation1 = rotation1;
  1564. data.rotation2 = rotation2;
  1565. data.rotation3 = rotation3;
  1566. data.spawntimesecs = spawntimedelay;
  1567. data.animprogress = 100;
  1568. data.spawnMask = 1;
  1569. data.go_state = GO_STATE_READY;
  1570. data.phaseMask = PHASEMASK_NORMAL;
  1571. data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
  1572. data.dbData = false;
  1573. AddGameobjectToGrid(guid, &data);
  1574. // Spawn if necessary (loaded grids only)
  1575. // We use spawn coords to spawn
  1576. if (!map->Instanceable() && map->IsGridLoaded(x, y))
  1577. {
  1578. GameObject* go = new GameObject;
  1579. if (!go->LoadGameObjectFromDB(guid, map))
  1580. {
  1581. TC_LOG_ERROR("misc", "AddGOData: cannot add gameobject entry %u to map", entry);
  1582. delete go;
  1583. return 0;
  1584. }
  1585. }
  1586. TC_LOG_DEBUG("maps", "AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, x, y, z, o);
  1587. return guid;
  1588. }
  1589. bool ObjectMgr::MoveCreData(uint32 guid, uint32 mapId, const Position& pos)
  1590. {
  1591. CreatureData& data = NewOrExistCreatureData(guid);
  1592. if (!data.id)
  1593. return false;
  1594. RemoveCreatureFromGrid(guid, &data);
  1595. if (data.posX == pos.GetPositionX() && data.posY == pos.GetPositionY() && data.posZ == pos.GetPositionZ())
  1596. return true;
  1597. data.posX = pos.GetPositionX();
  1598. data.posY = pos.GetPositionY();
  1599. data.posZ = pos.GetPositionZ();
  1600. data.orientation = pos.GetOrientation();
  1601. AddCreatureToGrid(guid, &data);
  1602. // Spawn if necessary (loaded grids only)
  1603. if (Map* map = sMapMgr->CreateBaseMap(mapId))
  1604. {
  1605. // We use spawn coords to spawn
  1606. if (!map->Instanceable() && map->IsGridLoaded(data.posX, data.posY))
  1607. {
  1608. Creature* creature = new Creature();
  1609. if (!creature->LoadCreatureFromDB(guid, map))
  1610. {
  1611. TC_LOG_ERROR("misc", "MoveCreData: Cannot add creature guid %u to map", guid);
  1612. delete creature;
  1613. return false;
  1614. }
  1615. }
  1616. }
  1617. return true;
  1618. }
  1619. uint32 ObjectMgr::AddCreData(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay /*= 0*/)
  1620. {
  1621. CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
  1622. if (!cInfo)
  1623. return 0;
  1624. uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
  1625. CreatureBaseStats const* stats = GetCreatureBaseStats(level, cInfo->unit_class);
  1626. uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
  1627. CreatureData& data = NewOrExistCreatureData(guid);
  1628. data.id = entry;
  1629. data.mapid = mapId;
  1630. data.displayid = 0;
  1631. data.equipmentId = 0;
  1632. data.posX = x;
  1633. data.posY = y;
  1634. data.posZ = z;
  1635. data.orientation = o;
  1636. data.spawntimesecs = spawntimedelay;
  1637. data.spawndist = 0;
  1638. data.currentwaypoint = 0;
  1639. data.curhealth = stats->GenerateHealth(cInfo);
  1640. data.curmana = stats->GenerateMana(cInfo);
  1641. data.movementType = cInfo->MovementType;
  1642. data.spawnMask = 1;
  1643. data.phaseMask = PHASEMASK_NORMAL;
  1644. data.dbData = false;
  1645. data.npcflag = cInfo->npcflag;
  1646. data.unit_flags = cInfo->unit_flags;
  1647. data.dynamicflags = cInfo->dynamicflags;
  1648. AddCreatureToGrid(guid, &data);
  1649. // Spawn if necessary (loaded grids only)
  1650. if (Map* map = sMapMgr->CreateBaseMap(mapId))
  1651. {
  1652. // We use spawn coords to spawn
  1653. if (!map->Instanceable() && !map->IsRemovalGrid(x, y))
  1654. {
  1655. Creature* creature = new Creature();
  1656. if (!creature->LoadCreatureFromDB(guid, map))
  1657. {
  1658. TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry);
  1659. delete creature;
  1660. return 0;
  1661. }
  1662. }
  1663. }
  1664. return guid;
  1665. }
  1666. void ObjectMgr::LoadGameobjects()
  1667. {
  1668. uint32 oldMSTime = getMSTime();
  1669. uint32 count = 0;
  1670. // 0 1 2 3 4 5 6
  1671. QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
  1672. // 7 8 9 10 11 12 13 14 15 16 17 18 19
  1673. "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, eventEntry, pool_entry, phaseid, phasegroup "
  1674. "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
  1675. "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");
  1676. if (!result)
  1677. {
  1678. TC_LOG_ERROR("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
  1679. return;
  1680. }
  1681. // build single time for check spawnmask
  1682. std::map<uint32, uint32> spawnMasks;
  1683. for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
  1684. if (sMapStore.LookupEntry(i))
  1685. for (int k = 0; k < MAX_DIFFICULTY; ++k)
  1686. if (GetMapDifficultyData(i, Difficulty(k)))
  1687. spawnMasks[i] |= (1 << k);
  1688. _gameObjectDataStore.rehash(result->GetRowCount());
  1689. do
  1690. {
  1691. Field* fields = result->Fetch();
  1692. uint32 guid = fields[0].GetUInt32();
  1693. uint32 entry = fields[1].GetUInt32();
  1694. GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
  1695. if (!gInfo)
  1696. {
  1697. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
  1698. continue;
  1699. }
  1700. if (!gInfo->displayId)
  1701. {
  1702. switch (gInfo->type)
  1703. {
  1704. case GAMEOBJECT_TYPE_TRAP:
  1705. case GAMEOBJECT_TYPE_SPELL_FOCUS:
  1706. break;
  1707. default:
  1708. 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);
  1709. break;
  1710. }
  1711. }
  1712. if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
  1713. {
  1714. 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);
  1715. continue;
  1716. }
  1717. GameObjectData& data = _gameObjectDataStore[guid];
  1718. data.id = entry;
  1719. data.mapid = fields[2].GetUInt16();
  1720. data.posX = fields[3].GetFloat();
  1721. data.posY = fields[4].GetFloat();
  1722. data.posZ = fields[5].GetFloat();
  1723. data.orientation = fields[6].GetFloat();
  1724. data.rotation0 = fields[7].GetFloat();
  1725. data.rotation1 = fields[8].GetFloat();
  1726. data.rotation2 = fields[9].GetFloat();
  1727. data.rotation3 = fields[10].GetFloat();
  1728. data.spawntimesecs = fields[11].GetInt32();
  1729. MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
  1730. if (!mapEntry)
  1731. {
  1732. 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.mapid);
  1733. continue;
  1734. }
  1735. if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
  1736. {
  1737. 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);
  1738. }
  1739. data.animprogress = fields[12].GetUInt8();
  1740. data.artKit = 0;
  1741. uint32 go_state = fields[13].GetUInt8();
  1742. if (go_state >= MAX_GO_STATE)
  1743. {
  1744. if (gInfo->type != GAMEOBJECT_TYPE_TRANSPORT || go_state > GO_STATE_TRANSPORT_ACTIVE + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)
  1745. {
  1746. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
  1747. continue;
  1748. }
  1749. }
  1750. data.go_state = GOState(go_state);
  1751. data.spawnMask = fields[14].GetUInt8();
  1752. if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid])
  1753. 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.mapid);
  1754. data.phaseMask = fields[15].GetUInt32();
  1755. int16 gameEvent = fields[16].GetInt8();
  1756. uint32 PoolId = fields[17].GetUInt32();
  1757. data.phaseid = fields[18].GetUInt32();
  1758. data.phaseGroup = fields[19].GetUInt32();
  1759. if (data.phaseGroup && data.phaseid)
  1760. {
  1761. TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: %u Entry: %u) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
  1762. data.phaseGroup = 0;
  1763. }
  1764. if (std::abs(data.orientation) > 2 * float(M_PI))
  1765. {
  1766. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id);
  1767. data.orientation = Position::NormalizeOrientation(data.orientation);
  1768. }
  1769. if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
  1770. {
  1771. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
  1772. continue;
  1773. }
  1774. if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
  1775. {
  1776. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
  1777. continue;
  1778. }
  1779. if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
  1780. {
  1781. TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
  1782. continue;
  1783. }
  1784. if (data.phaseMask == 0)
  1785. {
  1786. 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);
  1787. data.phaseMask = 1;
  1788. }
  1789. if (sWorld->getBoolConfig(CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA))
  1790. {
  1791. uint32 zoneId = 0;
  1792. uint32 areaId = 0;
  1793. sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ);
  1794. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
  1795. stmt->setUInt32(0, zoneId);
  1796. stmt->setUInt32(1, areaId);
  1797. stmt->setUInt64(2, guid);
  1798. WorldDatabase.Execute(stmt);
  1799. }
  1800. if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
  1801. AddGameobjectToGrid(guid, &data);
  1802. ++count;
  1803. } while (result->NextRow());
  1804. TC_LOG_INFO("server.loading", ">> Loaded %lu gameobjects in %u ms", (unsigned long)_gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
  1805. }
  1806. void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
  1807. {
  1808. uint8 mask = data->spawnMask;
  1809. for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1810. {
  1811. if (mask & 1)
  1812. {
  1813. CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
  1814. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
  1815. cell_guids.gameobjects.insert(guid);
  1816. }
  1817. }
  1818. }
  1819. void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
  1820. {
  1821. uint8 mask = data->spawnMask;
  1822. for (uint8 i = 0; mask != 0; i++, mask >>= 1)
  1823. {
  1824. if (mask & 1)
  1825. {
  1826. CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY);
  1827. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()];
  1828. cell_guids.gameobjects.erase(guid);
  1829. }
  1830. }
  1831. }
  1832. Player* ObjectMgr::GetPlayerByLowGUID(uint32 lowguid) const
  1833. {
  1834. ObjectGuid guid(HIGHGUID_PLAYER, lowguid);
  1835. return ObjectAccessor::FindPlayer(guid);
  1836. }
  1837. // name must be checked to correctness (if received) before call this function
  1838. ObjectGuid ObjectMgr::GetPlayerGUIDByName(std::string const& name) const
  1839. {
  1840. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME);
  1841. stmt->setString(0, name);
  1842. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1843. if (result)
  1844. return ObjectGuid(HIGHGUID_PLAYER, (*result)[0].GetUInt32());
  1845. return ObjectGuid::Empty;
  1846. }
  1847. bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string& name) const
  1848. {
  1849. // prevent DB access for online player
  1850. if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
  1851. {
  1852. name = player->GetName();
  1853. return true;
  1854. }
  1855. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME);
  1856. stmt->setUInt32(0, guid.GetCounter());
  1857. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1858. if (result)
  1859. {
  1860. name = (*result)[0].GetString();
  1861. return true;
  1862. }
  1863. return false;
  1864. }
  1865. uint32 ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const
  1866. {
  1867. // prevent DB access for online player
  1868. if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
  1869. {
  1870. return Player::TeamForRace(player->getRace());
  1871. }
  1872. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_RACE);
  1873. stmt->setUInt32(0, guid.GetCounter());
  1874. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1875. if (result)
  1876. {
  1877. uint8 race = (*result)[0].GetUInt8();
  1878. return Player::TeamForRace(race);
  1879. }
  1880. return 0;
  1881. }
  1882. uint32 ObjectMgr::GetPlayerAccountIdByGUID(ObjectGuid guid) const
  1883. {
  1884. // prevent DB access for online player
  1885. if (Player* player = ObjectAccessor::FindConnectedPlayer(guid))
  1886. {
  1887. return player->GetSession()->GetAccountId();
  1888. }
  1889. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BY_GUID);
  1890. stmt->setUInt32(0, guid.GetCounter());
  1891. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1892. if (result)
  1893. {
  1894. uint32 acc = (*result)[0].GetUInt32();
  1895. return acc;
  1896. }
  1897. return 0;
  1898. }
  1899. uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
  1900. {
  1901. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_BY_NAME);
  1902. stmt->setString(0, name);
  1903. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1904. if (result)
  1905. {
  1906. uint32 acc = (*result)[0].GetUInt32();
  1907. return acc;
  1908. }
  1909. return 0;
  1910. }
  1911. void ObjectMgr::LoadItemLocales()
  1912. {
  1913. uint32 oldMSTime = getMSTime();
  1914. _itemLocaleStore.clear(); // need for reload case
  1915. QueryResult result = WorldDatabase.Query("SELECT entry, name_loc1, description_loc1, name_loc2, description_loc2, name_loc3, description_loc3, name_loc4, description_loc4, name_loc5, description_loc5, name_loc6, description_loc6, name_loc7, description_loc7, name_loc8, description_loc8 FROM locales_item");
  1916. if (!result)
  1917. return;
  1918. do
  1919. {
  1920. Field* fields = result->Fetch();
  1921. uint32 entry = fields[0].GetUInt32();
  1922. ItemLocale& data = _itemLocaleStore[entry];
  1923. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  1924. {
  1925. LocaleConstant locale = (LocaleConstant) i;
  1926. AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.Name);
  1927. AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.Description);
  1928. }
  1929. } while (result->NextRow());
  1930. TC_LOG_INFO("server.loading", ">> Loaded %u Item locale strings in %u ms", uint32(_itemLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  1931. }
  1932. void FillItemDamageFields(float* minDamage, float* maxDamage, float* dps, uint32 itemLevel, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32 delay, float statScalingFactor, uint32 inventoryType, uint32 flags2)
  1933. {
  1934. *minDamage = *maxDamage = *dps = 0.0f;
  1935. if (itemClass != ITEM_CLASS_WEAPON || quality > ITEM_QUALITY_ARTIFACT)
  1936. return;
  1937. DBCStorage<ItemDamageEntry>* store = NULL;
  1938. // get the right store here
  1939. if (inventoryType > 0xD + 13)
  1940. return;
  1941. switch (inventoryType)
  1942. {
  1943. case INVTYPE_AMMO:
  1944. store = &sItemDamageAmmoStore;
  1945. break;
  1946. case INVTYPE_2HWEAPON:
  1947. if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON)
  1948. store = &sItemDamageTwoHandCasterStore;
  1949. else
  1950. store = &sItemDamageTwoHandStore;
  1951. break;
  1952. case INVTYPE_RANGED:
  1953. case INVTYPE_THROWN:
  1954. case INVTYPE_RANGEDRIGHT:
  1955. switch (itemSubClass)
  1956. {
  1957. case ITEM_SUBCLASS_WEAPON_WAND:
  1958. store = &sItemDamageWandStore;
  1959. break;
  1960. case ITEM_SUBCLASS_WEAPON_THROWN:
  1961. store = &sItemDamageThrownStore;
  1962. break;
  1963. case ITEM_SUBCLASS_WEAPON_BOW:
  1964. case ITEM_SUBCLASS_WEAPON_GUN:
  1965. case ITEM_SUBCLASS_WEAPON_CROSSBOW:
  1966. store = &sItemDamageRangedStore;
  1967. break;
  1968. default:
  1969. return;
  1970. }
  1971. break;
  1972. case INVTYPE_WEAPON:
  1973. case INVTYPE_WEAPONMAINHAND:
  1974. case INVTYPE_WEAPONOFFHAND:
  1975. if (flags2 & ITEM_FLAGS_EXTRA_CASTER_WEAPON)
  1976. store = &sItemDamageOneHandCasterStore;
  1977. else
  1978. store = &sItemDamageOneHandStore;
  1979. break;
  1980. default:
  1981. return;
  1982. }
  1983. if (!store)
  1984. return;
  1985. ItemDamageEntry const* damageInfo = store->LookupEntry(itemLevel);
  1986. if (!damageInfo)
  1987. return;
  1988. *dps = damageInfo->DPS[quality];
  1989. float avgDamage = *dps * delay * 0.001f;
  1990. *minDamage = (statScalingFactor * -0.5f + 1.0f) * avgDamage;
  1991. *maxDamage = floor(float(avgDamage* (statScalingFactor * 0.5f + 1.0f) + 0.5f));
  1992. }
  1993. uint32 FillItemArmor(uint32 itemlevel, uint32 itemClass, uint32 itemSubclass, uint32 quality, uint32 inventoryType)
  1994. {
  1995. if (quality > ITEM_QUALITY_ARTIFACT)
  1996. return 0;
  1997. // all items but shields
  1998. if (itemClass != ITEM_CLASS_ARMOR || itemSubclass != ITEM_SUBCLASS_ARMOR_SHIELD)
  1999. {
  2000. ItemArmorQualityEntry const* armorQuality = sItemArmorQualityStore.LookupEntry(itemlevel);
  2001. ItemArmorTotalEntry const* armorTotal = sItemArmorTotalStore.LookupEntry(itemlevel);
  2002. if (!armorQuality || !armorTotal)
  2003. return 0;
  2004. if (inventoryType == INVTYPE_ROBE)
  2005. inventoryType = INVTYPE_CHEST;
  2006. ArmorLocationEntry const* location = sArmorLocationStore.LookupEntry(inventoryType);
  2007. if (!location)
  2008. return 0;
  2009. if (itemSubclass < ITEM_SUBCLASS_ARMOR_CLOTH || itemSubclass > ITEM_SUBCLASS_ARMOR_PLATE)
  2010. return 0;
  2011. return uint32(armorQuality->Value[quality] * armorTotal->Value[itemSubclass - 1] * location->Value[itemSubclass - 1] + 0.5f);
  2012. }
  2013. // shields
  2014. ItemArmorShieldEntry const* shield = sItemArmorShieldStore.LookupEntry(itemlevel);
  2015. if (!shield)
  2016. return 0;
  2017. return uint32(shield->Value[quality] + 0.5f);
  2018. }
  2019. uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
  2020. {
  2021. if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON)
  2022. return 0;
  2023. static float const qualityMultipliers[MAX_ITEM_QUALITY] =
  2024. {
  2025. 1.0f, 1.0f, 1.0f, 1.17f, 1.37f, 1.68f, 0.0f, 0.0f
  2026. };
  2027. static float const armorMultipliers[MAX_INVTYPE] =
  2028. {
  2029. 0.00f, // INVTYPE_NON_EQUIP
  2030. 0.59f, // INVTYPE_HEAD
  2031. 0.00f, // INVTYPE_NECK
  2032. 0.59f, // INVTYPE_SHOULDERS
  2033. 0.00f, // INVTYPE_BODY
  2034. 1.00f, // INVTYPE_CHEST
  2035. 0.35f, // INVTYPE_WAIST
  2036. 0.75f, // INVTYPE_LEGS
  2037. 0.49f, // INVTYPE_FEET
  2038. 0.35f, // INVTYPE_WRISTS
  2039. 0.35f, // INVTYPE_HANDS
  2040. 0.00f, // INVTYPE_FINGER
  2041. 0.00f, // INVTYPE_TRINKET
  2042. 0.00f, // INVTYPE_WEAPON
  2043. 1.00f, // INVTYPE_SHIELD
  2044. 0.00f, // INVTYPE_RANGED
  2045. 0.00f, // INVTYPE_CLOAK
  2046. 0.00f, // INVTYPE_2HWEAPON
  2047. 0.00f, // INVTYPE_BAG
  2048. 0.00f, // INVTYPE_TABARD
  2049. 1.00f, // INVTYPE_ROBE
  2050. 0.00f, // INVTYPE_WEAPONMAINHAND
  2051. 0.00f, // INVTYPE_WEAPONOFFHAND
  2052. 0.00f, // INVTYPE_HOLDABLE
  2053. 0.00f, // INVTYPE_AMMO
  2054. 0.00f, // INVTYPE_THROWN
  2055. 0.00f, // INVTYPE_RANGEDRIGHT
  2056. 0.00f, // INVTYPE_QUIVER
  2057. 0.00f, // INVTYPE_RELIC
  2058. };
  2059. static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] =
  2060. {
  2061. 0.89f, // ITEM_SUBCLASS_WEAPON_AXE
  2062. 1.03f, // ITEM_SUBCLASS_WEAPON_AXE2
  2063. 0.77f, // ITEM_SUBCLASS_WEAPON_BOW
  2064. 0.77f, // ITEM_SUBCLASS_WEAPON_GUN
  2065. 0.89f, // ITEM_SUBCLASS_WEAPON_MACE
  2066. 1.03f, // ITEM_SUBCLASS_WEAPON_MACE2
  2067. 1.03f, // ITEM_SUBCLASS_WEAPON_POLEARM
  2068. 0.89f, // ITEM_SUBCLASS_WEAPON_SWORD
  2069. 1.03f, // ITEM_SUBCLASS_WEAPON_SWORD2
  2070. 0.00f, // ITEM_SUBCLASS_WEAPON_Obsolete
  2071. 1.03f, // ITEM_SUBCLASS_WEAPON_STAFF
  2072. 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC
  2073. 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2
  2074. 0.64f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON
  2075. 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
  2076. 0.64f, // ITEM_SUBCLASS_WEAPON_DAGGER
  2077. 0.64f, // ITEM_SUBCLASS_WEAPON_THROWN
  2078. 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR
  2079. 0.77f, // ITEM_SUBCLASS_WEAPON_CROSSBOW
  2080. 0.64f, // ITEM_SUBCLASS_WEAPON_WAND
  2081. 0.64f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE
  2082. };
  2083. float levelPenalty = 1.0f;
  2084. if (itemLevel <= 28)
  2085. levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f;
  2086. if (itemClass == ITEM_CLASS_ARMOR)
  2087. {
  2088. if (inventoryType > INVTYPE_ROBE)
  2089. return 0;
  2090. return 5 * uint32(23.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty + 0.5f);
  2091. }
  2092. return 5 * uint32(17.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty + 0.5f);
  2093. };
  2094. void FillDisenchantFields(uint32* disenchantID, uint32* requiredDisenchantSkill, ItemTemplate const& itemTemplate)
  2095. {
  2096. *disenchantID = 0;
  2097. *(int32*)requiredDisenchantSkill = -1;
  2098. if ((itemTemplate.Flags & (ITEM_PROTO_FLAG_CONJURED | ITEM_PROTO_FLAG_UNK6)) ||
  2099. itemTemplate.Bonding == BIND_QUEST_ITEM || itemTemplate.Area || itemTemplate.Map ||
  2100. itemTemplate.Stackable > 1 ||
  2101. itemTemplate.Quality < ITEM_QUALITY_UNCOMMON || itemTemplate.Quality > ITEM_QUALITY_EPIC ||
  2102. !(itemTemplate.Class == ITEM_CLASS_ARMOR || itemTemplate.Class == ITEM_CLASS_WEAPON) ||
  2103. !(Item::GetSpecialPrice(&itemTemplate) || sItemCurrencyCostStore.LookupEntry(itemTemplate.ItemId)))
  2104. return;
  2105. for (uint32 i = 0; i < sItemDisenchantLootStore.GetNumRows(); ++i)
  2106. {
  2107. ItemDisenchantLootEntry const* disenchant = sItemDisenchantLootStore.LookupEntry(i);
  2108. if (!disenchant)
  2109. continue;
  2110. if (disenchant->ItemClass == itemTemplate.Class &&
  2111. disenchant->ItemQuality == itemTemplate.Quality &&
  2112. disenchant->MinItemLevel <= itemTemplate.ItemLevel &&
  2113. disenchant->MaxItemLevel >= itemTemplate.ItemLevel)
  2114. {
  2115. if (disenchant->Id == 60 || disenchant->Id == 61) // epic item disenchant ilvl range 66-99 (classic)
  2116. {
  2117. if (itemTemplate.RequiredLevel > 60 || itemTemplate.RequiredSkillRank > 300)
  2118. continue; // skip to epic item disenchant ilvl range 90-199 (TBC)
  2119. }
  2120. else if (disenchant->Id == 66 || disenchant->Id == 67) // epic item disenchant ilvl range 90-199 (TBC)
  2121. {
  2122. if (itemTemplate.RequiredLevel <= 60 || (itemTemplate.RequiredSkill && itemTemplate.RequiredSkillRank <= 300))
  2123. continue;
  2124. }
  2125. *disenchantID = disenchant->Id;
  2126. *requiredDisenchantSkill = disenchant->RequiredDisenchantSkill;
  2127. return;
  2128. }
  2129. }
  2130. }
  2131. void ObjectMgr::LoadItemTemplates()
  2132. {
  2133. uint32 oldMSTime = getMSTime();
  2134. uint32 sparseCount = 0;
  2135. uint32 dbCount = 0;
  2136. for (uint32 itemId = 0; itemId < sItemSparseStore.GetNumRows(); ++itemId)
  2137. {
  2138. ItemSparseEntry const* sparse = sItemSparseStore.LookupEntry(itemId);
  2139. ItemEntry const* db2Data = sItemStore.LookupEntry(itemId);
  2140. if (!sparse || !db2Data)
  2141. continue;
  2142. ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
  2143. itemTemplate.ItemId = itemId;
  2144. itemTemplate.Class = db2Data->Class;
  2145. itemTemplate.SubClass = db2Data->SubClass;
  2146. itemTemplate.SoundOverrideSubclass = db2Data->SoundOverrideSubclass;
  2147. itemTemplate.Name1 = sparse->Name->Str[sWorld->GetDefaultDbcLocale()];
  2148. itemTemplate.DisplayInfoID = db2Data->DisplayId;
  2149. itemTemplate.Quality = sparse->Quality;
  2150. itemTemplate.Flags = sparse->Flags;
  2151. itemTemplate.Flags2 = sparse->Flags2;
  2152. itemTemplate.Unk430_1 = sparse->Unk430_1;
  2153. itemTemplate.Unk430_2 = sparse->Unk430_2;
  2154. itemTemplate.BuyCount = std::max(sparse->BuyCount, 1u);
  2155. itemTemplate.BuyPrice = sparse->BuyPrice;
  2156. itemTemplate.SellPrice = sparse->SellPrice;
  2157. itemTemplate.InventoryType = db2Data->InventoryType;
  2158. itemTemplate.AllowableClass = sparse->AllowableClass;
  2159. itemTemplate.AllowableRace = sparse->AllowableRace;
  2160. itemTemplate.ItemLevel = sparse->ItemLevel;
  2161. itemTemplate.RequiredLevel = sparse->RequiredLevel;
  2162. itemTemplate.RequiredSkill = sparse->RequiredSkill;
  2163. itemTemplate.RequiredSkillRank = sparse->RequiredSkillRank;
  2164. itemTemplate.RequiredSpell = sparse->RequiredSpell;
  2165. itemTemplate.RequiredHonorRank = sparse->RequiredHonorRank;
  2166. itemTemplate.RequiredCityRank = sparse->RequiredCityRank;
  2167. itemTemplate.RequiredReputationFaction = sparse->RequiredReputationFaction;
  2168. itemTemplate.RequiredReputationRank = sparse->RequiredReputationRank;
  2169. itemTemplate.MaxCount = sparse->MaxCount;
  2170. itemTemplate.Stackable = sparse->Stackable;
  2171. itemTemplate.ContainerSlots = sparse->ContainerSlots;
  2172. for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
  2173. {
  2174. itemTemplate.ItemStat[i].ItemStatType = sparse->ItemStatType[i];
  2175. itemTemplate.ItemStat[i].ItemStatValue = sparse->ItemStatValue[i];
  2176. itemTemplate.ItemStat[i].ItemStatUnk1 = sparse->ItemStatUnk1[i];
  2177. itemTemplate.ItemStat[i].ItemStatUnk2 = sparse->ItemStatUnk2[i];
  2178. }
  2179. itemTemplate.ScalingStatDistribution = sparse->ScalingStatDistribution;
  2180. // cache item damage
  2181. FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, sparse->ItemLevel,
  2182. db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->Delay, sparse->StatScalingFactor,
  2183. sparse->InventoryType, sparse->Flags2);
  2184. itemTemplate.DamageType = sparse->DamageType;
  2185. itemTemplate.Armor = FillItemArmor(sparse->ItemLevel, db2Data->Class, db2Data->SubClass, sparse->Quality, sparse->InventoryType);
  2186. itemTemplate.Delay = sparse->Delay;
  2187. itemTemplate.RangedModRange = sparse->RangedModRange;
  2188. for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  2189. {
  2190. itemTemplate.Spells[i].SpellId = sparse->SpellId[i];
  2191. itemTemplate.Spells[i].SpellTrigger = sparse->SpellTrigger[i];
  2192. itemTemplate.Spells[i].SpellCharges = sparse->SpellCharges[i];
  2193. itemTemplate.Spells[i].SpellCooldown = sparse->SpellCooldown[i];
  2194. itemTemplate.Spells[i].SpellCategory = sparse->SpellCategory[i];
  2195. itemTemplate.Spells[i].SpellCategoryCooldown = sparse->SpellCategoryCooldown[i];
  2196. }
  2197. itemTemplate.SpellPPMRate = 0.0f;
  2198. itemTemplate.Bonding = sparse->Bonding;
  2199. itemTemplate.Description = sparse->Description->Str[sWorld->GetDefaultDbcLocale()];
  2200. itemTemplate.PageText = sparse->PageText;
  2201. itemTemplate.LanguageID = sparse->LanguageID;
  2202. itemTemplate.PageMaterial = sparse->PageMaterial;
  2203. itemTemplate.StartQuest = sparse->StartQuest;
  2204. itemTemplate.LockID = sparse->LockID;
  2205. itemTemplate.Material = sparse->Material;
  2206. itemTemplate.Sheath = sparse->Sheath;
  2207. itemTemplate.RandomProperty = sparse->RandomProperty;
  2208. itemTemplate.RandomSuffix = sparse->RandomSuffix;
  2209. itemTemplate.ItemSet = sparse->ItemSet;
  2210. itemTemplate.MaxDurability = FillMaxDurability(db2Data->Class, db2Data->SubClass, sparse->InventoryType, sparse->Quality, sparse->ItemLevel);
  2211. itemTemplate.Area = sparse->Area;
  2212. itemTemplate.Map = sparse->Map;
  2213. itemTemplate.BagFamily = sparse->BagFamily;
  2214. itemTemplate.TotemCategory = sparse->TotemCategory;
  2215. for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
  2216. {
  2217. itemTemplate.Socket[i].Color = sparse->Color[i];
  2218. itemTemplate.Socket[i].Content = sparse->Content[i];
  2219. }
  2220. itemTemplate.socketBonus = sparse->SocketBonus;
  2221. itemTemplate.GemProperties = sparse->GemProperties;
  2222. FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate);
  2223. itemTemplate.ArmorDamageModifier = sparse->ArmorDamageModifier;
  2224. itemTemplate.Duration = sparse->Duration;
  2225. itemTemplate.ItemLimitCategory = sparse->ItemLimitCategory;
  2226. itemTemplate.HolidayId = sparse->HolidayId;
  2227. itemTemplate.StatScalingFactor = sparse->StatScalingFactor;
  2228. itemTemplate.CurrencySubstitutionId = sparse->CurrencySubstitutionId;
  2229. itemTemplate.CurrencySubstitutionCount = sparse->CurrencySubstitutionCount;
  2230. itemTemplate.ScriptId = 0;
  2231. itemTemplate.FoodType = 0;
  2232. itemTemplate.MinMoneyLoot = 0;
  2233. itemTemplate.MaxMoneyLoot = 0;
  2234. ++sparseCount;
  2235. }
  2236. // Load missing items from item_template AND overwrite data from Item-sparse.db2 (item_template is supposed to contain Item-sparse.adb data)
  2237. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
  2238. QueryResult result = WorldDatabase.Query("SELECT entry, Class, SubClass, SoundOverrideSubclass, Name, DisplayId, Quality, Flags, FlagsExtra, Unk430_1, Unk430_2, BuyCount, BuyPrice, SellPrice, "
  2239. // 14 15 16 17 18 19 20 21
  2240. "InventoryType, AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, RequiredSpell, "
  2241. // 22 23 24 25 26 27 28
  2242. "RequiredHonorRank, RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, MaxCount, Stackable, ContainerSlots, "
  2243. // 29 30 31 32 33 34 35 36
  2244. "stat_type1, stat_value1, stat_unk1_1, stat_unk2_1, stat_type2, stat_value2, stat_unk1_2, stat_unk2_2, "
  2245. // 37 38 39 40 41 42 43 44
  2246. "stat_type3, stat_value3, stat_unk1_3, stat_unk2_3, stat_type4, stat_value4, stat_unk1_4, stat_unk2_4, "
  2247. // 45 46 47 48 49 50 51 52
  2248. "stat_type5, stat_value5, stat_unk1_5, stat_unk2_5, stat_type6, stat_value6, stat_unk1_6, stat_unk2_6, "
  2249. // 53 54 55 56 57 58 59 60
  2250. "stat_type7, stat_value7, stat_unk1_7, stat_unk2_7, stat_type8, stat_value8, stat_unk1_8, stat_unk2_8, "
  2251. // 61 62 63 64 65 66 67 68
  2252. "stat_type9, stat_value9, stat_unk1_9, stat_unk2_9, stat_type10, stat_value10, stat_unk1_10, stat_unk2_10, "
  2253. // 69 70 71 72
  2254. "ScalingStatDistribution, DamageType, Delay, RangedModRange, "
  2255. // 73 74 75 76 77 78
  2256. "spellid_1, spelltrigger_1, spellcharges_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, "
  2257. // 79 80 81 82 83 84
  2258. "spellid_2, spelltrigger_2, spellcharges_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, "
  2259. // 85 86 87 88 89 90
  2260. "spellid_3, spelltrigger_3, spellcharges_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, "
  2261. // 91 92 93 94 95 96
  2262. "spellid_4, spelltrigger_4, spellcharges_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, "
  2263. // 97 98 99 100 101 102
  2264. "spellid_5, spelltrigger_5, spellcharges_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, "
  2265. // 103 104 105 106 107 108 109 110
  2266. "Bonding, Description, PageText, LanguageID, PageMaterial, StartQuest, LockID, Material, "
  2267. // 111 112 113 114 115 116 117 118
  2268. "Sheath, RandomProperty, RandomSuffix, ItemSet, Area, Map, BagFamily, TotemCategory, "
  2269. // 119 120 121 122 123 124 125
  2270. "SocketColor_1, SocketContent_1, SocketColor_2, SocketContent_2, SocketColor_3, SocketContent_3, SocketBonus, "
  2271. // 126 127 128 129 130 131
  2272. "GemProperties, ArmorDamageModifier, Duration, ItemLimitCategory, HolidayId, StatScalingFactor, "
  2273. // 132 133
  2274. "CurrencySubstitutionId, CurrencySubstitutionCount "
  2275. "FROM item_template");
  2276. if (result)
  2277. {
  2278. do
  2279. {
  2280. Field* fields = result->Fetch();
  2281. uint32 itemId = fields[0].GetUInt32();
  2282. if (_itemTemplateStore.find(itemId) != _itemTemplateStore.end())
  2283. --sparseCount;
  2284. ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
  2285. itemTemplate.ItemId = itemId;
  2286. itemTemplate.Class = uint32(fields[1].GetUInt8());
  2287. itemTemplate.SubClass = uint32(fields[2].GetUInt8());
  2288. itemTemplate.SoundOverrideSubclass = fields[3].GetInt32();
  2289. itemTemplate.Name1 = fields[4].GetString();
  2290. itemTemplate.DisplayInfoID = fields[5].GetUInt32();
  2291. itemTemplate.Quality = uint32(fields[6].GetUInt8());
  2292. itemTemplate.Flags = fields[7].GetUInt32();
  2293. itemTemplate.Flags2 = fields[8].GetUInt32();
  2294. itemTemplate.Unk430_1 = fields[9].GetFloat();
  2295. itemTemplate.Unk430_2 = fields[10].GetFloat();
  2296. itemTemplate.BuyCount = uint32(fields[11].GetUInt8());
  2297. itemTemplate.BuyPrice = int32(fields[12].GetInt64());
  2298. itemTemplate.SellPrice = fields[13].GetUInt32();
  2299. itemTemplate.InventoryType = uint32(fields[14].GetUInt8());
  2300. itemTemplate.AllowableClass = fields[15].GetInt32();
  2301. itemTemplate.AllowableRace = fields[16].GetInt32();
  2302. itemTemplate.ItemLevel = uint32(fields[17].GetUInt16());
  2303. itemTemplate.RequiredLevel = uint32(fields[18].GetUInt8());
  2304. itemTemplate.RequiredSkill = uint32(fields[19].GetUInt16());
  2305. itemTemplate.RequiredSkillRank = uint32(fields[20].GetUInt16());
  2306. itemTemplate.RequiredSpell = fields[21].GetUInt32();
  2307. itemTemplate.RequiredHonorRank = fields[22].GetUInt32();
  2308. itemTemplate.RequiredCityRank = fields[23].GetUInt32();
  2309. itemTemplate.RequiredReputationFaction = uint32(fields[24].GetUInt16());
  2310. itemTemplate.RequiredReputationRank = uint32(fields[25].GetUInt16());
  2311. itemTemplate.MaxCount = fields[26].GetInt32();
  2312. itemTemplate.Stackable = fields[27].GetInt32();
  2313. itemTemplate.ContainerSlots = uint32(fields[28].GetUInt8());
  2314. for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
  2315. {
  2316. itemTemplate.ItemStat[i].ItemStatType = uint32(fields[29 + i * 4 + 0].GetUInt8());
  2317. itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i * 4 + 1].GetInt16());
  2318. itemTemplate.ItemStat[i].ItemStatUnk1 = fields[29 + i * 4 + 2].GetInt32();
  2319. itemTemplate.ItemStat[i].ItemStatUnk2 = fields[29 + i * 4 + 3].GetInt32();
  2320. }
  2321. itemTemplate.ScalingStatDistribution = uint32(fields[69].GetUInt16());
  2322. // cache item damage
  2323. FillItemDamageFields(&itemTemplate.DamageMin, &itemTemplate.DamageMax, &itemTemplate.DPS, itemTemplate.ItemLevel,
  2324. itemTemplate.Class, itemTemplate.SubClass, itemTemplate.Quality, fields[71].GetUInt16(),
  2325. fields[131].GetFloat(), itemTemplate.InventoryType, itemTemplate.Flags2);
  2326. itemTemplate.DamageType = fields[70].GetUInt8();
  2327. itemTemplate.Armor = FillItemArmor(itemTemplate.ItemLevel, itemTemplate.Class,
  2328. itemTemplate.SubClass, itemTemplate.Quality,
  2329. itemTemplate.InventoryType);
  2330. itemTemplate.Delay = fields[71].GetUInt16();
  2331. itemTemplate.RangedModRange = fields[72].GetFloat();
  2332. for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
  2333. {
  2334. itemTemplate.Spells[i].SpellId = fields[73 + 6 * i + 0].GetInt32();
  2335. itemTemplate.Spells[i].SpellTrigger = uint32(fields[73 + 6 * i + 1].GetUInt8());
  2336. itemTemplate.Spells[i].SpellCharges = int32(fields[73 + 6 * i + 2].GetInt16());
  2337. itemTemplate.Spells[i].SpellCooldown = fields[73 + 6 * i + 3].GetInt32();
  2338. itemTemplate.Spells[i].SpellCategory = uint32(fields[73 + 6 * i + 4].GetUInt16());
  2339. itemTemplate.Spells[i].SpellCategoryCooldown = fields[73 + 6 * i + 5].GetInt32();
  2340. }
  2341. itemTemplate.SpellPPMRate = 0.0f;
  2342. itemTemplate.Bonding = uint32(fields[103].GetUInt8());
  2343. itemTemplate.Description = fields[104].GetString();
  2344. itemTemplate.PageText = fields[105].GetUInt32();
  2345. itemTemplate.LanguageID = uint32(fields[106].GetUInt8());
  2346. itemTemplate.PageMaterial = uint32(fields[107].GetUInt8());
  2347. itemTemplate.StartQuest = fields[108].GetUInt32();
  2348. itemTemplate.LockID = fields[109].GetUInt32();
  2349. itemTemplate.Material = int32(fields[110].GetInt8());
  2350. itemTemplate.Sheath = uint32(fields[111].GetUInt8());
  2351. itemTemplate.RandomProperty = fields[112].GetUInt32();
  2352. itemTemplate.RandomSuffix = fields[113].GetInt32();
  2353. itemTemplate.ItemSet = fields[114].GetUInt32();
  2354. itemTemplate.MaxDurability = FillMaxDurability(itemTemplate.Class, itemTemplate.SubClass,
  2355. itemTemplate.InventoryType, itemTemplate.Quality, itemTemplate.ItemLevel);
  2356. itemTemplate.Area = fields[115].GetUInt32();
  2357. itemTemplate.Map = uint32(fields[116].GetUInt16());
  2358. itemTemplate.BagFamily = fields[117].GetUInt32();
  2359. itemTemplate.TotemCategory = fields[118].GetUInt32();
  2360. for (uint32 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
  2361. {
  2362. itemTemplate.Socket[i].Color = uint32(fields[119 + i*2].GetUInt8());
  2363. itemTemplate.Socket[i].Content = fields[119 + i * 2 + 1].GetUInt32();
  2364. }
  2365. itemTemplate.socketBonus = fields[125].GetUInt32();
  2366. itemTemplate.GemProperties = fields[126].GetUInt32();
  2367. FillDisenchantFields(&itemTemplate.DisenchantID, &itemTemplate.RequiredDisenchantSkill, itemTemplate);
  2368. itemTemplate.ArmorDamageModifier = fields[127].GetFloat();
  2369. itemTemplate.Duration = fields[128].GetUInt32();
  2370. itemTemplate.ItemLimitCategory = uint32(fields[129].GetInt16());
  2371. itemTemplate.HolidayId = fields[130].GetUInt32();
  2372. itemTemplate.StatScalingFactor = fields[131].GetFloat();
  2373. itemTemplate.CurrencySubstitutionId = fields[132].GetInt32();
  2374. itemTemplate.CurrencySubstitutionCount = fields[133].GetInt32();
  2375. itemTemplate.ScriptId = 0;
  2376. itemTemplate.FoodType = 0;
  2377. itemTemplate.MinMoneyLoot = 0;
  2378. itemTemplate.MaxMoneyLoot = 0;
  2379. ++dbCount;
  2380. } while (result->NextRow());
  2381. }
  2382. // Check if item templates for DBC referenced character start outfit are present
  2383. std::set<uint32> notFoundOutfit;
  2384. for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
  2385. {
  2386. CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
  2387. if (!entry)
  2388. continue;
  2389. for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
  2390. {
  2391. if (entry->ItemId[j] <= 0)
  2392. continue;
  2393. uint32 item_id = entry->ItemId[j];
  2394. if (!GetItemTemplate(item_id))
  2395. notFoundOutfit.insert(item_id);
  2396. }
  2397. }
  2398. for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
  2399. TC_LOG_ERROR("sql.sql", "Item (Entry: %u) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr);
  2400. TC_LOG_INFO("server.loading", ">> Loaded %u item templates from Item-sparse.db2 and %u from database in %u ms", sparseCount, dbCount, GetMSTimeDiffToNow(oldMSTime));
  2401. }
  2402. void ObjectMgr::LoadItemTemplateAddon()
  2403. {
  2404. uint32 oldMSTime = getMSTime();
  2405. uint32 count = 0;
  2406. QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance FROM item_template_addon");
  2407. if (result)
  2408. {
  2409. do
  2410. {
  2411. Field* fields = result->Fetch();
  2412. uint32 itemId = fields[0].GetUInt32();
  2413. if (!GetItemTemplate(itemId))
  2414. {
  2415. TC_LOG_ERROR("sql.sql", "Item %u specified in `item_template_addon` does not exist, skipped.", itemId);
  2416. continue;
  2417. }
  2418. uint32 minMoneyLoot = fields[3].GetUInt32();
  2419. uint32 maxMoneyLoot = fields[4].GetUInt32();
  2420. if (minMoneyLoot > maxMoneyLoot)
  2421. {
  2422. TC_LOG_ERROR("sql.sql", "Minimum money loot specified in `item_template_addon` for item %u was greater than maximum amount, swapping.", itemId);
  2423. std::swap(minMoneyLoot, maxMoneyLoot);
  2424. }
  2425. ItemTemplate& itemTemplate = _itemTemplateStore[itemId];
  2426. itemTemplate.FlagsCu = fields[1].GetUInt32();
  2427. itemTemplate.FoodType = fields[2].GetUInt8();
  2428. itemTemplate.MinMoneyLoot = minMoneyLoot;
  2429. itemTemplate.MaxMoneyLoot = maxMoneyLoot;
  2430. itemTemplate.SpellPPMRate = fields[5].GetFloat();
  2431. ++count;
  2432. } while (result->NextRow());
  2433. }
  2434. TC_LOG_INFO("server.loading", ">> Loaded %u item addon templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2435. }
  2436. void ObjectMgr::LoadItemScriptNames()
  2437. {
  2438. uint32 oldMSTime = getMSTime();
  2439. uint32 count = 0;
  2440. QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names");
  2441. if (result)
  2442. {
  2443. do
  2444. {
  2445. Field* fields = result->Fetch();
  2446. uint32 itemId = fields[0].GetUInt32();
  2447. if (!GetItemTemplate(itemId))
  2448. {
  2449. TC_LOG_ERROR("sql.sql", "Item %u specified in `item_script_names` does not exist, skipped.", itemId);
  2450. continue;
  2451. }
  2452. _itemTemplateStore[itemId].ScriptId = GetScriptId(fields[1].GetCString());
  2453. ++count;
  2454. } while (result->NextRow());
  2455. }
  2456. TC_LOG_INFO("server.loading", ">> Loaded %u item script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2457. }
  2458. ItemTemplate const* ObjectMgr::GetItemTemplate(uint32 entry)
  2459. {
  2460. ItemTemplateContainer::const_iterator itr = _itemTemplateStore.find(entry);
  2461. if (itr != _itemTemplateStore.end())
  2462. return &(itr->second);
  2463. return NULL;
  2464. }
  2465. void ObjectMgr::LoadVehicleTemplateAccessories()
  2466. {
  2467. uint32 oldMSTime = getMSTime();
  2468. _vehicleTemplateAccessoryStore.clear(); // needed for reload case
  2469. uint32 count = 0;
  2470. // 0 1 2 3 4 5
  2471. QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
  2472. if (!result)
  2473. {
  2474. TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
  2475. return;
  2476. }
  2477. do
  2478. {
  2479. Field* fields = result->Fetch();
  2480. uint32 entry = fields[0].GetUInt32();
  2481. uint32 accessory = fields[1].GetUInt32();
  2482. int8 seatId = fields[2].GetInt8();
  2483. bool isMinion = fields[3].GetBool();
  2484. uint8 summonType = fields[4].GetUInt8();
  2485. uint32 summonTimer = fields[5].GetUInt32();
  2486. if (!sObjectMgr->GetCreatureTemplate(entry))
  2487. {
  2488. TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u does not exist.", entry);
  2489. continue;
  2490. }
  2491. if (!sObjectMgr->GetCreatureTemplate(accessory))
  2492. {
  2493. TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory %u does not exist.", accessory);
  2494. continue;
  2495. }
  2496. if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
  2497. {
  2498. TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u has no data in npc_spellclick_spells", entry);
  2499. continue;
  2500. }
  2501. _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
  2502. ++count;
  2503. }
  2504. while (result->NextRow());
  2505. TC_LOG_INFO("server.loading", ">> Loaded %u Vehicle Template Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2506. }
  2507. void ObjectMgr::LoadVehicleAccessories()
  2508. {
  2509. uint32 oldMSTime = getMSTime();
  2510. _vehicleAccessoryStore.clear(); // needed for reload case
  2511. uint32 count = 0;
  2512. // 0 1 2 3 4 5
  2513. QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
  2514. if (!result)
  2515. {
  2516. TC_LOG_INFO("server.loading", ">> Loaded 0 Vehicle Accessories in %u ms", GetMSTimeDiffToNow(oldMSTime));
  2517. return;
  2518. }
  2519. do
  2520. {
  2521. Field* fields = result->Fetch();
  2522. uint32 uiGUID = fields[0].GetUInt32();
  2523. uint32 uiAccessory = fields[1].GetUInt32();
  2524. int8 uiSeat = int8(fields[2].GetInt16());
  2525. bool bMinion = fields[3].GetBool();
  2526. uint8 uiSummonType = fields[4].GetUInt8();
  2527. uint32 uiSummonTimer= fields[5].GetUInt32();
  2528. if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
  2529. {
  2530. TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory %u does not exist.", uiAccessory);
  2531. continue;
  2532. }
  2533. _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
  2534. ++count;
  2535. }
  2536. while (result->NextRow());
  2537. TC_LOG_INFO("server.loading", ">> Loaded %u Vehicle Accessories in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2538. }
  2539. void ObjectMgr::LoadPetLevelInfo()
  2540. {
  2541. uint32 oldMSTime = getMSTime();
  2542. // 0 1 2 3 4 5 6 7 8 9
  2543. QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
  2544. if (!result)
  2545. {
  2546. TC_LOG_ERROR("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
  2547. return;
  2548. }
  2549. uint32 count = 0;
  2550. do
  2551. {
  2552. Field* fields = result->Fetch();
  2553. uint32 creature_id = fields[0].GetUInt32();
  2554. if (!sObjectMgr->GetCreatureTemplate(creature_id))
  2555. {
  2556. TC_LOG_ERROR("sql.sql", "Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
  2557. continue;
  2558. }
  2559. uint32 current_level = fields[1].GetUInt8();
  2560. if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2561. {
  2562. if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
  2563. TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  2564. else
  2565. {
  2566. TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
  2567. ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
  2568. }
  2569. continue;
  2570. }
  2571. else if (current_level < 1)
  2572. {
  2573. TC_LOG_ERROR("sql.sql", "Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
  2574. continue;
  2575. }
  2576. PetLevelInfo*& pInfoMapEntry = _petInfoStore[creature_id];
  2577. if (pInfoMapEntry == NULL)
  2578. pInfoMapEntry = new PetLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
  2579. // data for level 1 stored in [0] array element, ...
  2580. PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
  2581. pLevelInfo->health = fields[2].GetUInt16();
  2582. pLevelInfo->mana = fields[3].GetUInt16();
  2583. pLevelInfo->armor = fields[9].GetUInt32();
  2584. for (int i = 0; i < MAX_STATS; i++)
  2585. {
  2586. pLevelInfo->stats[i] = fields[i+4].GetUInt16();
  2587. }
  2588. ++count;
  2589. }
  2590. while (result->NextRow());
  2591. // Fill gaps and check integrity
  2592. for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
  2593. {
  2594. PetLevelInfo* pInfo = itr->second;
  2595. // fatal error if no level 1 data
  2596. if (!pInfo || pInfo[0].health == 0)
  2597. {
  2598. TC_LOG_ERROR("sql.sql", "Creature %u does not have pet stats data for Level 1!", itr->first);
  2599. exit(1);
  2600. }
  2601. // fill level gaps
  2602. for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  2603. {
  2604. if (pInfo[level].health == 0)
  2605. {
  2606. 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);
  2607. pInfo[level] = pInfo[level - 1];
  2608. }
  2609. }
  2610. }
  2611. TC_LOG_INFO("server.loading", ">> Loaded %u level pet stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2612. }
  2613. PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
  2614. {
  2615. if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  2616. level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
  2617. PetLevelInfoContainer::const_iterator itr = _petInfoStore.find(creature_id);
  2618. if (itr == _petInfoStore.end())
  2619. return NULL;
  2620. return &itr->second[level-1]; // data for level 1 stored in [0] array element, ...
  2621. }
  2622. void ObjectMgr::PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
  2623. {
  2624. if (!_playerInfo[race_][class_])
  2625. return;
  2626. if (count > 0)
  2627. _playerInfo[race_][class_]->item.push_back(PlayerCreateInfoItem(itemId, count));
  2628. else
  2629. {
  2630. if (count < -1)
  2631. TC_LOG_ERROR("sql.sql", "Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId);
  2632. for (uint32 gender = 0; gender < GENDER_NONE; ++gender)
  2633. {
  2634. if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender))
  2635. {
  2636. bool found = false;
  2637. for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
  2638. {
  2639. if (entry->ItemId[x] > 0 && uint32(entry->ItemId[x]) == itemId)
  2640. {
  2641. found = true;
  2642. const_cast<CharStartOutfitEntry*>(entry)->ItemId[x] = 0;
  2643. break;
  2644. }
  2645. }
  2646. if (!found)
  2647. TC_LOG_ERROR("sql.sql", "Item %u specified to be removed from original create info not found in dbc!", itemId);
  2648. }
  2649. }
  2650. }
  2651. }
  2652. void ObjectMgr::LoadPlayerInfo()
  2653. {
  2654. // Load playercreate
  2655. {
  2656. uint32 oldMSTime = getMSTime();
  2657. // 0 1 2 3 4 5 6
  2658. QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
  2659. if (!result)
  2660. {
  2661. TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
  2662. exit(1);
  2663. }
  2664. else
  2665. {
  2666. uint32 count = 0;
  2667. do
  2668. {
  2669. Field* fields = result->Fetch();
  2670. uint32 current_race = fields[0].GetUInt8();
  2671. uint32 current_class = fields[1].GetUInt8();
  2672. uint32 mapId = fields[2].GetUInt16();
  2673. uint32 areaId = fields[3].GetUInt32(); // zone
  2674. float positionX = fields[4].GetFloat();
  2675. float positionY = fields[5].GetFloat();
  2676. float positionZ = fields[6].GetFloat();
  2677. float orientation = fields[7].GetFloat();
  2678. if (current_race >= MAX_RACES)
  2679. {
  2680. TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
  2681. continue;
  2682. }
  2683. ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
  2684. if (!rEntry)
  2685. {
  2686. TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
  2687. continue;
  2688. }
  2689. if (current_class >= MAX_CLASSES)
  2690. {
  2691. TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
  2692. continue;
  2693. }
  2694. if (!sChrClassesStore.LookupEntry(current_class))
  2695. {
  2696. TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
  2697. continue;
  2698. }
  2699. // accept DB data only for valid position (and non instanceable)
  2700. if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
  2701. {
  2702. TC_LOG_ERROR("sql.sql", "Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
  2703. continue;
  2704. }
  2705. if (sMapStore.LookupEntry(mapId)->Instanceable())
  2706. {
  2707. TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
  2708. continue;
  2709. }
  2710. PlayerInfo* info = new PlayerInfo();
  2711. info->mapId = mapId;
  2712. info->areaId = areaId;
  2713. info->positionX = positionX;
  2714. info->positionY = positionY;
  2715. info->positionZ = positionZ;
  2716. info->orientation = orientation;
  2717. info->displayId_m = rEntry->model_m;
  2718. info->displayId_f = rEntry->model_f;
  2719. _playerInfo[current_race][current_class] = info;
  2720. ++count;
  2721. }
  2722. while (result->NextRow());
  2723. TC_LOG_INFO("server.loading", ">> Loaded %u player create definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2724. }
  2725. }
  2726. // Load playercreate items
  2727. TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
  2728. {
  2729. uint32 oldMSTime = getMSTime();
  2730. // 0 1 2 3
  2731. QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
  2732. if (!result)
  2733. {
  2734. TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
  2735. }
  2736. else
  2737. {
  2738. uint32 count = 0;
  2739. do
  2740. {
  2741. Field* fields = result->Fetch();
  2742. uint32 current_race = fields[0].GetUInt8();
  2743. if (current_race >= MAX_RACES)
  2744. {
  2745. TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
  2746. continue;
  2747. }
  2748. uint32 current_class = fields[1].GetUInt8();
  2749. if (current_class >= MAX_CLASSES)
  2750. {
  2751. TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
  2752. continue;
  2753. }
  2754. uint32 item_id = fields[2].GetUInt32();
  2755. if (!GetItemTemplate(item_id))
  2756. {
  2757. 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);
  2758. continue;
  2759. }
  2760. int32 amount = fields[3].GetInt8();
  2761. if (!amount)
  2762. {
  2763. 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);
  2764. continue;
  2765. }
  2766. if (!current_race || !current_class)
  2767. {
  2768. uint32 min_race = current_race ? current_race : 1;
  2769. uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
  2770. uint32 min_class = current_class ? current_class : 1;
  2771. uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
  2772. for (uint32 r = min_race; r < max_race; ++r)
  2773. for (uint32 c = min_class; c < max_class; ++c)
  2774. PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
  2775. }
  2776. else
  2777. PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
  2778. ++count;
  2779. }
  2780. while (result->NextRow());
  2781. TC_LOG_INFO("server.loading", ">> Loaded %u custom player create items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2782. }
  2783. }
  2784. // Load playercreate skills
  2785. TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
  2786. {
  2787. uint32 oldMSTime = getMSTime();
  2788. QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills");
  2789. if (!result)
  2790. {
  2791. TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
  2792. }
  2793. else
  2794. {
  2795. uint32 count = 0;
  2796. do
  2797. {
  2798. Field* fields = result->Fetch();
  2799. uint32 raceMask = fields[0].GetUInt32();
  2800. uint32 classMask = fields[1].GetUInt32();
  2801. PlayerCreateInfoSkill skill;
  2802. skill.SkillId = fields[2].GetUInt16();
  2803. skill.Rank = fields[3].GetUInt16();
  2804. if (skill.Rank >= MAX_SKILL_STEP)
  2805. {
  2806. 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);
  2807. continue;
  2808. }
  2809. if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  2810. {
  2811. TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask);
  2812. continue;
  2813. }
  2814. if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  2815. {
  2816. TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask);
  2817. continue;
  2818. }
  2819. if (!sSkillLineStore.LookupEntry(skill.SkillId))
  2820. {
  2821. TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
  2822. continue;
  2823. }
  2824. for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  2825. {
  2826. if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  2827. {
  2828. for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  2829. {
  2830. if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  2831. {
  2832. if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
  2833. continue;
  2834. if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
  2835. {
  2836. info->skills.push_back(skill);
  2837. ++count;
  2838. }
  2839. }
  2840. }
  2841. }
  2842. }
  2843. } while (result->NextRow());
  2844. TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2845. }
  2846. }
  2847. // Load playercreate spells
  2848. TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
  2849. {
  2850. uint32 oldMSTime = getMSTime();
  2851. QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
  2852. if (!result)
  2853. {
  2854. TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
  2855. }
  2856. else
  2857. {
  2858. uint32 count = 0;
  2859. do
  2860. {
  2861. Field* fields = result->Fetch();
  2862. uint32 raceMask = fields[0].GetUInt32();
  2863. uint32 classMask = fields[1].GetUInt32();
  2864. uint32 spellId = fields[2].GetUInt32();
  2865. if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  2866. {
  2867. TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
  2868. continue;
  2869. }
  2870. if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  2871. {
  2872. TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask);
  2873. continue;
  2874. }
  2875. for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  2876. {
  2877. if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  2878. {
  2879. for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  2880. {
  2881. if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  2882. {
  2883. if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
  2884. {
  2885. info->customSpells.push_back(spellId);
  2886. ++count;
  2887. }
  2888. // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
  2889. // Either split the masks per class, or per race, which kind of kills the point yet.
  2890. // else if (raceMask != 0 && classMask != 0)
  2891. // 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);
  2892. }
  2893. }
  2894. }
  2895. }
  2896. }
  2897. while (result->NextRow());
  2898. TC_LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2899. }
  2900. }
  2901. // Load playercreate cast spell
  2902. TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
  2903. {
  2904. uint32 oldMSTime = getMSTime();
  2905. QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
  2906. if (!result)
  2907. TC_LOG_ERROR("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
  2908. else
  2909. {
  2910. uint32 count = 0;
  2911. do
  2912. {
  2913. Field* fields = result->Fetch();
  2914. uint32 raceMask = fields[0].GetUInt32();
  2915. uint32 classMask = fields[1].GetUInt32();
  2916. uint32 spellId = fields[2].GetUInt32();
  2917. if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
  2918. {
  2919. TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
  2920. continue;
  2921. }
  2922. if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
  2923. {
  2924. TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_cast_spell` table, ignoring.", classMask);
  2925. continue;
  2926. }
  2927. for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
  2928. {
  2929. if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
  2930. {
  2931. for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
  2932. {
  2933. if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
  2934. {
  2935. if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])
  2936. {
  2937. info->castSpells.push_back(spellId);
  2938. ++count;
  2939. }
  2940. }
  2941. }
  2942. }
  2943. }
  2944. } while (result->NextRow());
  2945. TC_LOG_INFO("server.loading", ">> Loaded %u player create cast spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2946. }
  2947. }
  2948. // Load playercreate actions
  2949. TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
  2950. {
  2951. uint32 oldMSTime = getMSTime();
  2952. // 0 1 2 3 4
  2953. QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
  2954. if (!result)
  2955. {
  2956. TC_LOG_ERROR("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
  2957. }
  2958. else
  2959. {
  2960. uint32 count = 0;
  2961. do
  2962. {
  2963. Field* fields = result->Fetch();
  2964. uint32 current_race = fields[0].GetUInt8();
  2965. if (current_race >= MAX_RACES)
  2966. {
  2967. TC_LOG_ERROR("sql.sql", "Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
  2968. continue;
  2969. }
  2970. uint32 current_class = fields[1].GetUInt8();
  2971. if (current_class >= MAX_CLASSES)
  2972. {
  2973. TC_LOG_ERROR("sql.sql", "Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
  2974. continue;
  2975. }
  2976. if (PlayerInfo* info = _playerInfo[current_race][current_class])
  2977. info->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
  2978. ++count;
  2979. }
  2980. while (result->NextRow());
  2981. TC_LOG_INFO("server.loading", ">> Loaded %u player create actions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  2982. }
  2983. }
  2984. // Loading levels data (class/race dependent)
  2985. TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
  2986. {
  2987. uint32 oldMSTime = getMSTime();
  2988. // 0 1 2 3 4 5 6 7
  2989. QueryResult result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
  2990. if (!result)
  2991. {
  2992. TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
  2993. exit(1);
  2994. }
  2995. uint32 count = 0;
  2996. do
  2997. {
  2998. Field* fields = result->Fetch();
  2999. uint32 current_race = fields[0].GetUInt8();
  3000. if (current_race >= MAX_RACES)
  3001. {
  3002. TC_LOG_ERROR("sql.sql", "Wrong race %u in `player_levelstats` table, ignoring.", current_race);
  3003. continue;
  3004. }
  3005. uint32 current_class = fields[1].GetUInt8();
  3006. if (current_class >= MAX_CLASSES)
  3007. {
  3008. TC_LOG_ERROR("sql.sql", "Wrong class %u in `player_levelstats` table, ignoring.", current_class);
  3009. continue;
  3010. }
  3011. uint32 current_level = fields[2].GetUInt8();
  3012. if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3013. {
  3014. if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
  3015. TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  3016. else
  3017. {
  3018. TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_levelstats` table, ignoring.", current_level);
  3019. ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
  3020. }
  3021. continue;
  3022. }
  3023. if (PlayerInfo* info = _playerInfo[current_race][current_class])
  3024. {
  3025. if (!info->levelInfo)
  3026. info->levelInfo = new PlayerLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
  3027. PlayerLevelInfo& levelInfo = info->levelInfo[current_level - 1];
  3028. for (int i = 0; i < MAX_STATS; i++)
  3029. levelInfo.stats[i] = fields[i + 3].GetUInt8();
  3030. }
  3031. ++count;
  3032. }
  3033. while (result->NextRow());
  3034. // Fill gaps and check integrity
  3035. for (int race = 0; race < MAX_RACES; ++race)
  3036. {
  3037. // skip non existed races
  3038. if (!sChrRacesStore.LookupEntry(race))
  3039. continue;
  3040. for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
  3041. {
  3042. // skip non existed classes
  3043. if (!sChrClassesStore.LookupEntry(class_))
  3044. continue;
  3045. PlayerInfo* info = _playerInfo[race][class_];
  3046. if (!info)
  3047. continue;
  3048. // skip expansion races if not playing with expansion
  3049. if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
  3050. continue;
  3051. // skip expansion classes if not playing with expansion
  3052. if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_WRATH_OF_THE_LICH_KING && class_ == CLASS_DEATH_KNIGHT)
  3053. continue;
  3054. // skip expansion races if not playing with expansion
  3055. if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_CATACLYSM && (race == RACE_GOBLIN || race == RACE_WORGEN))
  3056. continue;
  3057. // fatal error if no level 1 data
  3058. if (!info->levelInfo || info->levelInfo[0].stats[0] == 0)
  3059. {
  3060. TC_LOG_ERROR("sql.sql", "Race %i Class %i Level 1 does not have stats data!", race, class_);
  3061. exit(1);
  3062. }
  3063. // fill level gaps
  3064. for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3065. {
  3066. if (info->levelInfo[level].stats[0] == 0)
  3067. {
  3068. 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);
  3069. info->levelInfo[level] = info->levelInfo[level - 1];
  3070. }
  3071. }
  3072. }
  3073. }
  3074. TC_LOG_INFO("server.loading", ">> Loaded %u level stats definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3075. }
  3076. // Loading xp per level data
  3077. TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
  3078. {
  3079. uint32 oldMSTime = getMSTime();
  3080. _playerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
  3081. for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3082. _playerXPperLevel[level] = 0;
  3083. // 0 1
  3084. QueryResult result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
  3085. if (!result)
  3086. {
  3087. TC_LOG_ERROR("server.loading", ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
  3088. exit(1);
  3089. }
  3090. uint32 count = 0;
  3091. do
  3092. {
  3093. Field* fields = result->Fetch();
  3094. uint32 current_level = fields[0].GetUInt8();
  3095. uint32 current_xp = fields[1].GetUInt32();
  3096. if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3097. {
  3098. if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
  3099. TC_LOG_ERROR("sql.sql", "Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
  3100. else
  3101. {
  3102. TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
  3103. ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
  3104. }
  3105. continue;
  3106. }
  3107. //PlayerXPperLevel
  3108. _playerXPperLevel[current_level] = current_xp;
  3109. ++count;
  3110. }
  3111. while (result->NextRow());
  3112. // fill level gaps
  3113. for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
  3114. {
  3115. if (_playerXPperLevel[level] == 0)
  3116. {
  3117. TC_LOG_ERROR("sql.sql", "Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
  3118. _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 100;
  3119. }
  3120. }
  3121. TC_LOG_INFO("server.loading", ">> Loaded %u xp for level definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  3122. }
  3123. }
  3124. void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseHP, uint32& baseMana) const
  3125. {
  3126. if (level < 1 || class_ >= MAX_CLASSES)
  3127. return;
  3128. if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3129. level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
  3130. GtOCTBaseHPByClassEntry const* hp = sGtOCTBaseHPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1);
  3131. GtOCTBaseMPByClassEntry const* mp = sGtOCTBaseMPByClassStore.LookupEntry((class_-1) * GT_MAX_LEVEL + level-1);
  3132. if (!hp || !mp)
  3133. {
  3134. TC_LOG_ERROR("misc", "Tried to get non-existant Class-Level combination data for base hp/mp. Class %u Level %u", class_, level);
  3135. return;
  3136. }
  3137. baseHP = uint32(hp->ratio);
  3138. baseMana = uint32(mp->ratio);
  3139. }
  3140. void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const
  3141. {
  3142. if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
  3143. return;
  3144. PlayerInfo const* pInfo = _playerInfo[race][class_];
  3145. if (!pInfo)
  3146. return;
  3147. if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
  3148. *info = pInfo->levelInfo[level-1];
  3149. else
  3150. BuildPlayerLevelInfo(race, class_, level, info);
  3151. }
  3152. void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
  3153. {
  3154. // base data (last known level)
  3155. *info = _playerInfo[race][_class]->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
  3156. // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
  3157. for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
  3158. {
  3159. switch (_class)
  3160. {
  3161. case CLASS_WARRIOR:
  3162. info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
  3163. info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
  3164. info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
  3165. info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3166. info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3167. break;
  3168. case CLASS_PALADIN:
  3169. info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
  3170. info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
  3171. info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
  3172. info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
  3173. info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
  3174. break;
  3175. case CLASS_HUNTER:
  3176. info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
  3177. info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
  3178. info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
  3179. info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
  3180. info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
  3181. break;
  3182. case CLASS_ROGUE:
  3183. info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
  3184. info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
  3185. info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
  3186. info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
  3187. info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
  3188. break;
  3189. case CLASS_PRIEST:
  3190. info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3191. info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
  3192. info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
  3193. info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
  3194. info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
  3195. break;
  3196. case CLASS_SHAMAN:
  3197. info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
  3198. info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
  3199. info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
  3200. info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
  3201. info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
  3202. break;
  3203. case CLASS_MAGE:
  3204. info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3205. info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
  3206. info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3207. info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
  3208. info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
  3209. break;
  3210. case CLASS_WARLOCK:
  3211. info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3212. info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
  3213. info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
  3214. info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
  3215. info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
  3216. break;
  3217. case CLASS_DRUID:
  3218. info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
  3219. info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
  3220. info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
  3221. info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
  3222. info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
  3223. }
  3224. }
  3225. }
  3226. void ObjectMgr::LoadQuests()
  3227. {
  3228. uint32 oldMSTime = getMSTime();
  3229. // For reload case
  3230. for (QuestMap::const_iterator itr=_questTemplates.begin(); itr != _questTemplates.end(); ++itr)
  3231. delete itr->second;
  3232. _questTemplates.clear();
  3233. mExclusiveQuestGroups.clear();
  3234. QueryResult result = WorldDatabase.Query("SELECT "
  3235. //0 1 2 3 4 5 6 7 8 9 10 11 12
  3236. "Id, Method, Level, MinLevel, MaxLevel, ZoneOrSort, Type, SuggestedPlayers, LimitTime, RequiredClasses, RequiredRaces, RequiredSkillId, RequiredSkillPoints, "
  3237. // 13 14 15 16 17 18 19 20
  3238. "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, "
  3239. // 21 22 23 24 25 26 27 28 29 30 31
  3240. "PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestIdChain, RewardXPId, RewardOrRequiredMoney, RewardMoneyMaxLevel, RewardSpell, RewardSpellCast, RewardHonor, RewardHonorMultiplier, "
  3241. // 32 33 34 35 36 37 38 39 40 41 42 43
  3242. "RewardMailTemplateId, RewardMailDelay, SourceItemId, SourceItemCount, SourceSpellId, Flags, SpecialFlags, MinimapTargetMark, RewardTitleId, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
  3243. // 44 45 46 47 48 49 50 51 52 53 54 55 56
  3244. "RewardSkillId, RewardSkillPoints, RewardReputationMask, QuestGiverPortrait, QuestTurnInPortrait, RewardItemId1, RewardItemId2, RewardItemId3, RewardItemId4, RewardItemCount1, RewardItemCount2, RewardItemCount3, RewardItemCount4, "
  3245. // 57 58 59 60 61 62 63 64 65 66 67 68
  3246. "RewardChoiceItemId1, RewardChoiceItemId2, RewardChoiceItemId3, RewardChoiceItemId4, RewardChoiceItemId5, RewardChoiceItemId6, RewardChoiceItemCount1, RewardChoiceItemCount2, RewardChoiceItemCount3, RewardChoiceItemCount4, RewardChoiceItemCount5, RewardChoiceItemCount6, "
  3247. // 69 70 71 72 73 74 75 76 77 78
  3248. "RewardFactionId1, RewardFactionId2, RewardFactionId3, RewardFactionId4, RewardFactionId5, RewardFactionValueId1, RewardFactionValueId2, RewardFactionValueId3, RewardFactionValueId4, RewardFactionValueId5, "
  3249. // 79 80 81 82 83
  3250. "RewardFactionValueIdOverride1, RewardFactionValueIdOverride2, RewardFactionValueIdOverride3, RewardFactionValueIdOverride4, RewardFactionValueIdOverride5, "
  3251. // 84 85 86 87 88 89 90 91 92 93 94
  3252. "PointMapId, PointX, PointY, PointOption, Title, Objectives, Details, EndText, CompletedText, OfferRewardText, RequestItemsText, "
  3253. // 95 96 97 98 99 100 101 102
  3254. "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
  3255. // 103 104 105 106 107 108 109 110
  3256. "RequiredSourceItemId1, RequiredSourceItemId2, RequiredSourceItemId3, RequiredSourceItemId4, RequiredSourceItemCount1, RequiredSourceItemCount2, RequiredSourceItemCount3, RequiredSourceItemCount4, "
  3257. // 111 112 113 114 115 116 117 118 119 120 121 122
  3258. "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
  3259. // 123 124 125 126 127 128 129 130 131 132 133 134 135
  3260. "RequiredSpell, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, RewardCurrencyId1, RewardCurrencyId2, RewardCurrencyId3, RewardCurrencyId4, RewardCurrencyCount1, RewardCurrencyCount2, RewardCurrencyCount3, RewardCurrencyCount4, "
  3261. // 136 137 138 139 140 141 142 143
  3262. "RequiredCurrencyId1, RequiredCurrencyId2, RequiredCurrencyId3, RequiredCurrencyId4, RequiredCurrencyCount1, RequiredCurrencyCount2, RequiredCurrencyCount3, RequiredCurrencyCount4, "
  3263. // 144 145 146 147 148 149
  3264. "QuestGiverTextWindow, QuestGiverTargetName, QuestTurnTextWindow, QuestTurnTargetName, SoundAccept, SoundTurnIn, "
  3265. // 150 151 152 153 154 155 156 157 158 159
  3266. "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4, EmoteOnIncomplete, EmoteOnComplete, "
  3267. // 160 161 162 163 164 165 166 167
  3268. "OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4, OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4"
  3269. " FROM quest_template");
  3270. if (!result)
  3271. {
  3272. TC_LOG_ERROR("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
  3273. return;
  3274. }
  3275. // create multimap previous quest for each existed quest
  3276. // some quests can have many previous maps set by NextQuestId in previous quest
  3277. // for example set of race quests can lead to single not race specific quest
  3278. do
  3279. {
  3280. Field* fields = result->Fetch();
  3281. Quest* newQuest = new Quest(fields);
  3282. _questTemplates[newQuest->GetQuestId()] = newQuest;
  3283. } while (result->NextRow());
  3284. std::map<uint32, uint32> usedMailTemplates;
  3285. // Post processing
  3286. for (QuestMap::iterator iter = _questTemplates.begin(); iter != _questTemplates.end(); ++iter)
  3287. {
  3288. // skip post-loading checks for disabled quests
  3289. if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, NULL))
  3290. continue;
  3291. Quest* qinfo = iter->second;
  3292. // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
  3293. if (qinfo->GetQuestMethod() >= 3)
  3294. TC_LOG_ERROR("sql.sql", "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
  3295. if (qinfo->SpecialFlags & ~QUEST_SPECIAL_FLAGS_DB_ALLOWED)
  3296. {
  3297. TC_LOG_ERROR("sql.sql", "Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
  3298. qinfo->GetQuestId(), qinfo->SpecialFlags, QUEST_SPECIAL_FLAGS_DB_ALLOWED);
  3299. qinfo->SpecialFlags &= QUEST_SPECIAL_FLAGS_DB_ALLOWED;
  3300. }
  3301. if (qinfo->Flags & QUEST_FLAGS_DAILY && qinfo->Flags & QUEST_FLAGS_WEEKLY)
  3302. {
  3303. TC_LOG_ERROR("sql.sql", "Weekly Quest %u is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
  3304. qinfo->Flags &= ~QUEST_FLAGS_DAILY;
  3305. }
  3306. if (qinfo->Flags & QUEST_FLAGS_DAILY)
  3307. {
  3308. if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  3309. {
  3310. TC_LOG_ERROR("sql.sql", "Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  3311. qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  3312. }
  3313. }
  3314. if (qinfo->Flags & QUEST_FLAGS_WEEKLY)
  3315. {
  3316. if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  3317. {
  3318. TC_LOG_ERROR("sql.sql", "Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  3319. qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  3320. }
  3321. }
  3322. if (qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_MONTHLY)
  3323. {
  3324. if (!(qinfo->SpecialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE))
  3325. {
  3326. TC_LOG_ERROR("sql.sql", "Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
  3327. qinfo->SpecialFlags |= QUEST_SPECIAL_FLAGS_REPEATABLE;
  3328. }
  3329. }
  3330. if (qinfo->Flags & QUEST_FLAGS_TRACKING)
  3331. {
  3332. // at auto-reward can be rewarded only RewardChoiceItemId[0]
  3333. for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
  3334. {
  3335. if (uint32 id = qinfo->RewardChoiceItemId[j])
  3336. {
  3337. 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.",
  3338. qinfo->GetQuestId(), j+1, id, j+1);
  3339. // no changes, quest ignore this data
  3340. }
  3341. }
  3342. }
  3343. if (qinfo->MinLevel == uint32(-1) || qinfo->MinLevel > DEFAULT_MAX_LEVEL)
  3344. {
  3345. TC_LOG_ERROR("sql.sql", "Quest %u should be disabled because `MinLevel` = %i", qinfo->GetQuestId(), int32(qinfo->MinLevel));
  3346. // no changes needed, sending -1 in SMSG_QUEST_QUERY_RESPONSE is valid
  3347. }
  3348. // client quest log visual (area case)
  3349. if (qinfo->ZoneOrSort > 0)
  3350. {
  3351. if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
  3352. {
  3353. TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
  3354. qinfo->GetQuestId(), qinfo->ZoneOrSort);
  3355. // no changes, quest not dependent from this value but can have problems at client
  3356. }
  3357. }
  3358. // client quest log visual (sort case)
  3359. if (qinfo->ZoneOrSort < 0)
  3360. {
  3361. QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
  3362. if (!qSort)
  3363. {
  3364. TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
  3365. qinfo->GetQuestId(), qinfo->ZoneOrSort);
  3366. // 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)
  3367. }
  3368. //check for proper RequiredSkillId value (skill case)
  3369. if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
  3370. {
  3371. if (qinfo->RequiredSkillId != skill_id)
  3372. {
  3373. TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %i but `RequiredSkillId` does not have a corresponding value (%d).",
  3374. qinfo->GetQuestId(), qinfo->ZoneOrSort, skill_id);
  3375. //override, and force proper value here?
  3376. }
  3377. }
  3378. }
  3379. // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
  3380. if (qinfo->RequiredClasses)
  3381. {
  3382. if (!(qinfo->RequiredClasses & CLASSMASK_ALL_PLAYABLE))
  3383. {
  3384. 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);
  3385. qinfo->RequiredClasses = 0;
  3386. }
  3387. }
  3388. // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
  3389. if (qinfo->RequiredRaces)
  3390. {
  3391. if (!(qinfo->RequiredRaces & RACEMASK_ALL_PLAYABLE))
  3392. {
  3393. TC_LOG_ERROR("sql.sql", "Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->RequiredRaces);
  3394. qinfo->RequiredRaces = 0;
  3395. }
  3396. }
  3397. // RequiredSkillId, can be 0
  3398. if (qinfo->RequiredSkillId)
  3399. {
  3400. if (!sSkillLineStore.LookupEntry(qinfo->RequiredSkillId))
  3401. {
  3402. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSkillId` = %u but this skill does not exist",
  3403. qinfo->GetQuestId(), qinfo->RequiredSkillId);
  3404. }
  3405. }
  3406. if (qinfo->RequiredSkillPoints)
  3407. {
  3408. if (qinfo->RequiredSkillPoints > sWorld->GetConfigMaxSkillValue())
  3409. {
  3410. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSkillPoints` = %u but max possible skill is %u, quest can't be done.",
  3411. qinfo->GetQuestId(), qinfo->RequiredSkillPoints, sWorld->GetConfigMaxSkillValue());
  3412. // no changes, quest can't be done for this requirement
  3413. }
  3414. }
  3415. // else Skill quests can have 0 skill level, this is ok
  3416. if (qinfo->RequiredFactionId2 && !sFactionStore.LookupEntry(qinfo->RequiredFactionId2))
  3417. {
  3418. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionId2` = %u but faction template %u does not exist, quest can't be done.",
  3419. qinfo->GetQuestId(), qinfo->RequiredFactionId2, qinfo->RequiredFactionId2);
  3420. // no changes, quest can't be done for this requirement
  3421. }
  3422. if (qinfo->RequiredFactionId1 && !sFactionStore.LookupEntry(qinfo->RequiredFactionId1))
  3423. {
  3424. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionId1` = %u but faction template %u does not exist, quest can't be done.",
  3425. qinfo->GetQuestId(), qinfo->RequiredFactionId1, qinfo->RequiredFactionId1);
  3426. // no changes, quest can't be done for this requirement
  3427. }
  3428. if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
  3429. {
  3430. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
  3431. qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
  3432. // no changes, quest can't be done for this requirement
  3433. }
  3434. if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
  3435. {
  3436. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
  3437. qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
  3438. // no changes, quest can't be done for this requirement
  3439. }
  3440. if (qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
  3441. {
  3442. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
  3443. qinfo->GetQuestId(), qinfo->RequiredMinRepValue, ReputationMgr::Reputation_Cap);
  3444. // no changes, quest can't be done for this requirement
  3445. }
  3446. if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
  3447. {
  3448. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
  3449. qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
  3450. // no changes, quest can't be done for this requirement
  3451. }
  3452. if (!qinfo->RequiredFactionId1 && qinfo->RequiredFactionValue1 != 0)
  3453. {
  3454. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionValue1` = %d but `RequiredFactionId1` is 0, value has no effect",
  3455. qinfo->GetQuestId(), qinfo->RequiredFactionValue1);
  3456. // warning
  3457. }
  3458. if (!qinfo->RequiredFactionId2 && qinfo->RequiredFactionValue2 != 0)
  3459. {
  3460. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredFactionValue2` = %d but `RequiredFactionId2` is 0, value has no effect",
  3461. qinfo->GetQuestId(), qinfo->RequiredFactionValue2);
  3462. // warning
  3463. }
  3464. if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue != 0)
  3465. {
  3466. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
  3467. qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
  3468. // warning
  3469. }
  3470. if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue != 0)
  3471. {
  3472. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
  3473. qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
  3474. // warning
  3475. }
  3476. if (qinfo->RewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->RewardTitleId))
  3477. {
  3478. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
  3479. qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
  3480. qinfo->RewardTitleId = 0;
  3481. // quest can't reward this title
  3482. }
  3483. if (qinfo->SourceItemId)
  3484. {
  3485. if (!sObjectMgr->GetItemTemplate(qinfo->SourceItemId))
  3486. {
  3487. TC_LOG_ERROR("sql.sql", "Quest %u has `SourceItemId` = %u but item with entry %u does not exist, quest can't be done.",
  3488. qinfo->GetQuestId(), qinfo->SourceItemId, qinfo->SourceItemId);
  3489. qinfo->SourceItemId = 0; // quest can't be done for this requirement
  3490. }
  3491. else if (qinfo->SourceItemIdCount == 0)
  3492. {
  3493. TC_LOG_ERROR("sql.sql", "Quest %u has `SourceItemId` = %u but `SourceItemIdCount` = 0, set to 1 but need fix in DB.",
  3494. qinfo->GetQuestId(), qinfo->SourceItemId);
  3495. qinfo->SourceItemIdCount = 1; // update to 1 for allow quest work for backward compatibility with DB
  3496. }
  3497. }
  3498. else if (qinfo->SourceItemIdCount>0)
  3499. {
  3500. TC_LOG_ERROR("sql.sql", "Quest %u has `SourceItemId` = 0 but `SourceItemIdCount` = %u, useless value.",
  3501. qinfo->GetQuestId(), qinfo->SourceItemIdCount);
  3502. qinfo->SourceItemIdCount=0; // no quest work changes in fact
  3503. }
  3504. if (qinfo->SourceSpellid)
  3505. {
  3506. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->SourceSpellid);
  3507. if (!spellInfo)
  3508. {
  3509. TC_LOG_ERROR("sql.sql", "Quest %u has `SourceSpellid` = %u but spell %u doesn't exist, quest can't be done.",
  3510. qinfo->GetQuestId(), qinfo->SourceSpellid, qinfo->SourceSpellid);
  3511. qinfo->SourceSpellid = 0; // quest can't be done for this requirement
  3512. }
  3513. else if (!SpellMgr::IsSpellValid(spellInfo))
  3514. {
  3515. TC_LOG_ERROR("sql.sql", "Quest %u has `SourceSpellid` = %u but spell %u is broken, quest can't be done.",
  3516. qinfo->GetQuestId(), qinfo->SourceSpellid, qinfo->SourceSpellid);
  3517. qinfo->SourceSpellid = 0; // quest can't be done for this requirement
  3518. }
  3519. }
  3520. for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
  3521. {
  3522. uint32 id = qinfo->RequiredItemId[j];
  3523. if (id)
  3524. {
  3525. if (qinfo->RequiredItemCount[j] == 0)
  3526. {
  3527. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = %u but `RequiredItemCount%d` = 0, quest can't be done.",
  3528. qinfo->GetQuestId(), j+1, id, j+1);
  3529. // no changes, quest can't be done for this requirement
  3530. }
  3531. qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER);
  3532. if (!sObjectMgr->GetItemTemplate(id))
  3533. {
  3534. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
  3535. qinfo->GetQuestId(), j+1, id, id);
  3536. qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
  3537. }
  3538. }
  3539. else if (qinfo->RequiredItemCount[j] > 0)
  3540. {
  3541. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredItemId%d` = 0 but `RequiredItemCount%d` = %u, quest can't be done.",
  3542. qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredItemCount[j]);
  3543. qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
  3544. }
  3545. }
  3546. for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
  3547. {
  3548. uint32 id = qinfo->RequiredSourceItemId[j];
  3549. if (id)
  3550. {
  3551. if (!sObjectMgr->GetItemTemplate(id))
  3552. {
  3553. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSourceItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
  3554. qinfo->GetQuestId(), j+1, id, id);
  3555. // no changes, quest can't be done for this requirement
  3556. }
  3557. }
  3558. else
  3559. {
  3560. if (qinfo->RequiredSourceItemCount[j]>0)
  3561. {
  3562. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSourceItemId%d` = 0 but `RequiredSourceItemCount%d` = %u.",
  3563. qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredSourceItemCount[j]);
  3564. // no changes, quest ignore this data
  3565. }
  3566. }
  3567. }
  3568. for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
  3569. {
  3570. int32 id = qinfo->RequiredNpcOrGo[j];
  3571. if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
  3572. {
  3573. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %i but gameobject %u does not exist, quest can't be done.",
  3574. qinfo->GetQuestId(), j+1, id, uint32(-id));
  3575. qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
  3576. }
  3577. if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
  3578. {
  3579. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %i but creature with entry %u does not exist, quest can't be done.",
  3580. qinfo->GetQuestId(), j+1, id, uint32(id));
  3581. qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
  3582. }
  3583. if (id)
  3584. {
  3585. // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
  3586. qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO);
  3587. if (!qinfo->RequiredNpcOrGoCount[j])
  3588. {
  3589. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = %u but `RequiredNpcOrGoCount%d` = 0, quest can't be done.",
  3590. qinfo->GetQuestId(), j+1, id, j+1);
  3591. // no changes, quest can be incorrectly done, but we already report this
  3592. }
  3593. }
  3594. else if (qinfo->RequiredNpcOrGoCount[j]>0)
  3595. {
  3596. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredNpcOrGo%d` = 0 but `RequiredNpcOrGoCount%d` = %u.",
  3597. qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredNpcOrGoCount[j]);
  3598. // no changes, quest ignore this data
  3599. }
  3600. }
  3601. for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
  3602. {
  3603. uint32 id = qinfo->RewardChoiceItemId[j];
  3604. if (id)
  3605. {
  3606. if (!sObjectMgr->GetItemTemplate(id))
  3607. {
  3608. 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.",
  3609. qinfo->GetQuestId(), j+1, id, id);
  3610. qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
  3611. }
  3612. if (!qinfo->RewardChoiceItemCount[j])
  3613. {
  3614. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = %u but `RewardChoiceItemCount%d` = 0, quest can't be done.",
  3615. qinfo->GetQuestId(), j+1, id, j+1);
  3616. // no changes, quest can't be done
  3617. }
  3618. }
  3619. else if (qinfo->RewardChoiceItemCount[j]>0)
  3620. {
  3621. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardChoiceItemId%d` = 0 but `RewardChoiceItemCount%d` = %u.",
  3622. qinfo->GetQuestId(), j+1, j+1, qinfo->RewardChoiceItemCount[j]);
  3623. // no changes, quest ignore this data
  3624. }
  3625. }
  3626. for (uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j)
  3627. {
  3628. uint32 id = qinfo->RewardItemId[j];
  3629. if (id)
  3630. {
  3631. if (!sObjectMgr->GetItemTemplate(id))
  3632. {
  3633. 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.",
  3634. qinfo->GetQuestId(), j+1, id, id);
  3635. qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
  3636. }
  3637. if (!qinfo->RewardItemIdCount[j])
  3638. {
  3639. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardItemId%d` = %u but `RewardItemIdCount%d` = 0, quest will not reward this item.",
  3640. qinfo->GetQuestId(), j+1, id, j+1);
  3641. // no changes
  3642. }
  3643. }
  3644. else if (qinfo->RewardItemIdCount[j]>0)
  3645. {
  3646. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardItemId%d` = 0 but `RewardItemIdCount%d` = %u.",
  3647. qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemIdCount[j]);
  3648. // no changes, quest ignore this data
  3649. }
  3650. }
  3651. for (uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
  3652. {
  3653. if (qinfo->RewardFactionId[j])
  3654. {
  3655. if (abs(qinfo->RewardFactionValueId[j]) > 9)
  3656. {
  3657. 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]);
  3658. }
  3659. if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
  3660. {
  3661. 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]);
  3662. qinfo->RewardFactionId[j] = 0; // quest will not reward this
  3663. }
  3664. }
  3665. else if (qinfo->RewardFactionValueIdOverride[j] != 0)
  3666. {
  3667. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardFactionId%d` = 0 but `RewardFactionValueIdOverride%d` = %i.",
  3668. qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionValueIdOverride[j]);
  3669. // no changes, quest ignore this data
  3670. }
  3671. }
  3672. if (qinfo->RewardSpell)
  3673. {
  3674. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RewardSpell);
  3675. if (!spellInfo)
  3676. {
  3677. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u does not exist, spell removed as display reward.",
  3678. qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
  3679. qinfo->RewardSpell = 0; // no spell reward will display for this quest
  3680. }
  3681. else if (!SpellMgr::IsSpellValid(spellInfo))
  3682. {
  3683. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u is broken, quest will not have a spell reward.",
  3684. qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
  3685. qinfo->RewardSpell = 0; // no spell reward will display for this quest
  3686. }
  3687. else if (GetTalentSpellCost(qinfo->RewardSpell))
  3688. {
  3689. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
  3690. qinfo->GetQuestId(), qinfo->RewardSpell, qinfo->RewardSpell);
  3691. qinfo->RewardSpell = 0; // no spell reward will display for this quest
  3692. }
  3693. }
  3694. if (qinfo->RewardSpellCast > 0)
  3695. {
  3696. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RewardSpellCast);
  3697. if (!spellInfo)
  3698. {
  3699. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
  3700. qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
  3701. qinfo->RewardSpellCast = 0; // no spell will be cast on player
  3702. }
  3703. else if (!SpellMgr::IsSpellValid(spellInfo))
  3704. {
  3705. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
  3706. qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
  3707. qinfo->RewardSpellCast = 0; // no spell will be cast on player
  3708. }
  3709. else if (GetTalentSpellCost(qinfo->RewardSpellCast))
  3710. {
  3711. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
  3712. qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
  3713. qinfo->RewardSpellCast = 0; // no spell will be cast on player
  3714. }
  3715. }
  3716. if (qinfo->RewardMailTemplateId)
  3717. {
  3718. if (!sMailTemplateStore.LookupEntry(qinfo->RewardMailTemplateId))
  3719. {
  3720. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
  3721. qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId);
  3722. qinfo->RewardMailTemplateId = 0; // no mail will send to player
  3723. qinfo->RewardMailDelay = 0; // no mail will send to player
  3724. }
  3725. else if (usedMailTemplates.find(qinfo->RewardMailTemplateId) != usedMailTemplates.end())
  3726. {
  3727. std::map<uint32, uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewardMailTemplateId);
  3728. 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.",
  3729. qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId, used_mt_itr->second);
  3730. qinfo->RewardMailTemplateId = 0; // no mail will send to player
  3731. qinfo->RewardMailDelay = 0; // no mail will send to player
  3732. }
  3733. else
  3734. usedMailTemplates[qinfo->RewardMailTemplateId] = qinfo->GetQuestId();
  3735. }
  3736. if (qinfo->NextQuestIdChain)
  3737. {
  3738. QuestMap::iterator qNextItr = _questTemplates.find(qinfo->NextQuestIdChain);
  3739. if (qNextItr == _questTemplates.end())
  3740. {
  3741. TC_LOG_ERROR("sql.sql", "Quest %u has `NextQuestIdChain` = %u but quest %u does not exist, quest chain will not work.",
  3742. qinfo->GetQuestId(), qinfo->NextQuestIdChain, qinfo->NextQuestIdChain);
  3743. qinfo->NextQuestIdChain = 0;
  3744. }
  3745. else
  3746. qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
  3747. }
  3748. for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
  3749. {
  3750. if (qinfo->RewardCurrencyId[j])
  3751. {
  3752. if (qinfo->RewardCurrencyCount[j] == 0)
  3753. {
  3754. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardCurrencyId%d` = %u but `RewardCurrencyCount%d` = 0, quest can't be done.",
  3755. qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], j+1);
  3756. // no changes, quest can't be done for this requirement
  3757. }
  3758. if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j]))
  3759. {
  3760. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.",
  3761. qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]);
  3762. qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
  3763. }
  3764. }
  3765. else if (qinfo->RewardCurrencyCount[j] > 0)
  3766. {
  3767. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardCurrencyId%d` = 0 but `RewardCurrencyCount%d` = %u, quest can't be done.",
  3768. qinfo->GetQuestId(), j+1, j+1, qinfo->RewardCurrencyCount[j]);
  3769. qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
  3770. }
  3771. }
  3772. for (uint8 j = 0; j < QUEST_REQUIRED_CURRENCY_COUNT; ++j)
  3773. {
  3774. if (qinfo->RequiredCurrencyId[j])
  3775. {
  3776. if (qinfo->RequiredCurrencyCount[j] == 0)
  3777. {
  3778. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredCurrencyId%d` = %u but `RequiredCurrencyCount%d` = 0, quest can't be done.",
  3779. qinfo->GetQuestId(), j+1, qinfo->RequiredCurrencyId[j], j+1);
  3780. // no changes, quest can't be done for this requirement
  3781. }
  3782. if (!sCurrencyTypesStore.LookupEntry(qinfo->RequiredCurrencyId[j]))
  3783. {
  3784. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredCurrencyId%d` = %u but currency with entry %u does not exist, quest can't be done.",
  3785. qinfo->GetQuestId(), j+1, qinfo->RequiredCurrencyId[j], qinfo->RequiredCurrencyId[j]);
  3786. qinfo->RequiredCurrencyCount[j] = 0; // prevent incorrect work of quest
  3787. }
  3788. }
  3789. else if (qinfo->RequiredCurrencyCount[j] > 0)
  3790. {
  3791. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredCurrencyId%d` = 0 but `RequiredCurrencyCount%d` = %u, quest can't be done.",
  3792. qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredCurrencyCount[j]);
  3793. qinfo->RequiredCurrencyCount[j] = 0; // prevent incorrect work of quest
  3794. }
  3795. }
  3796. if (qinfo->SoundAccept)
  3797. {
  3798. if (!sSoundEntriesStore.LookupEntry(qinfo->SoundAccept))
  3799. {
  3800. TC_LOG_ERROR("sql.sql", "Quest %u has `SoundAccept` = %u but sound %u does not exist, set to 0.",
  3801. qinfo->GetQuestId(), qinfo->SoundAccept, qinfo->SoundAccept);
  3802. qinfo->SoundAccept = 0; // no sound will be played
  3803. }
  3804. }
  3805. if (qinfo->SoundTurnIn)
  3806. {
  3807. if (!sSoundEntriesStore.LookupEntry(qinfo->SoundTurnIn))
  3808. {
  3809. TC_LOG_ERROR("sql.sql", "Quest %u has `SoundTurnIn` = %u but sound %u does not exist, set to 0.",
  3810. qinfo->GetQuestId(), qinfo->SoundTurnIn, qinfo->SoundTurnIn);
  3811. qinfo->SoundTurnIn = 0; // no sound will be played
  3812. }
  3813. }
  3814. if (qinfo->RequiredSpell > 0)
  3815. {
  3816. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->RequiredSpell);
  3817. if (!spellInfo)
  3818. {
  3819. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSpell` = %u but spell %u does not exist, quest will not require a spell.",
  3820. qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell);
  3821. qinfo->RequiredSpell = 0; // no spell will be required
  3822. }
  3823. else if (!SpellMgr::IsSpellValid(spellInfo))
  3824. {
  3825. TC_LOG_ERROR("sql.sql", "Quest %u has `RequiredSpell` = %u but spell %u is broken, quest will not require a spell.",
  3826. qinfo->GetQuestId(), qinfo->RequiredSpell, qinfo->RequiredSpell);
  3827. qinfo->RequiredSpell = 0; // no spell will be required
  3828. }
  3829. /* Can we require talents?
  3830. else if (GetTalentSpellCost(qinfo->RewardSpellCast))
  3831. {
  3832. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSpell` = %u but spell %u is talent, quest will not have a spell reward.",
  3833. qinfo->GetQuestId(), qinfo->RewardSpellCast, qinfo->RewardSpellCast);
  3834. qinfo->RewardSpellCast = 0; // no spell will be casted on player
  3835. }
  3836. }*/
  3837. }
  3838. if (qinfo->RewardSkillId)
  3839. {
  3840. if (!sSkillLineStore.LookupEntry(qinfo->RewardSkillId))
  3841. {
  3842. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSkillId` = %u but this skill does not exist",
  3843. qinfo->GetQuestId(), qinfo->RewardSkillId);
  3844. }
  3845. if (!qinfo->RewardSkillPoints)
  3846. {
  3847. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSkillId` = %u but `RewardSkillPoints` is 0",
  3848. qinfo->GetQuestId(), qinfo->RewardSkillId);
  3849. }
  3850. }
  3851. if (qinfo->RewardSkillPoints)
  3852. {
  3853. if (qinfo->RewardSkillPoints > sWorld->GetConfigMaxSkillValue())
  3854. {
  3855. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSkillPoints` = %u but max possible skill is %u, quest can't be done.",
  3856. qinfo->GetQuestId(), qinfo->RewardSkillPoints, sWorld->GetConfigMaxSkillValue());
  3857. // no changes, quest can't be done for this requirement
  3858. }
  3859. if (!qinfo->RewardSkillId)
  3860. {
  3861. TC_LOG_ERROR("sql.sql", "Quest %u has `RewardSkillPoints` = %u but `RewardSkillId` is 0",
  3862. qinfo->GetQuestId(), qinfo->RewardSkillPoints);
  3863. }
  3864. }
  3865. // fill additional data stores
  3866. if (qinfo->PrevQuestId)
  3867. {
  3868. if (_questTemplates.find(abs(qinfo->GetPrevQuestId())) == _questTemplates.end())
  3869. TC_LOG_ERROR("sql.sql", "Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
  3870. else
  3871. qinfo->prevQuests.push_back(qinfo->PrevQuestId);
  3872. }
  3873. if (qinfo->NextQuestId)
  3874. {
  3875. QuestMap::iterator qNextItr = _questTemplates.find(abs(qinfo->GetNextQuestId()));
  3876. if (qNextItr == _questTemplates.end())
  3877. TC_LOG_ERROR("sql.sql", "Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
  3878. else
  3879. {
  3880. int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
  3881. qNextItr->second->prevQuests.push_back(signedQuestId);
  3882. }
  3883. }
  3884. if (qinfo->ExclusiveGroup)
  3885. mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
  3886. if (qinfo->LimitTime)
  3887. qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED);
  3888. if (qinfo->RequiredPlayerKills)
  3889. qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL);
  3890. }
  3891. // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
  3892. for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
  3893. {
  3894. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
  3895. if (!spellInfo)
  3896. continue;
  3897. for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
  3898. {
  3899. if (spellInfo->Effects[j].Effect != SPELL_EFFECT_QUEST_COMPLETE)
  3900. continue;
  3901. uint32 quest_id = spellInfo->Effects[j].MiscValue;
  3902. Quest const* quest = GetQuestTemplate(quest_id);
  3903. // some quest referenced in spells not exist (outdated spells)
  3904. if (!quest)
  3905. continue;
  3906. if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  3907. {
  3908. 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);
  3909. // this will prevent quest completing without objective
  3910. const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  3911. }
  3912. }
  3913. }
  3914. TC_LOG_INFO("server.loading", ">> Loaded %lu quests definitions in %u ms", (unsigned long)_questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
  3915. }
  3916. void ObjectMgr::LoadQuestLocales()
  3917. {
  3918. uint32 oldMSTime = getMSTime();
  3919. _questLocaleStore.clear(); // need for reload case
  3920. QueryResult result = WorldDatabase.Query("SELECT Id, "
  3921. "Title_loc1, Details_loc1, Objectives_loc1, OfferRewardText_loc1, RequestItemsText_loc1, EndText_loc1, CompletedText_loc1, ObjectiveText1_loc1, ObjectiveText2_loc1, ObjectiveText3_loc1, ObjectiveText4_loc1, QuestGiverTextWindow_loc1, QuestGiverTargetName_loc1, QuestTurnTextWindow_loc1, QuestTurnTargetName_loc1,"
  3922. "Title_loc2, Details_loc2, Objectives_loc2, OfferRewardText_loc2, RequestItemsText_loc2, EndText_loc2, CompletedText_loc2, ObjectiveText1_loc2, ObjectiveText2_loc2, ObjectiveText3_loc2, ObjectiveText4_loc2, QuestGiverTextWindow_loc2, QuestGiverTargetName_loc2, QuestTurnTextWindow_loc2, QuestTurnTargetName_loc2,"
  3923. "Title_loc3, Details_loc3, Objectives_loc3, OfferRewardText_loc3, RequestItemsText_loc3, EndText_loc3, CompletedText_loc3, ObjectiveText1_loc3, ObjectiveText2_loc3, ObjectiveText3_loc3, ObjectiveText4_loc3, QuestGiverTextWindow_loc3, QuestGiverTargetName_loc3, QuestTurnTextWindow_loc3, QuestTurnTargetName_loc3,"
  3924. "Title_loc4, Details_loc4, Objectives_loc4, OfferRewardText_loc4, RequestItemsText_loc4, EndText_loc4, CompletedText_loc4, ObjectiveText1_loc4, ObjectiveText2_loc4, ObjectiveText3_loc4, ObjectiveText4_loc4, QuestGiverTextWindow_loc4, QuestGiverTargetName_loc4, QuestTurnTextWindow_loc4, QuestTurnTargetName_loc4,"
  3925. "Title_loc5, Details_loc5, Objectives_loc5, OfferRewardText_loc5, RequestItemsText_loc5, EndText_loc5, CompletedText_loc5, ObjectiveText1_loc5, ObjectiveText2_loc5, ObjectiveText3_loc5, ObjectiveText4_loc5, QuestGiverTextWindow_loc5, QuestGiverTargetName_loc5, QuestTurnTextWindow_loc5, QuestTurnTargetName_loc5,"
  3926. "Title_loc6, Details_loc6, Objectives_loc6, OfferRewardText_loc6, RequestItemsText_loc6, EndText_loc6, CompletedText_loc6, ObjectiveText1_loc6, ObjectiveText2_loc6, ObjectiveText3_loc6, ObjectiveText4_loc6, QuestGiverTextWindow_loc6, QuestGiverTargetName_loc6, QuestTurnTextWindow_loc6, QuestTurnTargetName_loc6,"
  3927. "Title_loc7, Details_loc7, Objectives_loc7, OfferRewardText_loc7, RequestItemsText_loc7, EndText_loc7, CompletedText_loc7, ObjectiveText1_loc7, ObjectiveText2_loc7, ObjectiveText3_loc7, ObjectiveText4_loc7, QuestGiverTextWindow_loc7, QuestGiverTargetName_loc7, QuestTurnTextWindow_loc7, QuestTurnTargetName_loc7,"
  3928. "Title_loc8, Details_loc8, Objectives_loc8, OfferRewardText_loc8, RequestItemsText_loc8, EndText_loc8, CompletedText_loc8, ObjectiveText1_loc8, ObjectiveText2_loc8, ObjectiveText3_loc8, ObjectiveText4_loc8, QuestGiverTextWindow_loc8, QuestGiverTargetName_loc8, QuestTurnTextWindow_loc8, QuestTurnTargetName_loc8"
  3929. " FROM locales_quest");
  3930. if (!result)
  3931. return;
  3932. do
  3933. {
  3934. Field* fields = result->Fetch();
  3935. uint32 entry = fields[0].GetUInt32();
  3936. QuestLocale& data = _questLocaleStore[entry];
  3937. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  3938. {
  3939. LocaleConstant locale = (LocaleConstant) i;
  3940. AddLocaleString(fields[1 + 15 * (i - 1)].GetString(), locale, data.Title);
  3941. AddLocaleString(fields[1 + 15 * (i - 1) + 1].GetString(), locale, data.Details);
  3942. AddLocaleString(fields[1 + 15 * (i - 1) + 2].GetString(), locale, data.Objectives);
  3943. AddLocaleString(fields[1 + 15 * (i - 1) + 3].GetString(), locale, data.OfferRewardText);
  3944. AddLocaleString(fields[1 + 15 * (i - 1) + 4].GetString(), locale, data.RequestItemsText);
  3945. AddLocaleString(fields[1 + 15 * (i - 1) + 5].GetString(), locale, data.EndText);
  3946. AddLocaleString(fields[1 + 15 * (i - 1) + 6].GetString(), locale, data.CompletedText);
  3947. for (uint8 k = 0; k < 4; ++k)
  3948. AddLocaleString(fields[1 + 15 * (i - 1) + 7 + k].GetString(), locale, data.ObjectiveText[k]);
  3949. AddLocaleString(fields[1 + 15 * (i - 1) + 11].GetString(), locale, data.QuestGiverTextWindow);
  3950. AddLocaleString(fields[1 + 15 * (i - 1) + 12].GetString(), locale, data.QuestGiverTargetName);
  3951. AddLocaleString(fields[1 + 15 * (i - 1) + 13].GetString(), locale, data.QuestTurnTextWindow);
  3952. AddLocaleString(fields[1 + 15 * (i - 1) + 14].GetString(), locale, data.QuestTurnTargetName);
  3953. }
  3954. } while (result->NextRow());
  3955. TC_LOG_INFO("server.loading", ">> Loaded %u Quest locale strings in %u ms", uint32(_questLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  3956. }
  3957. void ObjectMgr::LoadScripts(ScriptsType type)
  3958. {
  3959. uint32 oldMSTime = getMSTime();
  3960. ScriptMapMap* scripts = GetScriptsMapByType(type);
  3961. if (!scripts)
  3962. return;
  3963. std::string tableName = GetScriptsTableNameByType(type);
  3964. if (tableName.empty())
  3965. return;
  3966. if (sScriptMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
  3967. return;
  3968. TC_LOG_INFO("server.loading", "Loading %s...", tableName.c_str());
  3969. scripts->clear(); // need for reload support
  3970. bool isSpellScriptTable = (type == SCRIPTS_SPELL);
  3971. // 0 1 2 3 4 5 6 7 8 9
  3972. QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o%s FROM %s", isSpellScriptTable ? ", effIndex" : "", tableName.c_str());
  3973. if (!result)
  3974. {
  3975. TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `%s` is empty!", tableName.c_str());
  3976. return;
  3977. }
  3978. uint32 count = 0;
  3979. do
  3980. {
  3981. Field* fields = result->Fetch();
  3982. ScriptInfo tmp;
  3983. tmp.type = type;
  3984. tmp.id = fields[0].GetUInt32();
  3985. if (isSpellScriptTable)
  3986. tmp.id |= fields[10].GetUInt8() << 24;
  3987. tmp.delay = fields[1].GetUInt32();
  3988. tmp.command = ScriptCommands(fields[2].GetUInt32());
  3989. tmp.Raw.nData[0] = fields[3].GetUInt32();
  3990. tmp.Raw.nData[1] = fields[4].GetUInt32();
  3991. tmp.Raw.nData[2] = fields[5].GetInt32();
  3992. tmp.Raw.fData[0] = fields[6].GetFloat();
  3993. tmp.Raw.fData[1] = fields[7].GetFloat();
  3994. tmp.Raw.fData[2] = fields[8].GetFloat();
  3995. tmp.Raw.fData[3] = fields[9].GetFloat();
  3996. // generic command args check
  3997. switch (tmp.command)
  3998. {
  3999. case SCRIPT_COMMAND_TALK:
  4000. {
  4001. if (tmp.Talk.ChatType > CHAT_TYPE_WHISPER && tmp.Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER)
  4002. {
  4003. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",
  4004. tableName.c_str(), tmp.Talk.ChatType, tmp.id);
  4005. continue;
  4006. }
  4007. if (!sObjectMgr->GetBroadcastText(uint32(tmp.Talk.TextID)))
  4008. {
  4009. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u",
  4010. tableName.c_str(), tmp.Talk.TextID, tmp.id);
  4011. continue;
  4012. }
  4013. break;
  4014. }
  4015. case SCRIPT_COMMAND_EMOTE:
  4016. {
  4017. if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
  4018. {
  4019. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",
  4020. tableName.c_str(), tmp.Emote.EmoteID, tmp.id);
  4021. continue;
  4022. }
  4023. break;
  4024. }
  4025. case SCRIPT_COMMAND_TELEPORT_TO:
  4026. {
  4027. if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
  4028. {
  4029. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",
  4030. tableName.c_str(), tmp.TeleportTo.MapID, tmp.id);
  4031. continue;
  4032. }
  4033. if (!Trinity::IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation))
  4034. {
  4035. 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",
  4036. tableName.c_str(), tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
  4037. continue;
  4038. }
  4039. break;
  4040. }
  4041. case SCRIPT_COMMAND_QUEST_EXPLORED:
  4042. {
  4043. Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
  4044. if (!quest)
  4045. {
  4046. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
  4047. tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
  4048. continue;
  4049. }
  4050. if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  4051. {
  4052. 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.",
  4053. tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
  4054. // this will prevent quest completing without objective
  4055. const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  4056. // continue; - quest objective requirement set and command can be allowed
  4057. }
  4058. if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
  4059. {
  4060. 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",
  4061. tableName.c_str(), tmp.QuestExplored.Distance, tmp.id);
  4062. continue;
  4063. }
  4064. if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
  4065. {
  4066. 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",
  4067. tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE);
  4068. continue;
  4069. }
  4070. if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) < INTERACTION_DISTANCE)
  4071. {
  4072. 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",
  4073. tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
  4074. continue;
  4075. }
  4076. break;
  4077. }
  4078. case SCRIPT_COMMAND_KILL_CREDIT:
  4079. {
  4080. if (!GetCreatureTemplate(tmp.KillCredit.CreatureEntry))
  4081. {
  4082. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u",
  4083. tableName.c_str(), tmp.KillCredit.CreatureEntry, tmp.id);
  4084. continue;
  4085. }
  4086. break;
  4087. }
  4088. case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
  4089. {
  4090. GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
  4091. if (!data)
  4092. {
  4093. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
  4094. tableName.c_str(), tmp.RespawnGameobject.GOGuid, tmp.id);
  4095. continue;
  4096. }
  4097. GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
  4098. if (!info)
  4099. {
  4100. 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",
  4101. tableName.c_str(), tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
  4102. continue;
  4103. }
  4104. if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
  4105. info->type == GAMEOBJECT_TYPE_FISHINGHOLE ||
  4106. info->type == GAMEOBJECT_TYPE_DOOR ||
  4107. info->type == GAMEOBJECT_TYPE_BUTTON ||
  4108. info->type == GAMEOBJECT_TYPE_TRAP)
  4109. {
  4110. TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",
  4111. tableName.c_str(), info->entry, tmp.id);
  4112. continue;
  4113. }
  4114. break;
  4115. }
  4116. case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
  4117. {
  4118. if (!Trinity::IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation))
  4119. {
  4120. 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",
  4121. tableName.c_str(), tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id);
  4122. continue;
  4123. }
  4124. if (!GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry))
  4125. {
  4126. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",
  4127. tableName.c_str(), tmp.TempSummonCreature.CreatureEntry, tmp.id);
  4128. continue;
  4129. }
  4130. break;
  4131. }
  4132. case SCRIPT_COMMAND_OPEN_DOOR:
  4133. case SCRIPT_COMMAND_CLOSE_DOOR:
  4134. {
  4135. GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
  4136. if (!data)
  4137. {
  4138. TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",
  4139. tableName.c_str(), tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  4140. continue;
  4141. }
  4142. GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
  4143. if (!info)
  4144. {
  4145. TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",
  4146. tableName.c_str(), tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  4147. continue;
  4148. }
  4149. if (info->type != GAMEOBJECT_TYPE_DOOR)
  4150. {
  4151. TC_LOG_ERROR("sql.sql", "Table `%s` has gameobject type (%u) unsupported by command %s for script id %u",
  4152. tableName.c_str(), info->entry, GetScriptCommandName(tmp.command).c_str(), tmp.id);
  4153. continue;
  4154. }
  4155. break;
  4156. }
  4157. case SCRIPT_COMMAND_REMOVE_AURA:
  4158. {
  4159. if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID))
  4160. {
  4161. TC_LOG_ERROR("sql.sql", "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
  4162. tableName.c_str(), tmp.RemoveAura.SpellID, tmp.id);
  4163. continue;
  4164. }
  4165. if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
  4166. {
  4167. TC_LOG_ERROR("sql.sql", "Table `%s` using unknown flags in datalong2 (%u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u",
  4168. tableName.c_str(), tmp.RemoveAura.Flags, tmp.id);
  4169. continue;
  4170. }
  4171. break;
  4172. }
  4173. case SCRIPT_COMMAND_CAST_SPELL:
  4174. {
  4175. if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID))
  4176. {
  4177. TC_LOG_ERROR("sql.sql", "Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  4178. tableName.c_str(), tmp.CastSpell.SpellID, tmp.id);
  4179. continue;
  4180. }
  4181. if (tmp.CastSpell.Flags > 4) // targeting type
  4182. {
  4183. TC_LOG_ERROR("sql.sql", "Table `%s` using unknown target in datalong2 (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  4184. tableName.c_str(), tmp.CastSpell.Flags, tmp.id);
  4185. continue;
  4186. }
  4187. if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
  4188. {
  4189. TC_LOG_ERROR("sql.sql", "Table `%s` using unknown flags in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  4190. tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
  4191. continue;
  4192. }
  4193. else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
  4194. {
  4195. TC_LOG_ERROR("sql.sql", "Table `%s` using invalid creature entry in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u",
  4196. tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
  4197. continue;
  4198. }
  4199. break;
  4200. }
  4201. case SCRIPT_COMMAND_CREATE_ITEM:
  4202. {
  4203. if (!GetItemTemplate(tmp.CreateItem.ItemEntry))
  4204. {
  4205. TC_LOG_ERROR("sql.sql", "Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u",
  4206. tableName.c_str(), tmp.CreateItem.ItemEntry, tmp.id);
  4207. continue;
  4208. }
  4209. if (!tmp.CreateItem.Amount)
  4210. {
  4211. TC_LOG_ERROR("sql.sql", "Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u",
  4212. tableName.c_str(), tmp.CreateItem.Amount, tmp.id);
  4213. continue;
  4214. }
  4215. break;
  4216. }
  4217. default:
  4218. break;
  4219. }
  4220. if (scripts->find(tmp.id) == scripts->end())
  4221. {
  4222. ScriptMap emptyMap;
  4223. (*scripts)[tmp.id] = emptyMap;
  4224. }
  4225. (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
  4226. ++count;
  4227. }
  4228. while (result->NextRow());
  4229. TC_LOG_INFO("server.loading", ">> Loaded %u script definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4230. }
  4231. void ObjectMgr::LoadSpellScripts()
  4232. {
  4233. LoadScripts(SCRIPTS_SPELL);
  4234. // check ids
  4235. for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
  4236. {
  4237. uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
  4238. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  4239. if (!spellInfo)
  4240. {
  4241. TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has not existing spell (Id: %u) as script id", spellId);
  4242. continue;
  4243. }
  4244. uint8 i = (uint8)((uint32(itr->first) >> 24) & 0x000000FF);
  4245. //check for correct spellEffect
  4246. if (!spellInfo->Effects[i].Effect || (spellInfo->Effects[i].Effect != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->Effects[i].Effect != SPELL_EFFECT_DUMMY))
  4247. TC_LOG_ERROR("sql.sql", "Table `spell_scripts` - spell %u effect %u is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, i);
  4248. }
  4249. }
  4250. void ObjectMgr::LoadEventScripts()
  4251. {
  4252. LoadScripts(SCRIPTS_EVENT);
  4253. std::set<uint32> evt_scripts;
  4254. // Load all possible script entries from gameobjects
  4255. GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
  4256. for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
  4257. if (uint32 eventId = itr->second.GetEventScriptId())
  4258. evt_scripts.insert(eventId);
  4259. // Load all possible script entries from spells
  4260. for (uint32 i = 1; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
  4261. if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(i))
  4262. for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
  4263. if (spell->Effects[j].Effect == SPELL_EFFECT_SEND_EVENT)
  4264. if (spell->Effects[j].MiscValue)
  4265. evt_scripts.insert(spell->Effects[j].MiscValue);
  4266. for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
  4267. {
  4268. for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
  4269. {
  4270. TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx];
  4271. if (node.arrivalEventID)
  4272. evt_scripts.insert(node.arrivalEventID);
  4273. if (node.departureEventID)
  4274. evt_scripts.insert(node.departureEventID);
  4275. }
  4276. }
  4277. // Then check if all scripts are in above list of possible script entries
  4278. for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
  4279. {
  4280. std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
  4281. if (itr2 == evt_scripts.end())
  4282. 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",
  4283. itr->first, SPELL_EFFECT_SEND_EVENT);
  4284. }
  4285. }
  4286. //Load WP Scripts
  4287. void ObjectMgr::LoadWaypointScripts()
  4288. {
  4289. LoadScripts(SCRIPTS_WAYPOINT);
  4290. std::set<uint32> actionSet;
  4291. for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
  4292. actionSet.insert(itr->first);
  4293. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
  4294. PreparedQueryResult result = WorldDatabase.Query(stmt);
  4295. if (result)
  4296. {
  4297. do
  4298. {
  4299. Field* fields = result->Fetch();
  4300. uint32 action = fields[0].GetUInt32();
  4301. actionSet.erase(action);
  4302. }
  4303. while (result->NextRow());
  4304. }
  4305. for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
  4306. TC_LOG_ERROR("sql.sql", "There is no waypoint which links to the waypoint script %u", *itr);
  4307. }
  4308. void ObjectMgr::LoadSpellScriptNames()
  4309. {
  4310. uint32 oldMSTime = getMSTime();
  4311. _spellScriptsStore.clear(); // need for reload case
  4312. QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
  4313. if (!result)
  4314. {
  4315. TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
  4316. return;
  4317. }
  4318. uint32 count = 0;
  4319. do
  4320. {
  4321. Field* fields = result->Fetch();
  4322. int32 spellId = fields[0].GetInt32();
  4323. char const* scriptName = fields[1].GetCString();
  4324. bool allRanks = false;
  4325. if (spellId < 0)
  4326. {
  4327. allRanks = true;
  4328. spellId = -spellId;
  4329. }
  4330. SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  4331. if (!spellInfo)
  4332. {
  4333. TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) does not exist.", scriptName, fields[0].GetInt32());
  4334. continue;
  4335. }
  4336. if (allRanks)
  4337. {
  4338. if (!spellInfo->IsRanked())
  4339. TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) has no ranks of spell.", scriptName, fields[0].GetInt32());
  4340. if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
  4341. {
  4342. TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is not first rank of spell.", scriptName, fields[0].GetInt32());
  4343. continue;
  4344. }
  4345. while (spellInfo)
  4346. {
  4347. _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
  4348. spellInfo = spellInfo->GetNextRankSpell();
  4349. }
  4350. }
  4351. else
  4352. {
  4353. if (spellInfo->IsRanked())
  4354. TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
  4355. _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
  4356. }
  4357. ++count;
  4358. }
  4359. while (result->NextRow());
  4360. TC_LOG_INFO("server.loading", ">> Loaded %u spell script names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4361. }
  4362. void ObjectMgr::ValidateSpellScripts()
  4363. {
  4364. uint32 oldMSTime = getMSTime();
  4365. if (_spellScriptsStore.empty())
  4366. {
  4367. TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
  4368. return;
  4369. }
  4370. uint32 count = 0;
  4371. for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
  4372. {
  4373. SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
  4374. std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
  4375. sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
  4376. itr = _spellScriptsStore.upper_bound(itr->first);
  4377. for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
  4378. {
  4379. SpellScript* spellScript = sitr->first->GetSpellScript();
  4380. AuraScript* auraScript = sitr->first->GetAuraScript();
  4381. bool valid = true;
  4382. if (!spellScript && !auraScript)
  4383. {
  4384. TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second));
  4385. valid = false;
  4386. }
  4387. if (spellScript)
  4388. {
  4389. spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
  4390. spellScript->_Register();
  4391. if (!spellScript->_Validate(spellEntry))
  4392. valid = false;
  4393. delete spellScript;
  4394. }
  4395. if (auraScript)
  4396. {
  4397. auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
  4398. auraScript->_Register();
  4399. if (!auraScript->_Validate(spellEntry))
  4400. valid = false;
  4401. delete auraScript;
  4402. }
  4403. if (!valid)
  4404. {
  4405. _spellScriptsStore.erase(sitr->second);
  4406. }
  4407. }
  4408. ++count;
  4409. }
  4410. TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4411. }
  4412. void ObjectMgr::LoadPageTexts()
  4413. {
  4414. uint32 oldMSTime = getMSTime();
  4415. // 0 1 2
  4416. QueryResult result = WorldDatabase.Query("SELECT entry, text, next_page FROM page_text");
  4417. if (!result)
  4418. {
  4419. TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
  4420. return;
  4421. }
  4422. uint32 count = 0;
  4423. do
  4424. {
  4425. Field* fields = result->Fetch();
  4426. PageText& pageText = _pageTextStore[fields[0].GetUInt32()];
  4427. pageText.Text = fields[1].GetString();
  4428. pageText.NextPage = fields[2].GetUInt32();
  4429. ++count;
  4430. }
  4431. while (result->NextRow());
  4432. for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
  4433. {
  4434. if (itr->second.NextPage)
  4435. {
  4436. PageTextContainer::const_iterator itr2 = _pageTextStore.find(itr->second.NextPage);
  4437. if (itr2 == _pageTextStore.end())
  4438. TC_LOG_ERROR("sql.sql", "Page text (Id: %u) has not existing next page (Id: %u)", itr->first, itr->second.NextPage);
  4439. }
  4440. }
  4441. TC_LOG_INFO("server.loading", ">> Loaded %u page texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4442. }
  4443. PageText const* ObjectMgr::GetPageText(uint32 pageEntry)
  4444. {
  4445. PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
  4446. if (itr != _pageTextStore.end())
  4447. return &(itr->second);
  4448. return NULL;
  4449. }
  4450. void ObjectMgr::LoadPageTextLocales()
  4451. {
  4452. uint32 oldMSTime = getMSTime();
  4453. _pageTextLocaleStore.clear(); // need for reload case
  4454. QueryResult result = WorldDatabase.Query("SELECT entry, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8 FROM locales_page_text");
  4455. if (!result)
  4456. return;
  4457. do
  4458. {
  4459. Field* fields = result->Fetch();
  4460. uint32 entry = fields[0].GetUInt32();
  4461. PageTextLocale& data = _pageTextLocaleStore[entry];
  4462. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  4463. AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.Text);
  4464. } while (result->NextRow());
  4465. TC_LOG_INFO("server.loading", ">> Loaded %u PageText locale strings in %u ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  4466. }
  4467. void ObjectMgr::LoadInstanceTemplate()
  4468. {
  4469. uint32 oldMSTime = getMSTime();
  4470. // 0 1 2 4
  4471. QueryResult result = WorldDatabase.Query("SELECT map, parent, script, allowMount FROM instance_template");
  4472. if (!result)
  4473. {
  4474. TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
  4475. return;
  4476. }
  4477. uint32 count = 0;
  4478. do
  4479. {
  4480. Field* fields = result->Fetch();
  4481. uint16 mapID = fields[0].GetUInt16();
  4482. if (!MapManager::IsValidMAP(mapID, true))
  4483. {
  4484. TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", mapID);
  4485. continue;
  4486. }
  4487. InstanceTemplate instanceTemplate;
  4488. instanceTemplate.AllowMount = fields[3].GetBool();
  4489. instanceTemplate.Parent = uint32(fields[1].GetUInt16());
  4490. instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetCString());
  4491. _instanceTemplateStore[mapID] = instanceTemplate;
  4492. ++count;
  4493. }
  4494. while (result->NextRow());
  4495. TC_LOG_INFO("server.loading", ">> Loaded %u instance templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4496. }
  4497. InstanceTemplate const* ObjectMgr::GetInstanceTemplate(uint32 mapID)
  4498. {
  4499. InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
  4500. if (itr != _instanceTemplateStore.end())
  4501. return &(itr->second);
  4502. return NULL;
  4503. }
  4504. void ObjectMgr::LoadInstanceEncounters()
  4505. {
  4506. uint32 oldMSTime = getMSTime();
  4507. // 0 1 2 3
  4508. QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
  4509. if (!result)
  4510. {
  4511. TC_LOG_ERROR("server.loading", ">> Loaded 0 instance encounters, table is empty!");
  4512. return;
  4513. }
  4514. uint32 count = 0;
  4515. std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
  4516. do
  4517. {
  4518. Field* fields = result->Fetch();
  4519. uint32 entry = fields[0].GetUInt32();
  4520. uint8 creditType = fields[1].GetUInt8();
  4521. uint32 creditEntry = fields[2].GetUInt32();
  4522. uint32 lastEncounterDungeon = fields[3].GetUInt16();
  4523. DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
  4524. if (!dungeonEncounter)
  4525. {
  4526. TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
  4527. continue;
  4528. }
  4529. if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon))
  4530. {
  4531. 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, lastEncounterDungeon);
  4532. continue;
  4533. }
  4534. std::map<uint32, DungeonEncounterEntry const*>::const_iterator itr = dungeonLastBosses.find(lastEncounterDungeon);
  4535. if (lastEncounterDungeon)
  4536. {
  4537. if (itr != dungeonLastBosses.end())
  4538. {
  4539. 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, itr->second->id, itr->second->encounterName);
  4540. continue;
  4541. }
  4542. dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
  4543. }
  4544. switch (creditType)
  4545. {
  4546. case ENCOUNTER_CREDIT_KILL_CREATURE:
  4547. {
  4548. CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry);
  4549. if (!creatureInfo)
  4550. {
  4551. 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);
  4552. continue;
  4553. }
  4554. const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
  4555. break;
  4556. }
  4557. case ENCOUNTER_CREDIT_CAST_SPELL:
  4558. if (!sSpellMgr->GetSpellInfo(creditEntry))
  4559. {
  4560. 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);
  4561. continue;
  4562. }
  4563. break;
  4564. default:
  4565. TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName);
  4566. continue;
  4567. }
  4568. if (dungeonEncounter->difficulty == -1)
  4569. {
  4570. for (uint32 i = 0; i < MAX_DIFFICULTY; ++i)
  4571. {
  4572. if (GetMapDifficultyData(dungeonEncounter->mapId, Difficulty(i)))
  4573. {
  4574. DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, i)];
  4575. encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
  4576. }
  4577. }
  4578. }
  4579. else
  4580. {
  4581. DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->mapId, dungeonEncounter->difficulty)];
  4582. encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
  4583. }
  4584. ++count;
  4585. } while (result->NextRow());
  4586. TC_LOG_INFO("server.loading", ">> Loaded %u instance encounters in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4587. }
  4588. GossipText const* ObjectMgr::GetGossipText(uint32 Text_ID) const
  4589. {
  4590. GossipTextContainer::const_iterator itr = _gossipTextStore.find(Text_ID);
  4591. if (itr != _gossipTextStore.end())
  4592. return &itr->second;
  4593. return NULL;
  4594. }
  4595. void ObjectMgr::LoadGossipText()
  4596. {
  4597. uint32 oldMSTime = getMSTime();
  4598. QueryResult result = WorldDatabase.Query("SELECT ID, "
  4599. "text0_0, text0_1, BroadcastTextID0, lang0, prob0, em0_0, em0_1, em0_2, em0_3, em0_4, em0_5, "
  4600. "text1_0, text1_1, BroadcastTextID1, lang1, prob1, em1_0, em1_1, em1_2, em1_3, em1_4, em1_5, "
  4601. "text2_0, text2_1, BroadcastTextID2, lang2, prob2, em2_0, em2_1, em2_2, em2_3, em2_4, em2_5, "
  4602. "text3_0, text3_1, BroadcastTextID3, lang3, prob3, em3_0, em3_1, em3_2, em3_3, em3_4, em3_5, "
  4603. "text4_0, text4_1, BroadcastTextID4, lang4, prob4, em4_0, em4_1, em4_2, em4_3, em4_4, em4_5, "
  4604. "text5_0, text5_1, BroadcastTextID5, lang5, prob5, em5_0, em5_1, em5_2, em5_3, em5_4, em5_5, "
  4605. "text6_0, text6_1, BroadcastTextID6, lang6, prob6, em6_0, em6_1, em6_2, em6_3, em6_4, em6_5, "
  4606. "text7_0, text7_1, BroadcastTextID7, lang7, prob7, em7_0, em7_1, em7_2, em7_3, em7_4, em7_5 "
  4607. "FROM npc_text");
  4608. if (!result)
  4609. {
  4610. TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
  4611. return;
  4612. }
  4613. _gossipTextStore.rehash(result->GetRowCount());
  4614. uint32 count = 0;
  4615. uint8 cic;
  4616. do
  4617. {
  4618. ++count;
  4619. cic = 0;
  4620. Field* fields = result->Fetch();
  4621. uint32 id = fields[cic++].GetUInt32();
  4622. if (!id)
  4623. {
  4624. TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
  4625. continue;
  4626. }
  4627. GossipText& gText = _gossipTextStore[id];
  4628. for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
  4629. {
  4630. gText.Options[i].Text_0 = fields[cic++].GetString();
  4631. gText.Options[i].Text_1 = fields[cic++].GetString();
  4632. gText.Options[i].BroadcastTextID = fields[cic++].GetUInt32();
  4633. gText.Options[i].Language = fields[cic++].GetUInt8();
  4634. gText.Options[i].Probability = fields[cic++].GetFloat();
  4635. for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
  4636. {
  4637. gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt16();
  4638. gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt16();
  4639. }
  4640. }
  4641. for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; i++)
  4642. {
  4643. if (gText.Options[i].BroadcastTextID)
  4644. {
  4645. if (!sObjectMgr->GetBroadcastText(gText.Options[i].BroadcastTextID))
  4646. {
  4647. 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);
  4648. gText.Options[i].BroadcastTextID = 0;
  4649. }
  4650. }
  4651. }
  4652. }
  4653. while (result->NextRow());
  4654. TC_LOG_INFO("server.loading", ">> Loaded %u npc texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4655. }
  4656. void ObjectMgr::LoadNpcTextLocales()
  4657. {
  4658. uint32 oldMSTime = getMSTime();
  4659. _npcTextLocaleStore.clear(); // need for reload case
  4660. QueryResult result = WorldDatabase.Query("SELECT ID, "
  4661. "Text0_0_loc1, Text0_1_loc1, Text1_0_loc1, Text1_1_loc1, Text2_0_loc1, Text2_1_loc1, Text3_0_loc1, Text3_1_loc1, Text4_0_loc1, Text4_1_loc1, Text5_0_loc1, Text5_1_loc1, Text6_0_loc1, Text6_1_loc1, Text7_0_loc1, Text7_1_loc1, "
  4662. "Text0_0_loc2, Text0_1_loc2, Text1_0_loc2, Text1_1_loc2, Text2_0_loc2, Text2_1_loc2, Text3_0_loc2, Text3_1_loc1, Text4_0_loc2, Text4_1_loc2, Text5_0_loc2, Text5_1_loc2, Text6_0_loc2, Text6_1_loc2, Text7_0_loc2, Text7_1_loc2, "
  4663. "Text0_0_loc3, Text0_1_loc3, Text1_0_loc3, Text1_1_loc3, Text2_0_loc3, Text2_1_loc3, Text3_0_loc3, Text3_1_loc1, Text4_0_loc3, Text4_1_loc3, Text5_0_loc3, Text5_1_loc3, Text6_0_loc3, Text6_1_loc3, Text7_0_loc3, Text7_1_loc3, "
  4664. "Text0_0_loc4, Text0_1_loc4, Text1_0_loc4, Text1_1_loc4, Text2_0_loc4, Text2_1_loc4, Text3_0_loc4, Text3_1_loc1, Text4_0_loc4, Text4_1_loc4, Text5_0_loc4, Text5_1_loc4, Text6_0_loc4, Text6_1_loc4, Text7_0_loc4, Text7_1_loc4, "
  4665. "Text0_0_loc5, Text0_1_loc5, Text1_0_loc5, Text1_1_loc5, Text2_0_loc5, Text2_1_loc5, Text3_0_loc5, Text3_1_loc1, Text4_0_loc5, Text4_1_loc5, Text5_0_loc5, Text5_1_loc5, Text6_0_loc5, Text6_1_loc5, Text7_0_loc5, Text7_1_loc5, "
  4666. "Text0_0_loc6, Text0_1_loc6, Text1_0_loc6, Text1_1_loc6, Text2_0_loc6, Text2_1_loc6, Text3_0_loc6, Text3_1_loc1, Text4_0_loc6, Text4_1_loc6, Text5_0_loc6, Text5_1_loc6, Text6_0_loc6, Text6_1_loc6, Text7_0_loc6, Text7_1_loc6, "
  4667. "Text0_0_loc7, Text0_1_loc7, Text1_0_loc7, Text1_1_loc7, Text2_0_loc7, Text2_1_loc7, Text3_0_loc7, Text3_1_loc1, Text4_0_loc7, Text4_1_loc7, Text5_0_loc7, Text5_1_loc7, Text6_0_loc7, Text6_1_loc7, Text7_0_loc7, Text7_1_loc7, "
  4668. "Text0_0_loc8, Text0_1_loc8, Text1_0_loc8, Text1_1_loc8, Text2_0_loc8, Text2_1_loc8, Text3_0_loc8, Text3_1_loc1, Text4_0_loc8, Text4_1_loc8, Text5_0_loc8, Text5_1_loc8, Text6_0_loc8, Text6_1_loc8, Text7_0_loc8, Text7_1_loc8 "
  4669. " FROM locales_npc_text");
  4670. if (!result)
  4671. return;
  4672. do
  4673. {
  4674. Field* fields = result->Fetch();
  4675. uint32 entry = fields[0].GetUInt32();
  4676. NpcTextLocale& data = _npcTextLocaleStore[entry];
  4677. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  4678. {
  4679. LocaleConstant locale = (LocaleConstant) i;
  4680. for (uint8 j = 0; j < MAX_GOSSIP_TEXT_OPTIONS; ++j)
  4681. {
  4682. AddLocaleString(fields[1 + 8 * 2 * (i - 1) + 2 * j].GetString(), locale, data.Text_0[j]);
  4683. AddLocaleString(fields[1 + 8 * 2 * (i - 1) + 2 * j + 1].GetString(), locale, data.Text_1[j]);
  4684. }
  4685. }
  4686. } while (result->NextRow());
  4687. TC_LOG_INFO("server.loading", ">> Loaded %u NpcText locale strings in %u ms", uint32(_npcTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  4688. }
  4689. //not very fast function but it is called only once a day, or on starting-up
  4690. void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
  4691. {
  4692. uint32 oldMSTime = getMSTime();
  4693. time_t curTime = time(NULL);
  4694. tm lt;
  4695. localtime_r(&curTime, &lt);
  4696. uint64 basetime(curTime);
  4697. TC_LOG_INFO("misc", "Returning mails current time: hour: %d, minute: %d, second: %d ", lt.tm_hour, lt.tm_min, lt.tm_sec);
  4698. // Delete all old mails without item and without body immediately, if starting server
  4699. if (!serverUp)
  4700. {
  4701. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EMPTY_EXPIRED_MAIL);
  4702. stmt->setUInt64(0, basetime);
  4703. CharacterDatabase.Execute(stmt);
  4704. }
  4705. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
  4706. stmt->setUInt64(0, basetime);
  4707. PreparedQueryResult result = CharacterDatabase.Query(stmt);
  4708. if (!result)
  4709. {
  4710. TC_LOG_INFO("server.loading", ">> No expired mails found.");
  4711. return; // any mails need to be returned or deleted
  4712. }
  4713. std::map<uint32 /*messageId*/, MailItemInfoVec> itemsCache;
  4714. stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
  4715. stmt->setUInt32(0, (uint32)basetime);
  4716. if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
  4717. {
  4718. MailItemInfo item;
  4719. do
  4720. {
  4721. Field* fields = items->Fetch();
  4722. item.item_guid = fields[0].GetUInt32();
  4723. item.item_template = fields[1].GetUInt32();
  4724. uint32 mailId = fields[2].GetUInt32();
  4725. itemsCache[mailId].push_back(item);
  4726. } while (items->NextRow());
  4727. }
  4728. uint32 deletedCount = 0;
  4729. uint32 returnedCount = 0;
  4730. do
  4731. {
  4732. Field* fields = result->Fetch();
  4733. Mail* m = new Mail;
  4734. m->messageID = fields[0].GetUInt32();
  4735. m->messageType = fields[1].GetUInt8();
  4736. m->sender = fields[2].GetUInt32();
  4737. m->receiver = fields[3].GetUInt32();
  4738. bool has_items = fields[4].GetBool();
  4739. m->expire_time = time_t(fields[5].GetUInt32());
  4740. m->deliver_time = 0;
  4741. m->COD = fields[6].GetUInt64();
  4742. m->checked = fields[7].GetUInt8();
  4743. m->mailTemplateId = fields[8].GetInt16();
  4744. Player* player = NULL;
  4745. if (serverUp)
  4746. player = ObjectAccessor::FindConnectedPlayer(ObjectGuid(HIGHGUID_PLAYER, m->receiver));
  4747. if (player && player->m_mailsLoaded)
  4748. { // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
  4749. // his in mailbox and he has already listed his mails)
  4750. delete m;
  4751. continue;
  4752. }
  4753. // Delete or return mail
  4754. if (has_items)
  4755. {
  4756. // read items from cache
  4757. m->items.swap(itemsCache[m->messageID]);
  4758. // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
  4759. if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
  4760. {
  4761. // mail open and then not returned
  4762. for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  4763. {
  4764. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
  4765. stmt->setUInt32(0, itr2->item_guid);
  4766. CharacterDatabase.Execute(stmt);
  4767. }
  4768. }
  4769. else
  4770. {
  4771. // Mail will be returned
  4772. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
  4773. stmt->setUInt32(0, m->receiver);
  4774. stmt->setUInt32(1, m->sender);
  4775. stmt->setUInt32(2, basetime + 30 * DAY);
  4776. stmt->setUInt32(3, basetime);
  4777. stmt->setUInt8 (4, uint8(MAIL_CHECK_MASK_RETURNED));
  4778. stmt->setUInt32(5, m->messageID);
  4779. CharacterDatabase.Execute(stmt);
  4780. for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
  4781. {
  4782. // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
  4783. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
  4784. stmt->setUInt32(0, m->sender);
  4785. stmt->setUInt32(1, itr2->item_guid);
  4786. CharacterDatabase.Execute(stmt);
  4787. stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
  4788. stmt->setUInt32(0, m->sender);
  4789. stmt->setUInt32(1, itr2->item_guid);
  4790. CharacterDatabase.Execute(stmt);
  4791. }
  4792. delete m;
  4793. ++returnedCount;
  4794. continue;
  4795. }
  4796. }
  4797. stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
  4798. stmt->setUInt32(0, m->messageID);
  4799. CharacterDatabase.Execute(stmt);
  4800. delete m;
  4801. ++deletedCount;
  4802. }
  4803. while (result->NextRow());
  4804. TC_LOG_INFO("server.loading", ">> Processed %u expired mails: %u deleted and %u returned in %u ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
  4805. }
  4806. void ObjectMgr::LoadQuestAreaTriggers()
  4807. {
  4808. uint32 oldMSTime = getMSTime();
  4809. _questAreaTriggerStore.clear(); // need for reload case
  4810. QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
  4811. if (!result)
  4812. {
  4813. TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
  4814. return;
  4815. }
  4816. uint32 count = 0;
  4817. do
  4818. {
  4819. ++count;
  4820. Field* fields = result->Fetch();
  4821. uint32 trigger_ID = fields[0].GetUInt32();
  4822. uint32 quest_ID = fields[1].GetUInt32();
  4823. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
  4824. if (!atEntry)
  4825. {
  4826. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", trigger_ID);
  4827. continue;
  4828. }
  4829. Quest const* quest = GetQuestTemplate(quest_ID);
  4830. if (!quest)
  4831. {
  4832. TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
  4833. continue;
  4834. }
  4835. if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
  4836. {
  4837. 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);
  4838. // this will prevent quest completing without objective
  4839. const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
  4840. // continue; - quest modified to required objective and trigger can be allowed.
  4841. }
  4842. _questAreaTriggerStore[trigger_ID] = quest_ID;
  4843. } while (result->NextRow());
  4844. TC_LOG_INFO("server.loading", ">> Loaded %u quest trigger points in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4845. }
  4846. void ObjectMgr::LoadTavernAreaTriggers()
  4847. {
  4848. uint32 oldMSTime = getMSTime();
  4849. _tavernAreaTriggerStore.clear(); // need for reload case
  4850. QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
  4851. if (!result)
  4852. {
  4853. TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
  4854. return;
  4855. }
  4856. uint32 count = 0;
  4857. do
  4858. {
  4859. ++count;
  4860. Field* fields = result->Fetch();
  4861. uint32 Trigger_ID = fields[0].GetUInt32();
  4862. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
  4863. if (!atEntry)
  4864. {
  4865. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
  4866. continue;
  4867. }
  4868. _tavernAreaTriggerStore.insert(Trigger_ID);
  4869. } while (result->NextRow());
  4870. TC_LOG_INFO("server.loading", ">> Loaded %u tavern triggers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4871. }
  4872. void ObjectMgr::LoadAreaTriggerScripts()
  4873. {
  4874. uint32 oldMSTime = getMSTime();
  4875. _areaTriggerScriptStore.clear(); // need for reload case
  4876. QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
  4877. if (!result)
  4878. {
  4879. TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
  4880. return;
  4881. }
  4882. uint32 count = 0;
  4883. do
  4884. {
  4885. ++count;
  4886. Field* fields = result->Fetch();
  4887. uint32 Trigger_ID = fields[0].GetUInt32();
  4888. const char *scriptName = fields[1].GetCString();
  4889. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
  4890. if (!atEntry)
  4891. {
  4892. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
  4893. continue;
  4894. }
  4895. _areaTriggerScriptStore[Trigger_ID] = GetScriptId(scriptName);
  4896. } while (result->NextRow());
  4897. TC_LOG_INFO("server.loading", ">> Loaded %u areatrigger scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  4898. }
  4899. uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
  4900. {
  4901. bool found = false;
  4902. float dist = 10000;
  4903. uint32 id = 0;
  4904. for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
  4905. {
  4906. TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
  4907. if (!node || node->map_id != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight
  4908. continue;
  4909. uint8 field = (uint8)((i - 1) / 8);
  4910. uint32 submask = 1 << ((i-1) % 8);
  4911. // skip not taxi network nodes
  4912. if ((sTaxiNodesMask[field] & submask) == 0)
  4913. continue;
  4914. float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
  4915. if (found)
  4916. {
  4917. if (dist2 < dist)
  4918. {
  4919. dist = dist2;
  4920. id = i;
  4921. }
  4922. }
  4923. else
  4924. {
  4925. found = true;
  4926. dist = dist2;
  4927. id = i;
  4928. }
  4929. }
  4930. return id;
  4931. }
  4932. void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
  4933. {
  4934. TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
  4935. if (src_i == sTaxiPathSetBySource.end())
  4936. {
  4937. path = 0;
  4938. cost = 0;
  4939. return;
  4940. }
  4941. TaxiPathSetForSource& pathSet = src_i->second;
  4942. TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
  4943. if (dest_i == pathSet.end())
  4944. {
  4945. path = 0;
  4946. cost = 0;
  4947. return;
  4948. }
  4949. cost = dest_i->second.price;
  4950. path = dest_i->second.ID;
  4951. }
  4952. uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
  4953. {
  4954. uint32 mount_id = 0;
  4955. // select mount creature id
  4956. TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
  4957. if (node)
  4958. {
  4959. uint32 mount_entry = 0;
  4960. if (team == ALLIANCE)
  4961. mount_entry = node->MountCreatureID[1];
  4962. else
  4963. mount_entry = node->MountCreatureID[0];
  4964. // Fix for Alliance not being able to use Acherus taxi
  4965. // only one mount type for both sides
  4966. if (mount_entry == 0 && allowed_alt_team)
  4967. {
  4968. // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
  4969. mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
  4970. }
  4971. CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
  4972. if (mount_info)
  4973. {
  4974. mount_id = mount_info->GetRandomValidModelId();
  4975. if (!mount_id)
  4976. {
  4977. TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry);
  4978. return 0;
  4979. }
  4980. }
  4981. }
  4982. // minfo is not actually used but the mount_id was updated
  4983. GetCreatureModelRandomGender(&mount_id);
  4984. return mount_id;
  4985. }
  4986. void ObjectMgr::LoadGraveyardZones()
  4987. {
  4988. uint32 oldMSTime = getMSTime();
  4989. GraveYardStore.clear(); // need for reload case
  4990. // 0 1 2
  4991. QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone");
  4992. if (!result)
  4993. {
  4994. TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `game_graveyard_zone` is empty.");
  4995. return;
  4996. }
  4997. uint32 count = 0;
  4998. do
  4999. {
  5000. ++count;
  5001. Field* fields = result->Fetch();
  5002. uint32 safeLocId = fields[0].GetUInt32();
  5003. uint32 zoneId = fields[1].GetUInt32();
  5004. uint32 team = fields[2].GetUInt16();
  5005. WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
  5006. if (!entry)
  5007. {
  5008. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
  5009. continue;
  5010. }
  5011. AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId);
  5012. if (!areaEntry)
  5013. {
  5014. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId);
  5015. continue;
  5016. }
  5017. if (areaEntry->zone != 0)
  5018. {
  5019. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for subzone id (%u) instead of zone, skipped.", zoneId);
  5020. continue;
  5021. }
  5022. if (team != 0 && team != HORDE && team != ALLIANCE)
  5023. {
  5024. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for non player faction (%u), skipped.", team);
  5025. continue;
  5026. }
  5027. if (!AddGraveYardLink(safeLocId, zoneId, team, false))
  5028. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
  5029. } while (result->NextRow());
  5030. TC_LOG_INFO("server.loading", ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5031. }
  5032. WorldSafeLocsEntry const* ObjectMgr::GetDefaultGraveYard(uint32 team)
  5033. {
  5034. enum DefaultGraveyard
  5035. {
  5036. HORDE_GRAVEYARD = 10, // Crossroads
  5037. ALLIANCE_GRAVEYARD = 4 // Westfall
  5038. };
  5039. if (team == HORDE)
  5040. return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
  5041. else if (team == ALLIANCE)
  5042. return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
  5043. else return NULL;
  5044. }
  5045. WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
  5046. {
  5047. // search for zone associated closest graveyard
  5048. uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z);
  5049. if (!zoneId)
  5050. {
  5051. if (z > -500)
  5052. {
  5053. TC_LOG_ERROR("misc", "ZoneId not found for map %u coords (%f, %f, %f)", MapId, x, y, z);
  5054. return GetDefaultGraveYard(team);
  5055. }
  5056. }
  5057. // Simulate std. algorithm:
  5058. // found some graveyard associated to (ghost_zone, ghost_map)
  5059. //
  5060. // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
  5061. // then check faction
  5062. // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
  5063. // then check faction
  5064. GraveYardMapBounds range = GraveYardStore.equal_range(zoneId);
  5065. MapEntry const* map = sMapStore.LookupEntry(MapId);
  5066. // not need to check validity of map object; MapId _MUST_ be valid here
  5067. if (range.first == range.second && !map->IsBattlegroundOrArena())
  5068. {
  5069. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
  5070. return GetDefaultGraveYard(team);
  5071. }
  5072. // at corpse map
  5073. bool foundNear = false;
  5074. float distNear = 10000;
  5075. WorldSafeLocsEntry const* entryNear = NULL;
  5076. // at entrance map for corpse map
  5077. bool foundEntr = false;
  5078. float distEntr = 10000;
  5079. WorldSafeLocsEntry const* entryEntr = NULL;
  5080. // some where other
  5081. WorldSafeLocsEntry const* entryFar = NULL;
  5082. MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
  5083. for (; range.first != range.second; ++range.first)
  5084. {
  5085. GraveYardData const& data = range.first->second;
  5086. WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
  5087. if (!entry)
  5088. {
  5089. TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId);
  5090. continue;
  5091. }
  5092. // skip enemy faction graveyard
  5093. // team == 0 case can be at call from .neargrave
  5094. if (data.team != 0 && team != 0 && data.team != team)
  5095. continue;
  5096. // find now nearest graveyard at other map
  5097. if (MapId != entry->map_id)
  5098. {
  5099. // if find graveyard at different map from where entrance placed (or no entrance data), use any first
  5100. if (!mapEntry
  5101. || mapEntry->entrance_map < 0
  5102. || uint32(mapEntry->entrance_map) != entry->map_id
  5103. || (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
  5104. {
  5105. // not have any corrdinates for check distance anyway
  5106. entryFar = entry;
  5107. continue;
  5108. }
  5109. // at entrance map calculate distance (2D);
  5110. float dist2 = (entry->x - mapEntry->entrance_x)*(entry->x - mapEntry->entrance_x)
  5111. +(entry->y - mapEntry->entrance_y)*(entry->y - mapEntry->entrance_y);
  5112. if (foundEntr)
  5113. {
  5114. if (dist2 < distEntr)
  5115. {
  5116. distEntr = dist2;
  5117. entryEntr = entry;
  5118. }
  5119. }
  5120. else
  5121. {
  5122. foundEntr = true;
  5123. distEntr = dist2;
  5124. entryEntr = entry;
  5125. }
  5126. }
  5127. // find now nearest graveyard at same map
  5128. else
  5129. {
  5130. float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
  5131. if (foundNear)
  5132. {
  5133. if (dist2 < distNear)
  5134. {
  5135. distNear = dist2;
  5136. entryNear = entry;
  5137. }
  5138. }
  5139. else
  5140. {
  5141. foundNear = true;
  5142. distNear = dist2;
  5143. entryNear = entry;
  5144. }
  5145. }
  5146. }
  5147. if (entryNear)
  5148. return entryNear;
  5149. if (entryEntr)
  5150. return entryEntr;
  5151. return entryFar;
  5152. }
  5153. GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
  5154. {
  5155. GraveYardMapBounds range = GraveYardStore.equal_range(zoneId);
  5156. for (; range.first != range.second; ++range.first)
  5157. {
  5158. GraveYardData const& data = range.first->second;
  5159. if (data.safeLocId == id)
  5160. return &data;
  5161. }
  5162. return NULL;
  5163. }
  5164. bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
  5165. {
  5166. if (FindGraveYardData(id, zoneId))
  5167. return false;
  5168. // add link to loaded data
  5169. GraveYardData data;
  5170. data.safeLocId = id;
  5171. data.team = team;
  5172. GraveYardStore.insert(GraveYardContainer::value_type(zoneId, data));
  5173. // add link to DB
  5174. if (persist)
  5175. {
  5176. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE);
  5177. stmt->setUInt32(0, id);
  5178. stmt->setUInt32(1, zoneId);
  5179. stmt->setUInt16(2, uint16(team));
  5180. WorldDatabase.Execute(stmt);
  5181. }
  5182. return true;
  5183. }
  5184. void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= false*/)
  5185. {
  5186. GraveYardMapBoundsNonConst range = GraveYardStore.equal_range(zoneId);
  5187. if (range.first == range.second)
  5188. {
  5189. //TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
  5190. return;
  5191. }
  5192. bool found = false;
  5193. for (; range.first != range.second; ++range.first)
  5194. {
  5195. GraveYardData & data = range.first->second;
  5196. // skip not matching safezone id
  5197. if (data.safeLocId != id)
  5198. continue;
  5199. // skip enemy faction graveyard at same map (normal area, city, or battleground)
  5200. // team == 0 case can be at call from .neargrave
  5201. if (data.team != 0 && team != 0 && data.team != team)
  5202. continue;
  5203. found = true;
  5204. break;
  5205. }
  5206. // no match, return
  5207. if (!found)
  5208. return;
  5209. // remove from links
  5210. GraveYardStore.erase(range.first);
  5211. // remove link from DB
  5212. if (persist)
  5213. {
  5214. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE);
  5215. stmt->setUInt32(0, id);
  5216. stmt->setUInt32(1, zoneId);
  5217. stmt->setUInt16(2, uint16(team));
  5218. WorldDatabase.Execute(stmt);
  5219. }
  5220. }
  5221. void ObjectMgr::LoadAreaTriggerTeleports()
  5222. {
  5223. uint32 oldMSTime = getMSTime();
  5224. _areaTriggerStore.clear(); // need for reload case
  5225. // 0 1 2 3 4 5
  5226. QueryResult result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
  5227. if (!result)
  5228. {
  5229. TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
  5230. return;
  5231. }
  5232. uint32 count = 0;
  5233. do
  5234. {
  5235. Field* fields = result->Fetch();
  5236. ++count;
  5237. uint32 Trigger_ID = fields[0].GetUInt32();
  5238. AreaTriggerStruct at;
  5239. at.target_mapId = fields[1].GetUInt16();
  5240. at.target_X = fields[2].GetFloat();
  5241. at.target_Y = fields[3].GetFloat();
  5242. at.target_Z = fields[4].GetFloat();
  5243. at.target_Orientation = fields[5].GetFloat();
  5244. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
  5245. if (!atEntry)
  5246. {
  5247. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
  5248. continue;
  5249. }
  5250. MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
  5251. if (!mapEntry)
  5252. {
  5253. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
  5254. continue;
  5255. }
  5256. if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
  5257. {
  5258. TC_LOG_ERROR("sql.sql", "Area trigger (ID:%u) target coordinates not provided.", Trigger_ID);
  5259. continue;
  5260. }
  5261. _areaTriggerStore[Trigger_ID] = at;
  5262. } while (result->NextRow());
  5263. TC_LOG_INFO("server.loading", ">> Loaded %u area trigger teleport definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5264. }
  5265. void ObjectMgr::LoadAccessRequirements()
  5266. {
  5267. uint32 oldMSTime = getMSTime();
  5268. if (!_accessRequirementStore.empty())
  5269. {
  5270. for (AccessRequirementContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
  5271. delete itr->second;
  5272. _accessRequirementStore.clear(); // need for reload case
  5273. }
  5274. // 0 1 2 3 4 5 6 7 8 9
  5275. QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text FROM access_requirement");
  5276. if (!result)
  5277. {
  5278. TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
  5279. return;
  5280. }
  5281. uint32 count = 0;
  5282. do
  5283. {
  5284. Field* fields = result->Fetch();
  5285. ++count;
  5286. uint32 mapid = fields[0].GetUInt32();
  5287. uint8 difficulty = fields[1].GetUInt8();
  5288. uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
  5289. AccessRequirement* ar = new AccessRequirement();
  5290. ar->levelMin = fields[2].GetUInt8();
  5291. ar->levelMax = fields[3].GetUInt8();
  5292. ar->item = fields[4].GetUInt32();
  5293. ar->item2 = fields[5].GetUInt32();
  5294. ar->quest_A = fields[6].GetUInt32();
  5295. ar->quest_H = fields[7].GetUInt32();
  5296. ar->achievement = fields[8].GetUInt32();
  5297. ar->questFailedText = fields[9].GetString();
  5298. if (ar->item)
  5299. {
  5300. ItemTemplate const* pProto = GetItemTemplate(ar->item);
  5301. if (!pProto)
  5302. {
  5303. TC_LOG_ERROR("misc", "Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item, mapid, difficulty);
  5304. ar->item = 0;
  5305. }
  5306. }
  5307. if (ar->item2)
  5308. {
  5309. ItemTemplate const* pProto = GetItemTemplate(ar->item2);
  5310. if (!pProto)
  5311. {
  5312. TC_LOG_ERROR("misc", "Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item2, mapid, difficulty);
  5313. ar->item2 = 0;
  5314. }
  5315. }
  5316. if (ar->quest_A)
  5317. {
  5318. if (!GetQuestTemplate(ar->quest_A))
  5319. {
  5320. 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);
  5321. ar->quest_A = 0;
  5322. }
  5323. }
  5324. if (ar->quest_H)
  5325. {
  5326. if (!GetQuestTemplate(ar->quest_H))
  5327. {
  5328. 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);
  5329. ar->quest_H = 0;
  5330. }
  5331. }
  5332. if (ar->achievement)
  5333. {
  5334. if (!sAchievementMgr->GetAchievement(ar->achievement))
  5335. {
  5336. TC_LOG_ERROR("sql.sql", "Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar->achievement, mapid, difficulty);
  5337. ar->achievement = 0;
  5338. }
  5339. }
  5340. _accessRequirementStore[requirement_ID] = ar;
  5341. } while (result->NextRow());
  5342. TC_LOG_INFO("server.loading", ">> Loaded %u access requirement definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5343. }
  5344. /*
  5345. * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
  5346. */
  5347. AreaTriggerStruct const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
  5348. {
  5349. bool useParentDbValue = false;
  5350. uint32 parentId = 0;
  5351. const MapEntry* mapEntry = sMapStore.LookupEntry(Map);
  5352. if (!mapEntry || mapEntry->entrance_map < 0)
  5353. return NULL;
  5354. if (mapEntry->IsDungeon())
  5355. {
  5356. const InstanceTemplate* iTemplate = sObjectMgr->GetInstanceTemplate(Map);
  5357. if (!iTemplate)
  5358. return NULL;
  5359. parentId = iTemplate->Parent;
  5360. useParentDbValue = true;
  5361. }
  5362. uint32 entrance_map = uint32(mapEntry->entrance_map);
  5363. for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
  5364. if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
  5365. {
  5366. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
  5367. if (atEntry && atEntry->mapid == Map)
  5368. return &itr->second;
  5369. }
  5370. return NULL;
  5371. }
  5372. /**
  5373. * Searches for the areatrigger which teleports players to the given map
  5374. */
  5375. AreaTriggerStruct const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
  5376. {
  5377. for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
  5378. {
  5379. if (itr->second.target_mapId == Map)
  5380. {
  5381. AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
  5382. if (atEntry)
  5383. return &itr->second;
  5384. }
  5385. }
  5386. return NULL;
  5387. }
  5388. void ObjectMgr::SetHighestGuids()
  5389. {
  5390. QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
  5391. if (result)
  5392. _hiCharGuid = (*result)[0].GetUInt32()+1;
  5393. result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
  5394. if (result)
  5395. _hiCreatureGuid = (*result)[0].GetUInt32()+1;
  5396. result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
  5397. if (result)
  5398. _hiItemGuid = (*result)[0].GetUInt32()+1;
  5399. // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
  5400. CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", _hiItemGuid); // One-time query
  5401. CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", _hiItemGuid); // One-time query
  5402. CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", _hiItemGuid); // One-time query
  5403. CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", _hiItemGuid); // One-time query
  5404. result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
  5405. if (result)
  5406. _hiGoGuid = (*result)[0].GetUInt32()+1;
  5407. result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
  5408. if (result)
  5409. _hiMoTransGuid = (*result)[0].GetUInt32()+1;
  5410. result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
  5411. if (result)
  5412. _auctionId = (*result)[0].GetUInt32()+1;
  5413. result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
  5414. if (result)
  5415. _mailId = (*result)[0].GetUInt32()+1;
  5416. result = CharacterDatabase.Query("SELECT MAX(corpseGuid) FROM corpse");
  5417. if (result)
  5418. _hiCorpseGuid = (*result)[0].GetUInt32()+1;
  5419. result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
  5420. if (result)
  5421. sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
  5422. result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
  5423. if (result)
  5424. _equipmentSetGuid = (*result)[0].GetUInt64()+1;
  5425. result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
  5426. if (result)
  5427. sGuildMgr->SetNextGuildId((*result)[0].GetUInt32()+1);
  5428. result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups");
  5429. if (result)
  5430. sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
  5431. result = CharacterDatabase.Query("SELECT MAX(itemId) from character_void_storage");
  5432. if (result)
  5433. _voidItemId = (*result)[0].GetUInt64()+1;
  5434. }
  5435. uint32 ObjectMgr::GenerateAuctionID()
  5436. {
  5437. if (_auctionId >= 0xFFFFFFFE)
  5438. {
  5439. TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. ");
  5440. World::StopNow(ERROR_EXIT_CODE);
  5441. }
  5442. return _auctionId++;
  5443. }
  5444. uint64 ObjectMgr::GenerateEquipmentSetGuid()
  5445. {
  5446. if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
  5447. {
  5448. TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. ");
  5449. World::StopNow(ERROR_EXIT_CODE);
  5450. }
  5451. return _equipmentSetGuid++;
  5452. }
  5453. uint32 ObjectMgr::GenerateMailID()
  5454. {
  5455. if (_mailId >= 0xFFFFFFFE)
  5456. {
  5457. TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. ");
  5458. World::StopNow(ERROR_EXIT_CODE);
  5459. }
  5460. return _mailId++;
  5461. }
  5462. uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
  5463. {
  5464. switch (guidhigh)
  5465. {
  5466. case HIGHGUID_ITEM:
  5467. {
  5468. ASSERT(_hiItemGuid < 0xFFFFFFFE && "Item guid overflow!");
  5469. return _hiItemGuid++;
  5470. }
  5471. case HIGHGUID_UNIT:
  5472. {
  5473. ASSERT(_hiCreatureGuid < 0x00FFFFFE && "Creature guid overflow!");
  5474. return _hiCreatureGuid++;
  5475. }
  5476. case HIGHGUID_PET:
  5477. {
  5478. ASSERT(_hiPetGuid < 0x00FFFFFE && "Pet guid overflow!");
  5479. return _hiPetGuid++;
  5480. }
  5481. case HIGHGUID_VEHICLE:
  5482. {
  5483. ASSERT(_hiVehicleGuid < 0x00FFFFFF && "Vehicle guid overflow!");
  5484. return _hiVehicleGuid++;
  5485. }
  5486. case HIGHGUID_PLAYER:
  5487. {
  5488. ASSERT(_hiCharGuid < 0xFFFFFFFE && "Player guid overflow!");
  5489. return _hiCharGuid++;
  5490. }
  5491. case HIGHGUID_GAMEOBJECT:
  5492. {
  5493. ASSERT(_hiGoGuid < 0x00FFFFFE && "Gameobject guid overflow!");
  5494. return _hiGoGuid++;
  5495. }
  5496. case HIGHGUID_CORPSE:
  5497. {
  5498. ASSERT(_hiCorpseGuid < 0xFFFFFFFE && "Corpse guid overflow!");
  5499. return _hiCorpseGuid++;
  5500. }
  5501. case HIGHGUID_AREATRIGGER:
  5502. {
  5503. ASSERT(_hiAreaTriggerGuid < 0xFFFFFFFE && "AreaTrigger guid overflow!");
  5504. return _hiAreaTriggerGuid++;
  5505. }
  5506. case HIGHGUID_DYNAMICOBJECT:
  5507. {
  5508. ASSERT(_hiDoGuid < 0xFFFFFFFE && "DynamicObject guid overflow!");
  5509. return _hiDoGuid++;
  5510. }
  5511. case HIGHGUID_MO_TRANSPORT:
  5512. {
  5513. ASSERT(_hiMoTransGuid < 0xFFFFFFFE && "MO Transport guid overflow!");
  5514. return _hiMoTransGuid++;
  5515. }
  5516. default:
  5517. ASSERT(false && "ObjectMgr::GenerateLowGuid - Unknown HIGHGUID type");
  5518. return 0;
  5519. }
  5520. }
  5521. void ObjectMgr::LoadGameObjectLocales()
  5522. {
  5523. uint32 oldMSTime = getMSTime();
  5524. _gameObjectLocaleStore.clear(); // need for reload case
  5525. QueryResult result = WorldDatabase.Query("SELECT entry, "
  5526. "name_loc1, name_loc2, name_loc3, name_loc4, name_loc5, name_loc6, name_loc7, name_loc8, "
  5527. "castbarcaption_loc1, castbarcaption_loc2, castbarcaption_loc3, castbarcaption_loc4, "
  5528. "castbarcaption_loc5, castbarcaption_loc6, castbarcaption_loc7, castbarcaption_loc8 FROM locales_gameobject");
  5529. if (!result)
  5530. return;
  5531. do
  5532. {
  5533. Field* fields = result->Fetch();
  5534. uint32 entry = fields[0].GetUInt32();
  5535. GameObjectLocale& data = _gameObjectLocaleStore[entry];
  5536. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  5537. {
  5538. AddLocaleString(fields[i].GetString(), LocaleConstant(i), data.Name);
  5539. AddLocaleString(fields[i + (TOTAL_LOCALES - 1)].GetString(), LocaleConstant(i), data.CastBarCaption);
  5540. }
  5541. } while (result->NextRow());
  5542. TC_LOG_INFO("server.loading", ">> Loaded %u gameobject locale strings in %u ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
  5543. }
  5544. inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  5545. {
  5546. if (sLockStore.LookupEntry(dataN))
  5547. return;
  5548. TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
  5549. goInfo->entry, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
  5550. }
  5551. inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  5552. {
  5553. if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
  5554. {
  5555. if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
  5556. 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.",
  5557. goInfo->entry, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
  5558. }
  5559. }
  5560. inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  5561. {
  5562. if (sSpellMgr->GetSpellInfo(dataN))
  5563. return;
  5564. TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
  5565. goInfo->entry, goInfo->type, N, dataN, dataN);
  5566. }
  5567. inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
  5568. {
  5569. if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR))
  5570. return;
  5571. TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
  5572. goInfo->entry, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR);
  5573. // prevent client and server unexpected work
  5574. dataN = 0;
  5575. }
  5576. inline void CheckGONoDamageImmuneId(GameObjectTemplate* goTemplate, uint32 dataN, uint32 N)
  5577. {
  5578. // 0/1 correct values
  5579. if (dataN <= 1)
  5580. return;
  5581. 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);
  5582. }
  5583. inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
  5584. {
  5585. // 0/1 correct values
  5586. if (dataN <= 1)
  5587. return;
  5588. TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
  5589. goInfo->entry, goInfo->type, N, dataN);
  5590. }
  5591. void ObjectMgr::LoadGameObjectTemplate()
  5592. {
  5593. uint32 oldMSTime = getMSTime();
  5594. // 0 1 2 3 4 5 6 7 8 9 10 11 12
  5595. QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, questItem1, questItem2, questItem3, "
  5596. // 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
  5597. "questItem4, questItem5, questItem6, data0, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, "
  5598. // 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
  5599. "data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, data24, data25, data26, data27, data28, "
  5600. // 45 46 47 48 49 50
  5601. "data29, data30, data31, unkInt32, AIName, ScriptName "
  5602. "FROM gameobject_template");
  5603. if (!result)
  5604. {
  5605. TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
  5606. return;
  5607. }
  5608. _gameObjectTemplateStore.rehash(result->GetRowCount());
  5609. uint32 count = 0;
  5610. do
  5611. {
  5612. Field* fields = result->Fetch();
  5613. uint32 entry = fields[0].GetUInt32();
  5614. GameObjectTemplate& got = _gameObjectTemplateStore[entry];
  5615. got.entry = entry;
  5616. got.type = uint32(fields[1].GetUInt8());
  5617. got.displayId = fields[2].GetUInt32();
  5618. got.name = fields[3].GetString();
  5619. got.IconName = fields[4].GetString();
  5620. got.castBarCaption = fields[5].GetString();
  5621. got.unk1 = fields[6].GetString();
  5622. got.faction = uint32(fields[7].GetUInt16());
  5623. got.flags = fields[8].GetUInt32();
  5624. got.size = fields[9].GetFloat();
  5625. for (uint8 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i)
  5626. got.questItems[i] = fields[10 + i].GetUInt32();
  5627. for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
  5628. got.raw.data[i] = fields[16 + i].GetUInt32();
  5629. got.unkInt32 = fields[48].GetInt32();
  5630. got.AIName = fields[49].GetString();
  5631. got.ScriptId = GetScriptId(fields[50].GetCString());
  5632. // Checks
  5633. switch (got.type)
  5634. {
  5635. case GAMEOBJECT_TYPE_DOOR: //0
  5636. {
  5637. if (got.door.lockId)
  5638. CheckGOLockId(&got, got.door.lockId, 1);
  5639. CheckGONoDamageImmuneId(&got, got.door.noDamageImmune, 3);
  5640. break;
  5641. }
  5642. case GAMEOBJECT_TYPE_BUTTON: //1
  5643. {
  5644. if (got.button.lockId)
  5645. CheckGOLockId(&got, got.button.lockId, 1);
  5646. CheckGONoDamageImmuneId(&got, got.button.noDamageImmune, 4);
  5647. break;
  5648. }
  5649. case GAMEOBJECT_TYPE_QUESTGIVER: //2
  5650. {
  5651. if (got.questgiver.lockId)
  5652. CheckGOLockId(&got, got.questgiver.lockId, 0);
  5653. CheckGONoDamageImmuneId(&got, got.questgiver.noDamageImmune, 5);
  5654. break;
  5655. }
  5656. case GAMEOBJECT_TYPE_CHEST: //3
  5657. {
  5658. if (got.chest.lockId)
  5659. CheckGOLockId(&got, got.chest.lockId, 0);
  5660. CheckGOConsumable(&got, got.chest.consumable, 3);
  5661. if (got.chest.linkedTrapId) // linked trap
  5662. CheckGOLinkedTrapId(&got, got.chest.linkedTrapId, 7);
  5663. break;
  5664. }
  5665. case GAMEOBJECT_TYPE_TRAP: //6
  5666. {
  5667. if (got.trap.lockId)
  5668. CheckGOLockId(&got, got.trap.lockId, 0);
  5669. break;
  5670. }
  5671. case GAMEOBJECT_TYPE_CHAIR: //7
  5672. CheckAndFixGOChairHeightId(&got, got.chair.height, 1);
  5673. break;
  5674. case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
  5675. {
  5676. if (got.spellFocus.focusId)
  5677. {
  5678. if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.focusId))
  5679. TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
  5680. entry, got.type, got.spellFocus.focusId, got.spellFocus.focusId);
  5681. }
  5682. if (got.spellFocus.linkedTrapId) // linked trap
  5683. CheckGOLinkedTrapId(&got, got.spellFocus.linkedTrapId, 2);
  5684. break;
  5685. }
  5686. case GAMEOBJECT_TYPE_GOOBER: //10
  5687. {
  5688. if (got.goober.lockId)
  5689. CheckGOLockId(&got, got.goober.lockId, 0);
  5690. CheckGOConsumable(&got, got.goober.consumable, 3);
  5691. if (got.goober.pageId) // pageId
  5692. {
  5693. if (!GetPageText(got.goober.pageId))
  5694. TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
  5695. entry, got.type, got.goober.pageId, got.goober.pageId);
  5696. }
  5697. CheckGONoDamageImmuneId(&got, got.goober.noDamageImmune, 11);
  5698. if (got.goober.linkedTrapId) // linked trap
  5699. CheckGOLinkedTrapId(&got, got.goober.linkedTrapId, 12);
  5700. break;
  5701. }
  5702. case GAMEOBJECT_TYPE_AREADAMAGE: //12
  5703. {
  5704. if (got.areadamage.lockId)
  5705. CheckGOLockId(&got, got.areadamage.lockId, 0);
  5706. break;
  5707. }
  5708. case GAMEOBJECT_TYPE_CAMERA: //13
  5709. {
  5710. if (got.camera.lockId)
  5711. CheckGOLockId(&got, got.camera.lockId, 0);
  5712. break;
  5713. }
  5714. case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
  5715. {
  5716. if (got.moTransport.taxiPathId)
  5717. {
  5718. if (got.moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[got.moTransport.taxiPathId].empty())
  5719. TC_LOG_ERROR("sql.sql", "GameObject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
  5720. entry, got.type, got.moTransport.taxiPathId, got.moTransport.taxiPathId);
  5721. }
  5722. if (uint32 transportMap = got.moTransport.mapID)
  5723. _transportMaps.insert(transportMap);
  5724. break;
  5725. }
  5726. case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
  5727. break;
  5728. case GAMEOBJECT_TYPE_SPELLCASTER: //22
  5729. {
  5730. // always must have spell
  5731. CheckGOSpellId(&got, got.spellcaster.spellId, 0);
  5732. break;
  5733. }
  5734. case GAMEOBJECT_TYPE_FLAGSTAND: //24
  5735. {
  5736. if (got.flagstand.lockId)
  5737. CheckGOLockId(&got, got.flagstand.lockId, 0);
  5738. CheckGONoDamageImmuneId(&got, got.flagstand.noDamageImmune, 5);
  5739. break;
  5740. }
  5741. case GAMEOBJECT_TYPE_FISHINGHOLE: //25
  5742. {
  5743. if (got.fishinghole.lockId)
  5744. CheckGOLockId(&got, got.fishinghole.lockId, 4);
  5745. break;
  5746. }
  5747. case GAMEOBJECT_TYPE_FLAGDROP: //26
  5748. {
  5749. if (got.flagdrop.lockId)
  5750. CheckGOLockId(&got, got.flagdrop.lockId, 0);
  5751. CheckGONoDamageImmuneId(&got, got.flagdrop.noDamageImmune, 3);
  5752. break;
  5753. }
  5754. case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
  5755. CheckAndFixGOChairHeightId(&got, got.barberChair.chairheight, 0);
  5756. break;
  5757. }
  5758. ++count;
  5759. }
  5760. while (result->NextRow());
  5761. TC_LOG_INFO("server.loading", ">> Loaded %u game object templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5762. }
  5763. void ObjectMgr::LoadExplorationBaseXP()
  5764. {
  5765. uint32 oldMSTime = getMSTime();
  5766. QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
  5767. if (!result)
  5768. {
  5769. TC_LOG_ERROR("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
  5770. return;
  5771. }
  5772. uint32 count = 0;
  5773. do
  5774. {
  5775. Field* fields = result->Fetch();
  5776. uint8 level = fields[0].GetUInt8();
  5777. uint32 basexp = fields[1].GetInt32();
  5778. _baseXPTable[level] = basexp;
  5779. ++count;
  5780. }
  5781. while (result->NextRow());
  5782. TC_LOG_INFO("server.loading", ">> Loaded %u BaseXP definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5783. }
  5784. uint32 ObjectMgr::GetBaseXP(uint8 level)
  5785. {
  5786. return _baseXPTable[level] ? _baseXPTable[level] : 0;
  5787. }
  5788. uint32 ObjectMgr::GetXPForLevel(uint8 level) const
  5789. {
  5790. if (level < _playerXPperLevel.size())
  5791. return _playerXPperLevel[level];
  5792. return 0;
  5793. }
  5794. void ObjectMgr::LoadPetNames()
  5795. {
  5796. uint32 oldMSTime = getMSTime();
  5797. // 0 1 2
  5798. QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
  5799. if (!result)
  5800. {
  5801. TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
  5802. return;
  5803. }
  5804. uint32 count = 0;
  5805. do
  5806. {
  5807. Field* fields = result->Fetch();
  5808. std::string word = fields[0].GetString();
  5809. uint32 entry = fields[1].GetUInt32();
  5810. bool half = fields[2].GetBool();
  5811. if (half)
  5812. _petHalfName1[entry].push_back(word);
  5813. else
  5814. _petHalfName0[entry].push_back(word);
  5815. ++count;
  5816. }
  5817. while (result->NextRow());
  5818. TC_LOG_INFO("server.loading", ">> Loaded %u pet name parts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5819. }
  5820. void ObjectMgr::LoadPetNumber()
  5821. {
  5822. uint32 oldMSTime = getMSTime();
  5823. QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
  5824. if (result)
  5825. {
  5826. Field* fields = result->Fetch();
  5827. _hiPetNumber = fields[0].GetUInt32()+1;
  5828. }
  5829. TC_LOG_INFO("server.loading", ">> Loaded the max pet number: %d in %u ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
  5830. }
  5831. std::string ObjectMgr::GeneratePetName(uint32 entry)
  5832. {
  5833. StringVector& list0 = _petHalfName0[entry];
  5834. StringVector& list1 = _petHalfName1[entry];
  5835. if (list0.empty() || list1.empty())
  5836. {
  5837. CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
  5838. if (!cinfo)
  5839. return std::string();
  5840. char const* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
  5841. if (petname)
  5842. return std::string(petname);
  5843. else
  5844. return cinfo->Name;
  5845. }
  5846. return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
  5847. }
  5848. uint32 ObjectMgr::GeneratePetNumber()
  5849. {
  5850. return ++_hiPetNumber;
  5851. }
  5852. uint64 ObjectMgr::GenerateVoidStorageItemId()
  5853. {
  5854. return ++_voidItemId;
  5855. }
  5856. void ObjectMgr::LoadCorpses()
  5857. {
  5858. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  5859. // SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, flags, dynFlags, time, corpseType, instanceId, phaseMask, corpseGuid, guid FROM corpse WHERE corpseType <> 0
  5860. uint32 oldMSTime = getMSTime();
  5861. PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSES));
  5862. if (!result)
  5863. {
  5864. TC_LOG_INFO("server.loading", ">> Loaded 0 corpses. DB table `corpse` is empty.");
  5865. return;
  5866. }
  5867. uint32 count = 0;
  5868. do
  5869. {
  5870. Field* fields = result->Fetch();
  5871. uint32 guid = fields[16].GetUInt32();
  5872. CorpseType type = CorpseType(fields[12].GetUInt8());
  5873. if (type >= MAX_CORPSE_TYPE)
  5874. {
  5875. TC_LOG_ERROR("misc", "Corpse (guid: %u) have wrong corpse type (%u), not loading.", guid, type);
  5876. continue;
  5877. }
  5878. Corpse* corpse = new Corpse(type);
  5879. if (!corpse->LoadCorpseFromDB(guid, fields))
  5880. {
  5881. delete corpse;
  5882. continue;
  5883. }
  5884. sObjectAccessor->AddCorpse(corpse);
  5885. ++count;
  5886. }
  5887. while (result->NextRow());
  5888. TC_LOG_INFO("server.loading", ">> Loaded %u corpses in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5889. }
  5890. void ObjectMgr::LoadReputationRewardRate()
  5891. {
  5892. uint32 oldMSTime = getMSTime();
  5893. _repRewardRateStore.clear(); // for reload case
  5894. uint32 count = 0; // 0 1 2 3 4 5 6 7
  5895. 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");
  5896. if (!result)
  5897. {
  5898. TC_LOG_ERROR("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
  5899. return;
  5900. }
  5901. do
  5902. {
  5903. Field* fields = result->Fetch();
  5904. uint32 factionId = fields[0].GetUInt32();
  5905. RepRewardRate repRate;
  5906. repRate.questRate = fields[1].GetFloat();
  5907. repRate.questDailyRate = fields[2].GetFloat();
  5908. repRate.questWeeklyRate = fields[3].GetFloat();
  5909. repRate.questMonthlyRate = fields[4].GetFloat();
  5910. repRate.questRepeatableRate = fields[5].GetFloat();
  5911. repRate.creatureRate = fields[6].GetFloat();
  5912. repRate.spellRate = fields[7].GetFloat();
  5913. FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
  5914. if (!factionEntry)
  5915. {
  5916. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
  5917. continue;
  5918. }
  5919. if (repRate.questRate < 0.0f)
  5920. {
  5921. TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.questRate, factionId);
  5922. continue;
  5923. }
  5924. if (repRate.questDailyRate < 0.0f)
  5925. {
  5926. 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);
  5927. continue;
  5928. }
  5929. if (repRate.questWeeklyRate < 0.0f)
  5930. {
  5931. 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);
  5932. continue;
  5933. }
  5934. if (repRate.questMonthlyRate < 0.0f)
  5935. {
  5936. 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);
  5937. continue;
  5938. }
  5939. if (repRate.questRepeatableRate < 0.0f)
  5940. {
  5941. 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);
  5942. continue;
  5943. }
  5944. if (repRate.creatureRate < 0.0f)
  5945. {
  5946. TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creatureRate, factionId);
  5947. continue;
  5948. }
  5949. if (repRate.spellRate < 0.0f)
  5950. {
  5951. TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spellRate, factionId);
  5952. continue;
  5953. }
  5954. _repRewardRateStore[factionId] = repRate;
  5955. ++count;
  5956. }
  5957. while (result->NextRow());
  5958. TC_LOG_INFO("server.loading", ">> Loaded %u reputation_reward_rate in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  5959. }
  5960. void ObjectMgr::LoadReputationOnKill()
  5961. {
  5962. uint32 oldMSTime = getMSTime();
  5963. // For reload case
  5964. _repOnKillStore.clear();
  5965. uint32 count = 0;
  5966. // 0 1 2
  5967. QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
  5968. // 3 4 5 6 7 8 9
  5969. "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
  5970. "FROM creature_onkill_reputation");
  5971. if (!result)
  5972. {
  5973. TC_LOG_ERROR("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
  5974. return;
  5975. }
  5976. do
  5977. {
  5978. Field* fields = result->Fetch();
  5979. uint32 creature_id = fields[0].GetUInt32();
  5980. ReputationOnKillEntry repOnKill;
  5981. repOnKill.RepFaction1 = fields[1].GetInt16();
  5982. repOnKill.RepFaction2 = fields[2].GetInt16();
  5983. repOnKill.IsTeamAward1 = fields[3].GetBool();
  5984. repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
  5985. repOnKill.RepValue1 = fields[5].GetInt32();
  5986. repOnKill.IsTeamAward2 = fields[6].GetBool();
  5987. repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
  5988. repOnKill.RepValue2 = fields[8].GetInt32();
  5989. repOnKill.TeamDependent = fields[9].GetBool();
  5990. if (!GetCreatureTemplate(creature_id))
  5991. {
  5992. TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry (%u), skipped", creature_id);
  5993. continue;
  5994. }
  5995. if (repOnKill.RepFaction1)
  5996. {
  5997. FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
  5998. if (!factionEntry1)
  5999. {
  6000. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
  6001. continue;
  6002. }
  6003. }
  6004. if (repOnKill.RepFaction2)
  6005. {
  6006. FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
  6007. if (!factionEntry2)
  6008. {
  6009. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
  6010. continue;
  6011. }
  6012. }
  6013. _repOnKillStore[creature_id] = repOnKill;
  6014. ++count;
  6015. } while (result->NextRow());
  6016. TC_LOG_INFO("server.loading", ">> Loaded %u creature award reputation definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6017. }
  6018. void ObjectMgr::LoadReputationSpilloverTemplate()
  6019. {
  6020. uint32 oldMSTime = getMSTime();
  6021. _repSpilloverTemplateStore.clear(); // for reload case
  6022. uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  6023. QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4, faction5, rate_5, rank_5 FROM reputation_spillover_template");
  6024. if (!result)
  6025. {
  6026. TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
  6027. return;
  6028. }
  6029. do
  6030. {
  6031. Field* fields = result->Fetch();
  6032. uint32 factionId = fields[0].GetUInt16();
  6033. RepSpilloverTemplate repTemplate;
  6034. repTemplate.faction[0] = fields[1].GetUInt16();
  6035. repTemplate.faction_rate[0] = fields[2].GetFloat();
  6036. repTemplate.faction_rank[0] = fields[3].GetUInt8();
  6037. repTemplate.faction[1] = fields[4].GetUInt16();
  6038. repTemplate.faction_rate[1] = fields[5].GetFloat();
  6039. repTemplate.faction_rank[1] = fields[6].GetUInt8();
  6040. repTemplate.faction[2] = fields[7].GetUInt16();
  6041. repTemplate.faction_rate[2] = fields[8].GetFloat();
  6042. repTemplate.faction_rank[2] = fields[9].GetUInt8();
  6043. repTemplate.faction[3] = fields[10].GetUInt16();
  6044. repTemplate.faction_rate[3] = fields[11].GetFloat();
  6045. repTemplate.faction_rank[3] = fields[12].GetUInt8();
  6046. repTemplate.faction[4] = fields[13].GetUInt16();
  6047. repTemplate.faction_rate[4] = fields[14].GetFloat();
  6048. repTemplate.faction_rank[4] = fields[15].GetUInt8();
  6049. FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
  6050. if (!factionEntry)
  6051. {
  6052. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
  6053. continue;
  6054. }
  6055. if (factionEntry->team == 0)
  6056. {
  6057. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId);
  6058. continue;
  6059. }
  6060. for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
  6061. {
  6062. if (repTemplate.faction[i])
  6063. {
  6064. FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
  6065. if (!factionSpillover)
  6066. {
  6067. 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);
  6068. continue;
  6069. }
  6070. if (factionSpillover->reputationListID < 0)
  6071. {
  6072. 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);
  6073. continue;
  6074. }
  6075. if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
  6076. {
  6077. 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]);
  6078. continue;
  6079. }
  6080. }
  6081. }
  6082. FactionEntry const* factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]);
  6083. if (repTemplate.faction[0] && !factionEntry0)
  6084. {
  6085. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]);
  6086. continue;
  6087. }
  6088. FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]);
  6089. if (repTemplate.faction[1] && !factionEntry1)
  6090. {
  6091. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]);
  6092. continue;
  6093. }
  6094. FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]);
  6095. if (repTemplate.faction[2] && !factionEntry2)
  6096. {
  6097. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]);
  6098. continue;
  6099. }
  6100. FactionEntry const* factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]);
  6101. if (repTemplate.faction[3] && !factionEntry3)
  6102. {
  6103. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]);
  6104. continue;
  6105. }
  6106. FactionEntry const* factionEntry4 = sFactionStore.LookupEntry(repTemplate.faction[4]);
  6107. if (repTemplate.faction[4] && !factionEntry4)
  6108. {
  6109. TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[4]);
  6110. continue;
  6111. }
  6112. _repSpilloverTemplateStore[factionId] = repTemplate;
  6113. ++count;
  6114. }
  6115. while (result->NextRow());
  6116. TC_LOG_INFO("server.loading", ">> Loaded %u reputation_spillover_template in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6117. }
  6118. void ObjectMgr::LoadPointsOfInterest()
  6119. {
  6120. uint32 oldMSTime = getMSTime();
  6121. _pointsOfInterestStore.clear(); // need for reload case
  6122. uint32 count = 0;
  6123. // 0 1 2 3 4 5 6
  6124. QueryResult result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
  6125. if (!result)
  6126. {
  6127. TC_LOG_ERROR("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
  6128. return;
  6129. }
  6130. do
  6131. {
  6132. Field* fields = result->Fetch();
  6133. uint32 point_id = fields[0].GetUInt32();
  6134. PointOfInterest POI;
  6135. POI.entry = point_id;
  6136. POI.x = fields[1].GetFloat();
  6137. POI.y = fields[2].GetFloat();
  6138. POI.icon = fields[3].GetUInt32();
  6139. POI.flags = fields[4].GetUInt32();
  6140. POI.data = fields[5].GetUInt32();
  6141. POI.icon_name = fields[6].GetString();
  6142. if (!Trinity::IsValidMapCoord(POI.x, POI.y))
  6143. {
  6144. TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id, POI.x, POI.y);
  6145. continue;
  6146. }
  6147. _pointsOfInterestStore[point_id] = POI;
  6148. ++count;
  6149. } while (result->NextRow());
  6150. TC_LOG_INFO("server.loading", ">> Loaded %u Points of Interest definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6151. }
  6152. void ObjectMgr::LoadQuestPOI()
  6153. {
  6154. uint32 oldMSTime = getMSTime();
  6155. _questPOIStore.clear(); // need for reload case
  6156. uint32 count = 0;
  6157. // 0 1 2 3 4 5 6 7
  6158. QueryResult result = WorldDatabase.Query("SELECT questId, id, objIndex, mapid, WorldMapAreaId, FloorId, unk3, unk4 FROM quest_poi order by questId");
  6159. if (!result)
  6160. {
  6161. TC_LOG_ERROR("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
  6162. return;
  6163. }
  6164. // 0 1 2 3
  6165. QueryResult points = WorldDatabase.Query("SELECT questId, id, x, y FROM quest_poi_points ORDER BY questId DESC, idx");
  6166. std::vector<std::vector<std::vector<QuestPOIPoint> > > POIs;
  6167. if (points)
  6168. {
  6169. // The first result should have the highest questId
  6170. Field* fields = points->Fetch();
  6171. uint32 questIdMax = fields[0].GetUInt32();
  6172. POIs.resize(questIdMax + 1);
  6173. do
  6174. {
  6175. fields = points->Fetch();
  6176. uint32 questId = fields[0].GetUInt32();
  6177. uint32 id = fields[1].GetUInt32();
  6178. int32 x = fields[2].GetInt32();
  6179. int32 y = fields[3].GetInt32();
  6180. if (POIs[questId].size() <= id + 1)
  6181. POIs[questId].resize(id + 10);
  6182. QuestPOIPoint point(x, y);
  6183. POIs[questId][id].push_back(point);
  6184. } while (points->NextRow());
  6185. }
  6186. do
  6187. {
  6188. Field* fields = result->Fetch();
  6189. uint32 questId = fields[0].GetUInt32();
  6190. uint32 id = fields[1].GetUInt32();
  6191. int32 objIndex = fields[2].GetInt32();
  6192. uint32 mapId = fields[3].GetUInt32();
  6193. uint32 WorldMapAreaId = fields[4].GetUInt32();
  6194. uint32 FloorId = fields[5].GetUInt32();
  6195. uint32 unk3 = fields[6].GetUInt32();
  6196. uint32 unk4 = fields[7].GetUInt32();
  6197. QuestPOI POI(id, objIndex, mapId, WorldMapAreaId, FloorId, unk3, unk4);
  6198. if (questId < POIs.size() && id < POIs[questId].size())
  6199. {
  6200. POI.points = POIs[questId][id];
  6201. _questPOIStore[questId].push_back(POI);
  6202. }
  6203. else
  6204. TC_LOG_ERROR("server.loading", "Table quest_poi references unknown quest points for quest %u POI id %u", questId, id);
  6205. ++count;
  6206. } while (result->NextRow());
  6207. TC_LOG_INFO("server.loading", ">> Loaded %u quest POI definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6208. }
  6209. void ObjectMgr::LoadNPCSpellClickSpells()
  6210. {
  6211. uint32 oldMSTime = getMSTime();
  6212. _spellClickInfoStore.clear();
  6213. // 0 1 2 3
  6214. QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
  6215. if (!result)
  6216. {
  6217. TC_LOG_ERROR("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
  6218. return;
  6219. }
  6220. uint32 count = 0;
  6221. do
  6222. {
  6223. Field* fields = result->Fetch();
  6224. uint32 npc_entry = fields[0].GetUInt32();
  6225. CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
  6226. if (!cInfo)
  6227. {
  6228. TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
  6229. continue;
  6230. }
  6231. uint32 spellid = fields[1].GetUInt32();
  6232. SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid);
  6233. if (!spellinfo)
  6234. {
  6235. TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: %u references unknown spellid %u. Skipping entry.", npc_entry, spellid);
  6236. continue;
  6237. }
  6238. uint8 userType = fields[3].GetUInt16();
  6239. if (userType >= SPELL_CLICK_USER_MAX)
  6240. TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: %u references unknown user type %u. Skipping entry.", npc_entry, uint32(userType));
  6241. uint8 castFlags = fields[2].GetUInt8();
  6242. SpellClickInfo info;
  6243. info.spellId = spellid;
  6244. info.castFlags = castFlags;
  6245. info.userType = SpellClickUserTypes(userType);
  6246. _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
  6247. ++count;
  6248. }
  6249. while (result->NextRow());
  6250. // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
  6251. // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
  6252. CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
  6253. for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
  6254. {
  6255. if ((itr->second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && _spellClickInfoStore.find(itr->second.Entry) == _spellClickInfoStore.end())
  6256. {
  6257. TC_LOG_ERROR("sql.sql", "npc_spellclick_spells: Creature template %u has UNIT_NPC_FLAG_SPELLCLICK but no data in spellclick table! Removing flag", itr->second.Entry);
  6258. const_cast<CreatureTemplate*>(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
  6259. }
  6260. }
  6261. TC_LOG_INFO("server.loading", ">> Loaded %u spellclick definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6262. }
  6263. void ObjectMgr::DeleteCreatureData(uint32 guid)
  6264. {
  6265. // remove mapid*cellid -> guid_set map
  6266. CreatureData const* data = GetCreatureData(guid);
  6267. if (data)
  6268. RemoveCreatureFromGrid(guid, data);
  6269. _creatureDataStore.erase(guid);
  6270. }
  6271. void ObjectMgr::DeleteGOData(uint32 guid)
  6272. {
  6273. // remove mapid*cellid -> guid_set map
  6274. GameObjectData const* data = GetGOData(guid);
  6275. if (data)
  6276. RemoveGameobjectFromGrid(guid, data);
  6277. _gameObjectDataStore.erase(guid);
  6278. }
  6279. void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
  6280. {
  6281. // corpses are always added to spawn mode 0 and they are spawned by their instance id
  6282. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid];
  6283. cell_guids.corpses[player_guid] = instance;
  6284. }
  6285. void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
  6286. {
  6287. // corpses are always added to spawn mode 0 and they are spawned by their instance id
  6288. CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid];
  6289. cell_guids.corpses.erase(player_guid);
  6290. }
  6291. void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go)
  6292. {
  6293. uint32 oldMSTime = getMSTime();
  6294. map.clear(); // need for reload case
  6295. uint32 count = 0;
  6296. 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());
  6297. if (!result)
  6298. {
  6299. TC_LOG_ERROR("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str());
  6300. return;
  6301. }
  6302. PooledQuestRelation* poolRelationMap = go ? &sPoolMgr->mQuestGORelation : &sPoolMgr->mQuestCreatureRelation;
  6303. if (starter)
  6304. poolRelationMap->clear();
  6305. do
  6306. {
  6307. uint32 id = result->Fetch()[0].GetUInt32();
  6308. uint32 quest = result->Fetch()[1].GetUInt32();
  6309. uint32 poolId = result->Fetch()[2].GetUInt32();
  6310. if (_questTemplates.find(quest) == _questTemplates.end())
  6311. {
  6312. TC_LOG_ERROR("sql.sql", "Table `%s`: Quest %u listed for entry %u does not exist.", table.c_str(), quest, id);
  6313. continue;
  6314. }
  6315. if (!poolId || !starter)
  6316. {
  6317. map.insert(QuestRelations::value_type(id, quest));
  6318. if (reverseMap)
  6319. reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
  6320. }
  6321. else if (starter)
  6322. poolRelationMap->insert(PooledQuestRelation::value_type(quest, id));
  6323. ++count;
  6324. } while (result->NextRow());
  6325. TC_LOG_INFO("server.loading", ">> Loaded %u quest relations from %s in %u ms", count, table.c_str(), GetMSTimeDiffToNow(oldMSTime));
  6326. }
  6327. void ObjectMgr::LoadGameobjectQuestStarters()
  6328. {
  6329. LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter", true, true);
  6330. for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
  6331. {
  6332. GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
  6333. if (!goInfo)
  6334. TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry (%u) and existed quest %u", itr->first, itr->second);
  6335. else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
  6336. 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);
  6337. }
  6338. }
  6339. void ObjectMgr::LoadGameobjectQuestEnders()
  6340. {
  6341. LoadQuestRelationsHelper(_goQuestInvolvedRelations, &_goQuestInvolvedRelationsReverse, "gameobject_questender", false, true);
  6342. for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
  6343. {
  6344. GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
  6345. if (!goInfo)
  6346. TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry (%u) and existed quest %u", itr->first, itr->second);
  6347. else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
  6348. 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);
  6349. }
  6350. }
  6351. void ObjectMgr::LoadCreatureQuestStarters()
  6352. {
  6353. LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter", true, false);
  6354. for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
  6355. {
  6356. CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
  6357. if (!cInfo)
  6358. TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry (%u) and existed quest %u", itr->first, itr->second);
  6359. else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
  6360. 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);
  6361. }
  6362. }
  6363. void ObjectMgr::LoadCreatureQuestEnders()
  6364. {
  6365. LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, &_creatureQuestInvolvedRelationsReverse, "creature_questender", false, false);
  6366. for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
  6367. {
  6368. CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
  6369. if (!cInfo)
  6370. TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry (%u) and existed quest %u", itr->first, itr->second);
  6371. else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
  6372. 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);
  6373. }
  6374. }
  6375. void ObjectMgr::LoadReservedPlayersNames()
  6376. {
  6377. uint32 oldMSTime = getMSTime();
  6378. _reservedNamesStore.clear(); // need for reload case
  6379. QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
  6380. if (!result)
  6381. {
  6382. TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
  6383. return;
  6384. }
  6385. uint32 count = 0;
  6386. Field* fields;
  6387. do
  6388. {
  6389. fields = result->Fetch();
  6390. std::string name= fields[0].GetString();
  6391. std::wstring wstr;
  6392. if (!Utf8toWStr (name, wstr))
  6393. {
  6394. TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: %s", name.c_str());
  6395. continue;
  6396. }
  6397. wstrToLower(wstr);
  6398. _reservedNamesStore.insert(wstr);
  6399. ++count;
  6400. }
  6401. while (result->NextRow());
  6402. TC_LOG_INFO("server.loading", ">> Loaded %u reserved player names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6403. }
  6404. bool ObjectMgr::IsReservedName(const std::string& name) const
  6405. {
  6406. std::wstring wstr;
  6407. if (!Utf8toWStr (name, wstr))
  6408. return false;
  6409. wstrToLower(wstr);
  6410. return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
  6411. }
  6412. enum LanguageType
  6413. {
  6414. LT_BASIC_LATIN = 0x0000,
  6415. LT_EXTENDEN_LATIN = 0x0001,
  6416. LT_CYRILLIC = 0x0002,
  6417. LT_EAST_ASIA = 0x0004,
  6418. LT_ANY = 0xFFFF
  6419. };
  6420. static LanguageType GetRealmLanguageType(bool create)
  6421. {
  6422. switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
  6423. {
  6424. case REALM_ZONE_UNKNOWN: // any language
  6425. case REALM_ZONE_DEVELOPMENT:
  6426. case REALM_ZONE_TEST_SERVER:
  6427. case REALM_ZONE_QA_SERVER:
  6428. return LT_ANY;
  6429. case REALM_ZONE_UNITED_STATES: // extended-Latin
  6430. case REALM_ZONE_OCEANIC:
  6431. case REALM_ZONE_LATIN_AMERICA:
  6432. case REALM_ZONE_ENGLISH:
  6433. case REALM_ZONE_GERMAN:
  6434. case REALM_ZONE_FRENCH:
  6435. case REALM_ZONE_SPANISH:
  6436. return LT_EXTENDEN_LATIN;
  6437. case REALM_ZONE_KOREA: // East-Asian
  6438. case REALM_ZONE_TAIWAN:
  6439. case REALM_ZONE_CHINA:
  6440. return LT_EAST_ASIA;
  6441. case REALM_ZONE_RUSSIAN: // Cyrillic
  6442. return LT_CYRILLIC;
  6443. default:
  6444. return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
  6445. }
  6446. }
  6447. bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
  6448. {
  6449. if (strictMask == 0) // any language, ignore realm
  6450. {
  6451. if (isExtendedLatinString(wstr, numericOrSpace))
  6452. return true;
  6453. if (isCyrillicString(wstr, numericOrSpace))
  6454. return true;
  6455. if (isEastAsianString(wstr, numericOrSpace))
  6456. return true;
  6457. return false;
  6458. }
  6459. if (strictMask & 0x2) // realm zone specific
  6460. {
  6461. LanguageType lt = GetRealmLanguageType(create);
  6462. if (lt & LT_EXTENDEN_LATIN)
  6463. if (isExtendedLatinString(wstr, numericOrSpace))
  6464. return true;
  6465. if (lt & LT_CYRILLIC)
  6466. if (isCyrillicString(wstr, numericOrSpace))
  6467. return true;
  6468. if (lt & LT_EAST_ASIA)
  6469. if (isEastAsianString(wstr, numericOrSpace))
  6470. return true;
  6471. }
  6472. if (strictMask & 0x1) // basic Latin
  6473. {
  6474. if (isBasicLatinString(wstr, numericOrSpace))
  6475. return true;
  6476. }
  6477. return false;
  6478. }
  6479. uint8 ObjectMgr::CheckPlayerName(const std::string& name, bool create)
  6480. {
  6481. std::wstring wname;
  6482. if (!Utf8toWStr(name, wname))
  6483. return CHAR_NAME_INVALID_CHARACTER;
  6484. if (wname.size() > MAX_PLAYER_NAME)
  6485. return CHAR_NAME_TOO_LONG;
  6486. uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
  6487. if (wname.size() < minName)
  6488. return CHAR_NAME_TOO_SHORT;
  6489. uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
  6490. if (!isValidString(wname, strictMask, false, create))
  6491. return CHAR_NAME_MIXED_LANGUAGES;
  6492. wstrToLower(wname);
  6493. for (size_t i = 2; i < wname.size(); ++i)
  6494. if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
  6495. return CHAR_NAME_THREE_CONSECUTIVE;
  6496. return CHAR_NAME_SUCCESS;
  6497. }
  6498. bool ObjectMgr::IsValidCharterName(const std::string& name)
  6499. {
  6500. std::wstring wname;
  6501. if (!Utf8toWStr(name, wname))
  6502. return false;
  6503. if (wname.size() > MAX_CHARTER_NAME)
  6504. return false;
  6505. uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
  6506. if (wname.size() < minName)
  6507. return false;
  6508. uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
  6509. return isValidString(wname, strictMask, true);
  6510. }
  6511. PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name)
  6512. {
  6513. std::wstring wname;
  6514. if (!Utf8toWStr(name, wname))
  6515. return PET_NAME_INVALID;
  6516. if (wname.size() > MAX_PET_NAME)
  6517. return PET_NAME_TOO_LONG;
  6518. uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
  6519. if (wname.size() < minName)
  6520. return PET_NAME_TOO_SHORT;
  6521. uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
  6522. if (!isValidString(wname, strictMask, false))
  6523. return PET_NAME_MIXED_LANGUAGES;
  6524. return PET_NAME_SUCCESS;
  6525. }
  6526. void ObjectMgr::LoadGameObjectForQuests()
  6527. {
  6528. uint32 oldMSTime = getMSTime();
  6529. _gameObjectForQuestStore.clear(); // need for reload case
  6530. if (sObjectMgr->GetGameObjectTemplates()->empty())
  6531. {
  6532. TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
  6533. return;
  6534. }
  6535. uint32 count = 0;
  6536. // collect GO entries for GO that must activated
  6537. GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
  6538. for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
  6539. {
  6540. switch (itr->second.type)
  6541. {
  6542. case GAMEOBJECT_TYPE_QUESTGIVER:
  6543. _gameObjectForQuestStore.insert(itr->second.entry);
  6544. ++count;
  6545. break;
  6546. case GAMEOBJECT_TYPE_CHEST:
  6547. {
  6548. // scan GO chest with loot including quest items
  6549. uint32 loot_id = (itr->second.GetLootId());
  6550. // find quest loot for GO
  6551. if (itr->second.chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
  6552. {
  6553. _gameObjectForQuestStore.insert(itr->second.entry);
  6554. ++count;
  6555. }
  6556. break;
  6557. }
  6558. case GAMEOBJECT_TYPE_GENERIC:
  6559. {
  6560. if (itr->second._generic.questID > 0) //quests objects
  6561. {
  6562. _gameObjectForQuestStore.insert(itr->second.entry);
  6563. ++count;
  6564. }
  6565. break;
  6566. }
  6567. case GAMEOBJECT_TYPE_GOOBER:
  6568. {
  6569. if (itr->second.goober.questId > 0) //quests objects
  6570. {
  6571. _gameObjectForQuestStore.insert(itr->second.entry);
  6572. ++count;
  6573. }
  6574. break;
  6575. }
  6576. default:
  6577. break;
  6578. }
  6579. }
  6580. TC_LOG_INFO("server.loading", ">> Loaded %u GameObjects for quests in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6581. }
  6582. bool ObjectMgr::LoadTrinityStrings()
  6583. {
  6584. uint32 oldMSTime = getMSTime();
  6585. _trinityStringStore.clear(); // for reload case
  6586. 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");
  6587. if (!result)
  6588. {
  6589. TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty.");
  6590. return false;
  6591. }
  6592. do
  6593. {
  6594. Field* fields = result->Fetch();
  6595. uint32 entry = fields[0].GetUInt32();
  6596. TrinityString& data = _trinityStringStore[entry];
  6597. data.Content.resize(DEFAULT_LOCALE + 1);
  6598. for (int8 i = TOTAL_LOCALES - 1; i >= 0; --i)
  6599. AddLocaleString(fields[i + 1].GetString(), LocaleConstant(i), data.Content);
  6600. }
  6601. while (result->NextRow());
  6602. TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " trinity strings in %u ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
  6603. return true;
  6604. }
  6605. char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
  6606. {
  6607. if (TrinityString const* ts = GetTrinityString(entry))
  6608. {
  6609. if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
  6610. return ts->Content[locale].c_str();
  6611. return ts->Content[DEFAULT_LOCALE].c_str();
  6612. }
  6613. TC_LOG_ERROR("sql.sql", "Trinity string entry %u not found in DB.", entry);
  6614. return "<error>";
  6615. }
  6616. void ObjectMgr::LoadFishingBaseSkillLevel()
  6617. {
  6618. uint32 oldMSTime = getMSTime();
  6619. _fishingBaseForAreaStore.clear(); // for reload case
  6620. QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
  6621. if (!result)
  6622. {
  6623. TC_LOG_ERROR("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
  6624. return;
  6625. }
  6626. uint32 count = 0;
  6627. do
  6628. {
  6629. Field* fields = result->Fetch();
  6630. uint32 entry = fields[0].GetUInt32();
  6631. int32 skill = fields[1].GetInt16();
  6632. AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
  6633. if (!fArea)
  6634. {
  6635. TC_LOG_ERROR("sql.sql", "AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
  6636. continue;
  6637. }
  6638. _fishingBaseForAreaStore[entry] = skill;
  6639. ++count;
  6640. }
  6641. while (result->NextRow());
  6642. TC_LOG_INFO("server.loading", ">> Loaded %u areas for fishing base skill level in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6643. }
  6644. bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
  6645. {
  6646. // get main part of the name
  6647. std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
  6648. // prepare flags
  6649. bool x = true;
  6650. bool y = true;
  6651. // check declined names
  6652. for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
  6653. {
  6654. std::wstring wname;
  6655. if (!Utf8toWStr(names.name[i], wname))
  6656. return false;
  6657. if (mainpart != GetMainPartOfName(wname, i+1))
  6658. x = false;
  6659. if (w_ownname != wname)
  6660. y = false;
  6661. }
  6662. return (x || y);
  6663. }
  6664. uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id)
  6665. {
  6666. AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
  6667. if (i!= _areaTriggerScriptStore.end())
  6668. return i->second;
  6669. return 0;
  6670. }
  6671. SpellScriptsBounds ObjectMgr::GetSpellScriptsBounds(uint32 spellId)
  6672. {
  6673. return SpellScriptsBounds(_spellScriptsStore.equal_range(spellId));
  6674. }
  6675. // this allows calculating base reputations to offline players, just by race and class
  6676. int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass)
  6677. {
  6678. if (!factionEntry)
  6679. return 0;
  6680. uint32 raceMask = (1 << (race - 1));
  6681. uint32 classMask = (1 << (playerClass-1));
  6682. for (int i = 0; i < 4; i++)
  6683. {
  6684. if ((!factionEntry->BaseRepClassMask[i] ||
  6685. factionEntry->BaseRepClassMask[i] & classMask) &&
  6686. (!factionEntry->BaseRepRaceMask[i] ||
  6687. factionEntry->BaseRepRaceMask[i] & raceMask))
  6688. return factionEntry->BaseRepValue[i];
  6689. }
  6690. return 0;
  6691. }
  6692. SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)
  6693. {
  6694. SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillId);
  6695. if (!skill)
  6696. return SKILL_RANGE_NONE;
  6697. if (sSkillTiersStore.LookupEntry(rcEntry->SkillTier))
  6698. return SKILL_RANGE_RANK;
  6699. if (rcEntry->SkillId == SKILL_RUNEFORGING)
  6700. return SKILL_RANGE_MONO;
  6701. switch (skill->categoryId)
  6702. {
  6703. case SKILL_CATEGORY_ARMOR:
  6704. return SKILL_RANGE_MONO;
  6705. case SKILL_CATEGORY_LANGUAGES:
  6706. return SKILL_RANGE_LANGUAGE;
  6707. }
  6708. return SKILL_RANGE_LEVEL;
  6709. }
  6710. void ObjectMgr::LoadGameTele()
  6711. {
  6712. uint32 oldMSTime = getMSTime();
  6713. _gameTeleStore.clear(); // for reload case
  6714. // 0 1 2 3 4 5 6
  6715. QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
  6716. if (!result)
  6717. {
  6718. TC_LOG_ERROR("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
  6719. return;
  6720. }
  6721. uint32 count = 0;
  6722. do
  6723. {
  6724. Field* fields = result->Fetch();
  6725. uint32 id = fields[0].GetUInt32();
  6726. GameTele gt;
  6727. gt.position_x = fields[1].GetFloat();
  6728. gt.position_y = fields[2].GetFloat();
  6729. gt.position_z = fields[3].GetFloat();
  6730. gt.orientation = fields[4].GetFloat();
  6731. gt.mapId = fields[5].GetUInt16();
  6732. gt.name = fields[6].GetString();
  6733. if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
  6734. {
  6735. TC_LOG_ERROR("sql.sql", "Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
  6736. continue;
  6737. }
  6738. if (!Utf8toWStr(gt.name, gt.wnameLow))
  6739. {
  6740. TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
  6741. continue;
  6742. }
  6743. wstrToLower(gt.wnameLow);
  6744. _gameTeleStore[id] = gt;
  6745. ++count;
  6746. }
  6747. while (result->NextRow());
  6748. TC_LOG_INFO("server.loading", ">> Loaded %u GameTeleports in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6749. }
  6750. GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
  6751. {
  6752. // explicit name case
  6753. std::wstring wname;
  6754. if (!Utf8toWStr(name, wname))
  6755. return NULL;
  6756. // converting string that we try to find to lower case
  6757. wstrToLower(wname);
  6758. // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
  6759. const GameTele* alt = NULL;
  6760. for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  6761. {
  6762. if (itr->second.wnameLow == wname)
  6763. return &itr->second;
  6764. else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
  6765. alt = &itr->second;
  6766. }
  6767. return alt;
  6768. }
  6769. GameTele const* ObjectMgr::GetGameTeleExactName(const std::string& name) const
  6770. {
  6771. // explicit name case
  6772. std::wstring wname;
  6773. if (!Utf8toWStr(name, wname))
  6774. return NULL;
  6775. // converting string that we try to find to lower case
  6776. wstrToLower(wname);
  6777. for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  6778. {
  6779. if (itr->second.wnameLow == wname)
  6780. return &itr->second;
  6781. }
  6782. return NULL;
  6783. }
  6784. bool ObjectMgr::AddGameTele(GameTele& tele)
  6785. {
  6786. // find max id
  6787. uint32 new_id = 0;
  6788. for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  6789. if (itr->first > new_id)
  6790. new_id = itr->first;
  6791. // use next
  6792. ++new_id;
  6793. if (!Utf8toWStr(tele.name, tele.wnameLow))
  6794. return false;
  6795. wstrToLower(tele.wnameLow);
  6796. _gameTeleStore[new_id] = tele;
  6797. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE);
  6798. stmt->setUInt32(0, new_id);
  6799. stmt->setFloat(1, tele.position_x);
  6800. stmt->setFloat(2, tele.position_y);
  6801. stmt->setFloat(3, tele.position_z);
  6802. stmt->setFloat(4, tele.orientation);
  6803. stmt->setUInt16(5, uint16(tele.mapId));
  6804. stmt->setString(6, tele.name);
  6805. WorldDatabase.Execute(stmt);
  6806. return true;
  6807. }
  6808. bool ObjectMgr::DeleteGameTele(const std::string& name)
  6809. {
  6810. // explicit name case
  6811. std::wstring wname;
  6812. if (!Utf8toWStr(name, wname))
  6813. return false;
  6814. // converting string that we try to find to lower case
  6815. wstrToLower(wname);
  6816. for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
  6817. {
  6818. if (itr->second.wnameLow == wname)
  6819. {
  6820. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE);
  6821. stmt->setString(0, itr->second.name);
  6822. WorldDatabase.Execute(stmt);
  6823. _gameTeleStore.erase(itr);
  6824. return true;
  6825. }
  6826. }
  6827. return false;
  6828. }
  6829. void ObjectMgr::LoadMailLevelRewards()
  6830. {
  6831. uint32 oldMSTime = getMSTime();
  6832. _mailLevelRewardStore.clear(); // for reload case
  6833. // 0 1 2 3
  6834. QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
  6835. if (!result)
  6836. {
  6837. TC_LOG_ERROR("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
  6838. return;
  6839. }
  6840. uint32 count = 0;
  6841. do
  6842. {
  6843. Field* fields = result->Fetch();
  6844. uint8 level = fields[0].GetUInt8();
  6845. uint32 raceMask = fields[1].GetUInt32();
  6846. uint32 mailTemplateId = fields[2].GetUInt32();
  6847. uint32 senderEntry = fields[3].GetUInt32();
  6848. if (level > MAX_LEVEL)
  6849. {
  6850. TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level %u that more supported by client (%u), ignoring.", level, MAX_LEVEL);
  6851. continue;
  6852. }
  6853. if (!(raceMask & RACEMASK_ALL_PLAYABLE))
  6854. {
  6855. TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask (%u) for level %u that not include any player races, ignoring.", raceMask, level);
  6856. continue;
  6857. }
  6858. if (!sMailTemplateStore.LookupEntry(mailTemplateId))
  6859. {
  6860. 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);
  6861. continue;
  6862. }
  6863. if (!GetCreatureTemplate(senderEntry))
  6864. {
  6865. 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);
  6866. continue;
  6867. }
  6868. _mailLevelRewardStore[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
  6869. ++count;
  6870. }
  6871. while (result->NextRow());
  6872. TC_LOG_INFO("server.loading", ">> Loaded %u level dependent mail rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6873. }
  6874. void ObjectMgr::AddSpellToTrainer(uint32 entry, uint32 spell, uint32 spellCost, uint32 reqSkill, uint32 reqSkillValue, uint32 reqLevel)
  6875. {
  6876. if (entry >= TRINITY_TRAINER_START_REF)
  6877. return;
  6878. CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
  6879. if (!cInfo)
  6880. {
  6881. TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains entries for a non-existing creature template (Entry: %u), ignoring", entry);
  6882. return;
  6883. }
  6884. if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
  6885. {
  6886. TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains entries for a creature template (Entry: %u) without trainer flag, ignoring", entry);
  6887. return;
  6888. }
  6889. SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spell);
  6890. if (!spellinfo)
  6891. {
  6892. TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u), ignoring", entry, spell);
  6893. return;
  6894. }
  6895. if (!SpellMgr::IsSpellValid(spellinfo))
  6896. {
  6897. TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an entry (Entry: %u) for a broken spell (Spell: %u), ignoring", entry, spell);
  6898. return;
  6899. }
  6900. if (GetTalentSpellCost(spell))
  6901. {
  6902. TC_LOG_ERROR("sql.sql", "Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u) which is a talent, ignoring", entry, spell);
  6903. return;
  6904. }
  6905. TrainerSpellData& data = _cacheTrainerSpellStore[entry];
  6906. TrainerSpell& trainerSpell = data.spellList[spell];
  6907. trainerSpell.spell = spell;
  6908. trainerSpell.spellCost = spellCost;
  6909. trainerSpell.reqSkill = reqSkill;
  6910. trainerSpell.reqSkillValue = reqSkillValue;
  6911. trainerSpell.reqLevel = reqLevel;
  6912. if (!trainerSpell.reqLevel)
  6913. trainerSpell.reqLevel = spellinfo->SpellLevel;
  6914. // calculate learned spell for profession case when stored cast-spell
  6915. trainerSpell.learnedSpell[0] = spell;
  6916. for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
  6917. {
  6918. if (spellinfo->Effects[i].Effect != SPELL_EFFECT_LEARN_SPELL)
  6919. continue;
  6920. if (trainerSpell.learnedSpell[0] == spell)
  6921. trainerSpell.learnedSpell[0] = 0;
  6922. // player must be able to cast spell on himself
  6923. if (spellinfo->Effects[i].TargetA.GetTarget() != 0 && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ALLY
  6924. && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_TARGET_ANY && spellinfo->Effects[i].TargetA.GetTarget() != TARGET_UNIT_CASTER)
  6925. {
  6926. 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!", spell, entry);
  6927. continue;
  6928. }
  6929. trainerSpell.learnedSpell[i] = spellinfo->Effects[i].TriggerSpell;
  6930. if (trainerSpell.learnedSpell[i])
  6931. {
  6932. SpellInfo const* learnedSpellInfo = sSpellMgr->GetSpellInfo(trainerSpell.learnedSpell[i]);
  6933. if (learnedSpellInfo && learnedSpellInfo->IsProfession())
  6934. data.trainerType = 2;
  6935. }
  6936. }
  6937. return;
  6938. }
  6939. void ObjectMgr::LoadTrainerSpell()
  6940. {
  6941. uint32 oldMSTime = getMSTime();
  6942. // For reload case
  6943. _cacheTrainerSpellStore.clear();
  6944. QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel FROM npc_trainer AS a "
  6945. "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) "
  6946. "UNION SELECT * FROM npc_trainer WHERE spell > 0");
  6947. if (!result)
  6948. {
  6949. TC_LOG_ERROR("server.loading", ">> Loaded 0 Trainers. DB table `npc_trainer` is empty!");
  6950. return;
  6951. }
  6952. uint32 count = 0;
  6953. do
  6954. {
  6955. Field* fields = result->Fetch();
  6956. uint32 entry = fields[0].GetUInt32();
  6957. uint32 spell = fields[1].GetUInt32();
  6958. uint32 spellCost = fields[2].GetUInt32();
  6959. uint32 reqSkill = fields[3].GetUInt16();
  6960. uint32 reqSkillValue = fields[4].GetUInt16();
  6961. uint32 reqLevel = fields[5].GetUInt8();
  6962. AddSpellToTrainer(entry, spell, spellCost, reqSkill, reqSkillValue, reqLevel);
  6963. ++count;
  6964. }
  6965. while (result->NextRow());
  6966. TC_LOG_INFO("server.loading", ">> Loaded %d Trainers in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  6967. }
  6968. int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, uint8 type, std::set<uint32> *skip_vendors)
  6969. {
  6970. // find all items from the reference vendor
  6971. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
  6972. stmt->setUInt32(0, uint32(item));
  6973. stmt->setUInt8(1, type);
  6974. PreparedQueryResult result = WorldDatabase.Query(stmt);
  6975. if (!result)
  6976. return 0;
  6977. uint32 count = 0;
  6978. do
  6979. {
  6980. Field* fields = result->Fetch();
  6981. int32 item_id = fields[0].GetInt32();
  6982. // if item is a negative, its a reference
  6983. if (item_id < 0)
  6984. count += LoadReferenceVendor(vendor, -item_id, type, skip_vendors);
  6985. else
  6986. {
  6987. int32 maxcount = fields[1].GetUInt32();
  6988. uint32 incrtime = fields[2].GetUInt32();
  6989. uint32 ExtendedCost = fields[3].GetUInt32();
  6990. uint8 type = fields[4].GetUInt8();
  6991. if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, type, NULL, skip_vendors))
  6992. continue;
  6993. VendorItemData& vList = _cacheVendorItemStore[vendor];
  6994. vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
  6995. ++count;
  6996. }
  6997. } while (result->NextRow());
  6998. return count;
  6999. }
  7000. void ObjectMgr::LoadVendors()
  7001. {
  7002. uint32 oldMSTime = getMSTime();
  7003. // For reload case
  7004. for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
  7005. itr->second.Clear();
  7006. _cacheVendorItemStore.clear();
  7007. std::set<uint32> skip_vendors;
  7008. QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type FROM npc_vendor ORDER BY entry, slot ASC");
  7009. if (!result)
  7010. {
  7011. TC_LOG_ERROR("server.loading", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
  7012. return;
  7013. }
  7014. uint32 count = 0;
  7015. do
  7016. {
  7017. Field* fields = result->Fetch();
  7018. uint32 entry = fields[0].GetUInt32();
  7019. int32 item_id = fields[1].GetInt32();
  7020. // if item is a negative, its a reference
  7021. if (item_id < 0)
  7022. count += LoadReferenceVendor(entry, -item_id, 0, &skip_vendors);
  7023. else
  7024. {
  7025. uint32 maxcount = fields[2].GetUInt32();
  7026. uint32 incrtime = fields[3].GetUInt32();
  7027. uint32 ExtendedCost = fields[4].GetUInt32();
  7028. uint8 type = fields[5].GetUInt8();
  7029. if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, type, NULL, &skip_vendors))
  7030. continue;
  7031. VendorItemData& vList = _cacheVendorItemStore[entry];
  7032. vList.AddItem(item_id, maxcount, incrtime, ExtendedCost, type);
  7033. ++count;
  7034. }
  7035. }
  7036. while (result->NextRow());
  7037. TC_LOG_INFO("server.loading", ">> Loaded %d Vendors in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7038. }
  7039. void ObjectMgr::LoadGossipMenu()
  7040. {
  7041. uint32 oldMSTime = getMSTime();
  7042. _gossipMenusStore.clear();
  7043. QueryResult result = WorldDatabase.Query("SELECT entry, text_id FROM gossip_menu");
  7044. if (!result)
  7045. {
  7046. TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu entries. DB table `gossip_menu` is empty!");
  7047. return;
  7048. }
  7049. uint32 count = 0;
  7050. do
  7051. {
  7052. Field* fields = result->Fetch();
  7053. GossipMenus gMenu;
  7054. gMenu.entry = fields[0].GetUInt16();
  7055. gMenu.text_id = fields[1].GetUInt32();
  7056. if (!GetGossipText(gMenu.text_id))
  7057. {
  7058. TC_LOG_ERROR("sql.sql", "Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
  7059. continue;
  7060. }
  7061. _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.entry, gMenu));
  7062. ++count;
  7063. }
  7064. while (result->NextRow());
  7065. TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7066. }
  7067. void ObjectMgr::LoadGossipMenuItems()
  7068. {
  7069. uint32 oldMSTime = getMSTime();
  7070. _gossipMenuItemsStore.clear();
  7071. QueryResult result = WorldDatabase.Query(
  7072. // 0 1 2 3 4 5 6 7 8 9 10 11 12
  7073. "SELECT menu_id, id, option_icon, option_text, OptionBroadcastTextID, option_id, npc_option_npcflag, action_menu_id, action_poi_id, box_coded, box_money, box_text, BoxBroadcastTextID "
  7074. "FROM gossip_menu_option ORDER BY menu_id, id");
  7075. if (!result)
  7076. {
  7077. TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu_option entries. DB table `gossip_menu_option` is empty!");
  7078. return;
  7079. }
  7080. uint32 count = 0;
  7081. do
  7082. {
  7083. Field* fields = result->Fetch();
  7084. GossipMenuItems gMenuItem;
  7085. gMenuItem.MenuId = fields[0].GetUInt16();
  7086. gMenuItem.OptionIndex = fields[1].GetUInt16();
  7087. gMenuItem.OptionIcon = fields[2].GetUInt32();
  7088. gMenuItem.OptionText = fields[3].GetString();
  7089. gMenuItem.OptionBroadcastTextId = fields[4].GetUInt32();
  7090. gMenuItem.OptionType = fields[5].GetUInt8();
  7091. gMenuItem.OptionNpcflag = fields[6].GetUInt32();
  7092. gMenuItem.ActionMenuId = fields[7].GetUInt32();
  7093. gMenuItem.ActionPoiId = fields[8].GetUInt32();
  7094. gMenuItem.BoxCoded = fields[9].GetBool();
  7095. gMenuItem.BoxMoney = fields[10].GetUInt32();
  7096. gMenuItem.BoxText = fields[11].GetString();
  7097. gMenuItem.BoxBroadcastTextId = fields[12].GetUInt32();
  7098. if (gMenuItem.OptionIcon >= GOSSIP_ICON_MAX)
  7099. {
  7100. 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.OptionIndex, gMenuItem.OptionIcon);
  7101. gMenuItem.OptionIcon = GOSSIP_ICON_CHAT;
  7102. }
  7103. if (gMenuItem.OptionBroadcastTextId)
  7104. {
  7105. if (!GetBroadcastText(gMenuItem.OptionBroadcastTextId))
  7106. {
  7107. 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.OptionIndex, gMenuItem.OptionBroadcastTextId);
  7108. gMenuItem.OptionBroadcastTextId = 0;
  7109. }
  7110. }
  7111. if (gMenuItem.OptionType >= GOSSIP_OPTION_MAX)
  7112. 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.OptionIndex, gMenuItem.OptionType);
  7113. if (gMenuItem.ActionPoiId && !GetPointOfInterest(gMenuItem.ActionPoiId))
  7114. {
  7115. TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.MenuId, gMenuItem.OptionIndex, gMenuItem.ActionPoiId);
  7116. gMenuItem.ActionPoiId = 0;
  7117. }
  7118. if (gMenuItem.BoxBroadcastTextId)
  7119. {
  7120. if (!GetBroadcastText(gMenuItem.BoxBroadcastTextId))
  7121. {
  7122. 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.OptionIndex, gMenuItem.BoxBroadcastTextId);
  7123. gMenuItem.BoxBroadcastTextId = 0;
  7124. }
  7125. }
  7126. _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuId, gMenuItem));
  7127. ++count;
  7128. }
  7129. while (result->NextRow());
  7130. TC_LOG_INFO("server.loading", ">> Loaded %u gossip_menu_option entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7131. }
  7132. void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, uint8 type, bool persist /*= true*/)
  7133. {
  7134. VendorItemData& vList = _cacheVendorItemStore[entry];
  7135. vList.AddItem(item, maxcount, incrtime, extendedCost, type);
  7136. if (persist)
  7137. {
  7138. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
  7139. stmt->setUInt32(0, entry);
  7140. stmt->setUInt32(1, item);
  7141. stmt->setUInt8(2, maxcount);
  7142. stmt->setUInt32(3, incrtime);
  7143. stmt->setUInt32(4, extendedCost);
  7144. stmt->setUInt8(5, type);
  7145. WorldDatabase.Execute(stmt);
  7146. }
  7147. }
  7148. bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/)
  7149. {
  7150. CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
  7151. if (iter == _cacheVendorItemStore.end())
  7152. return false;
  7153. if (!iter->second.RemoveItem(item, type))
  7154. return false;
  7155. if (persist)
  7156. {
  7157. PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR);
  7158. stmt->setUInt32(0, entry);
  7159. stmt->setUInt32(1, item);
  7160. stmt->setUInt8(2, type);
  7161. WorldDatabase.Execute(stmt);
  7162. }
  7163. return true;
  7164. }
  7165. bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, uint8 type, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
  7166. {
  7167. CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
  7168. if (!cInfo)
  7169. {
  7170. if (player)
  7171. ChatHandler(player->GetSession()).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
  7172. else
  7173. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: %u), ignore", vendor_entry);
  7174. return false;
  7175. }
  7176. if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
  7177. {
  7178. if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
  7179. {
  7180. if (player)
  7181. ChatHandler(player->GetSession()).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
  7182. else
  7183. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
  7184. if (skip_vendors)
  7185. skip_vendors->insert(vendor_entry);
  7186. }
  7187. return false;
  7188. }
  7189. if ((type == ITEM_VENDOR_TYPE_ITEM && !sObjectMgr->GetItemTemplate(id)) ||
  7190. (type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(id)))
  7191. {
  7192. if (player)
  7193. ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_NOT_FOUND, id, type);
  7194. else
  7195. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u, type %u), ignore", vendor_entry, id, type);
  7196. return false;
  7197. }
  7198. if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
  7199. {
  7200. if (player)
  7201. ChatHandler(player->GetSession()).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
  7202. else
  7203. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", id, ExtendedCost, vendor_entry);
  7204. return false;
  7205. }
  7206. if (type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
  7207. {
  7208. if (maxcount > 0 && incrtime == 0)
  7209. {
  7210. if (player)
  7211. ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
  7212. else
  7213. 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, id, vendor_entry);
  7214. return false;
  7215. }
  7216. else if (maxcount == 0 && incrtime > 0)
  7217. {
  7218. if (player)
  7219. ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
  7220. else
  7221. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", id, vendor_entry);
  7222. return false;
  7223. }
  7224. }
  7225. VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
  7226. if (!vItems)
  7227. return true; // later checks for non-empty lists
  7228. if (vItems->FindItemCostPair(id, ExtendedCost, type))
  7229. {
  7230. if (player)
  7231. ChatHandler(player->GetSession()).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, id, ExtendedCost, type);
  7232. else
  7233. TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items %u (with extended cost %u, type %u) for vendor (Entry: %u), ignoring", id, ExtendedCost, type, vendor_entry);
  7234. return false;
  7235. }
  7236. if (type == ITEM_VENDOR_TYPE_CURRENCY && maxcount == 0)
  7237. {
  7238. TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: %u, type: %u) with missing maxcount for vendor (%u), ignore", id, type, vendor_entry);
  7239. return false;
  7240. }
  7241. return true;
  7242. }
  7243. void ObjectMgr::LoadScriptNames()
  7244. {
  7245. uint32 oldMSTime = getMSTime();
  7246. _scriptNamesStore.push_back("");
  7247. QueryResult result = WorldDatabase.Query(
  7248. "SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
  7249. "UNION "
  7250. "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
  7251. "UNION "
  7252. "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
  7253. "UNION "
  7254. "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
  7255. "UNION "
  7256. "SELECT DISTINCT(ScriptName) FROM item_script_names WHERE ScriptName <> '' "
  7257. "UNION "
  7258. "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
  7259. "UNION "
  7260. "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
  7261. "UNION "
  7262. "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
  7263. "UNION "
  7264. "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
  7265. "UNION "
  7266. "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
  7267. "UNION "
  7268. "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
  7269. "UNION "
  7270. "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
  7271. if (!result)
  7272. {
  7273. TC_LOG_ERROR("server.loading", ">> Loaded empty set of Script Names!");
  7274. return;
  7275. }
  7276. uint32 count = 1;
  7277. do
  7278. {
  7279. _scriptNamesStore.push_back((*result)[0].GetString());
  7280. ++count;
  7281. }
  7282. while (result->NextRow());
  7283. std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
  7284. TC_LOG_INFO("server.loading", ">> Loaded %d Script Names in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7285. }
  7286. uint32 ObjectMgr::GetScriptId(const char *name)
  7287. {
  7288. // use binary search to find the script name in the sorted vector
  7289. // assume "" is the first element
  7290. if (!name)
  7291. return 0;
  7292. ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name);
  7293. if (itr == _scriptNamesStore.end() || *itr != name)
  7294. return 0;
  7295. return uint32(itr - _scriptNamesStore.begin());
  7296. }
  7297. void ObjectMgr::LoadBroadcastTexts()
  7298. {
  7299. uint32 oldMSTime = getMSTime();
  7300. _broadcastTextStore.clear(); // for reload case
  7301. // 0 1 2 3 4 5 6 7 8 9 10 11 12
  7302. QueryResult result = WorldDatabase.Query("SELECT ID, Language, MaleText, FemaleText, EmoteID0, EmoteID1, EmoteID2, EmoteDelay0, EmoteDelay1, EmoteDelay2, SoundId, Unk1, Unk2 FROM broadcast_text");
  7303. if (!result)
  7304. {
  7305. TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast texts. DB table `broadcast_text` is empty.");
  7306. return;
  7307. }
  7308. _broadcastTextStore.rehash(result->GetRowCount());
  7309. uint32 count = 0;
  7310. do
  7311. {
  7312. Field* fields = result->Fetch();
  7313. BroadcastText bct;
  7314. bct.Id = fields[0].GetUInt32();
  7315. bct.Language = fields[1].GetUInt32();
  7316. bct.MaleText[DEFAULT_LOCALE] = fields[2].GetString();
  7317. bct.FemaleText[DEFAULT_LOCALE] = fields[3].GetString();
  7318. bct.EmoteId0 = fields[4].GetUInt32();
  7319. bct.EmoteId1 = fields[5].GetUInt32();
  7320. bct.EmoteId2 = fields[6].GetUInt32();
  7321. bct.EmoteDelay0 = fields[7].GetUInt32();
  7322. bct.EmoteDelay1 = fields[8].GetUInt32();
  7323. bct.EmoteDelay2 = fields[9].GetUInt32();
  7324. bct.SoundId = fields[10].GetUInt32();
  7325. bct.Unk1 = fields[11].GetUInt32();
  7326. bct.Unk2 = fields[12].GetUInt32();
  7327. if (bct.SoundId)
  7328. {
  7329. if (!sSoundEntriesStore.LookupEntry(bct.SoundId))
  7330. {
  7331. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has SoundId %u but sound does not exist. Skipped.", bct.Id, bct.SoundId);
  7332. // don't load bct of higher expansions
  7333. continue;
  7334. }
  7335. }
  7336. if (!GetLanguageDescByID(bct.Language))
  7337. {
  7338. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` using Language %u but Language does not exist. Skipped.", bct.Id, bct.Language);
  7339. // don't load bct of higher expansions
  7340. continue;
  7341. }
  7342. if (bct.EmoteId0)
  7343. {
  7344. if (!sEmotesStore.LookupEntry(bct.EmoteId0))
  7345. {
  7346. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId0 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId0);
  7347. // don't load bct of higher expansions
  7348. continue;
  7349. }
  7350. }
  7351. if (bct.EmoteId1)
  7352. {
  7353. if (!sEmotesStore.LookupEntry(bct.EmoteId1))
  7354. {
  7355. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId1 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId1);
  7356. // don't load bct of higher expansions
  7357. continue;
  7358. }
  7359. }
  7360. if (bct.EmoteId2)
  7361. {
  7362. if (!sEmotesStore.LookupEntry(bct.EmoteId2))
  7363. {
  7364. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `broadcast_text` has EmoteId2 %u but emote does not exist. Skipped.", bct.Id, bct.EmoteId2);
  7365. // don't load bct of higher expansions
  7366. continue;
  7367. }
  7368. }
  7369. _broadcastTextStore[bct.Id] = bct;
  7370. ++count;
  7371. }
  7372. while (result->NextRow());
  7373. TC_LOG_INFO("server.loading", ">> Loaded %u broadcast texts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7374. }
  7375. void ObjectMgr::LoadBroadcastTextLocales()
  7376. {
  7377. uint32 oldMSTime = getMSTime();
  7378. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  7379. QueryResult result = WorldDatabase.Query("SELECT Id, MaleText_loc1, MaleText_loc2, MaleText_loc3, MaleText_loc4, MaleText_loc5, MaleText_loc6, MaleText_loc7, MaleText_loc8, FemaleText_loc1, FemaleText_loc2, FemaleText_loc3, FemaleText_loc4, FemaleText_loc5, FemaleText_loc6, FemaleText_loc7, FemaleText_loc8 FROM locales_broadcast_text");
  7380. if (!result)
  7381. {
  7382. TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast text locales. DB table `locales_broadcast_text` is empty.");
  7383. return;
  7384. }
  7385. uint32 count = 0;
  7386. do
  7387. {
  7388. Field* fields = result->Fetch();
  7389. uint32 id = fields[0].GetUInt32();
  7390. BroadcastTextContainer::iterator bct = _broadcastTextStore.find(id);
  7391. if (bct == _broadcastTextStore.end())
  7392. {
  7393. TC_LOG_INFO("sql.sql", "BroadcastText (Id: %u) in table `locales_broadcast_text` does not exist or is incompatible. Skipped!", id);
  7394. // don't load bct of higher expansions
  7395. continue;
  7396. }
  7397. for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i)
  7398. {
  7399. LocaleConstant locale = LocaleConstant(i);
  7400. AddLocaleString(fields[1 + (i - 1)].GetString(), locale, bct->second.MaleText);
  7401. AddLocaleString(fields[9 + (i - 1)].GetString(), locale, bct->second.FemaleText);
  7402. }
  7403. ++count;
  7404. }
  7405. while (result->NextRow());
  7406. TC_LOG_INFO("server.loading", ">> Loaded %u broadcast text locales in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7407. }
  7408. CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unitClass)
  7409. {
  7410. CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
  7411. if (it != _creatureBaseStatsStore.end())
  7412. return &(it->second);
  7413. struct DefaultCreatureBaseStats : public CreatureBaseStats
  7414. {
  7415. DefaultCreatureBaseStats()
  7416. {
  7417. BaseArmor = 1;
  7418. for (uint8 j = 0; j < MAX_EXPANSIONS; ++j)
  7419. {
  7420. BaseHealth[j] = 1;
  7421. BaseDamage[j] = 0.0f;
  7422. }
  7423. BaseMana = 0;
  7424. AttackPower = 0;
  7425. RangedAttackPower = 0;
  7426. }
  7427. };
  7428. static const DefaultCreatureBaseStats defStats;
  7429. return &defStats;
  7430. }
  7431. void ObjectMgr::LoadCreatureClassLevelStats()
  7432. {
  7433. uint32 oldMSTime = getMSTime();
  7434. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
  7435. QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2, damage_exp3 FROM creature_classlevelstats");
  7436. if (!result)
  7437. {
  7438. TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
  7439. return;
  7440. }
  7441. uint32 count = 0;
  7442. do
  7443. {
  7444. Field* fields = result->Fetch();
  7445. uint8 Level = fields[0].GetUInt8();
  7446. uint8 Class = fields[1].GetUInt8();
  7447. if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
  7448. TC_LOG_ERROR("sql.sql", "Creature base stats for level %u has invalid class %u", Level, Class);
  7449. CreatureBaseStats stats;
  7450. for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
  7451. {
  7452. stats.BaseHealth[i] = fields[2 + i].GetUInt32();
  7453. if (stats.BaseHealth[i] == 0)
  7454. {
  7455. 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);
  7456. stats.BaseHealth[i] = 1;
  7457. }
  7458. stats.BaseDamage[i] = fields[10 + i].GetFloat();
  7459. if (stats.BaseDamage[i] < 0.0f)
  7460. {
  7461. 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);
  7462. stats.BaseDamage[i] = 0.0f;
  7463. }
  7464. }
  7465. stats.BaseMana = fields[6].GetUInt16();
  7466. stats.BaseArmor = fields[7].GetUInt16();
  7467. stats.AttackPower = fields[8].GetUInt16();
  7468. stats.RangedAttackPower = fields[9].GetUInt16();
  7469. _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
  7470. ++count;
  7471. }
  7472. while (result->NextRow());
  7473. CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
  7474. for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
  7475. {
  7476. for (uint16 lvl = itr->second.minlevel; lvl <= itr->second.maxlevel; ++lvl)
  7477. {
  7478. if (_creatureBaseStatsStore.find(MAKE_PAIR16(lvl, itr->second.unit_class)) == _creatureBaseStatsStore.end())
  7479. TC_LOG_ERROR("sql.sql", "Missing base stats for creature class %u level %u", itr->second.unit_class, lvl);
  7480. }
  7481. }
  7482. TC_LOG_INFO("server.loading", ">> Loaded %u creature base stats in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7483. }
  7484. void ObjectMgr::LoadFactionChangeAchievements()
  7485. {
  7486. uint32 oldMSTime = getMSTime();
  7487. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
  7488. if (!result)
  7489. {
  7490. TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
  7491. return;
  7492. }
  7493. uint32 count = 0;
  7494. do
  7495. {
  7496. Field* fields = result->Fetch();
  7497. uint32 alliance = fields[0].GetUInt32();
  7498. uint32 horde = fields[1].GetUInt32();
  7499. if (!sAchievementMgr->GetAchievement(alliance))
  7500. TC_LOG_ERROR("sql.sql", "Achievement %u (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
  7501. else if (!sAchievementMgr->GetAchievement(horde))
  7502. TC_LOG_ERROR("sql.sql", "Achievement %u (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
  7503. else
  7504. FactionChangeAchievements[alliance] = horde;
  7505. ++count;
  7506. }
  7507. while (result->NextRow());
  7508. TC_LOG_INFO("server.loading", ">> Loaded %u faction change achievement pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7509. }
  7510. void ObjectMgr::LoadFactionChangeItems()
  7511. {
  7512. uint32 oldMSTime = getMSTime();
  7513. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
  7514. if (!result)
  7515. {
  7516. TC_LOG_INFO("server.loading", ">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
  7517. return;
  7518. }
  7519. uint32 count = 0;
  7520. do
  7521. {
  7522. Field* fields = result->Fetch();
  7523. uint32 alliance = fields[0].GetUInt32();
  7524. uint32 horde = fields[1].GetUInt32();
  7525. if (!GetItemTemplate(alliance))
  7526. TC_LOG_ERROR("sql.sql", "Item %u (alliance_id) referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
  7527. else if (!GetItemTemplate(horde))
  7528. TC_LOG_ERROR("sql.sql", "Item %u (horde_id) referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
  7529. else
  7530. FactionChangeItems[alliance] = horde;
  7531. ++count;
  7532. }
  7533. while (result->NextRow());
  7534. TC_LOG_INFO("server.loading", ">> Loaded %u faction change item pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7535. }
  7536. void ObjectMgr::LoadFactionChangeQuests()
  7537. {
  7538. uint32 oldMSTime = getMSTime();
  7539. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
  7540. if (!result)
  7541. {
  7542. TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
  7543. return;
  7544. }
  7545. uint32 count = 0;
  7546. do
  7547. {
  7548. Field* fields = result->Fetch();
  7549. uint32 alliance = fields[0].GetUInt32();
  7550. uint32 horde = fields[1].GetUInt32();
  7551. if (!sObjectMgr->GetQuestTemplate(alliance))
  7552. TC_LOG_ERROR("sql.sql", "Quest %u (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
  7553. else if (!sObjectMgr->GetQuestTemplate(horde))
  7554. TC_LOG_ERROR("sql.sql", "Quest %u (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
  7555. else
  7556. FactionChangeQuests[alliance] = horde;
  7557. ++count;
  7558. }
  7559. while (result->NextRow());
  7560. TC_LOG_INFO("server.loading", ">> Loaded %u faction change quest pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7561. }
  7562. void ObjectMgr::LoadFactionChangeReputations()
  7563. {
  7564. uint32 oldMSTime = getMSTime();
  7565. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
  7566. if (!result)
  7567. {
  7568. TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
  7569. return;
  7570. }
  7571. uint32 count = 0;
  7572. do
  7573. {
  7574. Field* fields = result->Fetch();
  7575. uint32 alliance = fields[0].GetUInt32();
  7576. uint32 horde = fields[1].GetUInt32();
  7577. if (!sFactionStore.LookupEntry(alliance))
  7578. TC_LOG_ERROR("sql.sql", "Reputation %u (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
  7579. else if (!sFactionStore.LookupEntry(horde))
  7580. TC_LOG_ERROR("sql.sql", "Reputation %u (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
  7581. else
  7582. FactionChangeReputation[alliance] = horde;
  7583. ++count;
  7584. }
  7585. while (result->NextRow());
  7586. TC_LOG_INFO("server.loading", ">> Loaded %u faction change reputation pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7587. }
  7588. void ObjectMgr::LoadHotfixData()
  7589. {
  7590. uint32 oldMSTime = getMSTime();
  7591. QueryResult result = WorldDatabase.Query("SELECT entry, type, UNIX_TIMESTAMP(hotfixDate) FROM hotfix_data");
  7592. if (!result)
  7593. {
  7594. TC_LOG_INFO("server.loading", ">> Loaded 0 hotfix info entries. DB table `hotfix_data` is empty.");
  7595. return;
  7596. }
  7597. uint32 count = 0;
  7598. _hotfixData.reserve(result->GetRowCount());
  7599. do
  7600. {
  7601. Field* fields = result->Fetch();
  7602. HotfixInfo info;
  7603. info.Entry = fields[0].GetUInt32();
  7604. info.Type = fields[1].GetUInt32();
  7605. info.Timestamp = fields[2].GetUInt64();
  7606. _hotfixData.push_back(info);
  7607. ++count;
  7608. }
  7609. while (result->NextRow());
  7610. TC_LOG_INFO("server.loading", ">> Loaded %u hotfix info entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7611. }
  7612. void ObjectMgr::LoadMissingKeyChains()
  7613. {
  7614. uint32 oldMSTime = getMSTime();
  7615. QueryResult result = WorldDatabase.Query("SELECT keyId, k1, k2, k3, k4, k5, k6, k7, k8, "
  7616. "k9, k10, k11, k12, k13, k14, k15, k16, "
  7617. "k17, k18, k19, k20, k21, k22, k23, k24, "
  7618. "k25, k26, k27, k28, k29, k30, k31, k32 "
  7619. "FROM keychain_db2 ORDER BY keyId DESC");
  7620. if (!result)
  7621. {
  7622. TC_LOG_INFO("server.loading", ">> Loaded 0 KeyChain entries. DB table `keychain_db2` is empty.");
  7623. return;
  7624. }
  7625. uint32 count = 0;
  7626. do
  7627. {
  7628. Field* fields = result->Fetch();
  7629. uint32 id = fields[0].GetUInt32();
  7630. KeyChainEntry* kce = sKeyChainStore.CreateEntry(id, true);
  7631. kce->Id = id;
  7632. for (uint32 i = 0; i < KEYCHAIN_SIZE; ++i)
  7633. kce->Key[i] = fields[1 + i].GetUInt8();
  7634. ++count;
  7635. }
  7636. while (result->NextRow());
  7637. TC_LOG_INFO("server.loading", ">> Loaded %u KeyChain entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7638. }
  7639. void ObjectMgr::LoadFactionChangeSpells()
  7640. {
  7641. uint32 oldMSTime = getMSTime();
  7642. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
  7643. if (!result)
  7644. {
  7645. TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
  7646. return;
  7647. }
  7648. uint32 count = 0;
  7649. do
  7650. {
  7651. Field* fields = result->Fetch();
  7652. uint32 alliance = fields[0].GetUInt32();
  7653. uint32 horde = fields[1].GetUInt32();
  7654. if (!sSpellMgr->GetSpellInfo(alliance))
  7655. TC_LOG_ERROR("sql.sql", "Spell %u (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
  7656. else if (!sSpellMgr->GetSpellInfo(horde))
  7657. TC_LOG_ERROR("sql.sql", "Spell %u (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
  7658. else
  7659. FactionChangeSpells[alliance] = horde;
  7660. ++count;
  7661. }
  7662. while (result->NextRow());
  7663. TC_LOG_INFO("server.loading", ">> Loaded %u faction change spell pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7664. }
  7665. void ObjectMgr::LoadFactionChangeTitles()
  7666. {
  7667. uint32 oldMSTime = getMSTime();
  7668. QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
  7669. if (!result)
  7670. {
  7671. TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
  7672. return;
  7673. }
  7674. uint32 count = 0;
  7675. do
  7676. {
  7677. Field* fields = result->Fetch();
  7678. uint32 alliance = fields[0].GetUInt32();
  7679. uint32 horde = fields[1].GetUInt32();
  7680. if (!sCharTitlesStore.LookupEntry(alliance))
  7681. TC_LOG_ERROR("sql.sql", "Title %u (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
  7682. else if (!sCharTitlesStore.LookupEntry(horde))
  7683. TC_LOG_ERROR("sql.sql", "Title %u (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
  7684. else
  7685. FactionChangeTitles[alliance] = horde;
  7686. ++count;
  7687. }
  7688. while (result->NextRow());
  7689. TC_LOG_INFO("server.loading", ">> Loaded %u faction change title pairs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
  7690. }
  7691. void ObjectMgr::LoadPhaseDefinitions()
  7692. {
  7693. _PhaseDefinitionStore.clear();
  7694. uint32 oldMSTime = getMSTime();
  7695. // 0 1 2 3
  7696. QueryResult result = WorldDatabase.Query("SELECT zoneId, entry, phaseId, phaseGroup FROM `phase_definitions` ORDER BY `entry` ASC");
  7697. if (!result)
  7698. {
  7699. TC_LOG_INFO("server.loading", ">> Loaded 0 phasing definitions. DB table `phase_definitions` is empty.");
  7700. return;
  7701. }
  7702. uint32 count = 0;
  7703. do
  7704. {
  7705. Field* fields = result->Fetch();
  7706. PhaseDefinition PhaseDefinition;
  7707. PhaseDefinition.zoneId = fields[0].GetUInt32();
  7708. PhaseDefinition.entry = fields[1].GetUInt32();
  7709. PhaseDefinition.phaseId = fields[2].GetUInt32();
  7710. PhaseDefinition.phaseGroup = fields[3].GetUInt32();
  7711. if (PhaseDefinition.phaseGroup && PhaseDefinition.phaseId)
  7712. {
  7713. TC_LOG_ERROR("sql.sql", "Phase definition for zone %u (Entry: %u) has phaseGroup and phaseId set, phaseGroup set to 0", PhaseDefinition.zoneId, PhaseDefinition.entry);
  7714. PhaseDefinition.phaseGroup = 0;
  7715. }
  7716. _PhaseDefinitionStore[PhaseDefinition.zoneId].push_back(PhaseDefinition);
  7717. ++count;
  7718. }
  7719. while (result->NextRow());
  7720. TC_LOG_INFO("server.loading", ">> Loaded %u phasing definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
  7721. }
  7722. void ObjectMgr::LoadPhaseInfo()
  7723. {
  7724. _PhaseInfoStore.clear();
  7725. uint32 oldMSTime = getMSTime();
  7726. // 0 1 2
  7727. QueryResult result = WorldDatabase.Query("SELECT id, worldmapareaswap, terrainswapmap FROM `phase_info`");
  7728. if (!result)
  7729. {
  7730. TC_LOG_INFO("server.loading", ">> Loaded 0 phase infos. DB table `phase_info` is empty.");
  7731. return;
  7732. }
  7733. uint32 count = 0;
  7734. do
  7735. {
  7736. Field* fields = result->Fetch();
  7737. PhaseInfo phaseInfo;
  7738. phaseInfo.phaseId = fields[0].GetUInt32();
  7739. PhaseEntry const* phase = sPhaseStore.LookupEntry(phaseInfo.phaseId);
  7740. if (!phase)
  7741. {
  7742. TC_LOG_ERROR("sql.sql", "Phase %u defined in `phase_info` does not exists, skipped.", phaseInfo.phaseId);
  7743. continue;
  7744. }
  7745. phaseInfo.worldMapAreaSwap = fields[1].GetUInt32();
  7746. phaseInfo.terrainSwapMap = fields[2].GetUInt32();
  7747. _PhaseInfoStore[phaseInfo.phaseId] = phaseInfo;
  7748. ++count;
  7749. }
  7750. while (result->NextRow());
  7751. TC_LOG_INFO("server.loading", ">> Loaded %u phase infos in %u ms.", count, GetMSTimeDiffToNow(oldMSTime));
  7752. }
  7753. GameObjectTemplate const* ObjectMgr::GetGameObjectTemplate(uint32 entry)
  7754. {
  7755. GameObjectTemplateContainer::const_iterator itr = _gameObjectTemplateStore.find(entry);
  7756. if (itr != _gameObjectTemplateStore.end())
  7757. return &(itr->second);
  7758. return NULL;
  7759. }
  7760. CreatureTemplate const* ObjectMgr::GetCreatureTemplate(uint32 entry)
  7761. {
  7762. CreatureTemplateContainer::const_iterator itr = _creatureTemplateStore.find(entry);
  7763. if (itr != _creatureTemplateStore.end())
  7764. return &(itr->second);
  7765. return NULL;
  7766. }
  7767. VehicleAccessoryList const* ObjectMgr::GetVehicleAccessoryList(Vehicle* veh) const
  7768. {
  7769. if (Creature* cre = veh->GetBase()->ToCreature())
  7770. {
  7771. // Give preference to GUID-based accessories
  7772. VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetDBTableGUIDLow());
  7773. if (itr != _vehicleAccessoryStore.end())
  7774. return &itr->second;
  7775. }
  7776. // Otherwise return entry-based
  7777. VehicleAccessoryContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
  7778. if (itr != _vehicleTemplateAccessoryStore.end())
  7779. return &itr->second;
  7780. return NULL;
  7781. }
  7782. PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const
  7783. {
  7784. if (race >= MAX_RACES)
  7785. return NULL;
  7786. if (class_ >= MAX_CLASSES)
  7787. return NULL;
  7788. PlayerInfo const* info = _playerInfo[race][class_];
  7789. if (!info)
  7790. return NULL;
  7791. return info;
  7792. }