TrinityCore
ObjectMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "ObjectMgr.h"
19#include "ArenaTeamMgr.h"
21#include "AreaTriggerTemplate.h"
23#include "AzeriteItem.h"
24#include "Chat.h"
25#include "Containers.h"
26#include "CreatureAIFactory.h"
27#include "CriteriaHandler.h"
28#include "DatabaseEnv.h"
29#include "DB2Stores.h"
30#include "DisableMgr.h"
31#include "GameObject.h"
32#include "GameObjectAIFactory.h"
33#include "GameTables.h"
34#include "GameTime.h"
35#include "GridDefines.h"
36#include "GossipDef.h"
37#include "GroupMgr.h"
38#include "GuildMgr.h"
39#include "InstanceScript.h"
40#include "Item.h"
41#include "ItemBonusMgr.h"
42#include "Language.h"
43#include "LFGMgr.h"
44#include "Log.h"
45#include "LootMgr.h"
46#include "Mail.h"
47#include "MapManager.h"
48#include "MotionMaster.h"
49#include "MovementTypedefs.h"
50#include "ObjectAccessor.h"
51#include "ObjectDefines.h"
52#include "PhasingHandler.h"
53#include "Player.h"
54#include "QueryPackets.h"
55#include "QuestDef.h"
56#include "Random.h"
57#include "Realm.h"
58#include "ReputationMgr.h"
59#include "ScriptMgr.h"
60#include "ScriptReloadMgr.h"
61#include "SpellInfo.h"
62#include "SpellMgr.h"
63#include "SpellScript.h"
64#include "StringConvert.h"
65#include "TemporarySummon.h"
66#include "TerrainMgr.h"
67#include "ThreadPool.h"
68#include "Timer.h"
69#include "TransportMgr.h"
70#include "Vehicle.h"
71#include "VMapFactory.h"
72#include "VMapManager2.h"
73#include "World.h"
74#include <G3D/g3dmath.h>
75#include <numeric>
76#include <limits>
77
80
82{
83 std::string res = "";
84 switch (type)
85 {
86 case SCRIPTS_SPELL: res = "spell_scripts"; break;
87 case SCRIPTS_EVENT: res = "event_scripts"; break;
88 default: break;
89 }
90 return res;
91}
92
94{
95 ScriptMapMap* res = nullptr;
96 switch (type)
97 {
98 case SCRIPTS_SPELL: res = &sSpellScripts; break;
99 case SCRIPTS_EVENT: res = &sEventScripts; break;
100 default: break;
101 }
102 return res;
103}
104
106{
107 std::string res = "";
108 switch (command)
109 {
110 case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
111 case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
112 case SCRIPT_COMMAND_FIELD_SET_DEPRECATED: res = "SCRIPT_COMMAND_FIELD_SET_DEPRECATED"; break;
113 case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
114 case SCRIPT_COMMAND_FLAG_SET_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_SET_DEPRECATED"; break;
115 case SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED"; break;
116 case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
117 case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
118 case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
119 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
120 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
121 case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
122 case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
123 case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
124 case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
125 case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
126 case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
127 case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
128 case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
129 case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
130 case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
131 case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
132 // TrinityCore only
133 case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
134 case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
135 case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
136 case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
137 case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
138 case SCRIPT_COMMAND_MOVEMENT: res = "SCRIPT_COMMAND_MOVEMENT"; break;
139 case SCRIPT_COMMAND_PLAY_ANIMKIT: res = "SCRIPT_COMMAND_PLAY_ANIMKIT"; break;
140 default:
141 {
142 res = Trinity::StringFormat("Unknown command: {}", command);
143 break;
144 }
145 }
146 return res;
147}
148
149std::string ScriptInfo::GetDebugInfo() const
150{
151 return Trinity::StringFormat("{} ('{}' script id: {})", GetScriptCommandName(command), GetScriptsTableNameByType(type), id);
152}
153
154bool normalizePlayerName(std::string& name)
155{
156 if (name.empty())
157 return false;
158
159 std::wstring tmp;
160 if (!Utf8toWStr(name, tmp))
161 return false;
162
163 wstrToLower(tmp);
164 if (!tmp.empty())
165 tmp[0] = wcharToUpper(tmp[0]);
166
167 if (!WStrToUtf8(tmp, name))
168 return false;
169
170 return true;
171}
172
173// Extracts player and realm names delimited by -
175{
176 size_t pos = name.find('-');
177 if (pos != std::string::npos)
178 return ExtendedPlayerName(name.substr(0, pos), name.substr(pos + 1));
179 else
180 return ExtendedPlayerName(name, "");
181}
182
183bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
184{
185 Player const* playerClicker = clicker->ToPlayer();
186 if (!playerClicker)
187 return true;
188
189 Unit const* summoner = nullptr;
190 // Check summoners for party
191 if (clickee->IsSummon())
192 summoner = clickee->ToTempSummon()->GetSummonerUnit();
193 if (!summoner)
194 summoner = clickee;
195
196 // This only applies to players
197 switch (userType)
198 {
200 if (!playerClicker->IsFriendlyTo(summoner))
201 return false;
202 break;
204 if (!playerClicker->IsInRaidWith(summoner))
205 return false;
206 break;
208 if (!playerClicker->IsInPartyWith(summoner))
209 return false;
210 break;
211 default:
212 break;
213 }
214
215 return true;
216}
217
219 _auctionId(1),
220 _equipmentSetGuid(1),
221 _mailId(1),
222 _hiPetNumber(1),
223 _creatureSpawnId(1),
224 _gameObjectSpawnId(1),
225 _voidItemId(1),
226 DBCLocaleIndex(LOCALE_enUS)
227{
228}
229
231{
232 static ObjectMgr instance;
233 return &instance;
234}
235
237{
238}
239
240void ObjectMgr::AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector<std::string>& data)
241{
242 if (data.size() <= size_t(localeConstant))
243 {
244 data.reserve(TOTAL_LOCALES);
245 data.resize(localeConstant + 1);
246 }
247
248 data[localeConstant] = value.empty() ? "" : value;
249}
250
252{
253 uint32 oldMSTime = getMSTime();
254
255 _creatureLocaleStore.clear(); // need for reload case
256
257 // 0 1 2 3 4 5
258 QueryResult result = WorldDatabase.Query("SELECT entry, locale, Name, NameAlt, Title, TitleAlt FROM creature_template_locale");
259 if (!result)
260 return;
261
262 do
263 {
264 Field* fields = result->Fetch();
265
266 uint32 id = fields[0].GetUInt32();
267 std::string_view localeName = fields[1].GetStringView();
268
269 LocaleConstant locale = GetLocaleByName(localeName);
270 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
271 continue;
272
274 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
275 AddLocaleString(fields[3].GetStringView(), locale, data.NameAlt);
276 AddLocaleString(fields[4].GetStringView(), locale, data.Title);
277 AddLocaleString(fields[5].GetStringView(), locale, data.TitleAlt);
278
279 } while (result->NextRow());
280
281 TC_LOG_INFO("server.loading", ">> Loaded {} creature locale strings in {} ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
282}
283
285{
286 uint32 oldMSTime = getMSTime();
287
288 _gossipMenuItemsLocaleStore.clear(); // need for reload case
289
290 // 0 1 2 3 4
291 QueryResult result = WorldDatabase.Query("SELECT MenuID, OptionID, Locale, OptionText, BoxText FROM gossip_menu_option_locale");
292
293 if (!result)
294 return;
295
296 do
297 {
298 Field* fields = result->Fetch();
299
300 uint32 menuId = fields[0].GetUInt32();
301 uint32 optionId = fields[1].GetUInt32();
302 std::string_view localeName = fields[2].GetStringView();
303
304 LocaleConstant locale = GetLocaleByName(localeName);
305 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
306 continue;
307
308 GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[std::make_pair(menuId, optionId)];
309 AddLocaleString(fields[3].GetStringView(), locale, data.OptionText);
310 AddLocaleString(fields[4].GetStringView(), locale, data.BoxText);
311 } while (result->NextRow());
312
313 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option locale strings in {} ms", _gossipMenuItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
314}
315
317{
318 uint32 oldMSTime = getMSTime();
319
320 _pointOfInterestLocaleStore.clear(); // need for reload case
321
322 // 0 1 2
323 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM points_of_interest_locale");
324 if (!result)
325 return;
326
327 do
328 {
329 Field* fields = result->Fetch();
330
331 uint32 id = fields[0].GetUInt32();
332 std::string_view localeName = fields[1].GetStringView();
333
334 LocaleConstant locale = GetLocaleByName(localeName);
335 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
336 continue;
337
339 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
340 } while (result->NextRow());
341
342 TC_LOG_INFO("server.loading", ">> Loaded {} points_of_interest locale strings in {} ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
343}
344
346{
347 uint32 oldMSTime = getMSTime();
348
349 // 0 1 2 3 4 5
350 // "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, "
351 // 6 7 8 9
352 // "TitleAlt, IconName, RequiredExpansion, VignetteID, "
353 // 10 11 12 13 14 15 16 17 18 19 20
354 // "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, "
355 // 21 22 23 24
356 // "unit_class, unit_flags, unit_flags2, unit_flags3, "
357 // 25 26 27 28 29 30
358 // "family, trainer_class, type, VehicleId, AIName, MovementType, "
359 // 31 32 33 34 35
360 // "ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, "
361 // 36 37 38 39 40
362 // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
363 // 41 42
364 // "CreatureImmunitiesId, flags_extra, "
365 // 43 44
366 // "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
367
369 stmt->setUInt32(0, 0);
370 stmt->setUInt32(1, 1);
371
372 PreparedQueryResult result = WorldDatabase.Query(stmt);
373 if (!result)
374 {
375 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
376 return;
377 }
378
379 _creatureTemplateStore.reserve(result->GetRowCount());
380 do
381 {
382 Field* fields = result->Fetch();
383 LoadCreatureTemplate(fields);
384 } while (result->NextRow());
385
388
389 // We load the creature models after loading but before checking
391
392 // Checking needs to be done after loading because of the difficulty self referencing
393 for (auto const& ctPair : _creatureTemplateStore)
394 CheckCreatureTemplate(&ctPair.second);
395
396 TC_LOG_INFO("server.loading", ">> Loaded {} creature definitions in {} ms", _creatureTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
397}
398
400{
401 uint32 entry = fields[0].GetUInt32();
402 CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
403
404 creatureTemplate.Entry = entry;
405
406 for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
407 creatureTemplate.KillCredit[i] = fields[1 + i].GetUInt32();
408
409 creatureTemplate.Name = fields[3].GetString();
410 creatureTemplate.FemaleName = fields[4].GetString();
411 creatureTemplate.SubName = fields[5].GetString();
412 creatureTemplate.TitleAlt = fields[6].GetString();
413 creatureTemplate.IconName = fields[7].GetString();
414 creatureTemplate.RequiredExpansion = fields[8].GetUInt32();
415 creatureTemplate.VignetteID = fields[9].GetUInt32();
416 creatureTemplate.faction = fields[10].GetUInt16();
417 creatureTemplate.npcflag = fields[11].GetUInt64();
418 creatureTemplate.speed_walk = fields[12].GetFloat();
419 creatureTemplate.speed_run = fields[13].GetFloat();
420 creatureTemplate.scale = fields[14].GetFloat();
421 creatureTemplate.Classification = CreatureClassifications(fields[15].GetUInt8());
422 creatureTemplate.dmgschool = uint32(fields[16].GetInt8());
423 creatureTemplate.BaseAttackTime = fields[17].GetUInt32();
424 creatureTemplate.RangeAttackTime = fields[18].GetUInt32();
425 creatureTemplate.BaseVariance = fields[19].GetFloat();
426 creatureTemplate.RangeVariance = fields[20].GetFloat();
427 creatureTemplate.unit_class = uint32(fields[21].GetUInt8());
428 creatureTemplate.unit_flags = fields[22].GetUInt32();
429 creatureTemplate.unit_flags2 = fields[23].GetUInt32();
430 creatureTemplate.unit_flags3 = fields[24].GetUInt32();
431 creatureTemplate.family = CreatureFamily(fields[25].GetInt32());
432 creatureTemplate.trainer_class = uint32(fields[26].GetUInt8());
433 creatureTemplate.type = uint32(fields[27].GetUInt8());
434
435 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
436 creatureTemplate.resistance[i] = 0;
437
438 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
439 creatureTemplate.spells[i] = 0;
440
441 creatureTemplate.VehicleId = fields[28].GetUInt32();
442 creatureTemplate.AIName = fields[29].GetString();
443 creatureTemplate.MovementType = uint32(fields[30].GetUInt8());
444
445 if (!fields[31].IsNull())
446 creatureTemplate.Movement.HoverInitiallyEnabled = fields[31].GetBool();
447
448 if (!fields[32].IsNull())
449 creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[32].GetUInt8());
450
451 if (!fields[33].IsNull())
452 creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[33].GetUInt8());
453
454 if (!fields[34].IsNull())
455 creatureTemplate.Movement.InteractionPauseTimer = fields[34].GetUInt32();
456
457 creatureTemplate.ModExperience = fields[35].GetFloat();
458 creatureTemplate.RacialLeader = fields[36].GetBool();
459 creatureTemplate.movementId = fields[37].GetUInt32();
460 creatureTemplate.WidgetSetID = fields[38].GetInt32();
461 creatureTemplate.WidgetSetUnitConditionID = fields[39].GetInt32();
462 creatureTemplate.RegenHealth = fields[40].GetBool();
463 creatureTemplate.CreatureImmunitiesId = fields[41].GetInt32();
464 creatureTemplate.flags_extra = fields[42].GetUInt32();
465 creatureTemplate.ScriptID = GetScriptId(fields[43].GetString());
466 creatureTemplate.StringId = fields[44].GetString();
467}
468
470{
471 uint32 oldMSTime = getMSTime();
472
473 // 0 1
474 QueryResult result = WorldDatabase.Query("SELECT CreatureID, MenuID FROM creature_template_gossip");
475
476 if (!result)
477 {
478 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template gossip definitions. DB table `creature_template_gossip` is empty.");
479 return;
480 }
481
482 uint32 count = 0;
483
484 do
485 {
486 Field* fields = result->Fetch();
487
488 uint32 creatureID = fields[0].GetUInt32();
489 uint32 menuID = fields[1].GetUInt32();
490
491 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
492 if (itr == _creatureTemplateStore.end())
493 {
494 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for creature {} but this creature doesn't exist", creatureID);
495 continue;
496 }
497
498 GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuID);
499 if (menuBounds.first == menuBounds.second)
500 {
501 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for menu id {} but this menu doesn't exist", menuID);
502 continue;
503 }
504
505 CreatureTemplate& creatureTemplate = itr->second;
506 creatureTemplate.GossipMenuIds.push_back(menuID);
507
508 ++count;
509
510 } while (result->NextRow());
511
512 TC_LOG_INFO("server.loading", ">> Loaded {} creature template gossip menus in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
513}
514
516{
517 uint32 oldMSTime = getMSTime();
518
519 // 0 1 2
520 QueryResult result = WorldDatabase.Query("SELECT CreatureID, School, Resistance FROM creature_template_resistance");
521
522 if (!result)
523 {
524 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template resistance definitions. DB table `creature_template_resistance` is empty.");
525 return;
526 }
527
528 uint32 count = 0;
529
530 do
531 {
532 Field* fields = result->Fetch();
533
534 uint32 creatureID = fields[0].GetUInt32();
535 uint8 school = fields[1].GetUInt8();
536
537 if (school == SPELL_SCHOOL_NORMAL || school >= MAX_SPELL_SCHOOL)
538 {
539 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this school {} doesn't exist", creatureID, school);
540 continue;
541 }
542
543 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
544 if (itr == _creatureTemplateStore.end())
545 {
546 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this creature doesn't exist", creatureID);
547 continue;
548 }
549
550 CreatureTemplate& creatureTemplate = itr->second;
551 creatureTemplate.resistance[school] = fields[2].GetInt16();
552
553 ++count;
554
555 } while (result->NextRow());
556
557 TC_LOG_INFO("server.loading", ">> Loaded {} creature template resistances in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
558}
559
561{
562 uint32 oldMSTime = getMSTime();
563
564 // 0 1 2
565 QueryResult result = WorldDatabase.Query("SELECT CreatureID, `Index`, Spell FROM creature_template_spell");
566
567 if (!result)
568 {
569 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template spell definitions. DB table `creature_template_spell` is empty.");
570 return;
571 }
572
573 uint32 count = 0;
574
575 do
576 {
577 Field* fields = result->Fetch();
578
579 uint32 creatureID = fields[0].GetUInt32();
580 uint8 index = fields[1].GetUInt8();
581
582 if (index >= MAX_CREATURE_SPELLS)
583 {
584 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} with a incorrect index {}", creatureID, index);
585 continue;
586 }
587
588 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
589 if (itr == _creatureTemplateStore.end())
590 {
591 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} but this creature doesn't exist", creatureID);
592 continue;
593 }
594
595 CreatureTemplate& creatureTemplate = itr->second;
596 creatureTemplate.spells[index] = fields[2].GetUInt32();;
597
598 ++count;
599
600 } while (result->NextRow());
601
602 TC_LOG_INFO("server.loading", ">> Loaded {} creature template spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
603}
604
606{
607 uint32 oldMSTime = getMSTime();
608
609 // 0 1 2 3
610 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureDisplayID, DisplayScale, Probability FROM creature_template_model ORDER BY Idx ASC");
611
612 if (!result)
613 {
614 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template model definitions. DB table `creature_template_model` is empty.");
615 return;
616 }
617
618 uint32 count = 0;
619 do
620 {
621 Field* fields = result->Fetch();
622
623 uint32 creatureId = fields[0].GetUInt32();
624 uint32 creatureDisplayId = fields[1].GetUInt32();
625 float displayScale = fields[2].GetFloat();
626 float probability = fields[3].GetFloat();
627
628 CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
629 if (!cInfo)
630 {
631 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_model`", creatureId);
632 continue;
633 }
634
635 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(creatureDisplayId);
636 if (!displayEntry)
637 {
638 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing CreatureDisplayID id ({}), this can crash the client.", creatureId, creatureDisplayId);
639 continue;
640 }
641
642 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(creatureDisplayId);
643 if (!modelInfo)
644 TC_LOG_ERROR("sql.sql", "No model data exist for `CreatureDisplayID` = {} listed by creature (Entry: {}).", creatureDisplayId, creatureId);
645
646 if (displayScale <= 0.0f)
647 displayScale = 1.0f;
648
649 const_cast<CreatureTemplate*>(cInfo)->Models.emplace_back(creatureDisplayId, displayScale, probability);
650
651 ++count;
652 }
653 while (result->NextRow());
654
655 TC_LOG_INFO("server.loading", ">> Loaded {} creature template models in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
656}
657
659{
660 uint32 oldMSTime = getMSTime();
661
662 // 0 1 2 3 4
663 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureIDVisibleToSummoner, GroundMountDisplayID, FlyingMountDisplayID, DespawnOnQuestsRemoved FROM creature_summoned_data");
664
665 if (!result)
666 {
667 TC_LOG_INFO("server.loading", ">> Loaded 0 creature summoned data definitions. DB table `creature_summoned_data` is empty.");
668 return;
669 }
670
671 do
672 {
673 Field* fields = result->Fetch();
674
675 uint32 creatureId = fields[0].GetUInt32();
676 if (!GetCreatureTemplate(creatureId))
677 {
678 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {}, skipped", creatureId);
679 continue;
680 }
681
682 CreatureSummonedData& summonedData = _creatureSummonedDataStore[creatureId];
683
684 if (!fields[1].IsNull())
685 {
686 summonedData.CreatureIDVisibleToSummoner = fields[1].GetUInt32();
688 {
689 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {} in CreatureIDVisibleToSummoner for creature {}, set to 0",
690 *summonedData.CreatureIDVisibleToSummoner, creatureId);
691 summonedData.CreatureIDVisibleToSummoner.reset();
692 }
693 }
694
695 if (!fields[2].IsNull())
696 {
697 summonedData.GroundMountDisplayID = fields[2].GetUInt32();
698 if (!sCreatureDisplayInfoStore.LookupEntry(*summonedData.GroundMountDisplayID))
699 {
700 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in GroundMountDisplayID for creature {}, set to 0",
701 *summonedData.GroundMountDisplayID, creatureId);
702 summonedData.CreatureIDVisibleToSummoner.reset();
703 }
704 }
705
706 if (!fields[3].IsNull())
707 {
708 summonedData.FlyingMountDisplayID = fields[3].GetUInt32();
709 if (!sCreatureDisplayInfoStore.LookupEntry(*summonedData.FlyingMountDisplayID))
710 {
711 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in FlyingMountDisplayID for creature {}, set to 0",
712 *summonedData.FlyingMountDisplayID, creatureId);
713 summonedData.GroundMountDisplayID.reset();
714 }
715 }
716
717 if (!fields[4].IsNull())
718 {
719 std::vector<uint32> questList;
720 for (std::string_view questStr : Trinity::Tokenize(fields[4].GetStringView(), ',', false))
721 {
722 Optional<uint32> questId = Trinity::StringTo<uint32>(questStr);
723 if (!questId)
724 continue;
725
726 Quest const* quest = GetQuestTemplate(*questId);
727 if (!quest)
728 {
729 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing quest {} in DespawnOnQuestsRemoved for creature {}, skipping",
730 *questId, creatureId);
731 continue;
732 }
733
734 questList.push_back(*questId);
735 }
736
737 if (!questList.empty())
738 summonedData.DespawnOnQuestsRemoved = std::move(questList);
739 }
740
741 } while (result->NextRow());
742
743 TC_LOG_INFO("server.loading", ">> Loaded {} creature summoned data definitions in {} ms", _creatureSummonedDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
744}
745
747{
748 uint32 oldMSTime = getMSTime();
749
750 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
751 QueryResult result = WorldDatabase.Query("SELECT entry, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon");
752
753 if (!result)
754 {
755 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
756 return;
757 }
758
759 uint32 count = 0;
760 do
761 {
762 Field* fields = result->Fetch();
763
764 uint32 entry = fields[0].GetUInt32();
765
766 if (!GetCreatureTemplate(entry))
767 {
768 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
769 continue;
770 }
771
772 CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
773
774 creatureAddon.PathId = fields[1].GetUInt32();
775 creatureAddon.mount = fields[2].GetUInt32();
776 creatureAddon.standState = fields[3].GetUInt8();
777 creatureAddon.animTier = fields[4].GetUInt8();
778 creatureAddon.visFlags = fields[5].GetUInt8();
779 creatureAddon.sheathState = fields[6].GetUInt8();
780 creatureAddon.pvpFlags = fields[7].GetUInt8();
781 creatureAddon.emote = fields[8].GetUInt32();
782 creatureAddon.aiAnimKit = fields[9].GetUInt16();
783 creatureAddon.movementAnimKit = fields[10].GetUInt16();
784 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
785 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
786
787 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
788 {
789 SpellInfo const* spellInfo = nullptr;
790 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
791 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
792
793 if (!spellInfo)
794 {
795 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong spell '{}' defined in `auras` field in `creature_template_addon`.", entry, std::string(aura));
796 continue;
797 }
798
799 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
800 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
801
802 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
803 {
804 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has duplicate aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
805 continue;
806 }
807
808 if (spellInfo->GetDuration() > 0)
809 {
810 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has temporary aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
811 continue;
812 }
813
814 creatureAddon.auras.push_back(spellInfo->Id);
815 }
816
817 if (creatureAddon.mount)
818 {
819 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
820 {
821 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid displayInfoId ({}) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
822 creatureAddon.mount = 0;
823 }
824 }
825
826 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
827 {
828 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit stand state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState);
829 creatureAddon.standState = 0;
830 }
831
832 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
833 {
834 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid animation tier ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier);
835 creatureAddon.animTier = 0;
836 }
837
838 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
839 {
840 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid sheath state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState);
841 creatureAddon.sheathState = 0;
842 }
843
844 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
845
846 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
847 {
848 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid emote ({}) defined in `creature_template_addon`.", entry, creatureAddon.emote);
849 creatureAddon.emote = 0;
850 }
851
852 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
853 {
854 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid aiAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.aiAnimKit);
855 creatureAddon.aiAnimKit = 0;
856 }
857
858 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
859 {
860 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid movementAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.movementAnimKit);
861 creatureAddon.movementAnimKit = 0;
862 }
863
864 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
865 {
866 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid meleeAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.meleeAnimKit);
867 creatureAddon.meleeAnimKit = 0;
868 }
869
871 {
872 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid visibilityDistanceType ({}) defined in `creature_template_addon`.",
873 entry, AsUnderlyingType(creatureAddon.visibilityDistanceType));
875 }
876
877 ++count;
878 }
879 while (result->NextRow());
880
881 TC_LOG_INFO("server.loading", ">> Loaded {} creature template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
882}
883
885{
886 uint32 oldMSTime = getMSTime();
887
888 // 0 1
889 QueryResult result = WorldDatabase.Query("SELECT Entry, NoNPCDamageBelowHealthPct FROM creature_template_sparring");
890
891 if (!result)
892 {
893 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template sparring definitions. DB table `creature_template_sparring` is empty.");
894 return;
895 }
896
897 uint32 count = 0;
898 do
899 {
900 Field* fields = result->Fetch();
901
902 uint32 entry = fields[0].GetUInt32();
903 float noNPCDamageBelowHealthPct = fields[1].GetFloat();
904
905 if (!sObjectMgr->GetCreatureTemplate(entry))
906 {
907 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_sparring`", entry);
908 continue;
909 }
910
911 if (noNPCDamageBelowHealthPct <= 0 || noNPCDamageBelowHealthPct > 100)
912 {
913 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid NoNPCDamageBelowHealthPct ({}) defined in `creature_template_sparring`. Skipping",
914 entry, noNPCDamageBelowHealthPct);
915 continue;
916 }
917 _creatureTemplateSparringStore[entry].push_back(noNPCDamageBelowHealthPct);
918
919 ++count;
920 } while (result->NextRow());
921
922 TC_LOG_INFO("server.loading", ">> Loaded {} creature template sparring rows in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
923}
924
926{
927 uint32 oldMSTime = getMSTime();
928
929 // 0 1 2 3 4 5
930 QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, "
931 // 6 7 8 9 10 11 12
932 "HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, "
933 // 13 14 15 16 17
934 "LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax,"
935 // 18 19 20 21 22 23 24 25
936 "StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 "
937 "FROM creature_template_difficulty ORDER BY Entry");
938
939 if (!result)
940 {
941 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
942 return;
943 }
944
945 uint32 count = 0;
946 do
947 {
948 Field* fields = result->Fetch();
949
950 uint32 entry = fields[0].GetUInt32();
951 Difficulty difficulty = Difficulty(fields[1].GetUInt8());
952
953 auto itr = _creatureTemplateStore.find(entry);
954 if (itr == _creatureTemplateStore.end())
955 {
956 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_difficulty`", entry);
957 continue;
958 }
959
960 CreatureDifficulty creatureDifficulty;
961 creatureDifficulty.DeltaLevelMin = fields[2].GetInt16();
962 creatureDifficulty.DeltaLevelMax = fields[3].GetInt16();
963 creatureDifficulty.ContentTuningID = fields[4].GetInt32();
964 creatureDifficulty.HealthScalingExpansion = fields[5].GetInt32();
965 creatureDifficulty.HealthModifier = fields[6].GetFloat();
966 creatureDifficulty.ManaModifier = fields[7].GetFloat();
967 creatureDifficulty.ArmorModifier = fields[8].GetFloat();
968 creatureDifficulty.DamageModifier = fields[9].GetFloat();
969 creatureDifficulty.CreatureDifficultyID = fields[10].GetInt32();
970 creatureDifficulty.TypeFlags = fields[11].GetUInt32();
971 creatureDifficulty.TypeFlags2 = fields[12].GetUInt32();
972 creatureDifficulty.LootID = fields[13].GetUInt32();
973 creatureDifficulty.PickPocketLootID = fields[14].GetUInt32();
974 creatureDifficulty.SkinLootID = fields[15].GetUInt32();
975 creatureDifficulty.GoldMin = fields[16].GetUInt32();
976 creatureDifficulty.GoldMax = fields[17].GetUInt32();
977 creatureDifficulty.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[18].GetUInt32()), CreatureStaticFlags2(fields[19].GetUInt32()),
978 CreatureStaticFlags3(fields[20].GetUInt32()), CreatureStaticFlags4(fields[21].GetUInt32()), CreatureStaticFlags5(fields[22].GetUInt32()),
979 CreatureStaticFlags6(fields[23].GetUInt32()), CreatureStaticFlags7(fields[24].GetUInt32()), CreatureStaticFlags8(fields[25].GetUInt32()));
980
981 // TODO: Check if this still applies
982 creatureDifficulty.DamageModifier *= Creature::GetDamageMod(itr->second.Classification);
983
984 if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS)
985 {
986 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.",
987 entry, creatureDifficulty.HealthScalingExpansion);
988 creatureDifficulty.HealthScalingExpansion = 0;
989 }
990
991 if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
992 {
993 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with `GoldMin` {} greater than `GoldMax` {}, setting `GoldMax` to {}.",
994 entry, creatureDifficulty.GoldMin, creatureDifficulty.GoldMax, creatureDifficulty.GoldMin);
995 creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
996 }
997
998 itr->second.difficultyStore[difficulty] = creatureDifficulty;
999
1000 ++count;
1001 } while (result->NextRow());
1002
1003 TC_LOG_INFO("server.loading", ">> Loaded {} creature template difficulty data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1004}
1005
1007{
1008 if (!cInfo)
1009 return;
1010
1011 if (!cInfo->AIName.empty())
1012 {
1013 auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
1014 if (!registryItem)
1015 {
1016 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-registered `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1017 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1018 }
1019 else
1020 {
1021 DBPermit const* permit = dynamic_cast<DBPermit const*>(registryItem);
1022 if (!ASSERT_NOTNULL(permit)->IsScriptNameAllowedInDB())
1023 {
1024 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has not-allowed `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1025 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1026 }
1027 }
1028 }
1029
1030 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
1031 if (!factionTemplate)
1032 {
1033 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing faction template ({}). This can lead to crashes, set to faction 35.", cInfo->Entry, cInfo->faction);
1034 const_cast<CreatureTemplate*>(cInfo)->faction = sFactionTemplateStore.AssertEntry(35)->ID; // this might seem stupid but all shit will would break if faction 35 did not exist
1035 }
1036
1037 for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
1038 {
1039 if (cInfo->KillCredit[k])
1040 {
1041 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
1042 {
1043 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing creature entry {} in `KillCredit{}`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
1044 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
1045 }
1046 }
1047 }
1048
1049 if (cInfo->Models.empty())
1050 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in creature_template_model.", cInfo->Entry);
1051
1052 if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
1053 {
1054 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit_class ({}) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
1055 const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
1056 }
1057
1058 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
1059 {
1060 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid spell school value ({}) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
1061 const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
1062 }
1063
1064 if (cInfo->BaseAttackTime == 0)
1065 const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime = BASE_ATTACK_TIME;
1066
1067 if (cInfo->RangeAttackTime == 0)
1068 const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
1069
1070 if (cInfo->speed_walk == 0.0f)
1071 {
1072 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
1073 const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
1074 }
1075
1076 if (cInfo->speed_run == 0.0f)
1077 {
1078 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
1079 const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
1080 }
1081
1082 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
1083 {
1084 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature type ({}) in `type`.", cInfo->Entry, cInfo->type);
1085 const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
1086 }
1087
1088 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family))
1089 {
1090 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature family ({}) in `family`.", cInfo->Entry, cInfo->family);
1091 const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
1092 }
1093
1094 CheckCreatureMovement("creature_template_movement", cInfo->Entry, const_cast<CreatureTemplate*>(cInfo)->Movement);
1095
1096 if (cInfo->VehicleId)
1097 {
1098 VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
1099 if (!vehId)
1100 {
1101 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has a non-existing VehicleId ({}). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
1102 const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
1103 }
1104 }
1105
1106 for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
1107 {
1108 if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j], DIFFICULTY_NONE))
1109 {
1110 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing Spell{} ({}), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
1111 const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
1112 }
1113 }
1114
1115 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
1116 {
1117 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong movement generator type ({}), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
1118 const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
1119 }
1120
1121 if (cInfo->RequiredExpansion >= MAX_EXPANSIONS)
1122 {
1123 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `RequiredExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->RequiredExpansion);
1124 const_cast<CreatureTemplate*>(cInfo)->RequiredExpansion = 0;
1125 }
1126
1127 if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
1128 {
1129 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `flags_extra` {}, removing incorrect flag.", cInfo->Entry, badFlags);
1130 const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
1131 }
1132
1133 if (uint32 disallowedUnitFlags = (cInfo->unit_flags & ~UNIT_FLAG_ALLOWED))
1134 {
1135 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags);
1136 const_cast<CreatureTemplate*>(cInfo)->unit_flags &= UNIT_FLAG_ALLOWED;
1137 }
1138
1139 if (uint32 disallowedUnitFlags2 = (cInfo->unit_flags2 & ~UNIT_FLAG2_ALLOWED))
1140 {
1141 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags2);
1142 const_cast<CreatureTemplate*>(cInfo)->unit_flags2 &= UNIT_FLAG2_ALLOWED;
1143 }
1144
1145 if (uint32 disallowedUnitFlags3 = (cInfo->unit_flags3 & ~UNIT_FLAG3_ALLOWED))
1146 {
1147 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags3);
1148 const_cast<CreatureTemplate*>(cInfo)->unit_flags3 &= UNIT_FLAG3_ALLOWED;
1149 }
1150
1151 if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP))
1152 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry);
1153 else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)
1154 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry);
1155
1156 if (cInfo->VignetteID && !sVignetteStore.HasRecord(cInfo->VignetteID))
1157 {
1158 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has non-existing Vignette {}, set to 0.", cInfo->Entry, cInfo->VignetteID);
1159 const_cast<CreatureTemplate*>(cInfo)->VignetteID = 0;
1160 }
1161}
1162
1163void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement)
1164{
1165 if (creatureMovement.Chase >= CreatureChaseMovementType::Max)
1166 {
1167 TC_LOG_ERROR("sql.sql", "`{}`.`Chase` wrong value ({}) for Id {}, setting to Run.",
1168 table, uint32(creatureMovement.Chase), id);
1169 creatureMovement.Chase = CreatureChaseMovementType::Run;
1170 }
1171
1172 if (creatureMovement.Random >= CreatureRandomMovementType::Max)
1173 {
1174 TC_LOG_ERROR("sql.sql", "`{}`.`Random` wrong value ({}) for Id {}, setting to Walk.",
1175 table, uint32(creatureMovement.Random), id);
1176 creatureMovement.Random = CreatureRandomMovementType::Walk;
1177 }
1178}
1179
1181{
1182 uint32 oldMSTime = getMSTime();
1183
1184 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1185 QueryResult result = WorldDatabase.Query("SELECT guid, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon");
1186
1187 if (!result)
1188 {
1189 TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
1190 return;
1191 }
1192
1193 uint32 count = 0;
1194 do
1195 {
1196 Field* fields = result->Fetch();
1197
1198 ObjectGuid::LowType guid = fields[0].GetUInt64();
1199
1200 CreatureData const* creData = GetCreatureData(guid);
1201 if (!creData)
1202 {
1203 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_addon`", guid);
1204 continue;
1205 }
1206
1207 CreatureAddon& creatureAddon = _creatureAddonStore[guid];
1208
1209 creatureAddon.PathId = fields[1].GetUInt32();
1210 if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.PathId)
1211 {
1212 const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
1213 TC_LOG_ERROR("sql.sql", "Creature (GUID {}) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
1214 }
1215
1216 creatureAddon.mount = fields[2].GetUInt32();
1217 creatureAddon.standState = fields[3].GetUInt8();
1218 creatureAddon.animTier = fields[4].GetUInt8();
1219 creatureAddon.visFlags = fields[5].GetUInt8();
1220 creatureAddon.sheathState = fields[6].GetUInt8();
1221 creatureAddon.pvpFlags = fields[7].GetUInt8();
1222 creatureAddon.emote = fields[8].GetUInt32();
1223 creatureAddon.aiAnimKit = fields[9].GetUInt16();
1224 creatureAddon.movementAnimKit = fields[10].GetUInt16();
1225 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
1226 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
1227
1228 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
1229 {
1230 SpellInfo const* spellInfo = nullptr;
1231 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
1232 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
1233
1234 if (!spellInfo)
1235 {
1236 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has wrong spell '{}' defined in `auras` field in `creature_addon`.", guid, std::string(aura));
1237 continue;
1238 }
1239
1240 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
1241 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1242
1243 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
1244 {
1245 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has duplicate aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1246 continue;
1247 }
1248
1249 if (spellInfo->GetDuration() > 0)
1250 {
1251 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has temporary aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1252 continue;
1253 }
1254
1255 creatureAddon.auras.push_back(spellInfo->Id);
1256 }
1257
1258 if (creatureAddon.mount)
1259 {
1260 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
1261 {
1262 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid displayInfoId ({}) for mount defined in `creature_addon`", guid, creatureAddon.mount);
1263 creatureAddon.mount = 0;
1264 }
1265 }
1266
1267 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
1268 {
1269 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid unit stand state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState);
1270 creatureAddon.standState = 0;
1271 }
1272
1273 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
1274 {
1275 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid animation tier ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier);
1276 creatureAddon.animTier = 0;
1277 }
1278
1279 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
1280 {
1281 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid sheath state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState);
1282 creatureAddon.sheathState = 0;
1283 }
1284
1285 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
1286
1287 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
1288 {
1289 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid emote ({}) defined in `creature_addon`.", guid, creatureAddon.emote);
1290 creatureAddon.emote = 0;
1291 }
1292
1293 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
1294 {
1295 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid aiAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.aiAnimKit);
1296 creatureAddon.aiAnimKit = 0;
1297 }
1298
1299 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
1300 {
1301 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid movementAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.movementAnimKit);
1302 creatureAddon.movementAnimKit = 0;
1303 }
1304
1305 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
1306 {
1307 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid meleeAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.meleeAnimKit);
1308 creatureAddon.meleeAnimKit = 0;
1309 }
1310
1312 {
1313 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid visibilityDistanceType ({}) defined in `creature_addon`.",
1314 guid, AsUnderlyingType(creatureAddon.visibilityDistanceType));
1316 }
1317
1318 ++count;
1319 }
1320 while (result->NextRow());
1321
1322 TC_LOG_INFO("server.loading", ">> Loaded {} creature addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1323}
1324
1326{
1327 uint32 oldMSTime = getMSTime();
1328
1329 // 0 1 2 3 4 5 6 7 8
1330 QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue, WorldEffectID, AIAnimKitID FROM gameobject_addon");
1331
1332 if (!result)
1333 {
1334 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
1335 return;
1336 }
1337
1338 uint32 count = 0;
1339 do
1340 {
1341 Field* fields = result->Fetch();
1342
1343 ObjectGuid::LowType guid = fields[0].GetUInt64();
1344
1345 GameObjectData const* goData = GetGameObjectData(guid);
1346 if (!goData)
1347 {
1348 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) does not exist but has a record in `gameobject_addon`", guid);
1349 continue;
1350 }
1351
1352 GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
1353 gameObjectAddon.ParentRotation = QuaternionData(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
1354 gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
1355 gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
1356 gameObjectAddon.WorldEffectID = fields[7].GetUInt32();
1357 gameObjectAddon.AIAnimKitID = fields[8].GetUInt32();
1358
1359 if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
1360 {
1361 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
1362 gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
1363 gameObjectAddon.InvisibilityValue = 0;
1364 }
1365
1366 if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue)
1367 {
1368 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid);
1369 gameObjectAddon.InvisibilityValue = 1;
1370 }
1371
1372 if (!gameObjectAddon.ParentRotation.isUnit())
1373 {
1374 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid parent rotation in `gameobject_addon`, set to default", guid);
1375 gameObjectAddon.ParentRotation = QuaternionData();
1376 }
1377
1378 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
1379 {
1380 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid WorldEffectID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.WorldEffectID);
1381 gameObjectAddon.WorldEffectID = 0;
1382 }
1383
1384 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
1385 {
1386 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid AIAnimKitID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.AIAnimKitID);
1387 gameObjectAddon.AIAnimKitID = 0;
1388 }
1389
1390 ++count;
1391 }
1392 while (result->NextRow());
1393
1394 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1395}
1396
1398{
1399 GameObjectAddonContainer::const_iterator itr = _gameObjectAddonStore.find(lowguid);
1400 if (itr != _gameObjectAddonStore.end())
1401 return &(itr->second);
1402
1403 return nullptr;
1404}
1405
1407{
1408 CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
1409 if (itr != _creatureAddonStore.end())
1410 return &(itr->second);
1411
1412 return nullptr;
1413}
1414
1416{
1417 CreatureTemplateAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
1418 if (itr != _creatureTemplateAddonStore.end())
1419 return &(itr->second);
1420
1421 return nullptr;
1422}
1423
1424std::vector<float> const* ObjectMgr::GetCreatureTemplateSparringValues(uint32 entry) const
1425{
1427}
1428
1430{
1432}
1433
1435{
1436 EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
1437 if (itr == _equipmentInfoStore.end())
1438 return nullptr;
1439
1440 if (itr->second.empty())
1441 return nullptr;
1442
1443 if (id == -1) // select a random element
1444 {
1445 EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
1446 std::advance(ritr, urand(0u, itr->second.size() - 1));
1447 id = std::distance(itr->second.begin(), ritr) + 1;
1448 return &ritr->second;
1449 }
1450 else
1451 {
1452 EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
1453 if (itr2 != itr->second.end())
1454 return &itr2->second;
1455 }
1456
1457 return nullptr;
1458}
1459
1461{
1462 uint32 oldMSTime = getMSTime();
1463
1464 // 0 1 2 3 4
1465 QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, AppearanceModID1, ItemVisual1, "
1466 // 5 6 7
1467 "ItemID2, AppearanceModID2, ItemVisual2, "
1468 // 8 9 10
1469 "ItemID3, AppearanceModID3, ItemVisual3 "
1470 "FROM creature_equip_template");
1471
1472 if (!result)
1473 {
1474 TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
1475 return;
1476 }
1477
1478 uint32 count = 0;
1479 do
1480 {
1481 Field* fields = result->Fetch();
1482
1483 uint32 entry = fields[0].GetUInt32();
1484
1485 if (!GetCreatureTemplate(entry))
1486 {
1487 TC_LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry);
1488 continue;
1489 }
1490
1491 uint8 id = fields[1].GetUInt8();
1492 if (!id)
1493 {
1494 TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature {}, skipped.", entry);
1495 continue;
1496 }
1497
1498 EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
1499 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1500 {
1501 equipmentInfo.Items[i].ItemId = fields[2 + i * 3].GetUInt32();
1502 equipmentInfo.Items[i].AppearanceModId = fields[3 + i * 3].GetUInt16();
1503 equipmentInfo.Items[i].ItemVisual = fields[4 + i * 3].GetUInt16();
1504
1505 if (!equipmentInfo.Items[i].ItemId)
1506 continue;
1507
1508 ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.Items[i].ItemId);
1509 if (!dbcItem)
1510 {
1511 TC_LOG_ERROR("sql.sql", "Unknown item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID={}, forced to 0.",
1512 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1513 equipmentInfo.Items[i].ItemId = 0;
1514 continue;
1515 }
1516
1517 if (!sDB2Manager.GetItemModifiedAppearance(equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId))
1518 {
1519 TC_LOG_ERROR("sql.sql", "Unknown item appearance for (ID={}, AppearanceModID={}) pair in creature_equip_template.ItemID{} creature_equip_template.AppearanceModID{} "
1520 "for CreatureID = {} and ID={}, forced to default.",
1521 equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId, i + 1, i + 1, entry, id);
1522 if (ItemModifiedAppearanceEntry const* defaultAppearance = sDB2Manager.GetDefaultItemModifiedAppearance(equipmentInfo.Items[i].ItemId))
1523 equipmentInfo.Items[i].AppearanceModId = defaultAppearance->ItemAppearanceModifierID;
1524 else
1525 equipmentInfo.Items[i].AppearanceModId = 0;
1526 continue;
1527 }
1528
1529 if (dbcItem->InventoryType != INVTYPE_WEAPON &&
1530 dbcItem->InventoryType != INVTYPE_SHIELD &&
1531 dbcItem->InventoryType != INVTYPE_RANGED &&
1532 dbcItem->InventoryType != INVTYPE_2HWEAPON &&
1535 dbcItem->InventoryType != INVTYPE_HOLDABLE &&
1536 dbcItem->InventoryType != INVTYPE_THROWN &&
1538 {
1539 TC_LOG_ERROR("sql.sql", "Item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID = {} is not equipable in a hand, forced to 0.",
1540 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1541 equipmentInfo.Items[i].ItemId = 0;
1542 }
1543 }
1544
1545 ++count;
1546 }
1547 while (result->NextRow());
1548
1549 TC_LOG_INFO("server.loading", ">> Loaded {} equipment templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1550}
1551
1553{
1554 uint32 oldMSTime = getMSTime();
1555
1557
1558 // Load the data from creature_movement_override and if NULL fallback to creature_template_movement
1559 QueryResult result = WorldDatabase.Query(
1560 "SELECT cmo.SpawnId,"
1561 "COALESCE(cmo.HoverInitiallyEnabled, ctm.HoverInitiallyEnabled),"
1562 "COALESCE(cmo.Chase, ctm.Chase),"
1563 "COALESCE(cmo.Random, ctm.Random),"
1564 "COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
1565 "FROM creature_movement_override AS cmo "
1566 "LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
1567 "LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
1568
1569 if (!result)
1570 {
1571 TC_LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
1572 return;
1573 }
1574
1575 do
1576 {
1577 Field* fields = result->Fetch();
1578 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
1579 if (!GetCreatureData(spawnId))
1580 {
1581 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_movement_override`", spawnId);
1582 continue;
1583 }
1584
1586 if (!fields[1].IsNull())
1587 movement.HoverInitiallyEnabled = fields[1].GetBool();
1588 if (!fields[2].IsNull())
1589 movement.Chase = static_cast<CreatureChaseMovementType>(fields[2].GetUInt8());
1590 if (!fields[3].IsNull())
1591 movement.Random = static_cast<CreatureRandomMovementType>(fields[3].GetUInt8());
1592 if (!fields[4].IsNull())
1593 movement.InteractionPauseTimer = fields[4].GetUInt32();
1594
1595 CheckCreatureMovement("creature_movement_override", spawnId, movement);
1596 }
1597 while (result->NextRow());
1598
1599 TC_LOG_INFO("server.loading", ">> Loaded {} movement overrides in {} ms", _creatureMovementOverrides.size(), GetMSTimeDiffToNow(oldMSTime));
1600}
1601
1603{
1604 CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
1605 if (itr != _creatureModelStore.end())
1606 return &(itr->second);
1607
1608 return nullptr;
1609}
1610
1612{
1614}
1615
1616CreatureModel const* ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
1617{
1618 // Load creature model (display id)
1619 if (data && data->display)
1620 return &*data->display;
1621
1623 if (CreatureModel const* model = cinfo->GetRandomValidModel())
1624 return model;
1625
1626 // Triggers by default receive the invisible model
1627 return cinfo->GetFirstInvisibleModel();
1628}
1629
1630void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cInfo, uint64* npcFlags, uint32* unitFlags, uint32* unitFlags2, uint32* unitFlags3, CreatureStaticFlagsHolder const& staticFlags, CreatureData const* data /*= nullptr*/)
1631{
1632#define ChooseCreatureFlagSource(field) ((data && data->field.has_value()) ? *data->field : cInfo->field)
1633
1634 if (npcFlags)
1635 *npcFlags = ChooseCreatureFlagSource(npcflag);
1636
1637 if (unitFlags)
1638 {
1639 *unitFlags = ChooseCreatureFlagSource(unit_flags);
1640 if (staticFlags.HasFlag(CREATURE_STATIC_FLAG_CAN_SWIM))
1641 *unitFlags |= UNIT_FLAG_CAN_SWIM;
1642
1644 *unitFlags |= UNIT_FLAG_CANT_SWIM;
1645 }
1646
1647 if (unitFlags2)
1648 {
1649 *unitFlags2 = ChooseCreatureFlagSource(unit_flags2);
1651 *unitFlags2 |= UNIT_FLAG2_CANNOT_TURN;
1652
1654 *unitFlags2 |= UNIT_FLAG2_INTERACT_WHILE_HOSTILE;
1655 }
1656
1657 if (unitFlags3)
1658 {
1659 *unitFlags3 = ChooseCreatureFlagSource(unit_flags3);
1662 }
1663
1664#undef ChooseCreatureFlagSource
1665}
1666
1668{
1669 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(model->CreatureDisplayID);
1670 if (!modelInfo)
1671 return nullptr;
1672
1673 // If a model for another gender exists, 50% chance to use it
1674 if (modelInfo->displayId_other_gender != 0 && urand(0, 1) == 0)
1675 {
1676 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->displayId_other_gender);
1677 if (!minfo_tmp)
1678 TC_LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", model->CreatureDisplayID, modelInfo->displayId_other_gender);
1679 else
1680 {
1681 // DisplayID changed
1682 model->CreatureDisplayID = modelInfo->displayId_other_gender;
1683 if (creatureTemplate)
1684 {
1685 auto itr = std::find_if(creatureTemplate->Models.begin(), creatureTemplate->Models.end(), [&](CreatureModel const& templateModel)
1686 {
1687 return templateModel.CreatureDisplayID == modelInfo->displayId_other_gender;
1688 });
1689 if (itr != creatureTemplate->Models.end())
1690 *model = *itr;
1691 }
1692 return minfo_tmp;
1693 }
1694 }
1695
1696 return modelInfo;
1697}
1698
1700{
1701 uint32 oldMSTime = getMSTime();
1702
1703 QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, DisplayID_Other_Gender FROM creature_model_info");
1704
1705 if (!result)
1706 {
1707 TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
1708 return;
1709 }
1710
1711 _creatureModelStore.reserve(result->GetRowCount());
1712 uint32 count = 0;
1713
1714 // List of model FileDataIDs that the client treats as invisible stalker
1715 uint32 trigggerCreatureModelFileID[5] = { 124640, 124641, 124642, 343863, 439302 };
1716
1717 do
1718 {
1719 Field* fields = result->Fetch();
1720
1721 uint32 displayId = fields[0].GetUInt32();
1722
1723 CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(displayId);
1724 if (!creatureDisplay)
1725 {
1726 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has a non-existent DisplayID (ID: {}). Skipped.", displayId);
1727 continue;
1728 }
1729
1730 CreatureModelInfo& modelInfo = _creatureModelStore[displayId];
1731
1732 modelInfo.bounding_radius = fields[1].GetFloat();
1733 modelInfo.combat_reach = fields[2].GetFloat();
1734 modelInfo.displayId_other_gender = fields[3].GetUInt32();
1735 modelInfo.gender = creatureDisplay->Gender;
1736 modelInfo.is_trigger = false;
1737
1738 // Checks
1739
1740 // to remove when the purpose of GENDER_UNKNOWN is known
1741 if (modelInfo.gender == GENDER_UNKNOWN)
1742 {
1743 // We don't need more errors
1744 //TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has an unimplemented Gender (ID: {}) being used by DisplayID (ID: {}). Gender set to GENDER_MALE.", modelInfo.gender, modelId);
1745 modelInfo.gender = GENDER_MALE;
1746 }
1747
1748 if (modelInfo.displayId_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.displayId_other_gender))
1749 {
1750 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has a non-existent DisplayID_Other_Gender (ID: {}) being used by DisplayID (ID: {}).", modelInfo.displayId_other_gender, displayId);
1751 modelInfo.displayId_other_gender = 0;
1752 }
1753
1754 if (modelInfo.combat_reach < 0.1f)
1756
1757 if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelID))
1758 {
1759 for (uint32 i = 0; i < 5; ++i)
1760 {
1761 if (modelData->FileDataID == trigggerCreatureModelFileID[i])
1762 {
1763 modelInfo.is_trigger = true;
1764 break;
1765 }
1766 }
1767 }
1768
1769 ++count;
1770 }
1771 while (result->NextRow());
1772
1773 TC_LOG_INFO("server.loading", ">> Loaded {} creature model based info in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1774}
1775
1777{
1778 uint32 oldMSTime = getMSTime();
1779
1780 _linkedRespawnStore.clear();
1781 // 0 1 2
1782 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1783
1784 if (!result)
1785 {
1786 TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1787 return;
1788 }
1789
1790 do
1791 {
1792 Field* fields = result->Fetch();
1793
1794 ObjectGuid::LowType guidLow = fields[0].GetUInt64();
1795 ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt64();
1796 uint8 linkType = fields[2].GetUInt8();
1797
1798 ObjectGuid guid, linkedGuid;
1799 bool error = false;
1800 switch (linkType)
1801 {
1803 {
1804 CreatureData const* slave = GetCreatureData(guidLow);
1805 if (!slave)
1806 {
1807 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1808 error = true;
1809 break;
1810 }
1811
1812 CreatureData const* master = GetCreatureData(linkedGuidLow);
1813 if (!master)
1814 {
1815 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1816 error = true;
1817 break;
1818 }
1819
1820 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1821 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1822 {
1823 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1824 error = true;
1825 break;
1826 }
1827
1828 // they must have a possibility to meet (normal/heroic difficulty)
1829 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1830 {
1831 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1832 error = true;
1833 break;
1834 }
1835
1836 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1837 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1838 break;
1839 }
1841 {
1842 CreatureData const* slave = GetCreatureData(guidLow);
1843 if (!slave)
1844 {
1845 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1846 error = true;
1847 break;
1848 }
1849
1850 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1851 if (!master)
1852 {
1853 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1854 error = true;
1855 break;
1856 }
1857
1858 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1859 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1860 {
1861 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1862 error = true;
1863 break;
1864 }
1865
1866 // they must have a possibility to meet (normal/heroic difficulty)
1867 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1868 {
1869 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1870 error = true;
1871 break;
1872 }
1873
1874 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1875 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1876 break;
1877 }
1879 {
1880 GameObjectData const* slave = GetGameObjectData(guidLow);
1881 if (!slave)
1882 {
1883 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1884 error = true;
1885 break;
1886 }
1887
1888 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1889 if (!master)
1890 {
1891 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1892 error = true;
1893 break;
1894 }
1895
1896 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1897 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1898 {
1899 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1900 error = true;
1901 break;
1902 }
1903
1904 // they must have a possibility to meet (normal/heroic difficulty)
1905 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1906 {
1907 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1908 error = true;
1909 break;
1910 }
1911
1912 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1913 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1914 break;
1915 }
1917 {
1918 GameObjectData const* slave = GetGameObjectData(guidLow);
1919 if (!slave)
1920 {
1921 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1922 error = true;
1923 break;
1924 }
1925
1926 CreatureData const* master = GetCreatureData(linkedGuidLow);
1927 if (!master)
1928 {
1929 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1930 error = true;
1931 break;
1932 }
1933
1934 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1935 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1936 {
1937 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1938 error = true;
1939 break;
1940 }
1941
1942 // they must have a possibility to meet (normal/heroic difficulty)
1943 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1944 {
1945 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1946 error = true;
1947 break;
1948 }
1949
1950 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1951 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1952 break;
1953 }
1954 }
1955
1956 if (!error)
1957 _linkedRespawnStore[guid] = linkedGuid;
1958 }
1959 while (result->NextRow());
1960
1961 TC_LOG_INFO("server.loading", ">> Loaded {} linked respawns in {} ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
1962}
1963
1965{
1966 if (!guidLow)
1967 return false;
1968
1969 CreatureData const* master = GetCreatureData(guidLow);
1970 ASSERT(master);
1971 ObjectGuid guid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, guidLow);
1972
1973 if (!linkedGuidLow) // we're removing the linking
1974 {
1975 _linkedRespawnStore.erase(guid);
1977 stmt->setUInt64(0, guidLow);
1979 WorldDatabase.Execute(stmt);
1980 return true;
1981 }
1982
1983 CreatureData const* slave = GetCreatureData(linkedGuidLow);
1984 if (!slave)
1985 {
1986 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to non-existent creature '{}'.", guidLow, linkedGuidLow);
1987 return false;
1988 }
1989
1990 MapEntry const* map = sMapStore.LookupEntry(master->mapId);
1991 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1992 {
1993 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1994 return false;
1995 }
1996
1997 // they must have a possibility to meet (normal/heroic difficulty)
1998 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1999 {
2000 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
2001 return false;
2002 }
2003
2004 ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, linkedGuidLow);
2005
2006 _linkedRespawnStore[guid] = linkedGuid;
2008 stmt->setUInt64(0, guidLow);
2009 stmt->setUInt64(1, linkedGuidLow);
2011 WorldDatabase.Execute(stmt);
2012 return true;
2013}
2014
2016{
2017 uint32 oldMSTime = getMSTime();
2018
2019 _tempSummonDataStore.clear(); // needed for reload case
2020
2021 // 0 1 2 3 4 5 6 7 8 9
2022 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
2023
2024 if (!result)
2025 {
2026 TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
2027 return;
2028 }
2029
2030 uint32 count = 0;
2031 do
2032 {
2033 Field* fields = result->Fetch();
2034
2035 uint32 summonerId = fields[0].GetUInt32();
2036 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
2037 uint8 group = fields[2].GetUInt8();
2038
2039 switch (summonerType)
2040 {
2042 if (!GetCreatureTemplate(summonerId))
2043 {
2044 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for creature summoner type, skipped.", summonerId);
2045 continue;
2046 }
2047 break;
2049 if (!GetGameObjectTemplate(summonerId))
2050 {
2051 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for gameobject summoner type, skipped.", summonerId);
2052 continue;
2053 }
2054 break;
2055 case SUMMONER_TYPE_MAP:
2056 if (!sMapStore.LookupEntry(summonerId))
2057 {
2058 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for map summoner type, skipped.", summonerId);
2059 continue;
2060 }
2061 break;
2062 default:
2063 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type {} for summoner {}, skipped.", summonerType, summonerId);
2064 continue;
2065 }
2066
2067 TempSummonData data;
2068 data.entry = fields[3].GetUInt32();
2069
2070 if (!GetCreatureTemplate(data.entry))
2071 {
2072 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has creature in group [Summoner ID: {}, Summoner Type: {}, Group ID: {}] with non existing creature entry {}, skipped.", summonerId, summonerType, group, data.entry);
2073 continue;
2074 }
2075
2076 float posX = fields[4].GetFloat();
2077 float posY = fields[5].GetFloat();
2078 float posZ = fields[6].GetFloat();
2079 float orientation = fields[7].GetFloat();
2080
2081 data.pos.Relocate(posX, posY, posZ, orientation);
2082
2083 data.type = TempSummonType(fields[8].GetUInt8());
2084
2086 {
2087 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled temp summon type {} in group [Summoner ID: {}, Summoner Type: {}, Group ID: {}] for creature entry {}, skipped.", data.type, summonerId, summonerType, group, data.entry);
2088 continue;
2089 }
2090
2091 data.time = Milliseconds(fields[9].GetUInt32());
2092
2093 TempSummonGroupKey key(summonerId, summonerType, group);
2094 _tempSummonDataStore[key].push_back(data);
2095
2096 ++count;
2097
2098 } while (result->NextRow());
2099
2100 TC_LOG_INFO("server.loading", ">> Loaded {} temp summons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
2101}
2102
2103std::vector<Difficulty> ObjectMgr::ParseSpawnDifficulties(std::string_view difficultyString, std::string_view table, ObjectGuid::LowType spawnId, uint32 mapId,
2104 std::set<Difficulty> const& mapDifficulties)
2105{
2106 std::vector<Difficulty> difficulties;
2107 bool isTransportMap = sObjectMgr->IsTransportMap(mapId);
2108 for (std::string_view token : Trinity::Tokenize(difficultyString, ',', false))
2109 {
2110 Difficulty difficultyId = Difficulty(Trinity::StringTo<std::underlying_type_t<Difficulty>>(token).value_or(DIFFICULTY_NONE));
2111 if (difficultyId && !sDifficultyStore.LookupEntry(difficultyId))
2112 {
2113 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) with non invalid difficulty id {}, skipped.",
2114 table, table, spawnId, uint32(difficultyId));
2115 continue;
2116 }
2117
2118 if (!isTransportMap && mapDifficulties.find(difficultyId) == mapDifficulties.end())
2119 {
2120 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) has unsupported difficulty {} for map (Id: {}).",
2121 table, table, spawnId, uint32(difficultyId), mapId);
2122 continue;
2123 }
2124
2125 difficulties.push_back(difficultyId);
2126 }
2127
2128 std::sort(difficulties.begin(), difficulties.end());
2129 return difficulties;
2130}
2131
2133{
2134 uint32 oldMSTime = getMSTime();
2135
2136 // 0 1 2 3 4 5 6 7 8 9 10
2137 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
2138 // 11 12 13 14 15 16 17 18 19 20 21
2139 "currentwaypoint, curhealth, curmana, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, "
2140 // 22 23 24 25 26 27
2141 "creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId "
2142 "FROM creature "
2143 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
2144 "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
2145
2146 if (!result)
2147 {
2148 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
2149 return;
2150 }
2151
2152 // Build single time for check spawnmask
2153 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2154 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2155 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2156
2157 PhaseShift phaseShift;
2158
2159 _creatureDataStore.reserve(result->GetRowCount());
2160
2161 do
2162 {
2163 Field* fields = result->Fetch();
2164
2165 ObjectGuid::LowType guid = fields[0].GetUInt64();
2166 uint32 entry = fields[1].GetUInt32();
2167
2168 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2169 if (!cInfo)
2170 {
2171 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, entry);
2172 continue;
2173 }
2174
2175 CreatureData& data = _creatureDataStore[guid];
2176 data.spawnId = guid;
2177 data.id = entry;
2178 data.mapId = fields[2].GetUInt16();
2179 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2180 if (uint32 displayId = fields[7].GetUInt32())
2181 data.display.emplace(displayId, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
2182 data.equipmentId = fields[8].GetInt8();
2183 data.spawntimesecs = fields[9].GetUInt32();
2184 data.wander_distance = fields[10].GetFloat();
2185 data.currentwaypoint= fields[11].GetUInt32();
2186 data.curhealth = fields[12].GetUInt32();
2187 data.curmana = fields[13].GetUInt32();
2188 data.movementType = fields[14].GetUInt8();
2189 data.spawnDifficulties = ParseSpawnDifficulties(fields[15].GetStringView(), "creature", guid, data.mapId, spawnMasks[data.mapId]);
2190 int16 gameEvent = fields[16].GetInt8();
2191 data.poolId = fields[17].GetUInt32();
2192 if (!fields[18].IsNull())
2193 data.npcflag = fields[18].GetUInt64();
2194 if (!fields[19].IsNull())
2195 data.unit_flags = fields[19].GetUInt32();
2196 if (!fields[20].IsNull())
2197 data.unit_flags2 = fields[20].GetUInt32();
2198 if (!fields[21].IsNull())
2199 data.unit_flags3 = fields[21].GetUInt32();
2200 data.phaseUseFlags = fields[22].GetUInt8();
2201 data.phaseId = fields[23].GetUInt32();
2202 data.phaseGroup = fields[24].GetUInt32();
2203 data.terrainSwapMap = fields[25].GetInt32();
2204 data.scriptId = GetScriptId(fields[26].GetString());
2205 data.StringId = fields[27].GetString();
2206 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2207
2208 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2209 if (!mapEntry)
2210 {
2211 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, data.mapId);
2212 continue;
2213 }
2214
2216 {
2218 {
2219 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2220 {
2222 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2223 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2224
2225 VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapId, gx, gy);
2226 if (result != VMAP::LoadResult::Success)
2227 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2228 guid, data.id, data.mapId, data.spawnPoint.ToString());
2229 }
2230 }
2231 }
2232
2233 if (data.spawnDifficulties.empty())
2234 {
2235 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2236 continue;
2237 }
2238
2239 // -1 random, 0 no equipment
2240 if (data.equipmentId != 0)
2241 {
2242 if (!GetEquipmentInfo(data.id, data.equipmentId))
2243 {
2244 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
2245 data.equipmentId = 0;
2246 }
2247 }
2248
2250 {
2251 if (!mapEntry->IsDungeon())
2252 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature is not in instance.", guid, data.id);
2253 }
2254
2255 if (data.movementType >= MAX_DB_MOTION_TYPE)
2256 {
2257 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with wrong movement generator type ({}), ignored and set to IDLE.", guid, data.id, data.movementType);
2259 }
2260
2261 if (data.wander_distance < 0.0f)
2262 {
2263 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance`< 0, set to 0.", guid, data.id);
2264 data.wander_distance = 0.0f;
2265 }
2266 else if (data.wander_distance > 0.0f && data.wander_distance < 0.1f)
2267 {
2268 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance` below the allowed minimum distance of 0.1, set to 0.", guid, data.id);
2269 data.wander_distance = 0.0f;
2270 }
2271 else if (data.movementType == RANDOM_MOTION_TYPE)
2272 {
2273 if (G3D::fuzzyEq(data.wander_distance, 0.0f))
2274 {
2275 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).", guid, data.id);
2277 }
2278 }
2279 else if (data.movementType == IDLE_MOTION_TYPE)
2280 {
2281 if (data.wander_distance != 0.0f)
2282 {
2283 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `MovementType`=0 (idle) have `wander_distance`<>0, set to 0.", guid, data.id);
2284 data.wander_distance = 0.0f;
2285 }
2286 }
2287
2289 {
2290 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2292 }
2293
2295 {
2296 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2297 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2298 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2299 }
2300
2301 if (data.phaseGroup && data.phaseId)
2302 {
2303 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2304 data.phaseGroup = 0;
2305 }
2306
2307 if (data.phaseId)
2308 {
2309 if (!sPhaseStore.LookupEntry(data.phaseId))
2310 {
2311 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2312 data.phaseId = 0;
2313 }
2314 }
2315
2316 if (data.phaseGroup)
2317 {
2318 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2319 {
2320 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phasegroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2321 data.phaseGroup = 0;
2322 }
2323 }
2324
2325 if (data.terrainSwapMap != -1)
2326 {
2327 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2328 if (!terrainSwapEntry)
2329 {
2330 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2331 data.terrainSwapMap = -1;
2332 }
2333 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2334 {
2335 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `terrainSwapMap` {} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
2336 data.terrainSwapMap = -1;
2337 }
2338 }
2339
2340 if (data.unit_flags.has_value())
2341 {
2342 if (uint32 disallowedUnitFlags = (*data.unit_flags & ~UNIT_FLAG_ALLOWED))
2343 {
2344 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags);
2346 }
2347 }
2348
2349 if (data.unit_flags2.has_value())
2350 {
2351 if (uint32 disallowedUnitFlags2 = (*data.unit_flags2 & ~UNIT_FLAG2_ALLOWED))
2352 {
2353 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags2);
2355 }
2356 }
2357
2358 if (data.unit_flags3.has_value())
2359 {
2360 if (uint32 disallowedUnitFlags3 = (*data.unit_flags3 & ~UNIT_FLAG3_ALLOWED))
2361 {
2362 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags3);
2364 }
2365 }
2366
2368 {
2369 uint32 zoneId = 0;
2370 uint32 areaId = 0;
2372 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2373
2375
2376 stmt->setUInt32(0, zoneId);
2377 stmt->setUInt32(1, areaId);
2378 stmt->setUInt64(2, guid);
2379
2380 WorldDatabase.Execute(stmt);
2381 }
2382
2383 // Add to grid if not managed by the game event
2384 if (gameEvent == 0)
2385 AddCreatureToGrid(&data);
2386 }
2387 while (result->NextRow());
2388
2389 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in {} ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2390}
2391
2393{
2394 if (CellObjectGuidsMap const* mapGuids = Trinity::Containers::MapGetValuePtr(_mapObjectGuidsStore, { mapid, spawnMode }))
2395 return Trinity::Containers::MapGetValuePtr(*mapGuids, cell_id);
2396
2397 return nullptr;
2398}
2399
2401{
2402 return Trinity::Containers::MapGetValuePtr(_mapObjectGuidsStore, { mapid, spawnMode });
2403}
2404
2405bool ObjectMgr::HasPersonalSpawns(uint32 mapid, Difficulty spawnMode, uint32 phaseId) const
2406{
2407 return Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }) != nullptr;
2408}
2409
2411{
2412 if (CellObjectGuidsMap const* guids = Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }))
2413 return Trinity::Containers::MapGetValuePtr(*guids, cell_id);
2414
2415 return nullptr;
2416}
2417
2418template<CellGuidSet CellObjectGuids::*guids>
2420{
2422 bool isPersonalPhase = PhasingHandler::IsPersonalPhase(data->phaseId);
2423 if (!isPersonalPhase)
2424 {
2425 for (Difficulty difficulty : data->spawnDifficulties)
2426 (_mapObjectGuidsStore[{ data->mapId, difficulty }][cellId].*guids).insert(data->spawnId);
2427 }
2428 else
2429 {
2430 for (Difficulty difficulty : data->spawnDifficulties)
2431 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][cellId].*guids).insert(data->spawnId);
2432 }
2433}
2434
2435template<CellGuidSet CellObjectGuids::*guids>
2437{
2439 bool isPersonalPhase = PhasingHandler::IsPersonalPhase(data->phaseId);
2440 if (!isPersonalPhase)
2441 {
2442 for (Difficulty difficulty : data->spawnDifficulties)
2443 (_mapObjectGuidsStore[{ data->mapId, difficulty }][cellId].*guids).erase(data->spawnId);
2444 }
2445 else
2446 {
2447 for (Difficulty difficulty : data->spawnDifficulties)
2448 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][cellId].*guids).erase(data->spawnId);
2449 }
2450}
2451
2453{
2454 AddSpawnDataToGrid<&CellObjectGuids::creatures>(data);
2455}
2456
2458{
2459 RemoveSpawnDataFromGrid<&CellObjectGuids::creatures>(data);
2460}
2461
2463{
2464 uint32 oldMSTime = getMSTime();
2465
2466 // 0 1 2 3 4 5 6
2467 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
2468 // 7 8 9 10 11 12 13 14 15 16
2469 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnDifficulties, eventEntry, poolSpawnId, "
2470 // 17 18 19 20 21 22
2471 "phaseUseFlags, phaseid, phasegroup, terrainSwapMap, ScriptName, StringId "
2472 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
2473 "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId");
2474
2475 if (!result)
2476 {
2477 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
2478 return;
2479 }
2480
2481 // build single time for check spawnmask
2482 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2483 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2484 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2485
2486 PhaseShift phaseShift;
2487
2488 _gameObjectDataStore.reserve(result->GetRowCount());
2489
2490 do
2491 {
2492 Field* fields = result->Fetch();
2493
2494 ObjectGuid::LowType guid = fields[0].GetUInt64();
2495 uint32 entry = fields[1].GetUInt32();
2496
2497 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
2498 if (!gInfo)
2499 {
2500 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non existing gameobject entry {}, skipped.", guid, entry);
2501 continue;
2502 }
2503
2504 if (!gInfo->displayId)
2505 {
2506 switch (gInfo->type)
2507 {
2510 break;
2511 default:
2512 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) doesn't have a displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2513 break;
2514 }
2515 }
2516
2517 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
2518 {
2519 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) has an invalid displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2520 continue;
2521 }
2522
2524
2525 data.spawnId = guid;
2526 data.id = entry;
2527 data.mapId = fields[2].GetUInt16();
2528 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2529 data.rotation.x = fields[7].GetFloat();
2530 data.rotation.y = fields[8].GetFloat();
2531 data.rotation.z = fields[9].GetFloat();
2532 data.rotation.w = fields[10].GetFloat();
2533 data.spawntimesecs = fields[11].GetInt32();
2534 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2535
2536 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2537 if (!mapEntry)
2538 {
2539 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existed map (Id: {}), skip", guid, data.id, data.mapId);
2540 continue;
2541 }
2542
2544 {
2546 {
2547 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2548 {
2550 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2551 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2552
2553 VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapId, gx, gy);
2554 if (result != VMAP::LoadResult::Success)
2555 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2556 guid, data.id, data.mapId, data.spawnPoint.ToString());
2557 }
2558 }
2559 }
2560
2561 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
2562 {
2563 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with `spawntimesecs` (0) value, but the gameobejct is marked as despawnable at action.", guid, data.id);
2564 }
2565
2566 data.animprogress = fields[12].GetUInt8();
2567 data.artKit = 0;
2568
2569 uint32 go_state = fields[13].GetUInt8();
2570 if (go_state >= MAX_GO_STATE)
2571 {
2573 {
2574 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skip", guid, data.id, go_state);
2575 continue;
2576 }
2577 }
2578 data.goState = GOState(go_state);
2579
2580 data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "gameobject", guid, data.mapId, spawnMasks[data.mapId]);
2581 if (data.spawnDifficulties.empty())
2582 {
2583 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2584 continue;
2585 }
2586
2587 int16 gameEvent = fields[15].GetInt8();
2588 data.poolId = fields[16].GetUInt32();
2589 data.phaseUseFlags = fields[17].GetUInt8();
2590 data.phaseId = fields[18].GetUInt32();
2591 data.phaseGroup = fields[19].GetUInt32();
2592
2594 {
2595 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2597 }
2598
2600 {
2601 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2602 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2603 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2604 }
2605
2606 if (data.phaseGroup && data.phaseId)
2607 {
2608 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2609 data.phaseGroup = 0;
2610 }
2611
2612 if (data.phaseId)
2613 {
2614 if (!sPhaseStore.LookupEntry(data.phaseId))
2615 {
2616 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2617 data.phaseId = 0;
2618 }
2619 }
2620
2621 if (data.phaseGroup)
2622 {
2623 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2624 {
2625 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseGroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2626 data.phaseGroup = 0;
2627 }
2628 }
2629
2630 data.terrainSwapMap = fields[20].GetInt32();
2631 if (data.terrainSwapMap != -1)
2632 {
2633 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2634 if (!terrainSwapEntry)
2635 {
2636 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2637 data.terrainSwapMap = -1;
2638 }
2639 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2640 {
2641 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `terrainSwapMap` {} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap);
2642 data.terrainSwapMap = -1;
2643 }
2644 }
2645
2646 data.scriptId = GetScriptId(fields[21].GetString());
2647 data.StringId = fields[22].GetString();
2648
2649 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
2650 {
2651 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skip", guid, data.id, data.rotation.x);
2652 continue;
2653 }
2654
2655 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
2656 {
2657 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skip", guid, data.id, data.rotation.y);
2658 continue;
2659 }
2660
2661 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
2662 {
2663 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skip", guid, data.id, data.rotation.z);
2664 continue;
2665 }
2666
2667 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
2668 {
2669 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skip", guid, data.id, data.rotation.w);
2670 continue;
2671 }
2672
2674 {
2675 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id);
2676 continue;
2677 }
2678
2679 if (!data.rotation.isUnit())
2680 {
2681 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotation quaternion (non-unit), defaulting to orientation on Z axis only", guid, data.id);
2683 }
2684
2686 {
2687 uint32 zoneId = 0;
2688 uint32 areaId = 0;
2690 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2691
2693
2694 stmt->setUInt32(0, zoneId);
2695 stmt->setUInt32(1, areaId);
2696 stmt->setUInt64(2, guid);
2697
2698 WorldDatabase.Execute(stmt);
2699 }
2700
2701 if (gameEvent == 0) // if not this is to be managed by GameEvent System
2702 AddGameobjectToGrid(&data);
2703 }
2704 while (result->NextRow());
2705
2706 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in {} ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2707}
2708
2710{
2711 uint32 oldMSTime = getMSTime();
2712
2713 // 0 1 2
2714 QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
2715
2716 if (result)
2717 {
2718 do
2719 {
2720 Field* fields = result->Fetch();
2721 uint32 groupId = fields[0].GetUInt32();
2723 group.groupId = groupId;
2724 group.name = fields[1].GetString();
2726 uint32 flags = fields[2].GetUInt32();
2728 {
2730 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag {} on group ID {} ({}), reduced to valid flag {}.", flags, groupId, group.name, uint32(group.flags));
2731 }
2733 {
2734 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
2735 TC_LOG_ERROR("sql.sql", "System spawn group {} ({}) has invalid manual spawn flag. Ignored.", groupId, group.name);
2736 }
2737 group.flags = SpawnGroupFlags(flags);
2738 } while (result->NextRow());
2739 }
2740
2741 if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
2742 {
2743 TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
2745 data.groupId = 0;
2746 data.name = "Default Group";
2747 data.mapId = 0;
2749 }
2750 if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
2751 {
2752 TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
2754 data.groupId = 1;
2755 data.name = "Legacy Group";
2756 data.mapId = 0;
2758 }
2759
2760 if (result)
2761 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group templates in {} ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2762 else
2763 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
2764
2765 return;
2766}
2767
2769{
2770 uint32 oldMSTime = getMSTime();
2771
2772 // 0 1 2
2773 QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
2774
2775 if (!result)
2776 {
2777 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
2778 return;
2779 }
2780
2781 uint32 numMembers = 0;
2782 do
2783 {
2784 Field* fields = result->Fetch();
2785 uint32 groupId = fields[0].GetUInt32();
2786 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
2787 if (!SpawnData::TypeIsValid(spawnType))
2788 {
2789 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type {} listed for spawn group {}. Skipped.", uint32(spawnType), groupId);
2790 continue;
2791 }
2792 ObjectGuid::LowType spawnId = fields[2].GetUInt64();
2793
2794 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
2795 if (!data)
2796 {
2797 TC_LOG_ERROR("sql.sql", "Spawn data with ID ({},{}) not found, but is listed as a member of spawn group {}!", uint32(spawnType), spawnId, groupId);
2798 continue;
2799 }
2800 else if (data->spawnGroupData->groupId)
2801 {
2802 TC_LOG_ERROR("sql.sql", "Spawn with ID ({},{}) is listed as a member of spawn group {}, but is already a member of spawn group {}. Skipping.", uint32(spawnType), spawnId, groupId, data->spawnGroupData->groupId);
2803 continue;
2804 }
2805 auto it = _spawnGroupDataStore.find(groupId);
2806 if (it == _spawnGroupDataStore.end())
2807 {
2808 TC_LOG_ERROR("sql.sql", "Spawn group {} assigned to spawn ID ({},{}), but group is found!", groupId, uint32(spawnType), spawnId);
2809 continue;
2810 }
2811 else
2812 {
2813 SpawnGroupTemplateData& groupTemplate = it->second;
2814 if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
2815 {
2816 groupTemplate.mapId = data->mapId;
2817 _spawnGroupsByMap[data->mapId].push_back(groupId);
2818 }
2819 else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2820 {
2821 TC_LOG_ERROR("sql.sql", "Spawn group {} has map ID {}, but spawn ({},{}) has map id {} - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->mapId);
2822 continue;
2823 }
2824 const_cast<SpawnMetadata*>(data)->spawnGroupData = &groupTemplate;
2825 if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2826 _spawnGroupMapStore.emplace(groupId, data);
2827 ++numMembers;
2828 }
2829 } while (result->NextRow());
2830
2831 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group members in {} ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
2832}
2833
2835{
2836 uint32 oldMSTime = getMSTime();
2837
2838 // 0 1 2 3 4
2839 QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
2840
2841 if (!result)
2842 {
2843 TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
2844 return;
2845 }
2846
2847 uint32 n = 0;
2848 do
2849 {
2850 Field* fields = result->Fetch();
2851 uint32 const spawnGroupId = fields[3].GetUInt32();
2852 auto it = _spawnGroupDataStore.find(spawnGroupId);
2853 if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
2854 {
2855 TC_LOG_ERROR("sql.sql", "Invalid spawn group {} specified for instance {}. Skipped.", spawnGroupId, fields[0].GetUInt16());
2856 continue;
2857 }
2858
2859 uint16 const instanceMapId = fields[0].GetUInt16();
2860 if (it->second.mapId != instanceMapId)
2861 {
2862 TC_LOG_ERROR("sql.sql", "Instance spawn group {} specified for instance {} has spawns on a different map {}. Skipped.",
2863 spawnGroupId, instanceMapId, it->second.mapId);
2864 continue;
2865 }
2866
2867 InstanceSpawnGroupInfo& info = _instanceSpawnGroupStore[instanceMapId].emplace_back();
2868 info.SpawnGroupId = spawnGroupId;
2869 info.BossStateId = fields[1].GetUInt8();
2870
2871 uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
2872 uint8 const states = fields[2].GetUInt8();
2873 if (states & ~ALL_STATES)
2874 {
2875 info.BossStates = states & ALL_STATES;
2876 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid boss state mask {} - truncated to {}.", instanceMapId, spawnGroupId, states, info.BossStates);
2877 }
2878 else
2879 info.BossStates = states;
2880
2881 uint8 const flags = fields[4].GetUInt8();
2883 {
2885 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid flags {} - truncated to {}.", instanceMapId, spawnGroupId, flags, info.Flags);
2886 }
2887 else
2888 info.Flags = flags;
2889
2891 {
2893 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) FLAG_ALLIANCE_ONLY and FLAG_HORDE_ONLY may not be used together in a single entry - truncated to {}.", instanceMapId, spawnGroupId, info.Flags);
2894 }
2895
2896 ++n;
2897 } while (result->NextRow());
2898
2899 TC_LOG_INFO("server.loading", ">> Loaded {} instance spawn groups in {} ms", n, GetMSTimeDiffToNow(oldMSTime));
2900}
2901
2903{
2904 if (!SpawnData::TypeHasData(type))
2905 return nullptr;
2906 switch (type)
2907 {
2909 return GetCreatureData(spawnId);
2911 return GetGameObjectData(spawnId);
2913 return sAreaTriggerDataStore->GetAreaTriggerSpawn(spawnId);
2914 default:
2915 ABORT_MSG("Invalid spawn object type %u", uint32(type));
2916 return nullptr;
2917 }
2918}
2919
2921{
2922 auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
2923 ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u," UI64FMTD ") is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
2924 if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
2925 return;
2926
2927 auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
2928 for (auto it = pair.first; it != pair.second; ++it)
2929 {
2930 if (it->second != data)
2931 continue;
2932 _spawnGroupMapStore.erase(it);
2933 return;
2934 }
2935 ABORT_MSG("Spawn data (%u," UI64FMTD ") being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
2936}
2937
2939{
2940 AddSpawnDataToGrid<&CellObjectGuids::gameobjects>(data);
2941}
2942
2944{
2945 RemoveSpawnDataFromGrid<&CellObjectGuids::gameobjects>(data);
2946}
2947
2948uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
2949{
2950 if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON)
2951 return 0;
2952
2953 static float const qualityMultipliers[MAX_ITEM_QUALITY] =
2954 {
2955 0.92f, 0.92f, 0.92f, 1.11f, 1.32f, 1.61f, 0.0f, 0.0f
2956 };
2957
2958 static float const armorMultipliers[MAX_INVTYPE] =
2959 {
2960 0.00f, // INVTYPE_NON_EQUIP
2961 0.60f, // INVTYPE_HEAD
2962 0.00f, // INVTYPE_NECK
2963 0.60f, // INVTYPE_SHOULDERS
2964 0.00f, // INVTYPE_BODY
2965 1.00f, // INVTYPE_CHEST
2966 0.33f, // INVTYPE_WAIST
2967 0.72f, // INVTYPE_LEGS
2968 0.48f, // INVTYPE_FEET
2969 0.33f, // INVTYPE_WRISTS
2970 0.33f, // INVTYPE_HANDS
2971 0.00f, // INVTYPE_FINGER
2972 0.00f, // INVTYPE_TRINKET
2973 0.00f, // INVTYPE_WEAPON
2974 0.72f, // INVTYPE_SHIELD
2975 0.00f, // INVTYPE_RANGED
2976 0.00f, // INVTYPE_CLOAK
2977 0.00f, // INVTYPE_2HWEAPON
2978 0.00f, // INVTYPE_BAG
2979 0.00f, // INVTYPE_TABARD
2980 1.00f, // INVTYPE_ROBE
2981 0.00f, // INVTYPE_WEAPONMAINHAND
2982 0.00f, // INVTYPE_WEAPONOFFHAND
2983 0.00f, // INVTYPE_HOLDABLE
2984 0.00f, // INVTYPE_AMMO
2985 0.00f, // INVTYPE_THROWN
2986 0.00f, // INVTYPE_RANGEDRIGHT
2987 0.00f, // INVTYPE_QUIVER
2988 0.00f, // INVTYPE_RELIC
2989 0.00f, // INVTYPE_PROFESSION_TOOL
2990 0.00f, // INVTYPE_PROFESSION_GEAR
2991 0.00f, // INVTYPE_EQUIPABLE_SPELL_OFFENSIVE
2992 0.00f, // INVTYPE_EQUIPABLE_SPELL_UTILITY
2993 0.00f, // INVTYPE_EQUIPABLE_SPELL_DEFENSIVE
2994 0.00f, // INVTYPE_EQUIPABLE_SPELL_MOBILITY
2995 };
2996
2997 static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] =
2998 {
2999 0.91f, // ITEM_SUBCLASS_WEAPON_AXE
3000 1.00f, // ITEM_SUBCLASS_WEAPON_AXE2
3001 1.00f, // ITEM_SUBCLASS_WEAPON_BOW
3002 1.00f, // ITEM_SUBCLASS_WEAPON_GUN
3003 0.91f, // ITEM_SUBCLASS_WEAPON_MACE
3004 1.00f, // ITEM_SUBCLASS_WEAPON_MACE2
3005 1.00f, // ITEM_SUBCLASS_WEAPON_POLEARM
3006 0.91f, // ITEM_SUBCLASS_WEAPON_SWORD
3007 1.00f, // ITEM_SUBCLASS_WEAPON_SWORD2
3008 1.00f, // ITEM_SUBCLASS_WEAPON_WARGLAIVES
3009 1.00f, // ITEM_SUBCLASS_WEAPON_STAFF
3010 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC
3011 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2
3012 0.66f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON
3013 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
3014 0.66f, // ITEM_SUBCLASS_WEAPON_DAGGER
3015 0.00f, // ITEM_SUBCLASS_WEAPON_THROWN
3016 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR
3017 1.00f, // ITEM_SUBCLASS_WEAPON_CROSSBOW
3018 0.66f, // ITEM_SUBCLASS_WEAPON_WAND
3019 0.66f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE
3020 };
3021
3022 float levelPenalty = 1.0f;
3023 if (itemLevel <= 28)
3024 levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f;
3025
3026 if (itemClass == ITEM_CLASS_ARMOR)
3027 {
3028 if (inventoryType > INVTYPE_ROBE)
3029 return 0;
3030
3031 return 5 * uint32(round(25.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty));
3032 }
3033
3034 return 5 * uint32(round(18.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty));
3035};
3036
3038{
3042
3044 {
3045 memset(ItemSpecStatTypes, -1, sizeof(ItemSpecStatTypes));
3046
3047 if (item->ClassID == ITEM_CLASS_WEAPON)
3048 {
3049 ItemType = 5;
3050 switch (item->SubclassID)
3051 {
3054 break;
3057 break;
3060 break;
3063 break;
3066 break;
3069 break;
3072 break;
3075 break;
3078 break;
3081 break;
3084 break;
3087 break;
3090 break;
3093 break;
3096 break;
3099 break;
3100 default:
3101 break;
3102 }
3103 }
3104 else if (item->ClassID == ITEM_CLASS_ARMOR)
3105 {
3106 switch (item->SubclassID)
3107 {
3109 if (sparse->InventoryType != INVTYPE_CLOAK)
3110 {
3111 ItemType = 1;
3112 break;
3113 }
3114
3115 ItemType = 0;
3117 break;
3119 ItemType = 2;
3120 break;
3122 ItemType = 3;
3123 break;
3125 ItemType = 4;
3126 break;
3127 default:
3129 {
3130 ItemType = 6;
3132 }
3134 {
3135 ItemType = 6;
3137 }
3138 else
3139 ItemType = 0;
3140 break;
3141 }
3142 }
3143 else if (item->ClassID == ITEM_CLASS_GEM)
3144 {
3145 ItemType = 7;
3146 if (GemPropertiesEntry const* gem = sGemPropertiesStore.LookupEntry(sparse->GemProperties))
3147 {
3148 if (gem->Type & SOCKET_COLOR_RELIC_IRON)
3150 if (gem->Type & SOCKET_COLOR_RELIC_BLOOD)
3152 if (gem->Type & SOCKET_COLOR_RELIC_SHADOW)
3154 if (gem->Type & SOCKET_COLOR_RELIC_FEL)
3156 if (gem->Type & SOCKET_COLOR_RELIC_ARCANE)
3158 if (gem->Type & SOCKET_COLOR_RELIC_FROST)
3160 if (gem->Type & SOCKET_COLOR_RELIC_FIRE)
3162 if (gem->Type & SOCKET_COLOR_RELIC_WATER)
3164 if (gem->Type & SOCKET_COLOR_RELIC_LIFE)
3166 if (gem->Type & SOCKET_COLOR_RELIC_WIND)
3168 if (gem->Type & SOCKET_COLOR_RELIC_HOLY)
3170 }
3171 }
3172 else
3173 ItemType = 0;
3174
3175 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3176 if (sparse->StatModifierBonusStat[i] != -1)
3178 }
3179
3180 void AddStat(ItemSpecStat statType)
3181 {
3183 return;
3184
3185 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3186 if (ItemSpecStatTypes[i] == uint32(statType))
3187 return;
3188
3190 }
3191
3192 void AddModStat(int32 itemStatType)
3193 {
3194 switch (itemStatType)
3195 {
3196 case ITEM_MOD_AGILITY:
3198 break;
3199 case ITEM_MOD_STRENGTH:
3201 break;
3202 case ITEM_MOD_INTELLECT:
3204 break;
3207 break;
3210 break;
3216 break;
3219 break;
3222 break;
3225 break;
3230 break;
3231 case ITEM_MOD_AGI_STR:
3234 break;
3235 case ITEM_MOD_AGI_INT:
3238 break;
3239 case ITEM_MOD_STR_INT:
3242 break;
3243 }
3244 }
3245};
3246
3248{
3249 uint32 oldMSTime = getMSTime();
3250
3251 for (ItemSparseEntry const* sparse : sItemSparseStore)
3252 {
3253 ItemEntry const* db2Data = sItemStore.LookupEntry(sparse->ID);
3254 if (!db2Data)
3255 continue;
3256
3257 ItemTemplate& itemTemplate = _itemTemplateStore[sparse->ID];
3258
3259 itemTemplate.BasicData = db2Data;
3260 itemTemplate.ExtendedData = sparse;
3261
3262 itemTemplate.MaxDurability = FillMaxDurability(db2Data->ClassID, db2Data->SubclassID, sparse->InventoryType, sparse->OverallQualityID, sparse->ItemLevel);
3263 itemTemplate.ScriptId = 0;
3264 itemTemplate.FoodType = 0;
3265 itemTemplate.MinMoneyLoot = 0;
3266 itemTemplate.MaxMoneyLoot = 0;
3267 itemTemplate.FlagsCu = 0;
3268 itemTemplate.SpellPPMRate = 0.0f;
3269 itemTemplate.RandomBonusListTemplateId = 0;
3270 itemTemplate.ItemSpecClassMask = 0;
3271 itemTemplate.QuestLogItemId = 0;
3272
3273 if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID))
3274 {
3275 for (ItemSpecOverrideEntry const* itemSpecOverride : *itemSpecOverrides)
3276 {
3277 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpecOverride->SpecID))
3278 {
3279 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3280 itemTemplate.Specializations[0].set(ItemTemplate::CalculateItemSpecBit(specialization));
3281 itemTemplate.Specializations[1] |= itemTemplate.Specializations[0];
3282 itemTemplate.Specializations[2] |= itemTemplate.Specializations[0];
3283 }
3284 }
3285 }
3286 else
3287 {
3288 ItemSpecStats itemSpecStats(db2Data, sparse);
3289
3290 for (ItemSpecEntry const* itemSpec : sItemSpecStore)
3291 {
3292 if (itemSpecStats.ItemType != itemSpec->ItemType)
3293 continue;
3294
3295 bool hasPrimary = itemSpec->PrimaryStat == ITEM_SPEC_STAT_NONE;
3296 bool hasSecondary = itemSpec->SecondaryStat == ITEM_SPEC_STAT_NONE;
3297 for (uint32 i = 0; i < itemSpecStats.ItemSpecStatCount; ++i)
3298 {
3299 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->PrimaryStat)
3300 hasPrimary = true;
3301 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->SecondaryStat)
3302 hasSecondary = true;
3303 }
3304
3305 if (!hasPrimary || !hasSecondary)
3306 continue;
3307
3308 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpec->SpecializationID))
3309 {
3310 if ((1 << (specialization->ClassID - 1)) & sparse->AllowableClass)
3311 {
3312 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3313 std::size_t specBit = ItemTemplate::CalculateItemSpecBit(specialization);
3314 itemTemplate.Specializations[0].set(specBit);
3315 if (itemSpec->MaxLevel > 40)
3316 itemTemplate.Specializations[1].set(specBit);
3317 if (itemSpec->MaxLevel >= 110)
3318 itemTemplate.Specializations[2].set(specBit);
3319 }
3320 }
3321 }
3322 }
3323
3324 // Items that have no specializations set can be used by everyone
3325 for (auto& specs : itemTemplate.Specializations)
3326 if (specs.count() == 0)
3327 specs.set();
3328 }
3329
3330 // Load item effects (spells)
3331 for (ItemXItemEffectEntry const* effectEntry : sItemXItemEffectStore)
3333 if (ItemEffectEntry const* effect = sItemEffectStore.LookupEntry(effectEntry->ItemEffectID))
3334 item->Effects.push_back(effect);
3335
3336 TC_LOG_INFO("server.loading", ">> Loaded {} item templates in {} ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3337}
3338
3340{
3341 uint32 oldMSTime = getMSTime();
3342 uint32 count = 0;
3343
3344 QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId, QuestLogItemId FROM item_template_addon");
3345 if (result)
3346 {
3347 do
3348 {
3349 Field* fields = result->Fetch();
3350 uint32 itemId = fields[0].GetUInt32();
3351 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3352 if (!itemTemplate)
3353 {
3354 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_template_addon` does not exist, skipped.", itemId);
3355 continue;
3356 }
3357
3358 uint32 minMoneyLoot = fields[3].GetUInt32();
3359 uint32 maxMoneyLoot = fields[4].GetUInt32();
3360 if (minMoneyLoot > maxMoneyLoot)
3361 {
3362 TC_LOG_ERROR("sql.sql", "Minimum money loot specified in `item_template_addon` for item {} was greater than maximum amount, swapping.", itemId);
3363 std::swap(minMoneyLoot, maxMoneyLoot);
3364 }
3365 itemTemplate->FlagsCu = fields[1].GetUInt32();
3366 itemTemplate->FoodType = fields[2].GetUInt8();
3367 itemTemplate->MinMoneyLoot = minMoneyLoot;
3368 itemTemplate->MaxMoneyLoot = maxMoneyLoot;
3369 itemTemplate->SpellPPMRate = fields[5].GetFloat();
3370 itemTemplate->RandomBonusListTemplateId = fields[6].GetUInt32();
3371 itemTemplate->QuestLogItemId = fields[7].GetInt32();
3372 ++count;
3373 } while (result->NextRow());
3374 }
3375 TC_LOG_INFO("server.loading", ">> Loaded {} item addon templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3376}
3377
3379{
3380 uint32 oldMSTime = getMSTime();
3381 uint32 count = 0;
3382
3383 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names");
3384 if (result)
3385 {
3386 do
3387 {
3388 Field* fields = result->Fetch();
3389 uint32 itemId = fields[0].GetUInt32();
3390 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3391 if (!itemTemplate)
3392 {
3393 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_script_names` does not exist, skipped.", itemId);
3394 continue;
3395 }
3396
3397 itemTemplate->ScriptId = GetScriptId(fields[1].GetString());
3398 ++count;
3399 } while (result->NextRow());
3400 }
3401
3402 TC_LOG_INFO("server.loading", ">> Loaded {} item script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3403}
3404
3406{
3408}
3409
3411{
3412 uint32 oldMSTime = getMSTime();
3413
3414 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
3415
3416 uint32 count = 0;
3417
3418 // 0 1 2 3 4 5
3419 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
3420
3421 if (!result)
3422 {
3423 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
3424 return;
3425 }
3426
3427 do
3428 {
3429 Field* fields = result->Fetch();
3430
3431 uint32 entry = fields[0].GetUInt32();
3432 uint32 accessory = fields[1].GetUInt32();
3433 int8 seatId = fields[2].GetInt8();
3434 bool isMinion = fields[3].GetBool();
3435 uint8 summonType = fields[4].GetUInt8();
3436 uint32 summonTimer = fields[5].GetUInt32();
3437
3438 if (!GetCreatureTemplate(entry))
3439 {
3440 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
3441 continue;
3442 }
3443
3444 if (!GetCreatureTemplate(accessory))
3445 {
3446 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", accessory);
3447 continue;
3448 }
3449
3450 if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
3451 {
3452 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} has no data in npc_spellclick_spells", entry);
3453 continue;
3454 }
3455
3456 _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
3457
3458 ++count;
3459 }
3460 while (result->NextRow());
3461
3462 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3463}
3464
3466{
3467 uint32 oldMSTime = getMSTime();
3468
3469 _vehicleTemplateStore.clear();
3470
3471 // 0 1
3472 QueryResult result = WorldDatabase.Query("SELECT creatureId, despawnDelayMs FROM vehicle_template");
3473
3474 if (!result)
3475 {
3476 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template. DB table `vehicle_template` is empty.");
3477 return;
3478 }
3479
3480 do
3481 {
3482 Field* fields = result->Fetch();
3483
3484 uint32 creatureId = fields[0].GetUInt32();
3485
3486 if (!GetCreatureTemplate(creatureId))
3487 {
3488 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Vehicle {} does not exist.", creatureId);
3489 continue;
3490 }
3491
3492 VehicleTemplate& vehicleTemplate = _vehicleTemplateStore[creatureId];
3493 vehicleTemplate.DespawnDelay = Milliseconds(fields[1].GetInt32());
3494
3495 } while (result->NextRow());
3496
3497 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template entries in {} ms", _vehicleTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3498}
3499
3501{
3502 uint32 oldMSTime = getMSTime();
3503
3504 _vehicleAccessoryStore.clear(); // needed for reload case
3505
3506 uint32 count = 0;
3507
3508 // 0 1 2 3 4 5
3509 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
3510
3511 if (!result)
3512 {
3513 TC_LOG_INFO("server.loading", ">> Loaded 0 Vehicle Accessories in {} ms", GetMSTimeDiffToNow(oldMSTime));
3514 return;
3515 }
3516
3517 do
3518 {
3519 Field* fields = result->Fetch();
3520
3521 ObjectGuid::LowType uiGUID = fields[0].GetUInt64();
3522 uint32 uiAccessory = fields[1].GetUInt32();
3523 int8 uiSeat = int8(fields[2].GetInt16());
3524 bool bMinion = fields[3].GetBool();
3525 uint8 uiSummonType = fields[4].GetUInt8();
3526 uint32 uiSummonTimer= fields[5].GetUInt32();
3527
3528 if (!GetCreatureTemplate(uiAccessory))
3529 {
3530 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
3531 continue;
3532 }
3533
3534 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
3535
3536 ++count;
3537 }
3538 while (result->NextRow());
3539
3540 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3541}
3542
3544{
3545 uint32 oldMSTime = getMSTime();
3546
3547 _vehicleSeatAddonStore.clear(); // needed for reload case
3548
3549 uint32 count = 0;
3550
3551 // 0 1 2 3 4 5 6
3552 QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
3553
3554 if (!result)
3555 {
3556 TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
3557 return;
3558 }
3559
3560 do
3561 {
3562 Field* fields = result->Fetch();
3563
3564 uint32 seatID = fields[0].GetUInt32();
3565 float orientation = fields[1].GetFloat();
3566 float exitX = fields[2].GetFloat();
3567 float exitY = fields[3].GetFloat();
3568 float exitZ = fields[4].GetFloat();
3569 float exitO = fields[5].GetFloat();
3570 uint8 exitParam = fields[6].GetUInt8();
3571
3572 if (!sVehicleSeatStore.LookupEntry(seatID))
3573 {
3574 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} does not exist in VehicleSeat.dbc. Skipping entry.", seatID);
3575 continue;
3576 }
3577
3578 // Sanitizing values
3579 if (orientation > float(M_PI * 2))
3580 {
3581 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid angle offset value ({}). Set Value to 0.", seatID, orientation);
3582 orientation = 0.0f;
3583 }
3584
3586 {
3587 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid exit parameter value ({}). Setting to 0 (none).", seatID, exitParam);
3588 continue;
3589 }
3590
3591 _vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
3592
3593 ++count;
3594 } while (result->NextRow());
3595
3596 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Seat Addon entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3597}
3598
3600{
3601 uint32 oldMSTime = getMSTime();
3602
3603 // 0 1 2 3 4 5 6 7 8 9
3604 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
3605
3606 if (!result)
3607 {
3608 TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
3609 return;
3610 }
3611
3612 uint32 count = 0;
3613
3614 do
3615 {
3616 Field* fields = result->Fetch();
3617
3618 uint32 creature_id = fields[0].GetUInt32();
3619 if (!GetCreatureTemplate(creature_id))
3620 {
3621 TC_LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
3622 continue;
3623 }
3624
3625 uint32 current_level = fields[1].GetUInt8();
3626 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3627 {
3628 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3629 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3630 else
3631 {
3632 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `pet_levelstats` table, ignoring.", current_level);
3633 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3634 }
3635 continue;
3636 }
3637 else if (current_level < 1)
3638 {
3639 TC_LOG_ERROR("sql.sql", "Wrong (<1) level {} in `pet_levelstats` table, ignoring.", current_level);
3640 continue;
3641 }
3642
3643 auto& pInfoMapEntry = _petInfoStore[creature_id];
3644 if (!pInfoMapEntry)
3645 pInfoMapEntry = std::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3646
3647 // data for level 1 stored in [0] array element, ...
3648 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
3649
3650 pLevelInfo->health = fields[2].GetUInt16();
3651 pLevelInfo->mana = fields[3].GetUInt16();
3652 pLevelInfo->armor = fields[9].GetUInt32();
3653
3654 for (uint8 i = 0; i < MAX_STATS; i++)
3655 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
3656
3657 ++count;
3658 }
3659 while (result->NextRow());
3660
3661 // Fill gaps and check integrity
3662 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
3663 {
3664 auto& pInfo = itr->second;
3665
3666 // fatal error if no level 1 data
3667 if (!pInfo || pInfo[0].health == 0)
3668 {
3669 TC_LOG_ERROR("sql.sql", "Creature {} does not have pet stats data for Level 1!", itr->first);
3670 ABORT();
3671 }
3672
3673 // fill level gaps
3674 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3675 {
3676 if (pInfo[level].health == 0)
3677 {
3678 TC_LOG_ERROR("sql.sql", "Creature {} has no data for Level {} pet stats data, using data of Level {}.", itr->first, level + 1, level);
3679 pInfo[level] = pInfo[level - 1];
3680 }
3681 }
3682 }
3683
3684 TC_LOG_INFO("server.loading", ">> Loaded {} level pet stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3685}
3686
3687PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
3688{
3689 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3690 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3691
3692 auto itr = _petInfoStore.find(creature_id);
3693 if (itr == _petInfoStore.end())
3694 return nullptr;
3695
3696 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
3697}
3698
3700{
3701 std::unique_ptr<PlayerInfo>* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race_), Classes(class_) });
3702 if (!playerInfo)
3703 return;
3704
3705 if (count > 0)
3706 playerInfo->get()->item.emplace_back(itemId, count);
3707 else
3708 {
3709 if (count < -1)
3710 TC_LOG_ERROR("sql.sql", "Invalid count {} specified on item {} be removed from original player create info (use -1)!", count, itemId);
3711
3712 PlayerCreateInfoItems& items = playerInfo->get()->item;
3713
3714 auto erased = std::remove_if(items.begin(), items.end(), [itemId](PlayerCreateInfoItem const& item) { return item.item_id == itemId; });
3715 if (erased == items.end())
3716 {
3717 TC_LOG_ERROR("sql.sql", "Item {} specified to be removed from original create info not found in db2!", itemId);
3718 return;
3719 }
3720
3721 items.erase(erased, items.end());
3722 }
3723}
3724
3726{
3727 // Load playercreate
3728 {
3729 uint32 oldMSTime = getMSTime();
3730 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3731 QueryResult result = WorldDatabase.Query("SELECT race, class, map, position_x, position_y, position_z, orientation, npe_map, npe_position_x, npe_position_y, npe_position_z, npe_orientation, npe_transport_guid, intro_movie_id, intro_scene_id, npe_intro_scene_id FROM playercreateinfo");
3732
3733 if (!result)
3734 {
3735 TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
3736 ABORT();
3737 }
3738 else
3739 {
3740 uint32 count = 0;
3741
3742 do
3743 {
3744 Field* fields = result->Fetch();
3745
3746 uint32 current_race = fields[0].GetUInt8();
3747 uint32 current_class = fields[1].GetUInt8();
3748 uint32 mapId = fields[2].GetUInt16();
3749 float positionX = fields[3].GetFloat();
3750 float positionY = fields[4].GetFloat();
3751 float positionZ = fields[5].GetFloat();
3752 float orientation = fields[6].GetFloat();
3753
3754 if (!sChrRacesStore.LookupEntry(current_race))
3755 {
3756 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3757 continue;
3758 }
3759
3760 if (!sChrClassesStore.LookupEntry(current_class))
3761 {
3762 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3763 continue;
3764 }
3765
3766 // accept DB data only for valid position (and non instanceable)
3767 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3768 {
3769 TC_LOG_ERROR("sql.sql", "Wrong home position for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3770 continue;
3771 }
3772
3773 if (sMapStore.LookupEntry(mapId)->Instanceable())
3774 {
3775 TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3776 continue;
3777 }
3778
3779 if (!sDB2Manager.GetChrModel(current_race, GENDER_MALE))
3780 {
3781 TC_LOG_ERROR("sql.sql", "Missing male model for race {}, ignoring.", current_race);
3782 continue;
3783 }
3784
3785 if (!sDB2Manager.GetChrModel(current_race, GENDER_FEMALE))
3786 {
3787 TC_LOG_ERROR("sql.sql", "Missing female model for race {}, ignoring.", current_race);
3788 continue;
3789 }
3790
3791 std::unique_ptr<PlayerInfo> info = std::make_unique<PlayerInfo>();
3792 info->createPosition.Loc.WorldRelocate(mapId, positionX, positionY, positionZ, orientation);
3793
3794 if (std::none_of(fields + 7, fields + 12, [](Field const& field) { return field.IsNull(); }))
3795 {
3796 info->createPositionNPE.emplace();
3797
3798 info->createPositionNPE->Loc.WorldRelocate(fields[7].GetUInt32(), fields[8].GetFloat(), fields[9].GetFloat(), fields[10].GetFloat(), fields[11].GetFloat());
3799 if (!fields[12].IsNull())
3800 info->createPositionNPE->TransportGuid = fields[12].GetUInt64();
3801
3802 if (!sMapStore.LookupEntry(info->createPositionNPE->Loc.GetMapId()))
3803 {
3804 TC_LOG_ERROR("sql.sql", "Invalid NPE map id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3805 info->createPositionNPE->Loc.GetMapId(), current_class, current_race);
3806 info->createPositionNPE.reset();
3807 }
3808
3809 if (info->createPositionNPE && info->createPositionNPE->TransportGuid && !sTransportMgr->GetTransportSpawn(*info->createPositionNPE->TransportGuid))
3810 {
3811 TC_LOG_ERROR("sql.sql", "Invalid NPE transport spawn id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3812 *info->createPositionNPE->TransportGuid, current_class, current_race);
3813 info->createPositionNPE.reset(); // remove entire NPE data - assume user put transport offsets into npe_position fields
3814 }
3815 }
3816
3817 if (!fields[13].IsNull())
3818 {
3819 uint32 introMovieId = fields[13].GetUInt32();
3820 if (sMovieStore.LookupEntry(introMovieId))
3821 info->introMovieId = introMovieId;
3822 else
3823 TC_LOG_ERROR("sql.sql", "Invalid intro movie id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3824 introMovieId, current_class, current_race);
3825 }
3826
3827 if (!fields[14].IsNull())
3828 {
3829 uint32 introSceneId = fields[14].GetUInt32();
3830 if (GetSceneTemplate(introSceneId))
3831 info->introSceneId = introSceneId;
3832 else
3833 TC_LOG_ERROR("sql.sql", "Invalid intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3834 introSceneId, current_class, current_race);
3835 }
3836
3837 if (!fields[15].IsNull())
3838 {
3839 uint32 introSceneId = fields[15].GetUInt32();
3840 if (GetSceneTemplate(introSceneId))
3841 info->introSceneIdNPE = introSceneId;
3842 else
3843 TC_LOG_ERROR("sql.sql", "Invalid NPE intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3844 introSceneId, current_class, current_race);
3845 }
3846
3847 _playerInfo[{ Races(current_race), Classes(current_class) }] = std::move(info);
3848
3849 ++count;
3850 }
3851 while (result->NextRow());
3852
3853 TC_LOG_INFO("server.loading", ">> Loaded {} player create definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3854 }
3855 }
3856
3857 // Load playercreate items
3858 TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
3859 {
3860 std::unordered_map<uint32, std::vector<ItemTemplate const*>> itemsByCharacterLoadout;
3861 for (CharacterLoadoutItemEntry const* characterLoadoutItem : sCharacterLoadoutItemStore)
3862 if (ItemTemplate const* itemTemplate = GetItemTemplate(characterLoadoutItem->ItemID))
3863 itemsByCharacterLoadout[characterLoadoutItem->CharacterLoadoutID].push_back(itemTemplate);
3864
3865 for (CharacterLoadoutEntry const* characterLoadout : sCharacterLoadoutStore)
3866 {
3867 if (!characterLoadout->IsForNewCharacter())
3868 continue;
3869
3870 std::vector<ItemTemplate const*> const* items = Trinity::Containers::MapGetValuePtr(itemsByCharacterLoadout, characterLoadout->ID);
3871 if (!items)
3872 continue;
3873
3874 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
3875 {
3876 if (!characterLoadout->RaceMask.HasRace(raceIndex))
3877 continue;
3878
3879 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(raceIndex), Classes(characterLoadout->ChrClassID) }))
3880 {
3881 playerInfo->get()->itemContext = ItemContext(characterLoadout->ItemContext);
3882
3883 for (ItemTemplate const* itemTemplate : *items)
3884 {
3885 // BuyCount by default
3886 uint32 count = itemTemplate->GetBuyCount();
3887
3888 // special amount for food/drink
3889 if (itemTemplate->GetClass() == ITEM_CLASS_CONSUMABLE && itemTemplate->GetSubClass() == ITEM_SUBCLASS_FOOD_DRINK)
3890 {
3891 if (!itemTemplate->Effects.empty())
3892 {
3893 switch (itemTemplate->Effects[0]->SpellCategoryID)
3894 {
3895 case SPELL_CATEGORY_FOOD: // food
3896 count = characterLoadout->ChrClassID == CLASS_DEATH_KNIGHT ? 10 : 4;
3897 break;
3898 case SPELL_CATEGORY_DRINK: // drink
3899 count = 2;
3900 break;
3901 }
3902 }
3903 if (itemTemplate->GetMaxStackSize() < count)
3904 count = itemTemplate->GetMaxStackSize();
3905 }
3906
3907 playerInfo->get()->item.emplace_back(itemTemplate->GetId(), count);
3908 }
3909 }
3910 }
3911 }
3912 }
3913
3914 TC_LOG_INFO("server.loading", "Loading Player Create Items Override Data...");
3915 {
3916 uint32 oldMSTime = getMSTime();
3917 // 0 1 2 3
3918 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
3919
3920 if (!result)
3921 {
3922 TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
3923 }
3924 else
3925 {
3926 uint32 count = 0;
3927
3928 do
3929 {
3930 Field* fields = result->Fetch();
3931
3932 uint32 current_race = fields[0].GetUInt8();
3933 if (current_race >= MAX_RACES)
3934 {
3935 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
3936 continue;
3937 }
3938
3939 uint32 current_class = fields[1].GetUInt8();
3940 if (current_class >= MAX_CLASSES)
3941 {
3942 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_item` table, ignoring.", current_class);
3943 continue;
3944 }
3945
3946 uint32 item_id = fields[2].GetUInt32();
3947
3948 if (!GetItemTemplate(item_id))
3949 {
3950 TC_LOG_ERROR("sql.sql", "Item id {} (race {} class {}) in `playercreateinfo_item` table but it does not exist, ignoring.", item_id, current_race, current_class);
3951 continue;
3952 }
3953
3954 int32 amount = fields[3].GetInt8();
3955
3956 if (!amount)
3957 {
3958 TC_LOG_ERROR("sql.sql", "Item id {} (class {} race {}) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
3959 continue;
3960 }
3961
3962 if (!current_race || !current_class)
3963 {
3964 uint32 min_race = current_race ? current_race : 1;
3965 uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
3966 uint32 min_class = current_class ? current_class : 1;
3967 uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
3968 for (uint32 r = min_race; r < max_race; ++r)
3969 for (uint32 c = min_class; c < max_class; ++c)
3970 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
3971 }
3972 else
3973 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
3974
3975 ++count;
3976 }
3977 while (result->NextRow());
3978
3979 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3980 }
3981 }
3982
3983 // Load playercreate skills
3984 TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
3985 {
3986 uint32 oldMSTime = getMSTime();
3987
3989 if (rcInfo->Availability == 1)
3990 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
3991 if (rcInfo->RaceMask.IsEmpty() || rcInfo->RaceMask.HasRace(raceIndex))
3992 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
3993 if (rcInfo->ClassMask == -1 || rcInfo->ClassMask == 0 || ((1 << (classIndex - 1)) & rcInfo->ClassMask))
3994 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(raceIndex), Classes(classIndex) }))
3995 playerInfo->get()->skills.push_back(rcInfo);
3996
3997 TC_LOG_INFO("server.loading", ">> Loaded player create skills in {} ms", GetMSTimeDiffToNow(oldMSTime));
3998 }
3999
4000 // Load playercreate custom spells
4001 TC_LOG_INFO("server.loading", "Loading Player Create Custom Spell Data...");
4002 {
4003 uint32 oldMSTime = getMSTime();
4004
4005 QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
4006
4007 if (!result)
4008 {
4009 TC_LOG_INFO("server.loading", ">> Loaded 0 player create custom spells. DB table `playercreateinfo_spell_custom` is empty.");
4010 }
4011 else
4012 {
4013 uint32 count = 0;
4014
4015 do
4016 {
4017 Field* fields = result->Fetch();
4018 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4019 uint32 classMask = fields[1].GetUInt32();
4020 uint32 spellId = fields[2].GetUInt32();
4021
4022 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4023 {
4024 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask.RawValue);
4025 continue;
4026 }
4027
4028 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4029 {
4030 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_spell_custom` table, ignoring.", classMask);
4031 continue;
4032 }
4033
4034 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4035 {
4036 if (raceMask.IsEmpty() || raceMask.HasRace(raceIndex))
4037 {
4038 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4039 {
4040 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4041 {
4042 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(raceIndex), Classes(classIndex) }))
4043 {
4044 playerInfo->get()->customSpells.push_back(spellId);
4045 ++count;
4046 }
4047 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
4048 // Either split the masks per class, or per race, which kind of kills the point yet.
4049 // else if (raceMask != 0 && classMask != 0)
4050 // TC_LOG_ERROR("sql.sql", "Racemask/classmask ({}/{}) combination was found containing an invalid race/class combination ({}/{}) in `{}` (Spell {}), ignoring.", raceMask, classMask, raceIndex, classIndex, tableName, spellId);
4051 }
4052 }
4053 }
4054 }
4055 }
4056 while (result->NextRow());
4057
4058 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4059 }
4060 }
4061
4062 // Load playercreate cast spell
4063 TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
4064 {
4065 uint32 oldMSTime = getMSTime();
4066
4067 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell, createMode FROM playercreateinfo_cast_spell");
4068
4069 if (!result)
4070 TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
4071 else
4072 {
4073 uint32 count = 0;
4074
4075 do
4076 {
4077 Field* fields = result->Fetch();
4078 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4079 uint32 classMask = fields[1].GetUInt32();
4080 uint32 spellId = fields[2].GetUInt32();
4081 int8 playerCreateMode = fields[3].GetInt8();
4082
4083 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4084 {
4085 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask.RawValue);
4086 continue;
4087 }
4088
4089 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4090 {
4091 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_cast_spell` table, ignoring.", classMask);
4092 continue;
4093 }
4094
4095 if (playerCreateMode < 0 || playerCreateMode >= AsUnderlyingType(PlayerCreateMode::Max))
4096 {
4097 TC_LOG_ERROR("sql.sql", "Uses invalid createMode {} in `playercreateinfo_cast_spell` table, ignoring.", playerCreateMode);
4098 continue;
4099 }
4100
4101 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4102 {
4103 if (raceMask.IsEmpty() || raceMask.HasRace(raceIndex))
4104 {
4105 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4106 {
4107 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4108 {
4109 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(raceIndex), Classes(classIndex) }))
4110 {
4111 playerInfo->get()->castSpells[playerCreateMode].push_back(spellId);
4112 ++count;
4113 }
4114 }
4115 }
4116 }
4117 }
4118 } while (result->NextRow());
4119
4120 TC_LOG_INFO("server.loading", ">> Loaded {} player create cast spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4121 }
4122 }
4123
4124 // Load playercreate actions
4125 TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
4126 {
4127 uint32 oldMSTime = getMSTime();
4128
4129 // 0 1 2 3 4
4130 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
4131
4132 if (!result)
4133 {
4134 TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
4135 }
4136 else
4137 {
4138 uint32 count = 0;
4139
4140 do
4141 {
4142 Field* fields = result->Fetch();
4143
4144 uint32 current_race = fields[0].GetUInt8();
4145 if (current_race >= MAX_RACES)
4146 {
4147 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
4148 continue;
4149 }
4150
4151 uint32 current_class = fields[1].GetUInt8();
4152 if (current_class >= MAX_CLASSES)
4153 {
4154 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_action` table, ignoring.", current_class);
4155 continue;
4156 }
4157
4158 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(current_race), Classes(current_class) }))
4159 playerInfo->get()->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
4160
4161 ++count;
4162 }
4163 while (result->NextRow());
4164
4165 TC_LOG_INFO("server.loading", ">> Loaded {} player create actions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4166 }
4167 }
4168
4169 // Loading levels data (class/race dependent)
4170 TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
4171 {
4172 struct RaceStats
4173 {
4174 std::array<int16, MAX_STATS> StatModifier = { };
4175 };
4176
4177 std::array<RaceStats, MAX_RACES> raceStatModifiers = { };
4178
4179 uint32 oldMSTime = getMSTime();
4180
4181 QueryResult raceStatsResult = WorldDatabase.Query("SELECT race, str, agi, sta, inte FROM player_racestats");
4182
4183 if (!raceStatsResult)
4184 {
4185 TC_LOG_ERROR("server.loading", ">> Loaded 0 race stats definitions. DB table `player_racestats` is empty.");
4186 ABORT();
4187 }
4188
4189 do
4190 {
4191 Field* fields = raceStatsResult->Fetch();
4192
4193 uint32 current_race = fields[0].GetUInt8();
4194 if (current_race >= MAX_RACES)
4195 {
4196 TC_LOG_ERROR("sql.sql", "Wrong race {} in `player_racestats` table, ignoring.", current_race);
4197 continue;
4198 }
4199
4200 for (uint32 i = 0; i < MAX_STATS; ++i)
4201 raceStatModifiers[current_race].StatModifier[i] = fields[i + 1].GetInt16();
4202
4203 } while (raceStatsResult->NextRow());
4204
4205 // 0 1 2 3 4 5
4206 QueryResult result = WorldDatabase.Query("SELECT class, level, str, agi, sta, inte FROM player_classlevelstats");
4207
4208 if (!result)
4209 {
4210 TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_classlevelstats` is empty.");
4211 ABORT();
4212 }
4213
4214 uint32 count = 0;
4215
4216 do
4217 {
4218 Field* fields = result->Fetch();
4219
4220 uint32 current_class = fields[0].GetUInt8();
4221 if (current_class >= MAX_CLASSES)
4222 {
4223 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
4224 continue;
4225 }
4226
4227 uint32 current_level = fields[1].GetUInt8();
4228 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4229 {
4230 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4231 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_classlevelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4232 else
4233 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
4234
4235 continue;
4236 }
4237
4238 for (std::size_t race = 0; race < raceStatModifiers.size(); ++race)
4239 {
4240 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(current_class) }))
4241 {
4242 if (!playerInfo->get()->levelInfo)
4243 playerInfo->get()->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4244
4245 PlayerLevelInfo& levelInfo = playerInfo->get()->levelInfo[current_level - 1];
4246 for (uint8 i = 0; i < MAX_STATS; ++i)
4247 levelInfo.stats[i] = fields[i + 2].GetUInt16() + raceStatModifiers[race].StatModifier[i];
4248 }
4249 }
4250
4251 ++count;
4252 }
4253 while (result->NextRow());
4254
4255 // Fill gaps and check integrity
4256 for (uint8 race = 0; race < MAX_RACES; ++race)
4257 {
4258 // skip non existed races
4259 if (!sChrRacesStore.LookupEntry(race))
4260 continue;
4261
4262 for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
4263 {
4264 // skip non existed classes
4265 if (!sChrClassesStore.LookupEntry(class_))
4266 continue;
4267
4268 auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
4269 if (!playerInfo)
4270 continue;
4271
4272 // skip expansion races if not playing with expansion
4273 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
4274 continue;
4275
4276 // skip expansion classes if not playing with expansion
4278 continue;
4279
4280 // skip expansion races if not playing with expansion
4281 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_CATACLYSM && (race == RACE_GOBLIN || race == RACE_WORGEN))
4282 continue;
4283
4285 continue;
4286
4287 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_LEGION && class_ == CLASS_DEMON_HUNTER)
4288 continue;
4289
4290 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_DRAGONFLIGHT && class_ == CLASS_EVOKER)
4291 continue;
4292
4293 // fatal error if no level 1 data
4294 if (!playerInfo->get()->levelInfo || playerInfo->get()->levelInfo[0].stats[0] == 0)
4295 {
4296 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level 1 does not have stats data!", race, class_);
4297 ABORT();
4298 }
4299
4300 // fill level gaps
4301 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4302 {
4303 if (playerInfo->get()->levelInfo[level].stats[0] == 0)
4304 {
4305 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level {} does not have stats data. Using stats data of level {}.", race, class_, level + 1, level);
4306 playerInfo->get()->levelInfo[level] = playerInfo->get()->levelInfo[level - 1];
4307 }
4308 }
4309 }
4310 }
4311
4312 TC_LOG_INFO("server.loading", ">> Loaded {} level stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4313 }
4314
4315 // Loading xp per level data
4316 TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
4317 {
4318 uint32 oldMSTime = getMSTime();
4319
4320 _playerXPperLevel.resize(sXpGameTable.GetTableRowCount(), 0);
4321
4322 // 0 1
4323 QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
4324
4325 // load the DBC's levels at first...
4326 for (uint32 level = 1; level < sXpGameTable.GetTableRowCount(); ++level)
4327 _playerXPperLevel[level] = sXpGameTable.GetRow(level)->Total;
4328
4329 uint32 count = 0;
4330
4331 // ...overwrite if needed (custom values)
4332 if (result)
4333 {
4334 do
4335 {
4336 Field* fields = result->Fetch();
4337
4338 uint32 current_level = fields[0].GetUInt8();
4339 uint32 current_xp = fields[1].GetUInt32();
4340
4341 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4342 {
4343 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4344 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4345 else
4346 {
4347 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_xp_for_level` table, ignoring.", current_level);
4348 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4349 }
4350 continue;
4351 }
4352 //PlayerXPperLevel
4353 _playerXPperLevel[current_level] = current_xp;
4354 ++count;
4355 } while (result->NextRow());
4356 }
4357
4358 // fill level gaps - only accounting levels > MAX_LEVEL
4359 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4360 {
4361 if (_playerXPperLevel[level] == 0)
4362 {
4363 TC_LOG_ERROR("sql.sql", "Level {} does not have XP for level data. Using data of level [{}] + 12000.", level + 1, level);
4364 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 12000;
4365 }
4366 }
4367
4368 TC_LOG_INFO("server.loading", ">> Loaded {} xp for level definition(s) from database in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4369 }
4370}
4371
4372void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseMana) const
4373{
4374 if (level < 1 || class_ >= MAX_CLASSES)
4375 return;
4376
4377 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4378 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
4379
4380 GtBaseMPEntry const* mp = sBaseMPGameTable.GetRow(level);
4381 if (!mp)
4382 {
4383 TC_LOG_ERROR("misc", "Tried to get non-existant Class-Level combination data for base hp/mp. Class {} Level {}", class_, level);
4384 return;
4385 }
4386
4387 baseMana = uint32(GetGameTableColumnForClass(mp, class_));
4388}
4389
4391{
4392 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
4393 return;
4394
4395 auto const& pInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
4396 if (!pInfo)
4397 return;
4398
4399 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4400 *info = pInfo->get()->levelInfo[level - 1];
4401 else
4402 BuildPlayerLevelInfo(race, class_, level, info);
4403}
4404
4406{
4407 // base data (last known level)
4408 *info = ASSERT_NOTNULL(Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(_class) }))->get()->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
4409
4410 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
4411 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
4412 {
4413 switch (_class)
4414 {
4415 case CLASS_WARRIOR:
4416 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4417 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4418 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4419 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4420 break;
4421 case CLASS_PALADIN:
4422 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
4423 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4424 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
4425 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
4426 break;
4427 case CLASS_HUNTER:
4428 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
4429 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4430 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4431 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
4432 break;
4433 case CLASS_ROGUE:
4434 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
4435 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4436 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
4437 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
4438 break;
4439 case CLASS_PRIEST:
4440 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4441 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4442 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
4443 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
4444 break;
4445 case CLASS_SHAMAN:
4446 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4447 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4448 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
4449 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
4450 break;
4451 case CLASS_MAGE:
4452 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4453 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4454 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4455 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
4456 break;
4457 case CLASS_WARLOCK:
4458 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4459 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4460 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4461 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4462 break;
4463 case CLASS_DRUID:
4464 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
4465 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
4466 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
4467 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
4468 break;
4469 }
4470 }
4471}
4472
4473std::vector<uint32> const* ObjectMgr::GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
4474{
4475 if (std::vector<uint32> const* items = Trinity::Containers::MapGetValuePtr(_creatureQuestItemStore, { creatureEntry, difficulty }))
4476 return items;
4477
4478 // If there is no data for the difficulty, try to get data for the fallback difficulty
4479 if (DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty))
4480 return GetCreatureQuestItemList(creatureEntry, Difficulty(difficultyEntry->FallbackDifficultyID));
4481
4482 return nullptr;
4483}
4484
4485std::vector<int32> const* ObjectMgr::GetCreatureQuestCurrencyList(uint32 creatureId) const
4486{
4488}
4489
4491{
4492 uint32 oldMSTime = getMSTime();
4493
4494 _questTemplates.clear();
4496 _questObjectives.clear();
4497
4498 _exclusiveQuestGroups.clear();
4499
4500 QueryResult result = WorldDatabase.Query("SELECT "
4501 //0 1 2 3 4 5 6 7 8 9
4502 "ID, QuestType, QuestPackageID, ContentTuningID, QuestSortID, QuestInfoID, SuggestedGroupNum, RewardNextQuest, RewardXPDifficulty, RewardXPMultiplier, "
4503 //10 11 12 13 14 15 16
4504 "RewardMoneyDifficulty, RewardMoneyMultiplier, RewardBonusMoney, RewardSpell, RewardHonor, RewardKillHonor, StartItem, "
4505 //17 18 19 20 21 22
4506 "RewardArtifactXPDifficulty, RewardArtifactXPMultiplier, RewardArtifactCategoryID, Flags, FlagsEx, FlagsEx2, "
4507 //23 24 25 26 27 28 29 30
4508 "RewardItem1, RewardAmount1, ItemDrop1, ItemDropQuantity1, RewardItem2, RewardAmount2, ItemDrop2, ItemDropQuantity2, "
4509 //31 32 33 34 35 36 37 38
4510 "RewardItem3, RewardAmount3, ItemDrop3, ItemDropQuantity3, RewardItem4, RewardAmount4, ItemDrop4, ItemDropQuantity4, "
4511 //39 40 41 42 43 44
4512 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemDisplayID1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemDisplayID2, "
4513 //45 46 47 48 49 50
4514 "RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemDisplayID3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemDisplayID4, "
4515 //51 52 53 54 55 56
4516 "RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemDisplayID5, RewardChoiceItemID6, RewardChoiceItemQuantity6, RewardChoiceItemDisplayID6, "
4517 //57 58 59 60 61 62 63 64
4518 "POIContinent, POIx, POIy, POIPriority, RewardTitle, RewardArenaPoints, RewardSkillLineID, RewardNumSkillUps, "
4519 //65 66 67 68
4520 "PortraitGiver, PortraitGiverMount, PortraitGiverModelSceneID, PortraitTurnIn, "
4521 //69 70 71 72 73 74 75 76
4522 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionCapIn1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionCapIn2, "
4523 //77 78 79 80 81 82 83 84
4524 "RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionCapIn3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionCapIn4, "
4525 //85 86 87 88 89
4526 "RewardFactionID5, RewardFactionValue5, RewardFactionOverride5, RewardFactionCapIn5, RewardFactionFlags, "
4527 //90 91 92 93 94 95 96 97
4528 "RewardCurrencyID1, RewardCurrencyQty1, RewardCurrencyID2, RewardCurrencyQty2, RewardCurrencyID3, RewardCurrencyQty3, RewardCurrencyID4, RewardCurrencyQty4, "
4529 //98 99 100 101 102 103 104 105 106
4530 "AcceptedSoundKitID, CompleteSoundKitID, AreaGroupID, TimeAllowed, AllowableRaces, TreasurePickerID, Expansion, ManagedWorldStateID, QuestSessionBonus, "
4531 //107 108 109 110 111 112 113 114 115
4532 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog "
4533 "FROM quest_template");
4534 if (!result)
4535 {
4536 TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
4537 return;
4538 }
4539
4540 _questTemplates.reserve(result->GetRowCount());
4541
4542 // create multimap previous quest for each existed quest
4543 // some quests can have many previous maps set by NextQuestId in previous quest
4544 // for example set of race quests can lead to single not race specific quest
4545 do
4546 {
4547 Field* fields = result->Fetch();
4548
4549 uint32 questId = fields[0].GetUInt32();
4550 auto itr = _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(new Quest(fields))).first;
4551 itr->second->_weakRef = itr->second;
4552 if (itr->second->IsAutoPush())
4553 _questTemplatesAutoPush.push_back(itr->second.get());
4554 } while (result->NextRow());
4555
4556 struct QuestLoaderHelper
4557 {
4558 typedef void(Quest::* QuestLoaderFunction)(Field* fields);
4559
4560 char const* QueryFields;
4561 char const* TableName;
4562 char const* QueryExtra;
4563 char const* TableDesc;
4564 QuestLoaderFunction LoaderFunction;
4565 };
4566
4567 // QuestID needs to be fields[0]
4568 QuestLoaderHelper const QuestLoaderHelpers[] =
4569 {
4570 // 0 1 2 3 4 5 6
4571 { "QuestID, Type1, Type2, Type3, Type4, Type5, Type6", "quest_reward_choice_items", "", "reward choice items", &Quest::LoadRewardChoiceItems },
4572
4573 // 0 1 2 3
4574 { "QuestID, SpellID, PlayerConditionID, Type", "quest_reward_display_spell", "ORDER BY QuestID ASC, Idx ASC", "reward display spells", &Quest::LoadRewardDisplaySpell },
4575
4576 // 0 1 2 3 4 5 6 7 8
4577 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4", "quest_details", "", "details", &Quest::LoadQuestDetails },
4578
4579 // 0 1 2 3 4 5
4580 { "ID, EmoteOnComplete, EmoteOnIncomplete, EmoteOnCompleteDelay, EmoteOnIncompleteDelay, CompletionText", "quest_request_items", "", "request items", &Quest::LoadQuestRequestItems },
4581
4582 // 0 1 2 3 4 5 6 7 8 9
4583 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "", "reward emotes", &Quest::LoadQuestOfferReward },
4584
4585 // 0 1 2 3 4 5 6 7 8 9
4586 { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
4587 // 10 11 12 13 14 15 16 17
4588 " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags,"
4589 // 18
4590 " ScriptName", "quest_template_addon", "", "template addons", &Quest::LoadQuestTemplateAddon },
4591
4592 // 0 1
4593 { "QuestId, RewardMailSenderEntry", "quest_mail_sender", "", "mail sender entries", &Quest::LoadQuestMailSender },
4594
4595 // 0 1 2 3 4 5 6 7 8 9
4596 { "qo.QuestID, qo.ID, qo.Type, qo.StorageIndex, qo.ObjectID, qo.Amount, qo.Flags, qo.Flags2, qo.ProgressBarWeight, qo.Description, "
4597 // 10 11 12 13 14
4598 "qoce.GameEventID, qoce.SpellID, qoce.ConversationID, qoce.UpdatePhaseShift, qoce.UpdateZoneAuras", "quest_objectives qo", "LEFT JOIN quest_objectives_completion_effect qoce ON qo.ID = qoce.ObjectiveID ORDER BY `Order` ASC, StorageIndex ASC", "quest objectives", &Quest::LoadQuestObjective },
4599
4600 // 0 1 2 3 4
4601 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_description_conditional", "ORDER BY OrderIndex", "conditional details", &Quest::LoadConditionalConditionalQuestDescription },
4602
4603 // 0 1 2 3 4
4604 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_request_items_conditional", "ORDER BY OrderIndex", "conditional request items", &Quest::LoadConditionalConditionalRequestItemsText },
4605
4606 // 0 1 2 3 4
4607 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_offer_reward_conditional", "ORDER BY OrderIndex", "conditional reward", &Quest::LoadConditionalConditionalOfferRewardText },
4608
4609 // 0 1 2 3 4
4610 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_completion_log_conditional", "ORDER BY OrderIndex", "conditional completion log", &Quest::LoadConditionalConditionalQuestCompletionLog }
4611 };
4612
4613 for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
4614 {
4615 result = WorldDatabase.PQuery("SELECT {} FROM {} {}", loader.QueryFields, loader.TableName, loader.QueryExtra);
4616
4617 if (!result)
4618 TC_LOG_INFO("server.loading", ">> Loaded 0 quest {}. DB table `{}` is empty.", loader.TableDesc, loader.TableName);
4619 else
4620 {
4621 do
4622 {
4623 Field* fields = result->Fetch();
4624 uint32 questId = fields[0].GetUInt32();
4625
4626 auto itr = _questTemplates.find(questId);
4627 if (itr != _questTemplates.end())
4628 (itr->second.get()->*loader.LoaderFunction)(fields);
4629 else
4630 TC_LOG_ERROR("server.loading", "Table `{}` has data for quest {} but such quest does not exist", loader.TableName, questId);
4631 } while (result->NextRow());
4632 }
4633 }
4634
4635 // Load `quest_visual_effect` join table with quest_objectives because visual effects are based on objective ID (core stores objectives by their index in quest)
4636 // 0 1 2 3 4
4637 result = WorldDatabase.Query("SELECT v.ID, o.ID, o.QuestID, v.Index, v.VisualEffect FROM quest_visual_effect AS v LEFT JOIN quest_objectives AS o ON v.ID = o.ID ORDER BY v.Index DESC");
4638
4639 if (!result)
4640 {
4641 TC_LOG_ERROR("server.loading", ">> Loaded 0 quest visual effects. DB table `quest_visual_effect` is empty.");
4642 }
4643 else
4644 {
4645 do
4646 {
4647 Field* fields = result->Fetch();
4648 uint32 vID = fields[0].GetUInt32();
4649 uint32 oID = fields[1].GetUInt32();
4650
4651 if (!vID)
4652 {
4653 TC_LOG_ERROR("server.loading", "Table `quest_visual_effect` has visual effect for null objective id");
4654 continue;
4655 }
4656
4657 // objID will be null if match for table join is not found
4658 if (vID != oID)
4659 {
4660 TC_LOG_ERROR("server.loading", "Table `quest_visual_effect` has visual effect for objective {} but such objective does not exist.", vID);
4661 continue;
4662 }
4663
4664 uint32 questId = fields[2].GetUInt32();
4665
4666 // Do not throw error here because error for non existing quest is thrown while loading quest objectives. we do not need duplication
4667 auto itr = _questTemplates.find(questId);
4668 if (itr != _questTemplates.end())
4669 itr->second->LoadQuestObjectiveVisualEffect(fields);
4670 } while (result->NextRow());
4671 }
4672
4673 std::map<uint32, uint32> usedMailTemplates;
4674
4675 // Post processing
4676 for (auto& questPair : _questTemplates)
4677 {
4678 // skip post-loading checks for disabled quests
4679 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
4680 continue;
4681
4682 Quest* qinfo = questPair.second.get();
4683
4684 // additional quest integrity checks (GO, creature_template and items must be loaded already)
4685
4687 TC_LOG_ERROR("sql.sql", "Quest {} has `Method` = {}, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestType());
4688
4690 {
4691 TC_LOG_ERROR("sql.sql", "Quest {} has `SpecialFlags` = {} > max allowed value. Correct `SpecialFlags` to value <= {}",
4694 }
4695
4696 if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
4697 {
4698 TC_LOG_ERROR("sql.sql", "Weekly Quest {} is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
4699 qinfo->_flags &= ~QUEST_FLAGS_DAILY;
4700 }
4701
4702 if (qinfo->_flags & QUEST_FLAGS_DAILY)
4703 {
4705 {
4706 TC_LOG_DEBUG("sql.sql", "Daily Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4708 }
4709 }
4710
4711 if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
4712 {
4714 {
4715 TC_LOG_DEBUG("sql.sql", "Weekly Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4717 }
4718 }
4719
4721 {
4723 {
4724 TC_LOG_DEBUG("sql.sql", "Monthly quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4726 }
4727 }
4728
4730 {
4731 // at auto-reward can be rewarded only RewardChoiceItemId[0]
4732 for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
4733 {
4734 if (uint32 id = qinfo->RewardChoiceItemId[j])
4735 {
4736 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item from `RewardChoiceItemId{}` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
4737 qinfo->GetQuestId(), j + 1, id, j + 1);
4738 // no changes, quest ignore this data
4739 }
4740 }
4741 }
4742
4743 if (qinfo->_contentTuningID && !sContentTuningStore.LookupEntry(qinfo->_contentTuningID))
4744 {
4745 TC_LOG_ERROR("sql.sql", "Quest {} has `ContentTuningID` = {} but content tuning with this id does not exist.",
4746 qinfo->GetQuestId(), qinfo->_contentTuningID);
4747 }
4748
4749 // client quest log visual (area case)
4750 if (qinfo->_questSortID > 0)
4751 {
4752 if (!sAreaTableStore.LookupEntry(qinfo->_questSortID))
4753 {
4754 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (zone case) but zone with this id does not exist.",
4755 qinfo->GetQuestId(), qinfo->_questSortID);
4756 // no changes, quest not dependent from this value but can have problems at client
4757 }
4758 }
4759 // client quest log visual (sort case)
4760 if (qinfo->_questSortID < 0)
4761 {
4762 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_questSortID));
4763 if (!qSort)
4764 {
4765 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (sort case) but quest sort with this id does not exist.",
4766 qinfo->GetQuestId(), qinfo->_questSortID);
4767 // 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)
4768 }
4769 //check for proper RequiredSkillId value (skill case)
4770 if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->_questSortID)))
4771 {
4772 if (qinfo->_requiredSkillId != skill_id)
4773 {
4774 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} but `RequiredSkillId` does not have a corresponding value ({}).",
4775 qinfo->GetQuestId(), qinfo->_questSortID, skill_id);
4776 //override, and force proper value here?
4777 }
4778 }
4779 }
4780
4781 // AllowableClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
4782 if (qinfo->_allowableClasses)
4783 {
4785 {
4786 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable classes in `AllowableClasses` ({}), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_allowableClasses);
4787 qinfo->_allowableClasses = 0;
4788 }
4789 }
4790 // AllowableRaces, can be -1/RACEMASK_ALL_PLAYABLE to allow any race
4791 if (qinfo->_allowableRaces.RawValue != uint64(-1))
4792 {
4793 if (!qinfo->_allowableRaces.IsEmpty() && (qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE).IsEmpty())
4794 {
4795 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` ({}), value set to -1 (all races).", qinfo->GetQuestId(), qinfo->_allowableRaces.RawValue);
4796 qinfo->_allowableRaces.RawValue = uint64(-1);
4797 }
4798 }
4799 // RequiredSkillId, can be 0
4800 if (qinfo->_requiredSkillId)
4801 {
4802 if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
4803 {
4804 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillId` = {} but this skill does not exist",
4805 qinfo->GetQuestId(), qinfo->_requiredSkillId);
4806 }
4807 }
4808
4809 if (qinfo->_requiredSkillPoints)
4810 {
4811 if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
4812 {
4813 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillPoints` = {} but max possible skill is {}, quest can't be done.",
4814 qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
4815 // no changes, quest can't be done for this requirement
4816 }
4817 }
4818 // else Skill quests can have 0 skill level, this is ok
4819
4820 if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
4821 {
4822 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4824 // no changes, quest can't be done for this requirement
4825 }
4826
4827 if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
4828 {
4829 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4831 // no changes, quest can't be done for this requirement
4832 }
4833
4835 {
4836 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but max reputation is {}, quest can't be done.",
4838 // no changes, quest can't be done for this requirement
4839 }
4840
4842 {
4843 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} and `RequiredMinRepValue` = {}, quest can't be done.",
4844 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
4845 // no changes, quest can't be done for this requirement
4846 }
4847
4848 if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
4849 {
4850 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but `RequiredMinRepFaction` is 0, value has no effect",
4851 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
4852 // warning
4853 }
4854
4855 if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
4856 {
4857 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} but `RequiredMaxRepFaction` is 0, value has no effect",
4858 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
4859 // warning
4860 }
4861
4862 if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
4863 {
4864 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardTitleId` = {} but CharTitle Id {} does not exist, quest can't be rewarded with title.",
4865 qinfo->GetQuestId(), qinfo->_rewardTitleId, qinfo->_rewardTitleId);
4866 qinfo->_rewardTitleId = 0;
4867 // quest can't reward this title
4868 }
4869
4870 if (qinfo->_sourceItemId)
4871 {
4872 if (!GetItemTemplate(qinfo->_sourceItemId))
4873 {
4874 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = {} but item with entry {} does not exist, quest can't be done.",
4875 qinfo->GetQuestId(), qinfo->_sourceItemId, qinfo->_sourceItemId);
4876 qinfo->_sourceItemId = 0; // quest can't be done for this requirement
4877 }
4878 else if (qinfo->_sourceItemIdCount == 0)
4879 {
4880 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but `ProvidedItemCount` = 0, set to 1 but need fix in DB.",
4881 qinfo->GetQuestId(), qinfo->_sourceItemId);
4882 qinfo->_sourceItemIdCount = 1; // update to 1 for allow quest work for backward compatibility with DB
4883 }
4884 }
4885 else if (qinfo->_sourceItemIdCount > 0)
4886 {
4887 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = 0 but `SourceItemIdCount` = {}, useless value.",
4888 qinfo->GetQuestId(), qinfo->_sourceItemIdCount);
4889 qinfo->_sourceItemIdCount = 0; // no quest work changes in fact
4890 }
4891
4892 if (qinfo->_sourceSpellID)
4893 {
4894 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellID, DIFFICULTY_NONE);
4895 if (!spellInfo)
4896 {
4897 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} doesn't exist, quest can't be done.",
4898 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4899 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4900 }
4901 else if (!SpellMgr::IsSpellValid(spellInfo))
4902 {
4903 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} is broken, quest can't be done.",
4904 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4905 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4906 }
4907 }
4908
4909 for (QuestObjective const& obj : qinfo->GetObjectives())
4910 {
4911 // Store objective for lookup by id
4912 _questObjectives[obj.ID] = &obj;
4913
4914 // Check storage index for objectives which store data
4915 if (obj.StorageIndex < 0)
4916 {
4917 switch (obj.Type)
4918 {
4927 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid StorageIndex = {} for objective type {}", qinfo->GetQuestId(), obj.ID, obj.StorageIndex, obj.Type);
4928 break;
4929 default:
4930 break;
4931 }
4932 }
4933
4934 switch (obj.Type)
4935 {
4937 if (!GetItemTemplate(obj.ObjectID))
4938 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing item entry {}, quest can't be done.",
4939 qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4940 break;
4942 if (!GetCreatureTemplate(obj.ObjectID))
4943 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4944 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4945 break;
4947 if (!GetGameObjectTemplate(obj.ObjectID))
4948 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing gameobject entry {}, quest can't be done.",
4949 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4950 break;
4952 if (!GetCreatureTemplate(obj.ObjectID))
4953 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4954 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4955 break;
4959 if (!sFactionStore.LookupEntry(obj.ObjectID))
4960 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing faction id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4961 break;
4963 if (obj.Amount <= 0)
4964 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid player kills count {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
4965 break;
4969 if (!sCurrencyTypesStore.LookupEntry(obj.ObjectID))
4970 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing currency {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4971 if (obj.Amount <= 0)
4972 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid currency amount {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
4973 break;
4975 if (!sSpellMgr->GetSpellInfo(obj.ObjectID, DIFFICULTY_NONE))
4976 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing spell id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4977 break;
4979 if (obj.ObjectID && !GetCreatureTemplate(obj.ObjectID))
4980 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4981 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4982 break;
4984 if (!sBattlePetSpeciesStore.LookupEntry(obj.ObjectID))
4985 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing battlepet species id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4986 break;
4988 if (!sCriteriaTreeStore.LookupEntry(obj.ObjectID))
4989 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing criteria tree id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4990 break;
4992 if (!sAreaTriggerStore.LookupEntry(uint32(obj.ObjectID)) && obj.ObjectID != -1)
4993 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing AreaTrigger.db2 id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4994 break;
4997 if (!sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), false }) && !sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), true }))
4998 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing areatrigger id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4999 break;
5003 break;
5004 default:
5005 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has unhandled type {}", qinfo->GetQuestId(), obj.ID, obj.Type);
5006 break;
5007 }
5008
5009 if (obj.Flags & QUEST_OBJECTIVE_FLAG_SEQUENCED)
5011 }
5012
5013 for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
5014 {
5015 uint32 id = qinfo->ItemDrop[j];
5016 if (id)
5017 {
5018 if (!GetItemTemplate(id))
5019 {
5020 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
5021 qinfo->GetQuestId(), j+1, id, id);
5022 // no changes, quest can't be done for this requirement
5023 }
5024 }
5025 else
5026 {
5027 if (qinfo->ItemDropQuantity[j]>0)
5028 {
5029 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = 0 but `ItemDropQuantity{}` = {}.",
5030 qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
5031 // no changes, quest ignore this data
5032 }
5033 }
5034 }
5035
5036 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
5037 {
5038 if (uint32 id = qinfo->RewardChoiceItemId[j])
5039 {
5040 switch (qinfo->RewardChoiceItemType[j])
5041 {
5042 case LootItemType::Item:
5043 if (!GetItemTemplate(id))
5044 {
5045 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5046 qinfo->GetQuestId(), j + 1, id, id);
5047 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5048 }
5049 break;
5051 if (!sCurrencyTypesStore.HasRecord(id))
5052 {
5053 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but currency with id {} does not exist, quest will not reward this currency.",
5054 qinfo->GetQuestId(), j + 1, id, id);
5055 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5056 }
5057 break;
5058 default:
5059 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemType{}` = {} but it is not a valid item type, reward removed.",
5060 qinfo->GetQuestId(), j + 1, uint32(qinfo->RewardChoiceItemType[j]));
5061 qinfo->RewardChoiceItemId[j] = 0;
5062 break;
5063 }
5064
5065 if (!qinfo->RewardChoiceItemCount[j])
5066 {
5067 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but `RewardChoiceItemCount{}` = 0.",
5068 qinfo->GetQuestId(), j + 1, id, j + 1);
5069 }
5070 }
5071 else if (qinfo->RewardChoiceItemCount[j] > 0)
5072 {
5073 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = 0 but `RewardChoiceItemCount{}` = {}.",
5074 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardChoiceItemCount[j]);
5075 // no changes, quest ignore this data
5076 }
5077 }
5078
5079 for (uint8 j = 0; j < QUEST_REWARD_ITEM_COUNT; ++j)
5080 {
5081 uint32 id = qinfo->RewardItemId[j];
5082 if (id)
5083 {
5084 if (!GetItemTemplate(id))
5085 {
5086 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5087 qinfo->GetQuestId(), j+1, id, id);
5088 qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
5089 }
5090
5091 if (!qinfo->RewardItemCount[j])
5092 {
5093 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but `RewardItemCount{}` = 0, quest will not reward this item.",
5094 qinfo->GetQuestId(), j+1, id, j+1);
5095 // no changes
5096 }
5097 }
5098 else if (qinfo->RewardItemCount[j]>0)
5099 {
5100 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = 0 but `RewardItemCount{}` = {}.",
5101 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemCount[j]);
5102 // no changes, quest ignore this data
5103 }
5104 }
5105
5106 for (uint8 j = 0; j < QUEST_REWARD_REPUTATIONS_COUNT; ++j)
5107 {
5108 if (qinfo->RewardFactionId[j])
5109 {
5110 if (std::abs(qinfo->RewardFactionValue[j]) > 9)
5111 {
5112 TC_LOG_ERROR("sql.sql", "Quest {} has RewardFactionValueId{} = {}. That is outside the range of valid values (-9 to 9).", qinfo->GetQuestId(), j+1, qinfo->RewardFactionValue[j]);
5113 }
5114 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
5115 {
5116 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = {} but raw faction (faction.dbc) {} does not exist, quest will not reward reputation for this faction.", qinfo->GetQuestId(), j+1, qinfo->RewardFactionId[j], qinfo->RewardFactionId[j]);
5117 qinfo->RewardFactionId[j] = 0; // quest will not reward this
5118 }
5119 }
5120
5121 else if (qinfo->RewardFactionOverride[j] != 0)
5122 {
5123 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = 0 but `RewardFactionValueIdOverride{}` = {}.",
5124 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionOverride[j]);
5125 // no changes, quest ignore this data
5126 }
5127 }
5128
5129 if (qinfo->_rewardSpell > 0)
5130 {
5131 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell, DIFFICULTY_NONE);
5132
5133 if (!spellInfo)
5134 {
5135 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} does not exist, quest will not have a spell reward.",
5136 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5137 qinfo->_rewardSpell = 0; // no spell will be cast on player
5138 }
5139
5140 else if (!SpellMgr::IsSpellValid(spellInfo))
5141 {
5142 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} is broken, quest will not have a spell reward.",
5143 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5144 qinfo->_rewardSpell = 0; // no spell will be cast on player
5145 }
5146 }
5147
5148 if (qinfo->_rewardMailTemplateId)
5149 {
5150 if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
5151 {
5152 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} does not exist, quest will not have a mail reward.",
5153 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
5154 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5155 qinfo->_rewardMailDelay = 0; // no mail will send to player
5156 qinfo->_rewardMailSenderEntry = 0;
5157 }
5158 else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
5159 {
5160 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
5161 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} already used for quest {}, quest will not have a mail reward.",
5162 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
5163 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5164 qinfo->_rewardMailDelay = 0; // no mail will send to player
5165 qinfo->_rewardMailSenderEntry = 0;
5166 }
5167 else
5168 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
5169 }
5170
5171 if (uint32 nextQuestInChain = qinfo->_nextQuestInChain)
5172 {
5173 if (!_questTemplates.count(nextQuestInChain))
5174 {
5175 TC_LOG_ERROR("sql.sql", "Quest {} has `NextQuestInChain` = {} but quest {} does not exist, quest chain will not work.",
5176 qinfo->GetQuestId(), qinfo->_nextQuestInChain, qinfo->_nextQuestInChain);
5177 qinfo->_nextQuestInChain = 0;
5178 }
5179 }
5180
5181 for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
5182 {
5183 if (qinfo->RewardCurrencyId[j])
5184 {
5185 if (qinfo->RewardCurrencyCount[j] == 0)
5186 {
5187 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but `RewardCurrencyCount{}` = 0, quest can't be done.",
5188 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], j+1);
5189 // no changes, quest can't be done for this requirement
5190 }
5191
5192 if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j]))
5193 {
5194 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but currency with entry {} does not exist, quest can't be done.",
5195 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]);
5196 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5197 }
5198 }
5199 else if (qinfo->RewardCurrencyCount[j] > 0)
5200 {
5201 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = 0 but `RewardCurrencyCount{}` = {}, quest can't be done.",
5202 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardCurrencyCount[j]);
5203 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5204 }
5205 }
5206
5207 if (qinfo->_soundAccept)
5208 {
5209 if (!sSoundKitStore.LookupEntry(qinfo->_soundAccept))
5210 {
5211 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundAccept` = {} but sound {} does not exist, set to 0.",
5212 qinfo->GetQuestId(), qinfo->_soundAccept, qinfo->_soundAccept);
5213 qinfo->_soundAccept = 0; // no sound will be played
5214 }
5215 }
5216
5217 if (qinfo->_soundTurnIn)
5218 {
5219 if (!sSoundKitStore.LookupEntry(qinfo->_soundTurnIn))
5220 {
5221 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundTurnIn` = {} but sound {} does not exist, set to 0.",
5222 qinfo->GetQuestId(), qinfo->_soundTurnIn, qinfo->_soundTurnIn);
5223 qinfo->_soundTurnIn = 0; // no sound will be played
5224 }
5225 }
5226
5227 if (qinfo->_rewardSkillId)
5228 {
5229 if (!sSkillLineStore.LookupEntry(qinfo->_rewardSkillId))
5230 {
5231 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but this skill does not exist",
5232 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5233 }
5234 if (!qinfo->_rewardSkillPoints)
5235 {
5236 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but `RewardSkillPoints` is 0",
5237 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5238 }
5239 }
5240
5241 if (qinfo->_rewardSkillPoints)
5242 {
5243 if (qinfo->_rewardSkillPoints > sWorld->GetConfigMaxSkillValue())
5244 {
5245 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but max possible skill is {}, quest can't be done.",
5246 qinfo->GetQuestId(), qinfo->_rewardSkillPoints, sWorld->GetConfigMaxSkillValue());
5247 // no changes, quest can't be done for this requirement
5248 }
5249 if (!qinfo->_rewardSkillId)
5250 {
5251 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but `RewardSkillId` is 0",
5252 qinfo->GetQuestId(), qinfo->_rewardSkillPoints);
5253 }
5254 }
5255
5256 // fill additional data stores
5257 if (uint32 prevQuestId = std::abs(qinfo->_prevQuestID))
5258 {
5259 auto prevQuestItr = _questTemplates.find(prevQuestId);
5260 if (prevQuestItr == _questTemplates.end())
5261 TC_LOG_ERROR("sql.sql", "Quest {} has PrevQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
5262 else if (prevQuestItr->second->_breadcrumbForQuestId)
5263 TC_LOG_ERROR("sql.sql", "Quest {} should not be unlocked by breadcrumb quest {}", qinfo->_id, prevQuestId);
5264 else if (qinfo->_prevQuestID > 0)
5265 qinfo->DependentPreviousQuests.push_back(prevQuestId);
5266 }
5267
5268 if (uint32 nextQuestId = qinfo->_nextQuestID)
5269 {
5270 auto nextQuestItr = _questTemplates.find(nextQuestId);
5271 if (nextQuestItr == _questTemplates.end())
5272 TC_LOG_ERROR("sql.sql", "Quest {} has NextQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestID);
5273 else
5274 nextQuestItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId());
5275 }
5276
5277 if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
5278 {
5279 if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
5280 {
5281 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb for quest {}, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
5282 qinfo->_breadcrumbForQuestId = 0;
5283 }
5284 if (qinfo->_nextQuestID)
5285 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb, should not unlock quest {}", qinfo->_id, qinfo->_nextQuestID);
5286 }
5287
5288 if (qinfo->_exclusiveGroup)
5289 _exclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->_exclusiveGroup, qinfo->GetQuestId()));
5290 }
5291
5292 // Disallow any breadcrumb loops and inform quests of their breadcrumbs
5293 for (auto& questPair : _questTemplates)
5294 {
5295 // skip post-loading checks for disabled quests
5296 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
5297 continue;
5298
5299 Quest* qinfo = questPair.second.get();
5300 uint32 qid = qinfo->GetQuestId();
5301 uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
5302 std::set<uint32> questSet;
5303
5304 while(breadcrumbForQuestId)
5305 {
5306 //a previously visited quest was found as a breadcrumb quest
5307 //breadcrumb loop found!
5308 if (!questSet.insert(qinfo->_id).second)
5309 {
5310 TC_LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} are in a loop", qid, breadcrumbForQuestId);
5311 qinfo->_breadcrumbForQuestId = 0;
5312 break;
5313 }
5314
5315 qinfo = const_cast<Quest*>(GetQuestTemplate(breadcrumbForQuestId));
5316
5317 //every quest has a list of every breadcrumb towards it
5318 qinfo->DependentBreadcrumbQuests.push_back(qid);
5319
5320 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
5321 }
5322 }
5323
5324 // don't check spells with SPELL_EFFECT_QUEST_COMPLETE, a lot of invalid db2 data
5325
5326 // Make all paragon reward quests repeatable
5327 for (ParagonReputationEntry const* paragonReputation : sParagonReputationStore)
5328 if (Quest const* quest = GetQuestTemplate(paragonReputation->QuestID))
5330
5331 TC_LOG_INFO("server.loading", ">> Loaded {} quests definitions in {} ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
5332}
5333
5335{
5336 TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
5338 TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
5340 TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
5342 TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
5344}
5345
5347{
5348 uint32 oldMSTime = getMSTime();
5349
5350 _questTemplateLocaleStore.clear(); // need for reload case
5351 // 0 1
5352 QueryResult result = WorldDatabase.Query("SELECT Id, locale, "
5353 // 2 3 4 5 6 7 8 9 10
5354 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog"
5355 " FROM quest_template_locale");
5356 if (!result)
5357 return;
5358
5359 do
5360 {
5361 Field* fields = result->Fetch();
5362
5363 uint32 id = fields[0].GetUInt32();
5364 std::string_view localeName = fields[1].GetStringView();
5365
5366 LocaleConstant locale = GetLocaleByName(localeName);
5367 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5368 continue;
5369
5371 AddLocaleString(fields[2].GetStringView(), locale, data.LogTitle);
5372 AddLocaleString(fields[3].GetStringView(), locale, data.LogDescription);
5373 AddLocaleString(fields[4].GetStringView(), locale, data.QuestDescription);
5374 AddLocaleString(fields[5].GetStringView(), locale, data.AreaDescription);
5375 AddLocaleString(fields[6].GetStringView(), locale, data.PortraitGiverText);
5376 AddLocaleString(fields[7].GetStringView(), locale, data.PortraitGiverName);
5377 AddLocaleString(fields[8].GetStringView(), locale, data.PortraitTurnInText);
5378 AddLocaleString(fields[9].GetStringView(), locale, data.PortraitTurnInName);
5379 AddLocaleString(fields[10].GetStringView(), locale, data.QuestCompletionLog);
5380 } while (result->NextRow());
5381
5382 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Template locale strings in {} ms", _questTemplateLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5383}
5384
5386{
5387 uint32 oldMSTime = getMSTime();
5388
5389 _questObjectivesLocaleStore.clear(); // need for reload case
5390 // 0 1 2
5391 QueryResult result = WorldDatabase.Query("SELECT Id, locale, Description FROM quest_objectives_locale");
5392 if (!result)
5393 return;
5394
5395 do
5396 {
5397 Field* fields = result->Fetch();
5398
5399 uint32 id = fields[0].GetUInt32();
5400 std::string_view localeName = fields[1].GetStringView();
5401
5402 LocaleConstant locale = GetLocaleByName(localeName);
5403 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5404 continue;
5405
5407 AddLocaleString(fields[2].GetStringView(), locale, data.Description);
5408 }
5409 while (result->NextRow());
5410
5411 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Objectives locale strings in {} ms", _questObjectivesLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5412}
5413
5415{
5416 uint32 oldMSTime = getMSTime();
5417
5418 for (std::size_t i = 0; i < _questGreetingLocaleStore.size(); ++i)
5419 _questGreetingLocaleStore[i].clear();
5420
5421 // 0 1 2 3
5422 QueryResult result = WorldDatabase.Query("SELECT Id, type, locale, Greeting FROM quest_greeting_locale");
5423 if (!result)
5424 return;
5425
5426 uint32 count = 0;
5427 do
5428 {
5429 Field* fields = result->Fetch();
5430
5431 uint32 id = fields[0].GetUInt32();
5432 uint8 type = fields[1].GetUInt8();
5433 switch (type)
5434 {
5435 case 0: // Creature
5436 if (!GetCreatureTemplate(id))
5437 {
5438 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id);
5439 continue;
5440 }
5441 break;
5442 case 1: // GameObject
5443 if (!GetGameObjectTemplate(id))
5444 {
5445 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id);
5446 continue;
5447 }
5448 break;
5449 default:
5450 continue;
5451 }
5452
5453 std::string_view localeName = fields[2].GetStringView();
5454
5455 LocaleConstant locale = GetLocaleByName(localeName);
5456 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5457 continue;
5458
5460 AddLocaleString(fields[3].GetStringView(), locale, data.Greeting);
5461 ++count;
5462 }
5463 while (result->NextRow());
5464
5465 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Greeting locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5466}
5467
5469{
5470 uint32 oldMSTime = getMSTime();
5471
5472 _questOfferRewardLocaleStore.clear(); // need for reload case
5473 // 0 1 2
5474 QueryResult result = WorldDatabase.Query("SELECT Id, locale, RewardText FROM quest_offer_reward_locale");
5475 if (!result)
5476 return;
5477
5478 do
5479 {
5480 Field* fields = result->Fetch();
5481
5482 uint32 id = fields[0].GetUInt32();
5483 std::string_view localeName = fields[1].GetStringView();
5484
5485 LocaleConstant locale = GetLocaleByName(localeName);
5486 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5487 continue;
5488
5490 AddLocaleString(fields[2].GetStringView(), locale, data.RewardText);
5491 } while (result->NextRow());
5492
5493 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Offer Reward locale strings in {} ms", _questOfferRewardLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5494}
5495
5497{
5498 uint32 oldMSTime = getMSTime();
5499
5500 _questRequestItemsLocaleStore.clear(); // need for reload case
5501 // 0 1 2
5502 QueryResult result = WorldDatabase.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale");
5503 if (!result)
5504 return;
5505
5506 do
5507 {
5508 Field* fields = result->Fetch();
5509
5510 uint32 id = fields[0].GetUInt32();
5511 std::string_view localeName = fields[1].GetStringView();
5512
5513 LocaleConstant locale = GetLocaleByName(localeName);
5514 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5515 continue;
5516
5518 AddLocaleString(fields[2].GetStringView(), locale, data.CompletionText);
5519 } while (result->NextRow());
5520
5521 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Request Items locale strings in {} ms", _questRequestItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5522}
5523
5525{
5526 uint32 oldMSTime = getMSTime();
5527
5528 ScriptMapMap* scripts = GetScriptsMapByType(type);
5529 if (!scripts)
5530 return;
5531
5532 std::string tableName = GetScriptsTableNameByType(type);
5533 if (tableName.empty())
5534 return;
5535
5536 if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
5537 return;
5538
5539 TC_LOG_INFO("server.loading", "Loading {}...", tableName);
5540
5541 scripts->clear(); // need for reload support
5542
5543 bool isSpellScriptTable = (type == SCRIPTS_SPELL);
5544 // 0 1 2 3 4 5 6 7 8 9
5545 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o{} FROM {}", isSpellScriptTable ? ", effIndex" : "", tableName);
5546
5547 if (!result)
5548 {
5549 TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `{}` is empty!", tableName);
5550 return;
5551 }
5552
5553 uint32 count = 0;
5554
5555 do
5556 {
5557 Field* fields = result->Fetch();
5558 ScriptInfo tmp;
5559 tmp.type = type;
5560 tmp.id = fields[0].GetUInt32();
5561 if (isSpellScriptTable)
5562 tmp.id |= fields[10].GetUInt8() << 24;
5563 tmp.delay = fields[1].GetUInt32();
5564 tmp.command = ScriptCommands(fields[2].GetUInt32());
5565 tmp.Raw.nData[0] = fields[3].GetUInt32();
5566 tmp.Raw.nData[1] = fields[4].GetUInt32();
5567 tmp.Raw.nData[2] = fields[5].GetInt32();
5568 tmp.Raw.fData[0] = fields[6].GetFloat();
5569 tmp.Raw.fData[1] = fields[7].GetFloat();
5570 tmp.Raw.fData[2] = fields[8].GetFloat();
5571 tmp.Raw.fData[3] = fields[9].GetFloat();
5572
5573 // generic command args check
5574 switch (tmp.command)
5575 {
5577 {
5579 {
5580 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk type (datalong = {}) in SCRIPT_COMMAND_TALK for script id {}",
5581 tableName, tmp.Talk.ChatType, tmp.id);
5582 continue;
5583 }
5584 if (!sBroadcastTextStore.LookupEntry(uint32(tmp.Talk.TextID)))
5585 {
5586 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk text id (dataint = {}) in SCRIPT_COMMAND_TALK for script id {}",
5587 tableName, tmp.Talk.TextID, tmp.id);
5588 continue;
5589 }
5590
5591 break;
5592 }
5593
5595 {
5596 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
5597 {
5598 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid emote id (datalong = {}) in SCRIPT_COMMAND_EMOTE for script id {}",
5599 tableName, tmp.Emote.EmoteID, tmp.id);
5600 continue;
5601 }
5602 break;
5603 }
5604
5606 {
5607 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
5608 {
5609 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid map (Id: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5610 tableName, tmp.TeleportTo.MapID, tmp.id);
5611 continue;
5612 }
5613
5615 {
5616 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5617 tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
5618 continue;
5619 }
5620 break;
5621 }
5622
5624 {
5625 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
5626 if (!quest)
5627 {
5628 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5629 tableName, tmp.QuestExplored.QuestID, tmp.id);
5630 continue;
5631 }
5632
5634 {
5635 TC_LOG_ERROR("sql.sql", "Table `{}` has quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}, but quest not have QUEST_FLAGS_COMPLETION_EVENT or QUEST_FLAGS_COMPLETION_AREA_TRIGGER in quest flags. Script command will do nothing.",
5636 tableName, tmp.QuestExplored.QuestID, tmp.id);
5637 continue;
5638 }
5639
5641 {
5642 TC_LOG_ERROR("sql.sql", "Table `{}` has too large distance ({}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5643 tableName, tmp.QuestExplored.Distance, tmp.id);
5644 continue;
5645 }
5646
5648 {
5649 TC_LOG_ERROR("sql.sql", "Table `{}` has too large distance ({}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}, max distance is {} or 0 for disable distance check",
5651 continue;
5652 }
5653
5655 {
5656 TC_LOG_ERROR("sql.sql", "Table `{}` has too small distance ({}) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}, min distance is {} or 0 for disable distance check",
5657 tableName, tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
5658 continue;
5659 }
5660
5661 break;
5662 }
5663
5665 {
5667 {
5668 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_KILL_CREDIT for script id {}",
5669 tableName, tmp.KillCredit.CreatureEntry, tmp.id);
5670 continue;
5671 }
5672 break;
5673 }
5674
5676 {
5678 if (!data)
5679 {
5680 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5681 tableName, tmp.RespawnGameobject.GOGuid, tmp.id);
5682 continue;
5683 }
5684
5685 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5686 if (!info)
5687 {
5688 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5689 tableName, tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
5690 continue;
5691 }
5692
5693 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
5695 info->type == GAMEOBJECT_TYPE_DOOR ||
5696 info->type == GAMEOBJECT_TYPE_BUTTON ||
5697 info->type == GAMEOBJECT_TYPE_TRAP)
5698 {
5699 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5700 tableName, info->entry, tmp.id);
5701 continue;
5702 }
5703 break;
5704 }
5705
5707 {
5709 {
5710 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5712 continue;
5713 }
5714
5716 {
5717 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5718 tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id);
5719 continue;
5720 }
5721 break;
5722 }
5723
5726 {
5728 if (!data)
5729 {
5730 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in {} for script id {}",
5731 tableName, tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command), tmp.id);
5732 continue;
5733 }
5734
5735 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5736 if (!info)
5737 {
5738 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in {} for script id {}",
5739 tableName, tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command), tmp.id);
5740 continue;
5741 }
5742
5743 if (info->type != GAMEOBJECT_TYPE_DOOR)
5744 {
5745 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command {} for script id {}",
5746 tableName, info->entry, GetScriptCommandName(tmp.command), tmp.id);
5747 continue;
5748 }
5749
5750 break;
5751 }
5752
5754 {
5755 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID, DIFFICULTY_NONE))
5756 {
5757 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5758 tableName, tmp.RemoveAura.SpellID, tmp.id);
5759 continue;
5760 }
5761 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
5762 {
5763 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in datalong2 ({}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5764 tableName, tmp.RemoveAura.Flags, tmp.id);
5765 continue;
5766 }
5767 break;
5768 }
5769
5771 {
5772 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID, DIFFICULTY_NONE))
5773 {
5774 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5775 tableName, tmp.CastSpell.SpellID, tmp.id);
5776 continue;
5777 }
5778 if (tmp.CastSpell.Flags > 4) // targeting type
5779 {
5780 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown target in datalong2 ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5781 tableName, tmp.CastSpell.Flags, tmp.id);
5782 continue;
5783 }
5784 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
5785 {
5786 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5787 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5788 continue;
5789 }
5790 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
5791 {
5792 TC_LOG_ERROR("sql.sql", "Table `{}` using invalid creature entry in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5793 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5794 continue;
5795 }
5796 break;
5797 }
5798
5800 {
5802 {
5803 TC_LOG_ERROR("sql.sql", "Table `{}` has nonexistent item (entry: {}) in SCRIPT_COMMAND_CREATE_ITEM for script id {}",
5804 tableName, tmp.CreateItem.ItemEntry, tmp.id);
5805 continue;
5806 }
5807 if (!tmp.CreateItem.Amount)
5808 {
5809 TC_LOG_ERROR("sql.sql", "Table `{}` SCRIPT_COMMAND_CREATE_ITEM but amount is {} for script id {}",
5810 tableName, tmp.CreateItem.Amount, tmp.id);
5811 continue;
5812 }
5813 break;
5814 }
5816 {
5817 if (!sAnimKitStore.LookupEntry(tmp.PlayAnimKit.AnimKitID))
5818 {
5819 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid AnimKid id (datalong = {}) in SCRIPT_COMMAND_PLAY_ANIMKIT for script id {}",
5820 tableName, tmp.PlayAnimKit.AnimKitID, tmp.id);
5821 continue;
5822 }
5823 break;
5824 }
5828 {
5829 TC_LOG_ERROR("sql.sql", "Table `{}` uses deprecated direct updatefield modify command {} for script id {}", tableName, GetScriptCommandName(tmp.command), tmp.id);
5830 continue;
5831 }
5832 default:
5833 break;
5834 }
5835
5836 if (scripts->find(tmp.id) == scripts->end())
5837 {
5838 ScriptMap emptyMap;
5839 (*scripts)[tmp.id] = emptyMap;
5840 }
5841 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
5842
5843 ++count;
5844 }
5845 while (result->NextRow());
5846
5847 TC_LOG_INFO("server.loading", ">> Loaded {} script definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5848}
5849
5851{
5853
5854 // check ids
5855 for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
5856 {
5857 uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
5858 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
5859
5860 if (!spellInfo)
5861 {
5862 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has not existing spell (Id: {}) as script id", spellId);
5863 continue;
5864 }
5865
5866 SpellEffIndex i = SpellEffIndex((uint32(itr->first) >> 24) & 0x000000FF);
5867 if (uint32(i) >= spellInfo->GetEffects().size())
5868 {
5869 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has too high effect index {} for spell (Id: {}) as script id", uint32(i), spellId);
5870 continue;
5871 }
5872
5873 //check for correct spellEffect
5874 if (!spellInfo->GetEffect(i).Effect || (spellInfo->GetEffect(i).Effect != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->GetEffect(i).Effect != SPELL_EFFECT_DUMMY))
5875 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` - spell {} effect {} is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, uint32(i));
5876 }
5877}
5878
5880{
5881 _eventStore.clear();
5882
5883 // Load all possible event ids from gameobjects
5884 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
5885 {
5886 EventContainer eventSet = gameObjectTemplatePair.second.GetEventScriptSet();
5887 _eventStore.insert(eventSet.begin(), eventSet.end());
5888 }
5889
5890 // Load all possible event ids from spells
5891 for (SpellEffectEntry const* spellEffect : sSpellEffectStore)
5892 if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT && spellEffect->EffectMiscValue[0])
5893 _eventStore.insert(spellEffect->EffectMiscValue[0]);
5894
5895 // Load all possible event ids from taxi path nodes
5896 for (TaxiPathNodeEntry const* node : sTaxiPathNodeStore)
5897 {
5898 if (node->ArrivalEventID)
5899 _eventStore.insert(node->ArrivalEventID);
5900
5901 if (node->DepartureEventID)
5902 _eventStore.insert(node->DepartureEventID);
5903 }
5904
5905 // Load all possible event ids from criterias
5906 auto addCriteriaEventsToStore = [&](CriteriaList const& criteriaList)
5907 {
5908 for (Criteria const* criteria : criteriaList)
5909 if (criteria->Entry->Asset.EventID)
5910 _eventStore.insert(criteria->Entry->Asset.EventID);
5911 };
5912
5913 std::array<CriteriaType, 2> eventCriteriaTypes = { CriteriaType::PlayerTriggerGameEvent, CriteriaType::AnyoneTriggerGameEventScenario };
5914 for (CriteriaType criteriaType : eventCriteriaTypes)
5915 {
5916 addCriteriaEventsToStore(sCriteriaMgr->GetPlayerCriteriaByType(criteriaType, 0));
5917 addCriteriaEventsToStore(sCriteriaMgr->GetGuildCriteriaByType(criteriaType));
5918 addCriteriaEventsToStore(sCriteriaMgr->GetQuestObjectiveCriteriaByType(criteriaType));
5919 }
5920
5921 for (ScenarioEntry const* scenario : sScenarioStore)
5922 for (CriteriaType criteriaType : eventCriteriaTypes)
5923 addCriteriaEventsToStore(sCriteriaMgr->GetScenarioCriteriaByTypeAndScenario(criteriaType, scenario->ID));
5924
5925 for (auto const& [gameEventId, _] : sCriteriaMgr->GetCriteriaByStartEvent(CriteriaStartEvent::SendEvent))
5926 if (gameEventId)
5927 _eventStore.insert(gameEventId);
5928
5929 for (auto const& [gameEventId, _] : sCriteriaMgr->GetCriteriaByFailEvent(CriteriaFailEvent::SendEvent))
5930 if (gameEventId)
5931 _eventStore.insert(gameEventId);
5932}
5933
5935{
5936 // Set of valid events referenced in several sources
5937 LoadEventSet();
5938
5939 // Deprecated
5941
5942 // Then check if all scripts are in above list of possible script entries
5943 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
5944 {
5945 if (!IsValidEvent(itr->first))
5946 TC_LOG_ERROR("sql.sql", "Table `event_scripts` has script (Id: {}) not referring to any gameobject_template (data field referencing GameEvent), any taxi path node, any criteria asset or any spell effect {}",
5947 itr->first, SPELL_EFFECT_SEND_EVENT);
5948 }
5949
5950 uint32 oldMSTime = getMSTime();
5951
5952 _eventScriptStore.clear(); // Reload case
5953
5954 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM event_script_names");
5955 if (!result)
5956 {
5957 TC_LOG_INFO("server.loading", ">> Loaded 0 event scripts. DB table `event_script_names` is empty.");
5958 return;
5959 }
5960
5961 do
5962 {
5963 Field* fields = result->Fetch();
5964
5965 uint32 eventId = fields[0].GetUInt32();
5966 std::string const scriptName = fields[1].GetString();
5967
5968 if (!IsValidEvent(eventId))
5969 {
5970 TC_LOG_ERROR("sql.sql", "Event (ID: {}) not referring to any gameobject_template (data field referencing GameEvent), any taxi path node, any criteria asset or any spell effect {}",
5971 eventId, SPELL_EFFECT_SEND_EVENT);
5972 continue;
5973 }
5974 _eventScriptStore[eventId] = GetScriptId(scriptName);
5975 } while (result->NextRow());
5976
5977 TC_LOG_INFO("server.loading", ">> Loaded {} event scripts in {} ms", _eventScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
5978}
5979
5981{
5982 uint32 oldMSTime = getMSTime();
5983
5984 _spellScriptsStore.clear(); // need for reload case
5985
5986 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
5987
5988 if (!result)
5989 {
5990 TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
5991 return;
5992 }
5993
5994 uint32 count = 0;
5995
5996 do
5997 {
5998
5999 Field* fields = result->Fetch();
6000
6001 int32 spellId = fields[0].GetInt32();
6002 std::string const scriptName = fields[1].GetString();
6003
6004 bool allRanks = false;
6005 if (spellId < 0)
6006 {
6007 allRanks = true;
6008 spellId = -spellId;
6009 }
6010
6011 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
6012 if (!spellInfo)
6013 {
6014 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) does not exist.", scriptName, fields[0].GetInt32());
6015 continue;
6016 }
6017
6018 if (allRanks)
6019 {
6020 if (!spellInfo->IsRanked())
6021 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) has no ranks of spell.", scriptName, fields[0].GetInt32());
6022
6023 if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
6024 {
6025 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is not first rank of spell.", scriptName, fields[0].GetInt32());
6026 continue;
6027 }
6028 while (spellInfo)
6029 {
6030 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6031 spellInfo = spellInfo->GetNextRankSpell();
6032 }
6033 }
6034 else
6035 {
6036 if (spellInfo->IsRanked())
6037 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
6038
6039 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6040 }
6041
6042 ++count;
6043 }
6044 while (result->NextRow());
6045
6046 TC_LOG_INFO("server.loading", ">> Loaded {} spell script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6047}
6048
6050{
6051 uint32 oldMSTime = getMSTime();
6052
6053 if (_spellScriptsStore.empty())
6054 {
6055 TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
6056 return;
6057 }
6058
6059 uint32 count = 0;
6060
6061 for (auto& spell : _spellScriptsStore)
6062 {
6063 SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spell.first, DIFFICULTY_NONE);
6064
6065 if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(spell.second.first))
6066 {
6067 ++count;
6068
6069 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
6070 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
6071
6072 if (!spellScript && !auraScript)
6073 {
6074 TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `{}` do not return objects - script skipped", GetScriptName(spell.second.first));
6075
6076 spell.second.second = false;
6077 continue;
6078 }
6079
6080 if (spellScript)
6081 {
6082 spellScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6083 spellScript->_Register();
6084
6085 if (!spellScript->_Validate(spellEntry))
6086 {
6087 spell.second.second = false;
6088 continue;
6089 }
6090 }
6091
6092 if (auraScript)
6093 {
6094 auraScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6095 auraScript->_Register();
6096
6097 if (!auraScript->_Validate(spellEntry))
6098 {
6099 spell.second.second = false;
6100 continue;
6101 }
6102 }
6103
6104 // Enable the script when all checks passed
6105 spell.second.second = true;
6106 }
6107 else
6108 spell.second.second = false;
6109 }
6110
6111 TC_LOG_INFO("server.loading", ">> Validated {} scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6112}
6113
6115{
6116 uint32 oldMSTime = getMSTime();
6117
6118 // 0 1 2 3 4
6119 QueryResult result = WorldDatabase.Query("SELECT ID, `Text`, NextPageID, PlayerConditionID, Flags FROM page_text");
6120 if (!result)
6121 {
6122 TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
6123 return;
6124 }
6125
6126 uint32 count = 0;
6127 do
6128 {
6129 Field* fields = result->Fetch();
6130
6131 uint32 id = fields[0].GetUInt32();
6132
6133 PageText& pageText = _pageTextStore[id];
6134 pageText.Text = fields[1].GetString();
6135 pageText.NextPageID = fields[2].GetUInt32();
6136 pageText.PlayerConditionID = fields[3].GetInt32();
6137 pageText.Flags = fields[4].GetUInt8();
6138
6139 ++count;
6140 }
6141 while (result->NextRow());
6142
6143 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
6144 if (itr->second.NextPageID)
6145 if (_pageTextStore.find(itr->second.NextPageID) == _pageTextStore.end())
6146 TC_LOG_ERROR("sql.sql", "Page text (ID: {}) has non-existing `NextPageID` ({})", itr->first, itr->second.NextPageID);
6147
6148 TC_LOG_INFO("server.loading", ">> Loaded {} page texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6149}
6150
6152{
6153 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
6154 if (itr != _pageTextStore.end())
6155 return &(itr->second);
6156
6157 return nullptr;
6158}
6159
6161{
6162 uint32 oldMSTime = getMSTime();
6163
6164 _pageTextLocaleStore.clear(); // needed for reload case
6165
6166 // 0 1 2
6167 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text` FROM page_text_locale");
6168 if (!result)
6169 return;
6170
6171 do
6172 {
6173 Field* fields = result->Fetch();
6174
6175 uint32 id = fields[0].GetUInt32();
6176 std::string_view localeName = fields[1].GetStringView();
6177
6178 LocaleConstant locale = GetLocaleByName(localeName);
6179 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
6180 continue;
6181
6183 AddLocaleString(fields[2].GetStringView(), locale, data.Text);
6184 } while (result->NextRow());
6185
6186 TC_LOG_INFO("server.loading", ">> Loaded {} PageText locale strings in {} ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6187}
6188
6190{
6191 uint32 oldMSTime = getMSTime();
6192
6193 // 0 1 2
6194 QueryResult result = WorldDatabase.Query("SELECT map, parent, script FROM instance_template");
6195
6196 if (!result)
6197 {
6198 TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
6199 return;
6200 }
6201
6202 uint32 count = 0;
6203 do
6204 {
6205 Field* fields = result->Fetch();
6206
6207 uint16 mapID = fields[0].GetUInt16();
6208
6209 if (!MapManager::IsValidMAP(mapID))
6210 {
6211 TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid {} for template!", mapID);
6212 continue;
6213 }
6214
6215 InstanceTemplate instanceTemplate;
6216
6217 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
6218 instanceTemplate.ScriptId = GetScriptId(fields[2].GetString());
6219
6220 _instanceTemplateStore[mapID] = instanceTemplate;
6221
6222 ++count;
6223 }
6224 while (result->NextRow());
6225
6226 TC_LOG_INFO("server.loading", ">> Loaded {} instance templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6227}
6228
6230{
6231 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
6232 if (itr != _instanceTemplateStore.end())
6233 return &(itr->second);
6234
6235 return nullptr;
6236}
6237
6239{
6240 NpcTextContainer::const_iterator itr = _npcTextStore.find(Text_ID);
6241 if (itr != _npcTextStore.end())
6242 return &itr->second;
6243 return nullptr;
6244}
6245
6247{
6248 uint32 oldMSTime = getMSTime();
6249
6250 QueryResult result = WorldDatabase.Query("SELECT ID, "
6251 "Probability0, Probability1, Probability2, Probability3, Probability4, Probability5, Probability6, Probability7, "
6252 "BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7"
6253 " FROM npc_text");
6254 if (!result)
6255 {
6256 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6257 return;
6258 }
6259
6260 _npcTextStore.reserve(result->GetRowCount());
6261
6262 do
6263 {
6264 Field* fields = result->Fetch();
6265
6266 uint32 textID = fields[0].GetUInt32();
6267 if (!textID)
6268 {
6269 TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
6270 continue;
6271 }
6272
6273 NpcText npcText;
6274
6275 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; ++i)
6276 {
6277 npcText.Data[i].Probability = fields[1 + i].GetFloat();
6278 npcText.Data[i].BroadcastTextID = fields[9 + i].GetUInt32();
6279 }
6280
6281 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6282 {
6283 if (npcText.Data[i].BroadcastTextID)
6284 {
6285 if (!sBroadcastTextStore.LookupEntry(npcText.Data[i].BroadcastTextID))
6286 {
6287 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a non-existing BroadcastText (ID: {}, Index: {})", textID, npcText.Data[i].BroadcastTextID, i);
6288 npcText.Data[i].Probability = 0.0f;
6289 npcText.Data[i].BroadcastTextID = 0;
6290 }
6291 }
6292 }
6293
6294 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6295 {
6296 if (npcText.Data[i].Probability > 0 && npcText.Data[i].BroadcastTextID == 0)
6297 {
6298 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability (Index: {}) set, but no BroadcastTextID to go with it", textID, i);
6299 npcText.Data[i].Probability = 0;
6300 }
6301 }
6302
6303 float probabilitySum = std::accumulate(std::begin(npcText.Data), std::end(npcText.Data), 0.0f, [](float sum, NpcTextData const& data) { return sum + data.Probability; });
6304 if (probabilitySum <= 0.0f)
6305 {
6306 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability sum 0, no text can be selected from it, skipped.", textID);
6307 continue;
6308 }
6309
6310 _npcTextStore[textID] = npcText;
6311 }
6312 while (result->NextRow());
6313
6314 TC_LOG_INFO("server.loading", ">> Loaded {} npc texts in {} ms", uint32(_npcTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
6315}
6316
6317//not very fast function but it is called only once a day, or on starting-up
6319{
6320 uint32 oldMSTime = getMSTime();
6321
6322 time_t curTime = GameTime::GetGameTime();
6323 tm lt;
6324 localtime_r(&curTime, &lt);
6325 TC_LOG_INFO("misc", "Returning mails current time: hour: {}, minute: {}, second: {} ", lt.tm_hour, lt.tm_min, lt.tm_sec);
6326
6327 // Delete all old mails without item and without body immediately, if starting server
6328 if (!serverUp)
6329 {
6331 stmt->setInt64(0, curTime);
6332 CharacterDatabase.Execute(stmt);
6333 }
6335 stmt->setInt64(0, curTime);
6336 PreparedQueryResult result = CharacterDatabase.Query(stmt);
6337 if (!result)
6338 {
6339 TC_LOG_INFO("server.loading", ">> No expired mails found.");
6340 return; // any mails need to be returned or deleted
6341 }
6342
6343 std::map<uint64 /*messageId*/, MailItemInfoVec> itemsCache;
6344 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
6345 stmt->setUInt32(0, curTime);
6346 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
6347 {
6348 MailItemInfo item;
6349 do
6350 {
6351 Field* fields = items->Fetch();
6352 item.item_guid = fields[0].GetUInt64();
6353 item.item_template = fields[1].GetUInt32();
6354 uint64 mailId = fields[2].GetUInt64();
6355 itemsCache[mailId].push_back(item);
6356 } while (items->NextRow());
6357 }
6358
6359 uint32 deletedCount = 0;
6360 uint32 returnedCount = 0;
6361 do
6362 {
6363 Field* fields = result->Fetch();
6364 ObjectGuid::LowType receiver = fields[3].GetUInt64();
6365 if (serverUp && ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(receiver)))
6366 continue;
6367
6368 Mail* m = new Mail;
6369 m->messageID = fields[0].GetUInt64();
6370 m->messageType = fields[1].GetUInt8();
6371 m->sender = fields[2].GetUInt64();
6372 m->receiver = receiver;
6373 bool has_items = fields[4].GetBool();
6374 m->expire_time = fields[5].GetInt64();
6375 m->deliver_time = 0;
6376 m->COD = fields[6].GetUInt64();
6377 m->checked = fields[7].GetUInt8();
6378 m->mailTemplateId = fields[8].GetInt16();
6379
6380 // Delete or return mail
6381 if (has_items)
6382 {
6383 // read items from cache
6384 m->items.swap(itemsCache[m->messageID]);
6385
6386 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
6388 {
6389 CharacterDatabaseTransaction nonTransactional(nullptr);
6390 // mail open and then not returned
6391 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6392 {
6393 Item::DeleteFromDB(nonTransactional, itr2->item_guid);
6394 AzeriteItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6395 AzeriteEmpoweredItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6396 }
6397
6398 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
6399 stmt->setUInt64(0, m->messageID);
6400 CharacterDatabase.Execute(stmt);
6401 }
6402 else
6403 {
6404 // Mail will be returned
6405 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
6406 stmt->setUInt64(0, m->receiver);
6407 stmt->setUInt64(1, m->sender);
6408 stmt->setInt64 (2, curTime + 30 * DAY);
6409 stmt->setInt64 (3, curTime);
6411 stmt->setUInt64(5, m->messageID);
6412 CharacterDatabase.Execute(stmt);
6413 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6414 {
6415 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
6416 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
6417 stmt->setUInt64(0, m->sender);
6418 stmt->setUInt64(1, itr2->item_guid);
6419 CharacterDatabase.Execute(stmt);
6420
6421 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
6422 stmt->setUInt64(0, m->sender);
6423 stmt->setUInt64(1, itr2->item_guid);
6424 CharacterDatabase.Execute(stmt);
6425 }
6426 delete m;
6427 ++returnedCount;
6428 continue;
6429 }
6430 }
6431
6432 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
6433 stmt->setUInt64(0, m->messageID);
6434 CharacterDatabase.Execute(stmt);
6435 delete m;
6436 ++deletedCount;
6437 }
6438 while (result->NextRow());
6439
6440 TC_LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
6441}
6442
6444{
6445 uint32 oldMSTime = getMSTime();
6446
6447 _questAreaTriggerStore.clear(); // need for reload case
6448
6449 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
6450
6451 if (!result)
6452 {
6453 TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
6454 return;
6455 }
6456
6457 uint32 count = 0;
6458
6459 do
6460 {
6461 ++count;
6462
6463 Field* fields = result->Fetch();
6464
6465 uint32 trigger_ID = fields[0].GetUInt32();
6466 uint32 quest_ID = fields[1].GetUInt32();
6467
6468 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
6469 if (!atEntry)
6470 {
6471 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
6472 continue;
6473 }
6474
6475 Quest const* quest = GetQuestTemplate(quest_ID);
6476
6477 if (!quest)
6478 {
6479 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not existing quest {}", trigger_ID, quest_ID);
6480 continue;
6481 }
6482
6484 {
6485 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not quest {}, but quest not have flag QUEST_FLAGS_COMPLETION_AREA_TRIGGER and no objective with type QUEST_OBJECTIVE_AREATRIGGER. Trigger is obsolete, skipped.", trigger_ID, quest_ID);
6486 continue;
6487 }
6488
6489 _questAreaTriggerStore[trigger_ID].insert(quest_ID);
6490
6491 } while (result->NextRow());
6492
6493 for (auto const& pair : _questObjectives)
6494 {
6495 QuestObjective const* objective = pair.second;
6496 if (objective->Type == QUEST_OBJECTIVE_AREATRIGGER)
6497 _questAreaTriggerStore[objective->ObjectID].insert(objective->QuestID);
6498 }
6499
6500 TC_LOG_INFO("server.loading", ">> Loaded {} quest trigger points in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6501}
6502
6504{
6505 uint32 typeIndex;
6506 if (type == TYPEID_UNIT)
6507 typeIndex = 0;
6508 else if (type == TYPEID_GAMEOBJECT)
6509 typeIndex = 1;
6510 else
6511 return nullptr;
6512
6514}
6515
6517{
6518 uint32 typeIndex;
6519 if (type == TYPEID_UNIT)
6520 typeIndex = 0;
6521 else if (type == TYPEID_GAMEOBJECT)
6522 typeIndex = 1;
6523 else
6524 return nullptr;
6525
6527}
6528
6530{
6531 uint32 oldMSTime = getMSTime();
6532
6533 for (std::size_t i = 0; i < _questGreetingStore.size(); ++i)
6534 _questGreetingStore[i].clear();
6535
6536 // 0 1 2 3
6537 QueryResult result = WorldDatabase.Query("SELECT ID, type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
6538 if (!result)
6539 {
6540 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6541 return;
6542 }
6543
6544 uint32 count = 0;
6545 do
6546 {
6547 Field* fields = result->Fetch();
6548
6549 uint32 id = fields[0].GetUInt32();
6550 uint8 type = fields[1].GetUInt8();
6551 switch (type)
6552 {
6553 case 0: // Creature
6554 if (!GetCreatureTemplate(id))
6555 {
6556 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id);
6557 continue;
6558 }
6559 break;
6560 case 1: // GameObject
6561 if (!GetGameObjectTemplate(id))
6562 {
6563 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id);
6564 continue;
6565 }
6566 break;
6567 default:
6568 continue;
6569 }
6570
6571 uint16 greetEmoteType = fields[2].GetUInt16();
6572 uint32 greetEmoteDelay = fields[3].GetUInt32();
6573 std::string greeting = fields[4].GetString();
6574
6575 _questGreetingStore[type].emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(greetEmoteType, greetEmoteDelay, std::move(greeting)));
6576 ++count;
6577 }
6578 while (result->NextRow());
6579
6580 TC_LOG_INFO("server.loading", ">> Loaded {} quest_greeting in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6581}
6582
6584{
6585 uint32 oldMSTime = getMSTime();
6586
6587 _tavernAreaTriggerStore.clear(); // need for reload case
6588
6589 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
6590
6591 if (!result)
6592 {
6593 TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
6594 return;
6595 }
6596
6597 uint32 count = 0;
6598
6599 do
6600 {
6601 ++count;
6602
6603 Field* fields = result->Fetch();
6604
6605 uint32 Trigger_ID = fields[0].GetUInt32();
6606
6607 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6608 if (!atEntry)
6609 {
6610 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6611 continue;
6612 }
6613
6614 _tavernAreaTriggerStore.insert(Trigger_ID);
6615 } while (result->NextRow());
6616
6617 TC_LOG_INFO("server.loading", ">> Loaded {} tavern triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6618}
6619
6621{
6622 uint32 oldMSTime = getMSTime();
6623
6624 _areaTriggerScriptStore.clear(); // need for reload case
6625
6626 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
6627 if (!result)
6628 {
6629 TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
6630 return;
6631 }
6632
6633 do
6634 {
6635 Field* fields = result->Fetch();
6636
6637 uint32 triggerId = fields[0].GetUInt32();
6638 std::string const scriptName = fields[1].GetString();
6639
6640 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
6641 if (!atEntry)
6642 {
6643 TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: {}) does not exist in `AreaTrigger.dbc`.", triggerId);
6644 continue;
6645 }
6646 _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
6647 }
6648 while (result->NextRow());
6649
6650 TC_LOG_INFO("server.loading", ">> Loaded {} areatrigger scripts in {} ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6651}
6652
6653uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
6654{
6655 bool found = false;
6656 float dist = 10000;
6657 uint32 id = 0;
6658
6660 for (TaxiNodesEntry const* node : sTaxiNodesStore)
6661 {
6662 if (!node || node->ContinentID != mapid || !node->GetFlags().HasFlag(requireFlag) || node->GetFlags().HasFlag(TaxiNodeFlags::IgnoreForFindNearest))
6663 continue;
6664
6665 uint32 field = uint32((node->ID - 1) / 8);
6666 uint32 submask = 1 << ((node->ID - 1) % 8);
6667
6668 // skip not taxi network nodes
6669 if ((sTaxiNodesMask[field] & submask) == 0)
6670 continue;
6671
6672 float dist2 = (node->Pos.X - x)*(node->Pos.X - x) + (node->Pos.Y - y)*(node->Pos.Y - y) + (node->Pos.Z - z)*(node->Pos.Z - z);
6673 if (found)
6674 {
6675 if (dist2 < dist)
6676 {
6677 dist = dist2;
6678 id = node->ID;
6679 }
6680 }
6681 else
6682 {
6683 found = true;
6684 dist = dist2;
6685 id = node->ID;
6686 }
6687 }
6688
6689 return id;
6690}
6691
6692void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
6693{
6694 TaxiPathEntry const* taxiPath = sDB2Manager.GetTaxiPath(source, destination);
6695 if (taxiPath)
6696 {
6697 path = taxiPath->ID;
6698 cost = taxiPath->Cost;
6699 }
6700 else
6701 {
6702 path = 0;
6703 cost = 0;
6704 }
6705}
6706
6707uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
6708{
6709 CreatureModel mountModel;
6710 CreatureTemplate const* mount_info = nullptr;
6711
6712 // select mount creature id
6713 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
6714 if (node)
6715 {
6716 uint32 mount_entry = 0;
6717 if (team == ALLIANCE)
6718 mount_entry = node->MountCreatureID[1];
6719 else
6720 mount_entry = node->MountCreatureID[0];
6721
6722 // Fix for Alliance not being able to use Acherus taxi
6723 // only one mount type for both sides
6724 if (mount_entry == 0 && allowed_alt_team)
6725 {
6726 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
6727 mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
6728 }
6729
6730 mount_info = GetCreatureTemplate(mount_entry);
6731 if (mount_info)
6732 {
6733 CreatureModel const* model = mount_info->GetRandomValidModel();
6734 if (!model)
6735 {
6736 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
6737 return 0;
6738 }
6739 mountModel = *model;
6740 }
6741 }
6742
6743 // minfo is not actually used but the mount_id was updated
6744 GetCreatureModelRandomGender(&mountModel, mount_info);
6745
6746 return mountModel.CreatureDisplayID;
6747}
6748
6750{
6751 auto itr = _questTemplates.find(quest_id);
6752 return itr != _questTemplates.end() ? itr->second.get() : nullptr;
6753}
6754
6756{
6757 uint32 oldMSTime = getMSTime();
6758
6759 GraveyardStore.clear(); // needed for reload case
6760
6761 // 0 1
6762 QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone FROM graveyard_zone");
6763
6764 if (!result)
6765 {
6766 TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
6767 return;
6768 }
6769
6770 uint32 count = 0;
6771
6772 do
6773 {
6774 ++count;
6775
6776 Field* fields = result->Fetch();
6777
6778 uint32 safeLocId = fields[0].GetUInt32();
6779 uint32 zoneId = fields[1].GetUInt32();
6780
6781 WorldSafeLocsEntry const* entry = GetWorldSafeLoc(safeLocId);
6782 if (!entry)
6783 {
6784 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: {}), skipped.", safeLocId);
6785 continue;
6786 }
6787
6788 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
6789 if (!areaEntry)
6790 {
6791 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: {}), skipped.", zoneId);
6792 continue;
6793 }
6794
6795 if (!AddGraveyardLink(safeLocId, zoneId, 0, false))
6796 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
6797 } while (result->NextRow());
6798
6799 TC_LOG_INFO("server.loading", ">> Loaded {} graveyard-zone links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6800}
6801
6803{
6804 enum DefaultGraveyard
6805 {
6806 HORDE_GRAVEYARD = 10, // Crossroads
6807 ALLIANCE_GRAVEYARD = 4 // Westfall
6808 };
6809
6810 if (team == HORDE)
6811 return GetWorldSafeLoc(HORDE_GRAVEYARD);
6812 else if (team == ALLIANCE)
6813 return GetWorldSafeLoc(ALLIANCE_GRAVEYARD);
6814 else return nullptr;
6815}
6816
6817WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(WorldLocation const& location, uint32 team, WorldObject* conditionObject) const
6818{
6819 float x, y, z;
6820 location.GetPosition(x, y, z);
6821 uint32 MapId = location.GetMapId();
6822
6823 // search for zone associated closest graveyard
6824 uint32 zoneId = sTerrainMgr.GetZoneId(conditionObject ? conditionObject->GetPhaseShift() : PhasingHandler::GetEmptyPhaseShift(), MapId, x, y, z);
6825
6826 if (!zoneId)
6827 {
6828 if (z > -500)
6829 {
6830 TC_LOG_ERROR("misc", "ZoneId not found for map {} coords ({}, {}, {})", MapId, x, y, z);
6831 return GetDefaultGraveyard(team);
6832 }
6833 }
6834
6835 WorldSafeLocsEntry const* graveyard = GetClosestGraveyardInZone(location, team, conditionObject, zoneId);
6836 AreaTableEntry const* zoneEntry = sAreaTableStore.AssertEntry(zoneId);
6837 AreaTableEntry const* parentEntry = sAreaTableStore.LookupEntry(zoneEntry->ParentAreaID);
6838
6839 while (!graveyard && parentEntry)
6840 {
6841 graveyard = GetClosestGraveyardInZone(location, team, conditionObject, parentEntry->ID);
6842 if (!graveyard && parentEntry->ParentAreaID != 0)
6843 parentEntry = sAreaTableStore.LookupEntry(parentEntry->ParentAreaID);
6844 else // nothing found, cant look further, give up.
6845 parentEntry = nullptr;
6846 }
6847
6848 return graveyard;
6849}
6850
6851WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyardInZone(WorldLocation const& location, uint32 team, WorldObject* conditionObject, uint32 zoneId) const
6852{
6853 float x, y, z;
6854 location.GetPosition(x, y, z);
6855 uint32 MapId = location.GetMapId();
6856
6857 // Simulate std. algorithm:
6858 // found some graveyard associated to (ghost_zone, ghost_map)
6859 //
6860 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
6861 // then check faction
6862 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
6863 // then check faction
6864 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6865 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
6866
6867 // not need to check validity of map object; MapId _MUST_ be valid here
6868 if (range.first == range.second && !mapEntry->IsBattlegroundOrArena())
6869 {
6870 if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones
6871 TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
6872 return GetDefaultGraveyard(team);
6873 }
6874
6875 // at corpse map
6876 bool foundNear = false;
6877 float distNear = 10000;
6878 WorldSafeLocsEntry const* entryNear = nullptr;
6879
6880 // at entrance map for corpse map
6881 bool foundEntr = false;
6882 float distEntr = 10000;
6883 WorldSafeLocsEntry const* entryEntr = nullptr;
6884
6885 // some where other
6886 WorldSafeLocsEntry const* entryFar = nullptr;
6887
6888 ConditionSourceInfo conditionSource(conditionObject);
6889
6890 for (; range.first != range.second; ++range.first)
6891 {
6892 GraveyardData const& data = range.first->second;
6893
6895
6896 if (conditionObject)
6897 {
6898 if (!data.Conditions.Meets(conditionSource))
6899 continue;
6900
6901 if (int16(entry->Loc.GetMapId()) == mapEntry->ParentMapID && !conditionObject->GetPhaseShift().HasVisibleMapId(entry->Loc.GetMapId()))
6902 continue;
6903 }
6904 else if (team != 0)
6905 {
6906 bool teamConditionMet = true;
6907 if (std::shared_ptr<std::vector<Condition>> conditions = data.Conditions.Conditions.lock())
6908 {
6909 for (Condition const& cond : *conditions)
6910 {
6911 if (cond.ConditionType != CONDITION_TEAM)
6912 continue;
6913
6914 if (cond.ConditionValue1 == team)
6915 continue;
6916
6917 teamConditionMet = false;
6918 }
6919 }
6920
6921 if (!teamConditionMet)
6922 continue;
6923 }
6924
6925 // find now nearest graveyard at other map
6926 if (MapId != entry->Loc.GetMapId() && int16(entry->Loc.GetMapId()) != mapEntry->ParentMapID)
6927 {
6928 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
6929 if (!mapEntry
6930 || mapEntry->CorpseMapID < 0
6931 || uint32(mapEntry->CorpseMapID) != entry->Loc.GetMapId()
6932 || (mapEntry->Corpse.X == 0 && mapEntry->Corpse.Y == 0)) // Check X and Y
6933 {
6934 // not have any corrdinates for check distance anyway
6935 entryFar = entry;
6936 continue;
6937 }
6938
6939 // at entrance map calculate distance (2D);
6940 float dist2 = (entry->Loc.GetPositionX() - mapEntry->Corpse.X) * (entry->Loc.GetPositionX() - mapEntry->Corpse.X)
6941 + (entry->Loc.GetPositionY() - mapEntry->Corpse.Y) * (entry->Loc.GetPositionY() - mapEntry->Corpse.Y);
6942 if (foundEntr)
6943 {
6944 if (dist2 < distEntr)
6945 {
6946 distEntr = dist2;
6947 entryEntr = entry;
6948 }
6949 }
6950 else
6951 {
6952 foundEntr = true;
6953 distEntr = dist2;
6954 entryEntr = entry;
6955 }
6956 }
6957 // find now nearest graveyard at same map
6958 else
6959 {
6960 float dist2 = (entry->Loc.GetPositionX() - x) * (entry->Loc.GetPositionX() - x)
6961 + (entry->Loc.GetPositionY() - y) * (entry->Loc.GetPositionY() - y)
6962 + (entry->Loc.GetPositionZ() - z) * (entry->Loc.GetPositionZ() - z);
6963 if (foundNear)
6964 {
6965 if (dist2 < distNear)
6966 {
6967 distNear = dist2;
6968 entryNear = entry;
6969 }
6970 }
6971 else
6972 {
6973 foundNear = true;
6974 distNear = dist2;
6975 entryNear = entry;
6976 }
6977 }
6978 }
6979
6980 if (entryNear)
6981 return entryNear;
6982
6983 if (entryEntr)
6984 return entryEntr;
6985
6986 return entryFar;
6987}
6988
6990{
6991 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6992 for (; range.first != range.second; ++range.first)
6993 {
6994 GraveyardData const& data = range.first->second;
6995 if (data.safeLocId == id)
6996 return &data;
6997 }
6998 return nullptr;
6999}
7000
7002{
7003 uint32 oldMSTime = getMSTime();
7004
7005 // 0 1 2 3 4 5
7006 if (QueryResult result = WorldDatabase.Query("SELECT ID, MapID, LocX, LocY, LocZ, Facing FROM world_safe_locs"))
7007 {
7008 do
7009 {
7010 Field* fields = result->Fetch();
7011 uint32 id = fields[0].GetUInt32();
7012 WorldLocation loc(fields[1].GetUInt32(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat(), DegToRad(fields[5].GetFloat()));
7014 {
7015 TC_LOG_ERROR("sql.sql", "World location (ID: {}) has a invalid position MapID: {} {}, skipped", id, loc.GetMapId(), loc.ToString());
7016 continue;
7017 }
7018
7019 WorldSafeLocsEntry& worldSafeLocs = _worldSafeLocs[id];
7020 worldSafeLocs.ID = id;
7021 worldSafeLocs.Loc.WorldRelocate(loc);
7022
7023 } while (result->NextRow());
7024
7025 TC_LOG_INFO("server.loading", ">> Loaded {} world locations {} ms", _worldSafeLocs.size(), GetMSTimeDiffToNow(oldMSTime));
7026 }
7027 else
7028 TC_LOG_INFO("server.loading", ">> Loaded 0 world locations. DB table `world_safe_locs` is empty.");
7029}
7030
7032{
7034}
7035
7037{
7038 return std::make_pair(_worldSafeLocs.begin(), _worldSafeLocs.end());
7039}
7040
7042{
7043 AreaTriggerContainer::const_iterator itr = _areaTriggerStore.find(trigger);
7044 if (itr != _areaTriggerStore.end())
7045 return &itr->second;
7046 return nullptr;
7047}
7048
7050{
7052}
7053
7054bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
7055{
7056 if (FindGraveyardData(id, zoneId))
7057 return false;
7058
7059 // add link to loaded data
7060 GraveyardData data;
7061 data.safeLocId = id;
7062
7063 GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
7064
7065 // add link to DB
7066 if (persist)
7067 {
7069
7070 stmt->setUInt32(0, id);
7071 stmt->setUInt32(1, zoneId);
7072
7073 WorldDatabase.Execute(stmt);
7074
7075 // Store graveyard condition if team is set
7076 if (team != 0)
7077 {
7078 WorldDatabasePreparedStatement* conditionStmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CONDITION);
7079 conditionStmt->setUInt32(0, CONDITION_SOURCE_TYPE_GRAVEYARD); // SourceTypeOrReferenceId
7080 conditionStmt->setUInt32(1, zoneId); // SourceGroup
7081 conditionStmt->setUInt32(2, id); // SourceEntry
7082 conditionStmt->setUInt32(3, 0); // SourceId
7083 conditionStmt->setUInt32(4, 0); // ElseGroup
7084 conditionStmt->setUInt32(5, CONDITION_TEAM); // ConditionTypeOrReference
7085 conditionStmt->setUInt8(6, 0); // ConditionTarget
7086 conditionStmt->setUInt32(7, team); // ConditionValue1
7087 conditionStmt->setUInt32(8, 0); // ConditionValue2
7088 conditionStmt->setUInt32(9, 0); // ConditionValue3
7089 conditionStmt->setUInt8(10, 0); // NegativeCondition
7090 conditionStmt->setUInt32(11, 0); // ErrorType
7091 conditionStmt->setUInt32(12, 0); // ErrorTextId
7092 conditionStmt->setString(13, ""); // ScriptName
7093 conditionStmt->setString(14, ""); // Comment
7094
7095 WorldDatabase.Execute(conditionStmt);
7096
7097 // reload conditions to make sure everything is loaded as it should be
7098 sConditionMgr->LoadConditions(true);
7099 sScriptMgr->NotifyScriptIDUpdate();
7100 }
7101 }
7102
7103 return true;
7104}
7105
7107{
7108 uint32 oldMSTime = getMSTime();
7109
7110 _areaTriggerStore.clear(); // needed for reload case
7111
7112 // 0 1
7113 QueryResult result = WorldDatabase.Query("SELECT ID, PortLocID FROM areatrigger_teleport");
7114 if (!result)
7115 {
7116 TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
7117 return;
7118 }
7119
7120 uint32 count = 0;
7121
7122 do
7123 {
7124 Field* fields = result->Fetch();
7125
7126 ++count;
7127
7128 uint32 Trigger_ID = fields[0].GetUInt32();
7129 uint32 PortLocID = fields[1].GetUInt32();
7130
7131 WorldSafeLocsEntry const* portLoc = GetWorldSafeLoc(PortLocID);
7132 if (!portLoc)
7133 {
7134 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) has a non-existing Port Loc (ID: {}) in WorldSafeLocs.dbc, skipped", Trigger_ID, PortLocID);
7135 continue;
7136 }
7137
7139
7140 at.target_mapId = portLoc->Loc.GetMapId();
7141 at.target_X = portLoc->Loc.GetPositionX();
7142 at.target_Y = portLoc->Loc.GetPositionY();
7143 at.target_Z = portLoc->Loc.GetPositionZ();
7144 at.target_Orientation = portLoc->Loc.GetOrientation();
7145
7146 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
7147 if (!atEntry)
7148 {
7149 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) does not exist in AreaTrigger.dbc.", Trigger_ID);
7150 continue;
7151 }
7152
7153 _areaTriggerStore[Trigger_ID] = at;
7154
7155 } while (result->NextRow());
7156
7157 TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7158}
7159
7161{
7162 uint32 oldMSTime = getMSTime();
7163
7164 _accessRequirementStore.clear(); // need for reload case
7165
7166 // 0 1 2 3 4 5 6 7 8 9
7167 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");
7168
7169 if (!result)
7170 {
7171 TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
7172 return;
7173 }
7174
7175 uint32 count = 0;
7176
7177 do
7178 {
7179 Field* fields = result->Fetch();
7180
7181 uint32 mapid = fields[0].GetUInt32();
7182 if (!sMapStore.LookupEntry(mapid))
7183 {
7184 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not exist, skipped.", mapid);
7185 continue;
7186 }
7187
7188 uint32 difficulty = fields[1].GetUInt8();
7189 if (!sDB2Manager.GetMapDifficultyData(mapid, Difficulty(difficulty)))
7190 {
7191 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not have difficulty {}, skipped", mapid, difficulty);
7192 continue;
7193 }
7194
7195 uint64 requirement_ID = MAKE_PAIR64(mapid, difficulty);
7196
7197 AccessRequirement* ar = &_accessRequirementStore[requirement_ID];
7198 ar->levelMin = fields[2].GetUInt8();
7199 ar->levelMax = fields[3].GetUInt8();
7200 ar->item = fields[4].GetUInt32();
7201 ar->item2 = fields[5].GetUInt32();
7202 ar->quest_A = fields[6].GetUInt32();
7203 ar->quest_H = fields[7].GetUInt32();
7204 ar->achievement = fields[8].GetUInt32();
7205 ar->questFailedText = fields[9].GetString();
7206
7207 if (ar->item)
7208 {
7209 ItemTemplate const* pProto = GetItemTemplate(ar->item);
7210 if (!pProto)
7211 {
7212 TC_LOG_ERROR("sql.sql", "Key item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item, mapid, difficulty);
7213 ar->item = 0;
7214 }
7215 }
7216
7217 if (ar->item2)
7218 {
7219 ItemTemplate const* pProto = GetItemTemplate(ar->item2);
7220 if (!pProto)
7221 {
7222 TC_LOG_ERROR("sql.sql", "Second item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item2, mapid, difficulty);
7223 ar->item2 = 0;
7224 }
7225 }
7226
7227 if (ar->quest_A)
7228 {
7229 if (!GetQuestTemplate(ar->quest_A))
7230 {
7231 TC_LOG_ERROR("sql.sql", "Required Alliance Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_A, mapid, difficulty);
7232 ar->quest_A = 0;
7233 }
7234 }
7235
7236 if (ar->quest_H)
7237 {
7238 if (!GetQuestTemplate(ar->quest_H))
7239 {
7240 TC_LOG_ERROR("sql.sql", "Required Horde Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_H, mapid, difficulty);
7241 ar->quest_H = 0;
7242 }
7243 }
7244
7245 if (ar->achievement)
7246 {
7247 if (!sAchievementStore.LookupEntry(ar->achievement))
7248 {
7249 TC_LOG_ERROR("sql.sql", "Required Achievement {} not exist for map {} difficulty {}, remove quest done requirement.", ar->achievement, mapid, difficulty);
7250 ar->achievement = 0;
7251 }
7252 }
7253 ++count;
7254
7255 } while (result->NextRow());
7256
7257 TC_LOG_INFO("server.loading", ">> Loaded {} access requirement definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7258}
7259
7260/*
7261 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
7262 */
7264{
7265 Optional<uint32> parentId;
7266 MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
7267 if (!mapEntry || mapEntry->CorpseMapID < 0)
7268 return nullptr;
7269
7270 if (mapEntry->IsDungeon())
7271 if (InstanceTemplate const* iTemplate = GetInstanceTemplate(Map))
7272 parentId = iTemplate->Parent;
7273
7274 uint32 entrance_map = parentId.value_or(mapEntry->CorpseMapID);
7275 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7276 {
7277 if (itr->second.target_mapId == entrance_map)
7278 {
7279 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7280 if (atEntry && atEntry->ContinentID == int32(Map))
7281 return &itr->second;
7282 }
7283 }
7284 return nullptr;
7285}
7286
7291{
7292 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7293 {
7294 if (itr->second.target_mapId == Map)
7295 {
7296 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7297 if (atEntry)
7298 return &itr->second;
7299 }
7300 }
7301 return nullptr;
7302}
7303
7305{
7306 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
7307 if (result)
7308 GetGuidSequenceGenerator(HighGuid::Player).Set((*result)[0].GetUInt64() + 1);
7309
7310 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
7311 if (result)
7312 GetGuidSequenceGenerator(HighGuid::Item).Set((*result)[0].GetUInt64() + 1);
7313
7314 // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
7315 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '{}'", GetGuidSequenceGenerator(HighGuid::Item).GetNextAfterMaxUsed()); // One-time query
7316 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGuidSequenceGenerator(HighGuid::Item).GetNextAfterMaxUsed()); // One-time query
7317 CharacterDatabase.PExecute("DELETE a, ab, ai FROM auctionhouse a LEFT JOIN auction_bidders ab ON ab.auctionId = a.id LEFT JOIN auction_items ai ON ai.auctionId = a.id WHERE ai.itemGuid >= '{}'",
7318 GetGuidSequenceGenerator(HighGuid::Item).GetNextAfterMaxUsed()); // One-time query
7319 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGuidSequenceGenerator(HighGuid::Item).GetNextAfterMaxUsed()); // One-time query
7320
7321 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
7322 if (result)
7323 GetGuidSequenceGenerator(HighGuid::Transport).Set((*result)[0].GetUInt64() + 1);
7324
7325 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
7326 if (result)
7327 _auctionId = (*result)[0].GetUInt32()+1;
7328
7329 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
7330 if (result)
7331 _mailId = (*result)[0].GetUInt64()+1;
7332
7333 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
7334 if (result)
7335 sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
7336
7337 result = CharacterDatabase.Query("SELECT MAX(maxguid) FROM ((SELECT MAX(setguid) AS maxguid FROM character_equipmentsets) UNION (SELECT MAX(setguid) AS maxguid FROM character_transmog_outfits)) allsets");
7338 if (result)
7339 _equipmentSetGuid = (*result)[0].GetUInt64()+1;
7340
7341 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
7342 if (result)
7343 sGuildMgr->SetNextGuildId((*result)[0].GetUInt64()+1);
7344
7345 result = CharacterDatabase.Query("SELECT MAX(guid) FROM `groups`");
7346 if (result)
7347 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
7348
7349 result = CharacterDatabase.Query("SELECT MAX(itemId) from character_void_storage");
7350 if (result)
7351 _voidItemId = (*result)[0].GetUInt64()+1;
7352
7353 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
7354 if (result)
7355 _creatureSpawnId = (*result)[0].GetUInt64() + 1;
7356
7357 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
7358 if (result)
7359 _gameObjectSpawnId = (*result)[0].GetUInt64() + 1;
7360}
7361
7363{
7364 auto itr = _guidGenerators.find(high);
7365 if (itr == _guidGenerators.end())
7366 itr = _guidGenerators.insert(std::make_pair(high, std::make_unique<ObjectGuidGenerator>(high))).first;
7367
7368 return *itr->second;
7369}
7370
7372{
7373 if (_auctionId >= 0xFFFFFFFE)
7374 {
7375 TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7377 }
7378 return _auctionId++;
7379}
7380
7382{
7383 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
7384 {
7385 TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7387 }
7388 return _equipmentSetGuid++;
7389}
7390
7392{
7393 if (_mailId >= UI64LIT(0xFFFFFFFFFFFFFFFE))
7394 {
7395 TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7397 }
7398 return _mailId++;
7399}
7400
7402{
7403 if (_hiPetNumber >= 0xFFFFFFFE)
7404 {
7405 TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7407 }
7408 return _hiPetNumber++;
7409}
7410
7412{
7413 if (_voidItemId >= uint64(0xFFFFFFFFFFFFFFFELL))
7414 {
7415 TC_LOG_ERROR("misc", "_voidItemId overflow!! Can't continue, shutting down server. ");
7417 }
7418 return _voidItemId++;
7419}
7420
7422{
7423 if (_creatureSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7424 {
7425 TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7427 }
7428 return _creatureSpawnId++;
7429}
7430
7432{
7433 if (_gameObjectSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7434 {
7435 TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7437 }
7438 return _gameObjectSpawnId++;
7439}
7440
7442{
7443 uint32 oldMSTime = getMSTime();
7444
7445 _gameObjectLocaleStore.clear(); // need for reload case
7446
7447 // 0 1 2 3 4
7448 QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption, unk1 FROM gameobject_template_locale");
7449 if (!result)
7450 return;
7451
7452 do
7453 {
7454 Field* fields = result->Fetch();
7455
7456 uint32 id = fields[0].GetUInt32();
7457 std::string_view localeName = fields[1].GetStringView();
7458
7459 LocaleConstant locale = GetLocaleByName(localeName);
7460 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
7461 continue;
7462
7464 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
7465 AddLocaleString(fields[3].GetStringView(), locale, data.CastBarCaption);
7466 AddLocaleString(fields[4].GetStringView(), locale, data.Unk1);
7467
7468 } while (result->NextRow());
7469
7470 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject_template_locale strings in {} ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
7471}
7472
7473inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7474{
7475 if (sLockStore.LookupEntry(dataN))
7476 return;
7477
7478 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but lock (Id: {}) not found.",
7479 goInfo->entry, goInfo->type, N, dataN, dataN);
7480}
7481
7482inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7483{
7484 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
7485 {
7486 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
7487 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but GO (Entry {}) have not GAMEOBJECT_TYPE_TRAP ({}) type.",
7488 goInfo->entry, goInfo->type, N, dataN, dataN, uint32(GAMEOBJECT_TYPE_TRAP));
7489 }
7490}
7491
7492inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7493{
7494 if (sSpellMgr->GetSpellInfo(dataN, DIFFICULTY_NONE))
7495 return;
7496
7497 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but Spell (Entry {}) not exist.",
7498 goInfo->entry, goInfo->type, N, dataN, dataN);
7499}
7500
7501inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
7502{
7504 return;
7505
7506 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but correct chair height in range 0..{}.",
7508
7509 // prevent client and server unexpected work
7510 dataN = 0;
7511}
7512
7514{
7515 // 0/1 correct values
7516 if (dataN <= 1)
7517 return;
7518
7519 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
7520}
7521
7522inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7523{
7524 // 0/1 correct values
7525 if (dataN <= 1)
7526 return;
7527
7528 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) consumable field value.",
7529 goInfo->entry, goInfo->type, N, dataN);
7530}
7531
7533{
7534 uint32 oldMSTime = getMSTime();
7535
7536 for (GameObjectsEntry const* db2go : sGameObjectsStore)
7537 {
7539 go.entry = db2go->ID;
7540 go.type = db2go->TypeID;
7541 go.displayId = db2go->DisplayID;
7542 go.name = db2go->Name[sWorld->GetDefaultDbcLocale()];
7543 go.size = db2go->Scale;
7544 memset(go.raw.data, 0, sizeof(go.raw.data));
7545 std::copy(db2go->PropValue.begin(), db2go->PropValue.end(), std::begin(go.raw.data));
7546 go.ContentTuningId = 0;
7547 go.ScriptId = 0;
7548 }
7549
7550 // 0 1 2 3 4 5 6 7
7551 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
7552 // 8 9 10 11 12 13 14 15 16 17 18 19 20
7553 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
7554 // 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
7555 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, "
7556 // 37 38 39 40 41 42 43 44 45 46
7557 "Data29, Data30, Data31, Data32, Data33, Data34, ContentTuningId, AIName, ScriptName, StringId "
7558 "FROM gameobject_template");
7559
7560 if (!result)
7561 {
7562 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
7563 return;
7564 }
7565
7566 _gameObjectTemplateStore.reserve(result->GetRowCount());
7567 do
7568 {
7569 Field* fields = result->Fetch();
7570
7571 uint32 entry = fields[0].GetUInt32();
7572
7574 got.entry = entry;
7575 got.type = uint32(fields[1].GetUInt8());
7576 got.displayId = fields[2].GetUInt32();
7577 got.name = fields[3].GetString();
7578 got.IconName = fields[4].GetString();
7579 got.castBarCaption = fields[5].GetString();
7580 got.unk1 = fields[6].GetString();
7581 got.size = fields[7].GetFloat();
7582
7583 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
7584 got.raw.data[i] = fields[8 + i].GetUInt32();
7585
7586 got.ContentTuningId = fields[43].GetInt32();
7587 got.AIName = fields[44].GetString();
7588 got.ScriptId = GetScriptId(fields[45].GetString());
7589 got.StringId = fields[46].GetString();
7590
7591 // Checks
7592 if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
7593 {
7594 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
7595 got.AIName.clear();
7596 }
7597
7598 switch (got.type)
7599 {
7600 case GAMEOBJECT_TYPE_DOOR: //0
7601 {
7602 if (got.door.open)
7603 CheckGOLockId(&got, got.door.open, 1);
7605 break;
7606 }
7607 case GAMEOBJECT_TYPE_BUTTON: //1
7608 {
7609 if (got.button.open)
7610 CheckGOLockId(&got, got.button.open, 1);
7612 break;
7613 }
7615 {
7616 if (got.questgiver.open)
7617 CheckGOLockId(&got, got.questgiver.open, 0);
7619 break;
7620 }
7621 case GAMEOBJECT_TYPE_CHEST: //3
7622 {
7623 if (got.chest.open)
7624 CheckGOLockId(&got, got.chest.open, 0);
7625
7626 CheckGOConsumable(&got, got.chest.consumable, 3);
7627
7628 if (got.chest.linkedTrap) // linked trap
7629 CheckGOLinkedTrapId(&got, got.chest.linkedTrap, 7);
7630 break;
7631 }
7632 case GAMEOBJECT_TYPE_TRAP: //6
7633 {
7634 if (got.trap.open)
7635 CheckGOLockId(&got, got.trap.open, 0);
7636 break;
7637 }
7638 case GAMEOBJECT_TYPE_CHAIR: //7
7640 break;
7642 {
7643 if (got.spellFocus.spellFocusType)
7644 {
7645 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.spellFocusType))
7646 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but SpellFocus (Id: {}) not exist.",
7648 }
7649
7650 if (got.spellFocus.linkedTrap) // linked trap
7652 break;
7653 }
7654 case GAMEOBJECT_TYPE_GOOBER: //10
7655 {
7656 if (got.goober.open)
7657 CheckGOLockId(&got, got.goober.open, 0);
7658
7659 CheckGOConsumable(&got, got.goober.consumable, 3);
7660
7661 if (got.goober.pageID) // pageId
7662 {
7663 if (!GetPageText(got.goober.pageID))
7664 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data7={} but PageText (Entry {}) not exist.",
7665 entry, got.type, got.goober.pageID, got.goober.pageID);
7666 }
7668 if (got.goober.linkedTrap) // linked trap
7669 CheckGOLinkedTrapId(&got, got.goober.linkedTrap, 12);
7670 break;
7671 }
7673 {
7674 if (got.areaDamage.open)
7675 CheckGOLockId(&got, got.areaDamage.open, 0);
7676 break;
7677 }
7678 case GAMEOBJECT_TYPE_CAMERA: //13
7679 {
7680 if (got.camera.open)
7681 CheckGOLockId(&got, got.camera.open, 0);
7682 break;
7683 }
7685 {
7686 if (got.moTransport.taxiPathID)
7687 {
7689 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but TaxiPath (Id: {}) not exist.",
7690 entry, got.type, got.moTransport.taxiPathID, got.moTransport.taxiPathID);
7691 }
7692 if (uint32 transportMap = got.moTransport.SpawnMap)
7693 _transportMaps.insert(transportMap);
7694 break;
7695 }
7696 case GAMEOBJECT_TYPE_RITUAL: //18
7697 break;
7699 {
7700 // always must have spell
7701 CheckGOSpellId(&got, got.spellCaster.spell, 0);
7702 break;
7703 }
7704 case GAMEOBJECT_TYPE_FLAGSTAND: //24
7705 {
7706 if (got.flagStand.open)
7707 CheckGOLockId(&got, got.flagStand.open, 0);
7709 break;
7710 }
7712 {
7713 if (got.fishingHole.open)
7714 CheckGOLockId(&got, got.fishingHole.open, 4);
7715 break;
7716 }
7717 case GAMEOBJECT_TYPE_FLAGDROP: //26
7718 {
7719 if (got.flagDrop.open)
7720 CheckGOLockId(&got, got.flagDrop.open, 0);
7722 break;
7723 }
7726
7727 if (got.barberChair.SitAnimKit && !sAnimKitStore.LookupEntry(got.barberChair.SitAnimKit))
7728 {
7729 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data2 = {} but AnimKit.dbc (Id: {}) not exist, set to 0.",
7730 entry, got.type, got.barberChair.SitAnimKit, got.barberChair.SitAnimKit);
7731 got.barberChair.SitAnimKit = 0;
7732 }
7733 break;
7735 if (uint32 transportMap = got.garrisonBuilding.SpawnMap)
7736 _transportMaps.insert(transportMap);
7737 break;
7739 if (got.gatheringNode.open)
7740 CheckGOLockId(&got, got.gatheringNode.open, 0);
7741 if (got.gatheringNode.linkedTrap)
7743 break;
7744 }
7745 } while (result->NextRow());
7746
7747 TC_LOG_INFO("server.loading", ">> Loaded {} game object templates in {} ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
7748}
7749
7751{
7752 uint32 oldMSTime = getMSTime();
7753
7754 // 0 1 2 3 4 5 6 7 8 9 10 11
7755 QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3, artkit4, WorldEffectID, AIAnimKitID FROM gameobject_template_addon");
7756
7757 if (!result)
7758 {
7759 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
7760 return;
7761 }
7762
7763 uint32 count = 0;
7764 do
7765 {
7766 Field* fields = result->Fetch();
7767
7768 uint32 entry = fields[0].GetUInt32();
7769
7770 GameObjectTemplate const* got = GetGameObjectTemplate(entry);
7771 if (!got)
7772 {
7773 TC_LOG_ERROR("sql.sql", "GameObject template (Entry: {}) does not exist but has a record in `gameobject_template_addon`", entry);
7774 continue;
7775 }
7776
7778 gameObjectAddon.Faction = uint32(fields[1].GetUInt16());
7779 gameObjectAddon.Flags = fields[2].GetUInt32();
7780 gameObjectAddon.Mingold = fields[3].GetUInt32();
7781 gameObjectAddon.Maxgold = fields[4].GetUInt32();
7782 gameObjectAddon.WorldEffectID = fields[10].GetUInt32();
7783 gameObjectAddon.AIAnimKitID = fields[11].GetUInt32();
7784
7785 for (uint32 i = 0; i < gameObjectAddon.ArtKits.size(); ++i)
7786 {
7787 uint32 artKitID = fields[5 + i].GetUInt32();
7788 if (!artKitID)
7789 continue;
7790
7791 if (!sGameObjectArtKitStore.LookupEntry(artKitID))
7792 {
7793 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` ({}) defined, set to zero instead.", entry, i, artKitID);
7794 continue;
7795 }
7796
7797 gameObjectAddon.ArtKits[i] = artKitID;
7798 }
7799
7800 // checks
7801 if (gameObjectAddon.Faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.Faction))
7802 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid faction ({}) defined in `gameobject_template_addon`.", entry, gameObjectAddon.Faction);
7803
7804 if (gameObjectAddon.Maxgold > 0)
7805 {
7806 switch (got->type)
7807 {
7810 break;
7811 default:
7812 TC_LOG_ERROR("sql.sql", "GameObject (Entry {} GoType: {}) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
7813 break;
7814 }
7815 }
7816
7817 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
7818 {
7819 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid WorldEffectID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.WorldEffectID);
7820 gameObjectAddon.WorldEffectID = 0;
7821 }
7822
7823 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
7824 {
7825 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid AIAnimKitID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.AIAnimKitID);
7826 gameObjectAddon.AIAnimKitID = 0;
7827 }
7828
7829 ++count;
7830 }
7831 while (result->NextRow());
7832
7833 TC_LOG_INFO("server.loading", ">> Loaded {} game object template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7834}
7835
7837{
7838 uint32 oldMSTime = getMSTime();
7839
7840 // 0 1 2
7841 QueryResult result = WorldDatabase.Query("SELECT spawnId, faction, flags FROM gameobject_overrides");
7842 if (!result)
7843 {
7844 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject faction and flags overrides. DB table `gameobject_overrides` is empty.");
7845 return;
7846 }
7847
7848 uint32 count = 0;
7849 do
7850 {
7851 Field* fields = result->Fetch();
7852
7853 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
7854 GameObjectData const* goData = GetGameObjectData(spawnId);
7855 if (!goData)
7856 {
7857 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) does not exist but has a record in `gameobject_overrides`", spawnId);
7858 continue;
7859 }
7860
7861 GameObjectOverride& gameObjectOverride = _gameObjectOverrideStore[spawnId];
7862 gameObjectOverride.Faction = fields[1].GetUInt16();
7863 gameObjectOverride.Flags = fields[2].GetUInt32();
7864
7865 if (gameObjectOverride.Faction && !sFactionTemplateStore.LookupEntry(gameObjectOverride.Faction))
7866 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) has invalid faction ({}) defined in `gameobject_overrides`.", spawnId, gameObjectOverride.Faction);
7867
7868 ++count;
7869 } while (result->NextRow());
7870
7871 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject faction and flags overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7872}
7873
7875{
7876 uint32 oldMSTime = getMSTime();
7877
7878 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
7879
7880 if (!result)
7881 {
7882 TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
7883 return;
7884 }
7885
7886 uint32 count = 0;
7887
7888 do
7889 {
7890 Field* fields = result->Fetch();
7891 uint8 level = fields[0].GetUInt8();
7892 uint32 basexp = fields[1].GetInt32();
7893 _baseXPTable[level] = basexp;
7894 ++count;
7895 }
7896 while (result->NextRow());
7897
7898 TC_LOG_INFO("server.loading", ">> Loaded {} BaseXP definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7899}
7900
7902{
7903 return _baseXPTable[level] ? _baseXPTable[level] : 0;
7904}
7905
7907{
7908 if (level < _playerXPperLevel.size())
7909 return _playerXPperLevel[level];
7910 return 0;
7911}
7912
7914{
7915 if (!areaEntry)
7916 return 0;
7917
7918 // Get level for the area
7919 FishingBaseSkillContainer::const_iterator itr = _fishingBaseForAreaStore.find(areaEntry->ID);
7920 if (itr != _fishingBaseForAreaStore.end())
7921 return itr->second;
7922
7923 // If there is no data for the current area and it has a parent area, get data from the last (recursive)
7924 if (AreaTableEntry const* parentAreaEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID))
7925 return GetFishingBaseSkillLevel(parentAreaEntry);
7926
7927 TC_LOG_ERROR("sql.sql", "Fishable areaId {} is not properly defined in `skill_fishing_base_level`.", areaEntry->ID);
7928
7929 return 0;
7930}
7931
7933{
7934 auto itr = _skillTiers.find(skillTierId);
7935 return itr != _skillTiers.end() ? &itr->second : nullptr;
7936}
7937
7939{
7940 if (tierIndex >= MAX_SKILL_STEP)
7941 tierIndex = MAX_SKILL_STEP - 1;
7942
7943 while (Value[tierIndex] == 0 && tierIndex > 0)
7944 --tierIndex;
7945
7946 return Value[tierIndex];
7947}
7948
7950{
7951 uint32 oldMSTime = getMSTime();
7952 // 0 1 2
7953 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
7954
7955 if (!result)
7956 {
7957 TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
7958 return;
7959 }
7960
7961 uint32 count = 0;
7962
7963 do
7964 {
7965 Field* fields = result->Fetch();
7966 std::string word = fields[0].GetString();
7967 uint32 entry = fields[1].GetUInt32();
7968 bool half = fields[2].GetBool();
7969 if (half)
7970 _petHalfName1[entry].push_back(word);
7971 else
7972 _petHalfName0[entry].push_back(word);
7973 ++count;
7974 }
7975 while (result->NextRow());
7976
7977 TC_LOG_INFO("server.loading", ">> Loaded {} pet name parts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7978}
7979
7981{
7982 uint32 oldMSTime = getMSTime();
7983
7984 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
7985 if (result)
7986 {
7987 Field* fields = result->Fetch();
7988 _hiPetNumber = fields[0].GetUInt32()+1;
7989 }
7990
7991 TC_LOG_INFO("server.loading", ">> Loaded the max pet number: {} in {} ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
7992}
7993
7995{
7996 std::vector<std::string>& list0 = _petHalfName0[entry];
7997 std::vector<std::string>& list1 = _petHalfName1[entry];
7998
7999 if (list0.empty() || list1.empty())
8000 {
8001 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
8002 if (!cinfo)
8003 return std::string();
8004
8005 char const* petname = DB2Manager::GetCreatureFamilyPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
8006 if (petname)
8007 return std::string(petname);
8008 else
8009 return cinfo->Name;
8010 }
8011
8012 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
8013}
8014
8016{
8017 uint32 oldMSTime = getMSTime();
8018
8019 _repRewardRateStore.clear(); // for reload case
8020
8021 uint32 count = 0; // 0 1 2 3 4 5 6 7
8022 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");
8023 if (!result)
8024 {
8025 TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
8026 return;
8027 }
8028
8029 do
8030 {
8031 Field* fields = result->Fetch();
8032
8033 uint32 factionId = fields[0].GetUInt32();
8034
8035 RepRewardRate repRate;
8036
8037 repRate.questRate = fields[1].GetFloat();
8038 repRate.questDailyRate = fields[2].GetFloat();
8039 repRate.questWeeklyRate = fields[3].GetFloat();
8040 repRate.questMonthlyRate = fields[4].GetFloat();
8041 repRate.questRepeatableRate = fields[5].GetFloat();
8042 repRate.creatureRate = fields[6].GetFloat();
8043 repRate.spellRate = fields[7].GetFloat();
8044
8045 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8046 if (!factionEntry)
8047 {
8048 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_reward_rate`", factionId);
8049 continue;
8050 }
8051
8052 if (repRate.questRate < 0.0f)
8053 {
8054 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate {}, skipping data for faction {}", repRate.questRate, factionId);
8055 continue;
8056 }
8057
8058 if (repRate.questDailyRate < 0.0f)
8059 {
8060 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate {}, skipping data for faction {}", repRate.questDailyRate, factionId);
8061 continue;
8062 }
8063
8064 if (repRate.questWeeklyRate < 0.0f)
8065 {
8066 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate {}, skipping data for faction {}", repRate.questWeeklyRate, factionId);
8067 continue;
8068 }
8069
8070 if (repRate.questMonthlyRate < 0.0f)
8071 {
8072 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate {}, skipping data for faction {}", repRate.questMonthlyRate, factionId);
8073 continue;
8074 }
8075
8076 if (repRate.questRepeatableRate < 0.0f)
8077 {
8078 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate {}, skipping data for faction {}", repRate.questRepeatableRate, factionId);
8079 continue;
8080 }
8081
8082 if (repRate.creatureRate < 0.0f)
8083 {
8084 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate {}, skipping data for faction {}", repRate.creatureRate, factionId);
8085 continue;
8086 }
8087
8088 if (repRate.spellRate < 0.0f)
8089 {
8090 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate {}, skipping data for faction {}", repRate.spellRate, factionId);
8091 continue;
8092 }
8093
8094 _repRewardRateStore[factionId] = repRate;
8095
8096 ++count;
8097 }
8098 while (result->NextRow());
8099
8100 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_reward_rate in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8101}
8102
8104{
8105 uint32 oldMSTime = getMSTime();
8106
8107 // For reload case
8108 _repOnKillStore.clear();
8109
8110 uint32 count = 0;
8111
8112 // 0 1 2
8113 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
8114 // 3 4 5 6 7 8 9
8115 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
8116 "FROM creature_onkill_reputation");
8117
8118 if (!result)
8119 {
8120 TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
8121 return;
8122 }
8123
8124 do
8125 {
8126 Field* fields = result->Fetch();
8127
8128 uint32 creature_id = fields[0].GetUInt32();
8129
8130 ReputationOnKillEntry repOnKill;
8131 repOnKill.RepFaction1 = fields[1].GetInt16();
8132 repOnKill.RepFaction2 = fields[2].GetInt16();
8133 repOnKill.IsTeamAward1 = fields[3].GetBool();
8134 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
8135 repOnKill.RepValue1 = fields[5].GetInt32();
8136 repOnKill.IsTeamAward2 = fields[6].GetBool();
8137 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
8138 repOnKill.RepValue2 = fields[8].GetInt32();
8139 repOnKill.TeamDependent = fields[9].GetBool();
8140
8141 if (!GetCreatureTemplate(creature_id))
8142 {
8143 TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry ({}), skipped", creature_id);
8144 continue;
8145 }
8146
8147 if (repOnKill.RepFaction1)
8148 {
8149 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
8150 if (!factionEntry1)
8151 {
8152 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
8153 continue;
8154 }
8155 }
8156
8157 if (repOnKill.RepFaction2)
8158 {
8159 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
8160 if (!factionEntry2)
8161 {
8162 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
8163 continue;
8164 }
8165 }
8166
8167 _repOnKillStore[creature_id] = repOnKill;
8168
8169 ++count;
8170 } while (result->NextRow());
8171
8172 TC_LOG_INFO("server.loading", ">> Loaded {} creature award reputation definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8173}
8174
8176{
8177 uint32 oldMSTime = getMSTime();
8178
8179 _repSpilloverTemplateStore.clear(); // for reload case
8180
8181 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8182 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");
8183
8184 if (!result)
8185 {
8186 TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
8187 return;
8188 }
8189
8190 do
8191 {
8192 Field* fields = result->Fetch();
8193
8194 uint32 factionId = fields[0].GetUInt16();
8195
8196 RepSpilloverTemplate repTemplate;
8197
8198 repTemplate.faction[0] = fields[1].GetUInt16();
8199 repTemplate.faction_rate[0] = fields[2].GetFloat();
8200 repTemplate.faction_rank[0] = fields[3].GetUInt8();
8201 repTemplate.faction[1] = fields[4].GetUInt16();
8202 repTemplate.faction_rate[1] = fields[5].GetFloat();
8203 repTemplate.faction_rank[1] = fields[6].GetUInt8();
8204 repTemplate.faction[2] = fields[7].GetUInt16();
8205 repTemplate.faction_rate[2] = fields[8].GetFloat();
8206 repTemplate.faction_rank[2] = fields[9].GetUInt8();
8207 repTemplate.faction[3] = fields[10].GetUInt16();
8208 repTemplate.faction_rate[3] = fields[11].GetFloat();
8209 repTemplate.faction_rank[3] = fields[12].GetUInt8();
8210 repTemplate.faction[4] = fields[13].GetUInt16();
8211 repTemplate.faction_rate[4] = fields[14].GetFloat();
8212 repTemplate.faction_rank[4] = fields[15].GetUInt8();
8213
8214 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8215
8216 if (!factionEntry)
8217 {
8218 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template`", factionId);
8219 continue;
8220 }
8221
8222 if (factionEntry->ParentFactionID == 0)
8223 {
8224 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} in `reputation_spillover_template` does not belong to any team, skipping", factionId);
8225 continue;
8226 }
8227
8228 bool invalidSpilloverFaction = false;
8229 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
8230 {
8231 if (repTemplate.faction[i])
8232 {
8233 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
8234
8235 if (!factionSpillover)
8236 {
8237 TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template` for faction {}, skipping", repTemplate.faction[i], factionId);
8238 invalidSpilloverFaction = true;
8239 break;
8240 }
8241
8242 if (!factionSpillover->CanHaveReputation())
8243 {
8244 TC_LOG_ERROR("sql.sql", "Spillover faction (faction.dbc) {} for faction {} in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate.faction[i], factionId);
8245 invalidSpilloverFaction = true;
8246 break;
8247 }
8248
8249 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
8250 {
8251 TC_LOG_ERROR("sql.sql", "Rank {} used in `reputation_spillover_template` for spillover faction {} is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
8252 invalidSpilloverFaction = true;
8253 break;
8254 }
8255 }
8256 }
8257
8258 if (invalidSpilloverFaction)
8259 continue;
8260
8261 _repSpilloverTemplateStore[factionId] = repTemplate;
8262
8263 ++count;
8264 }
8265 while (result->NextRow());
8266
8267 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_spillover_template in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8268}
8269
8271{
8272 uint32 oldMSTime = getMSTime();
8273
8274 _pointsOfInterestStore.clear(); // need for reload case
8275
8276 uint32 count = 0;
8277
8278 // 0 1 2 3 4 5 6 7 8
8279 QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, PositionZ, Icon, Flags, Importance, Name, WMOGroupID FROM points_of_interest");
8280
8281 if (!result)
8282 {
8283 TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
8284 return;
8285 }
8286
8287 do
8288 {
8289 Field* fields = result->Fetch();
8290
8291 uint32 id = fields[0].GetUInt32();
8292
8293 PointOfInterest pointOfInterest;
8294 pointOfInterest.ID = id;
8295 pointOfInterest.Pos.Relocate(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat());
8296 pointOfInterest.Icon = fields[4].GetUInt32();
8297 pointOfInterest.Flags = fields[5].GetUInt32();
8298 pointOfInterest.Importance = fields[6].GetUInt32();
8299 pointOfInterest.Name = fields[7].GetString();
8300 pointOfInterest.WMOGroupID = fields[8].GetInt32();
8301
8302 if (!Trinity::IsValidMapCoord(pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ()))
8303 {
8304 TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: {}) have invalid coordinates (PositionX: {} PositionY: {}, PositionZ: {}), ignored.",
8305 id, pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ());
8306 continue;
8307 }
8308
8309 _pointsOfInterestStore[id] = pointOfInterest;
8310
8311 ++count;
8312 } while (result->NextRow());
8313
8314 TC_LOG_INFO("server.loading", ">> Loaded {} Points of Interest definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8315}
8316
8318{
8319 uint32 oldMSTime = getMSTime();
8320
8321 _questPOIStore.clear(); // need for reload case
8322
8323 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
8324 QueryResult result = WorldDatabase.Query("SELECT QuestID, BlobIndex, Idx1, ObjectiveIndex, QuestObjectiveID, QuestObjectID, MapID, UiMapID, Priority, Flags, WorldEffectID, PlayerConditionID, NavigationPlayerConditionID, SpawnTrackingID, AlwaysAllowMergingBlobs FROM quest_poi order by QuestID, Idx1");
8325 if (!result)
8326 {
8327 TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
8328 return;
8329 }
8330
8331 // 0 1 2 3 4
8332 QueryResult pointsResult = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y, Z FROM quest_poi_points ORDER BY QuestID DESC, Idx1, Idx2");
8333
8334 std::unordered_map<int32, std::map<int32, std::vector<QuestPOIBlobPoint>>> allPoints;
8335
8336 if (pointsResult)
8337 {
8338 do
8339 {
8340 Field* fields = pointsResult->Fetch();
8341
8342 int32 QuestID = fields[0].GetInt32();
8343 int32 Idx1 = fields[1].GetInt32();
8344 int32 x = fields[2].GetInt32();
8345 int32 y = fields[3].GetInt32();
8346 int32 z = fields[4].GetInt32();
8347
8348 allPoints[QuestID][Idx1].emplace_back(x, y, z);
8349 } while (pointsResult->NextRow());
8350 }
8351
8352 do
8353 {
8354 Field* fields = result->Fetch();
8355
8356 int32 questID = fields[0].GetInt32();
8357 int32 blobIndex = fields[1].GetInt32();
8358 int32 idx1 = fields[2].GetInt32();
8359 int32 objectiveIndex = fields[3].GetInt32();
8360 int32 questObjectiveID = fields[4].GetInt32();
8361 int32 questObjectID = fields[5].GetInt32();
8362 int32 mapID = fields[6].GetInt32();
8363 int32 uiMapID = fields[7].GetInt32();
8364 int32 priority = fields[8].GetInt32();
8365 int32 flags = fields[9].GetInt32();
8366 int32 worldEffectID = fields[10].GetInt32();
8367 int32 playerConditionID = fields[11].GetInt32();
8368 int32 navigationPlayerConditionID = fields[12].GetInt32();
8369 int32 spawnTrackingID = fields[13].GetInt32();
8370 bool alwaysAllowMergingBlobs = fields[14].GetBool();
8371
8372 if (!GetQuestTemplate(questID))
8373 TC_LOG_ERROR("sql.sql", "`quest_poi` quest id ({}) Idx1 ({}) does not exist in `quest_template`", questID, idx1);
8374
8375 if (std::map<int32, std::vector<QuestPOIBlobPoint>>* blobs = Trinity::Containers::MapGetValuePtr(allPoints, questID))
8376 {
8377 if (std::vector<QuestPOIBlobPoint>* points = Trinity::Containers::MapGetValuePtr(*blobs, idx1))
8378 {
8379 QuestPOIData& poiData = _questPOIStore[questID];
8380 poiData.QuestID = questID;
8381 poiData.Blobs.emplace_back(blobIndex, objectiveIndex, questObjectiveID, questObjectID, mapID, uiMapID, priority, flags,
8382 worldEffectID, playerConditionID, navigationPlayerConditionID, spawnTrackingID, std::move(*points), alwaysAllowMergingBlobs);
8383 continue;
8384 }
8385 }
8386
8387 TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest {} POI id {}", questID, blobIndex);
8388
8389 } while (result->NextRow());
8390
8391 TC_LOG_INFO("server.loading", ">> Loaded {} quest POI definitions in {} ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
8392}
8393
8395{
8396 uint32 oldMSTime = getMSTime();
8397
8398 _spellClickInfoStore.clear();
8399 // 0 1 2 3
8400 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
8401
8402 if (!result)
8403 {
8404 TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
8405 return;
8406 }
8407
8408 uint32 count = 0;
8409
8410 do
8411 {
8412 Field* fields = result->Fetch();
8413
8414 uint32 npc_entry = fields[0].GetUInt32();
8415 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
8416 if (!cInfo)
8417 {
8418 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template {}. Skipping entry.", npc_entry);
8419 continue;
8420 }
8421
8422 uint32 spellid = fields[1].GetUInt32();
8423 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid, DIFFICULTY_NONE);
8424 if (!spellinfo)
8425 {
8426 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown spellid {}. Skipping entry.", npc_entry, spellid);
8427 continue;
8428 }
8429
8430 uint8 userType = fields[3].GetUInt16();
8431 if (userType >= SPELL_CLICK_USER_MAX)
8432 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown user type {}. Skipping entry.", npc_entry, uint32(userType));
8433
8434 uint8 castFlags = fields[2].GetUInt8();
8435 SpellClickInfo info;
8436 info.spellId = spellid;
8437 info.castFlags = castFlags;
8438 info.userType = SpellClickUserTypes(userType);
8439 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
8440
8441 ++count;
8442 }
8443 while (result->NextRow());
8444
8445 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
8446 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
8447 for (auto& creatureTemplatePair : _creatureTemplateStore)
8448 {
8449 if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
8450 {
8451 TC_LOG_ERROR("sql.sql", "npc_spellclick_spells: Creature template {} has UNIT_NPC_FLAG_SPELLCLICK but no data in spellclick table! Removing flag", creatureTemplatePair.first);
8452 creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
8453 }
8454 }
8455
8456 TC_LOG_INFO("server.loading", ">> Loaded {} spellclick definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8457}
8458
8460{
8461 // remove mapid*cellid -> guid_set map
8462 CreatureData const* data = GetCreatureData(guid);
8463 if (data)
8464 {
8466 OnDeleteSpawnData(data);
8467 }
8468
8469 _creatureDataStore.erase(guid);
8470}
8471
8473{
8474 // remove mapid*cellid -> guid_set map
8475 GameObjectData const* data = GetGameObjectData(guid);
8476 if (data)
8477 {
8479 OnDeleteSpawnData(data);
8480 }
8481
8482 _gameObjectDataStore.erase(guid);
8483}
8484
8485void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table)
8486{
8487 uint32 oldMSTime = getMSTime();
8488
8489 map.clear(); // need for reload case
8490
8491 uint32 count = 0;
8492
8493 QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM {}", table);
8494
8495 if (!result)
8496 {
8497 TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `{}`, table is empty.", table);
8498 return;
8499 }
8500
8501 do
8502 {
8503 uint32 id = result->Fetch()[0].GetUInt32();
8504 uint32 quest = result->Fetch()[1].GetUInt32();
8505
8506 if (!_questTemplates.count(quest))
8507 {
8508 TC_LOG_ERROR("sql.sql", "Table `{}`: Quest {} listed for entry {} does not exist.", table, quest, id);
8509 continue;
8510 }
8511
8512 map.insert(QuestRelations::value_type(id, quest));
8513 if (reverseMap)
8514 reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
8515 ++count;
8516 } while (result->NextRow());
8517
8518 TC_LOG_INFO("server.loading", ">> Loaded {} quest relations from {} in {} ms", count, table, GetMSTimeDiffToNow(oldMSTime));
8519}
8520
8522{
8523 LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter");
8524
8525 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
8526 {
8527 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8528 if (!goInfo)
8529 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8530 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8531 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data gameobject entry ({}) for quest {}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
8532 }
8533}
8534
8536{
8538
8539 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
8540 {
8541 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8542 if (!goInfo)
8543 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8544 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8545 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data gameobject entry ({}) for quest {}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
8546 }
8547}
8548
8550{
8551 LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter");
8552
8553 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
8554 {
8555 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8556 if (!cInfo)
8557 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8558 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8559 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has creature entry ({}) for quest {}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
8560 }
8561}
8562
8564{
8566
8567 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
8568 {
8569 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8570 if (!cInfo)
8571 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8572 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8573 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has creature entry ({}) for quest {}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
8574 }
8575}
8576
8578{
8579 while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second))
8580 ++_it;
8581}
8582
8584{
8585 return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId));
8586}
8587
8589{
8590 uint32 oldMSTime = getMSTime();
8591
8592 _reservedNamesStore.clear(); // need for reload case
8593
8594 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
8595
8596 if (!result)
8597 {
8598 TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
8599 return;
8600 }
8601
8602 uint32 count = 0;
8603
8604 Field* fields;
8605 do
8606 {
8607 fields = result->Fetch();
8608 std::string name= fields[0].GetString();
8609
8610 std::wstring wstr;
8611 if (!Utf8toWStr (name, wstr))
8612 {
8613 TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: {}", name);
8614 continue;
8615 }
8616
8617 wstrToLower(wstr);
8618
8619 _reservedNamesStore.insert(wstr);
8620 ++count;
8621 }
8622 while (result->NextRow());
8623
8624 TC_LOG_INFO("server.loading", ">> Loaded {} reserved player names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8625}
8626
8627bool ObjectMgr::IsReservedName(std::string_view name) const
8628{
8629 std::wstring wstr;
8630 if (!Utf8toWStr (name, wstr))
8631 return false;
8632
8633 wstrToLower(wstr);
8634
8635 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
8636}
8637
8639{
8640 if (Cfg_CategoriesEntry const* category = sCfgCategoriesStore.LookupEntry(realm.Timezone))
8641 return create ? category->GetCreateCharsetMask() : category->GetExistingCharsetMask();
8642
8643 return create ? CfgCategoriesCharsets::English : CfgCategoriesCharsets::Any; // basic-Latin at create, any at login
8644}
8645
8646bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
8647{
8648 if (strictMask == 0) // any language, ignore realm
8649 {
8650 if (isExtendedLatinString(wstr, numericOrSpace))
8651 return true;
8652 if (isCyrillicString(wstr, numericOrSpace))
8653 return true;
8654 if (isKoreanString(wstr, numericOrSpace))
8655 return true;
8656 if (isChineseString(wstr, numericOrSpace))
8657 return true;
8658 return false;
8659 }
8660
8661 if (strictMask & 0x2) // realm zone specific
8662 {
8665 return true;
8666 if (lt.HasFlag(CfgCategoriesCharsets::Latin1) && isExtendedLatinString(wstr, numericOrSpace))
8667 return true;
8668 if (lt.HasFlag(CfgCategoriesCharsets::English) && isBasicLatinString(wstr, numericOrSpace))
8669 return true;
8670 if (lt.HasFlag(CfgCategoriesCharsets::Russian) && isCyrillicString(wstr, numericOrSpace))
8671 return true;
8672 if (lt.HasFlag(CfgCategoriesCharsets::Korean) && isKoreanString(wstr, numericOrSpace))
8673 return true;
8674 if (lt.HasFlag(CfgCategoriesCharsets::Chinese) && isChineseString(wstr, numericOrSpace))
8675 return true;
8676 }
8677
8678 if (strictMask & 0x1) // basic Latin
8679 {
8680 if (isBasicLatinString(wstr, numericOrSpace))
8681 return true;
8682 }
8683
8684 return false;
8685}
8686
8687ResponseCodes ObjectMgr::CheckPlayerName(std::string_view name, LocaleConstant locale, bool create /*= false*/)
8688{
8689 std::wstring wname;
8690 if (!Utf8toWStr(name, wname))
8692
8693 if (wname.size() > MAX_PLAYER_NAME)
8694 return CHAR_NAME_TOO_LONG;
8695
8696 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
8697 if (wname.size() < minName)
8698 return CHAR_NAME_TOO_SHORT;
8699
8700 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
8701 if (!isValidString(wname, strictMask, false, create))
8703
8704 wstrToLower(wname);
8705 for (size_t i = 2; i < wname.size(); ++i)
8706 if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
8708
8709 return sDB2Manager.ValidateName(wname, locale);
8710}
8711
8712bool ObjectMgr::IsValidCharterName(std::string_view name)
8713{
8714 std::wstring wname;
8715 if (!Utf8toWStr(name, wname))
8716 return false;
8717
8718 if (wname.size() > MAX_CHARTER_NAME)
8719 return false;
8720
8721 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
8722 if (wname.size() < minName)
8723 return false;
8724
8725 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
8726
8727 return isValidString(wname, strictMask, true);
8728}
8729
8731{
8732 std::wstring wname;
8733 if (!Utf8toWStr(name, wname))
8734 return PET_NAME_INVALID;
8735
8736 if (wname.size() > MAX_PET_NAME)
8737 return PET_NAME_TOO_LONG;
8738
8739 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
8740 if (wname.size() < minName)
8741 return PET_NAME_TOO_SHORT;
8742
8743 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
8744 if (!isValidString(wname, strictMask, false))
8746
8747 return PET_NAME_SUCCESS;
8748}
8749
8751{
8752 uint32 oldMSTime = getMSTime();
8753
8754 _gameObjectForQuestStore.clear(); // need for reload case
8755
8756 if (_gameObjectTemplateStore.empty())
8757 {
8758 TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
8759 return;
8760 }
8761
8762 uint32 count = 0;
8763
8764 // collect GO entries for GO that must activated
8765 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
8766 {
8767 switch (gameObjectTemplatePair.second.type)
8768 {
8770 break;
8772 {
8773 // scan GO chest with loot including quest items
8774 // find quest loot for GO
8775 if (gameObjectTemplatePair.second.chest.questID
8776 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestLoot)
8777 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPersonalLoot)
8778 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPushLoot))
8779 break;
8780 continue;
8781 }
8783 {
8784 if (gameObjectTemplatePair.second.generic.questID > 0) //quests objects
8785 break;
8786 continue;
8787 }
8789 {
8790 if (gameObjectTemplatePair.second.goober.questID > 0) //quests objects
8791 break;
8792 continue;
8793 }
8795 {
8796 // scan GO chest with loot including quest items
8797 // find quest loot for GO
8798 if (LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.gatheringNode.chestLoot))
8799 break;
8800 continue;
8801 }
8802 default:
8803 continue;
8804 }
8805
8806 _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
8807 ++count;
8808 }
8809
8810 TC_LOG_INFO("server.loading", ">> Loaded {} GameObjects for quests in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8811}
8812
8814{
8815 uint32 oldMSTime = getMSTime();
8816
8817 _trinityStringStore.clear(); // for reload case
8818
8819 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");
8820 if (!result)
8821 {
8822 TC_LOG_INFO("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum.");
8823 return false;
8824 }
8825
8826 do
8827 {
8828 Field* fields = result->Fetch();
8829
8830 uint32 entry = fields[0].GetUInt32();
8831
8832 TrinityString& data = _trinityStringStore[entry];
8833
8834 data.Content.resize(DEFAULT_LOCALE + 1);
8835
8836 for (int8 i = OLD_TOTAL_LOCALES - 1; i >= 0; --i)
8837 AddLocaleString(fields[i + 1].GetStringView(), LocaleConstant(i), data.Content);
8838 }
8839 while (result->NextRow());
8840
8841 TC_LOG_INFO("server.loading", ">> Loaded {} trinity strings in {} ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
8842 return true;
8843}
8844
8845char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
8846{
8847 if (TrinityString const* ts = GetTrinityString(entry))
8848 {
8849 if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
8850 return ts->Content[locale].c_str();
8851 return ts->Content[DEFAULT_LOCALE].c_str();
8852 }
8853
8854 TC_LOG_ERROR("sql.sql", "Trinity string entry {} not found in DB.", entry);
8855 return "<error>";
8856}
8857
8859{
8860 uint32 oldMSTime = getMSTime();
8861
8862 _fishingBaseForAreaStore.clear(); // for reload case
8863
8864 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
8865
8866 if (!result)
8867 {
8868 TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
8869 return;
8870 }
8871
8872 uint32 count = 0;
8873
8874 do
8875 {
8876 Field* fields = result->Fetch();
8877 uint32 entry = fields[0].GetUInt32();
8878 int32 skill = fields[1].GetInt16();
8879
8880 AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
8881 if (!fArea)
8882 {
8883 TC_LOG_ERROR("sql.sql", "AreaId {} defined in `skill_fishing_base_level` does not exist", entry);
8884 continue;
8885 }
8886
8887 _fishingBaseForAreaStore[entry] = skill;
8888 ++count;
8889 }
8890 while (result->NextRow());
8891
8892 TC_LOG_INFO("server.loading", ">> Loaded {} areas for fishing base skill level in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8893}
8894
8896{
8897 uint32 oldMSTime = getMSTime();
8898
8899 QueryResult result = WorldDatabase.Query("SELECT ID, Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9, Value10, "
8900 " Value11, Value12, Value13, Value14, Value15, Value16 FROM skill_tiers");
8901
8902 if (!result)
8903 {
8904 TC_LOG_ERROR("server.loading", ">> Loaded 0 skill max values. DB table `skill_tiers` is empty.");
8905 return;
8906 }
8907
8908 do
8909 {
8910 Field* fields = result->Fetch();
8911 uint32 id = fields[0].GetUInt32();
8912 SkillTiersEntry& tier = _skillTiers[id];
8913 for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
8914 tier.Value[i] = fields[1 + i].GetUInt32();
8915
8916 } while (result->NextRow());
8917
8918 TC_LOG_INFO("server.loading", ">> Loaded {} skill max values in {} ms", uint32(_skillTiers.size()), GetMSTimeDiffToNow(oldMSTime));
8919}
8920
8921bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
8922{
8923 // get main part of the name
8924 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
8925 // prepare flags
8926 bool x = true;
8927 bool y = true;
8928
8929 // check declined names
8930 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
8931 {
8932 std::wstring wname;
8933 if (!Utf8toWStr(names.name[i], wname))
8934 return false;
8935
8936 if (mainpart != GetMainPartOfName(wname, i+1))
8937 x = false;
8938
8939 if (w_ownname != wname)
8940 y = false;
8941 }
8942 return (x || y);
8943}
8944
8946{
8947 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
8948 if (i!= _areaTriggerScriptStore.end())
8949 return i->second;
8950 return 0;
8951}
8952
8954{
8955 return SpellScriptsBounds(_spellScriptsStore.equal_range(spellId));
8956}
8957
8959{
8960 EventScriptContainer::const_iterator i = _eventScriptStore.find(eventId);
8961 if (i != _eventScriptStore.end())
8962 return i->second;
8963 return 0;
8964}
8965
8966// this allows calculating base reputations to offline players, just by race and class
8967int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
8968{
8969 if (!factionEntry)
8970 return 0;
8971
8972 uint32 classMask = 1 << (playerClass - 1);
8973
8974 for (uint8 i = 0; i < 4; ++i)
8975 {
8976 if ((!factionEntry->ReputationClassMask[i] ||
8977 factionEntry->ReputationClassMask[i] & classMask) &&
8978 (factionEntry->ReputationRaceMask[i].IsEmpty() ||
8979 factionEntry->ReputationRaceMask[i].HasRace(race)))
8980 return factionEntry->ReputationBase[i];
8981 }
8982
8983 return 0;
8984}
8985
8987{
8988 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
8989 if (!skill)
8990 return SKILL_RANGE_NONE;
8991
8992 if (sObjectMgr->GetSkillTier(rcEntry->SkillTierID))
8993 return SKILL_RANGE_RANK;
8994
8995 if (rcEntry->SkillID == SKILL_RUNEFORGING)
8996 return SKILL_RANGE_MONO;
8997
8998 switch (skill->CategoryID)
8999 {
9001 return SKILL_RANGE_MONO;
9003 return SKILL_RANGE_LANGUAGE;
9004 }
9005
9006 return SKILL_RANGE_LEVEL;
9007}
9008
9010{
9011 uint32 oldMSTime = getMSTime();
9012
9013 _gameTeleStore.clear(); // for reload case
9014
9015 // 0 1 2 3 4 5 6
9016 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
9017
9018 if (!result)
9019 {
9020 TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
9021 return;
9022 }
9023
9024 uint32 count = 0;
9025
9026 do
9027 {
9028 Field* fields = result->Fetch();
9029
9030 uint32 id = fields[0].GetUInt32();
9031
9032 GameTele gt;
9033
9034 gt.position_x = fields[1].GetFloat();
9035 gt.position_y = fields[2].GetFloat();
9036 gt.position_z = fields[3].GetFloat();
9037 gt.orientation = fields[4].GetFloat();
9038 gt.mapId = fields[5].GetUInt16();
9039 gt.name = fields[6].GetString();
9040
9042 {
9043 TC_LOG_ERROR("sql.sql", "Wrong position for id {} (name: {}) in `game_tele` table, ignoring.", id, gt.name);
9044 continue;
9045 }
9046
9047 if (!Utf8toWStr(gt.name, gt.wnameLow))
9048 {
9049 TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_tele` table, ignoring.", id);
9050 continue;
9051 }
9052
9054
9055 _gameTeleStore[id] = gt;
9056
9057 ++count;
9058 }
9059 while (result->NextRow());
9060
9061 TC_LOG_INFO("server.loading", ">> Loaded {} GameTeleports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9062}
9063
9064GameTele const* ObjectMgr::GetGameTele(std::string_view name) const
9065{
9066 // explicit name case
9067 std::wstring wname;
9068 if (!Utf8toWStr(name, wname))
9069 return nullptr;
9070
9071 // converting string that we try to find to lower case
9072 wstrToLower(wname);
9073
9074 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
9075 GameTele const* alt = nullptr;
9076 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9077 {
9078 if (itr->second.wnameLow == wname)
9079 return &itr->second;
9080 else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
9081 alt = &itr->second;
9082 }
9083
9084 return alt;
9085}
9086
9087GameTele const* ObjectMgr::GetGameTeleExactName(std::string_view name) const
9088{
9089 // explicit name case
9090 std::wstring wname;
9091 if (!Utf8toWStr(name, wname))
9092 return nullptr;
9093
9094 // converting string that we try to find to lower case
9095 wstrToLower(wname);
9096
9097 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9098 {
9099 if (itr->second.wnameLow == wname)
9100 return &itr->second;
9101 }
9102
9103 return nullptr;
9104}
9105
9107{
9108 // find max id
9109 uint32 new_id = 0;
9110 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9111 if (itr->first > new_id)
9112 new_id = itr->first;
9113
9114 // use next
9115 ++new_id;
9116
9117 if (!Utf8toWStr(tele.name, tele.wnameLow))
9118 return false;
9119
9120 wstrToLower(tele.wnameLow);
9121
9122 _gameTeleStore[new_id] = tele;
9123
9125
9126 stmt->setUInt32(0, new_id);
9127 stmt->setFloat(1, tele.position_x);
9128 stmt->setFloat(2, tele.position_y);
9129 stmt->setFloat(3, tele.position_z);
9130 stmt->setFloat(4, tele.orientation);
9131 stmt->setUInt16(5, uint16(tele.mapId));
9132 stmt->setString(6, tele.name);
9133
9134 WorldDatabase.Execute(stmt);
9135
9136 return true;
9137}
9138
9139bool ObjectMgr::DeleteGameTele(std::string_view name)
9140{
9141 // explicit name case
9142 std::wstring wname;
9143 if (!Utf8toWStr(name, wname))
9144 return false;
9145
9146 // converting string that we try to find to lower case
9147 wstrToLower(wname);
9148
9149 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9150 {
9151 if (itr->second.wnameLow == wname)
9152 {
9154
9155 stmt->setString(0, itr->second.name);
9156
9157 WorldDatabase.Execute(stmt);
9158
9159 _gameTeleStore.erase(itr);
9160 return true;
9161 }
9162 }
9163
9164 return false;
9165}
9166
9168{
9169 uint32 oldMSTime = getMSTime();
9170
9171 _mailLevelRewardStore.clear(); // for reload case
9172
9173 // 0 1 2 3
9174 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
9175
9176 if (!result)
9177 {
9178 TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
9179 return;
9180 }
9181
9182 uint32 count = 0;
9183
9184 do
9185 {
9186 Field* fields = result->Fetch();
9187
9188 uint8 level = fields[0].GetUInt8();
9189 Trinity::RaceMask<uint64> raceMask = { fields[1].GetUInt64() };
9190 uint32 mailTemplateId = fields[2].GetUInt32();
9191 uint32 senderEntry = fields[3].GetUInt32();
9192
9193 if (level > MAX_LEVEL)
9194 {
9195 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level {} that more supported by client ({}), ignoring.", level, MAX_LEVEL);
9196 continue;
9197 }
9198
9199 if ((raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
9200 {
9201 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask ({}) for level {} that not include any player races, ignoring.", raceMask.RawValue, level);
9202 continue;
9203 }
9204
9205 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
9206 {
9207 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId ({}) for level {} that invalid not include any player races, ignoring.", mailTemplateId, level);
9208 continue;
9209 }
9210
9211 if (!GetCreatureTemplate(senderEntry))
9212 {
9213 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has nonexistent sender creature entry ({}) for level {} that invalid not include any player races, ignoring.", senderEntry, level);
9214 continue;
9215 }
9216
9217 _mailLevelRewardStore[level].emplace_back(raceMask, mailTemplateId, senderEntry);
9218
9219 ++count;
9220 }
9221 while (result->NextRow());
9222
9223 TC_LOG_INFO("server.loading", ">> Loaded {} level dependent mail rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9224}
9225
9227{
9228 uint32 oldMSTime = getMSTime();
9229
9230 // For reload case
9231 _trainers.clear();
9232
9233 std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
9234 if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
9235 {
9236 do
9237 {
9238 Field* fields = trainerSpellsResult->Fetch();
9239
9240 Trainer::Spell spell;
9241 uint32 trainerId = fields[0].GetUInt32();
9242 spell.SpellId = fields[1].GetUInt32();
9243 spell.MoneyCost = fields[2].GetUInt32();
9244 spell.ReqSkillLine = fields[3].GetUInt32();
9245 spell.ReqSkillRank = fields[4].GetUInt32();
9246 spell.ReqAbility[0] = fields[5].GetUInt32();
9247 spell.ReqAbility[1] = fields[6].GetUInt32();
9248 spell.ReqAbility[2] = fields[7].GetUInt32();
9249 spell.ReqLevel = fields[8].GetUInt8();
9250
9251 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId, DIFFICULTY_NONE);
9252 if (!spellInfo)
9253 {
9254 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) for TrainerId {}, ignoring", spell.SpellId, trainerId);
9255 continue;
9256 }
9257
9258 if (spell.ReqSkillLine && !sSkillLineStore.LookupEntry(spell.ReqSkillLine))
9259 {
9260 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing skill (ReqSkillLine: {}) for TrainerId {} and SpellId {}, ignoring",
9261 spell.ReqSkillLine, trainerId, spell.SpellId);
9262 continue;
9263 }
9264
9265 bool allReqValid = true;
9266 for (std::size_t i = 0; i < spell.ReqAbility.size(); ++i)
9267 {
9268 uint32 requiredSpell = spell.ReqAbility[i];
9269 if (requiredSpell && !sSpellMgr->GetSpellInfo(requiredSpell, DIFFICULTY_NONE))
9270 {
9271 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (ReqAbility{}: {}) for TrainerId {} and SpellId {}, ignoring",
9272 i + 1, requiredSpell, trainerId, spell.SpellId);
9273 allReqValid = false;
9274 }
9275 }
9276
9277 if (!allReqValid)
9278 continue;
9279
9280 spellsByTrainer[trainerId].push_back(spell);
9281
9282 } while (trainerSpellsResult->NextRow());
9283 }
9284
9285 if (QueryResult trainersResult = WorldDatabase.Query("SELECT Id, Type, Greeting FROM trainer"))
9286 {
9287 do
9288 {
9289 Field* fields = trainersResult->Fetch();
9290 uint32 trainerId = fields[0].GetUInt32();
9291 Trainer::Type trainerType = Trainer::Type(fields[1].GetUInt8());
9292 std::string_view greeting = fields[2].GetStringView();
9293 std::vector<Trainer::Spell> spells;
9294 auto spellsItr = spellsByTrainer.find(trainerId);
9295 if (spellsItr != spellsByTrainer.end())
9296 {
9297 spells = std::move(spellsItr->second);
9298 spellsByTrainer.erase(spellsItr);
9299 }
9300
9301 _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, greeting, std::move(spells)));
9302
9303 } while (trainersResult->NextRow());
9304 }
9305
9306 for (auto const& unusedSpells : spellsByTrainer)
9307 {
9308 for (Trainer::Spell const& unusedSpell : unusedSpells.second)
9309 {
9310 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing trainer (TrainerId: {}) for SpellId {}, ignoring", unusedSpells.first, unusedSpell.SpellId);
9311 }
9312 }
9313
9314 if (QueryResult trainerLocalesResult = WorldDatabase.Query("SELECT Id, locale, Greeting_lang FROM trainer_locale"))
9315 {
9316 do
9317 {
9318 Field* fields = trainerLocalesResult->Fetch();
9319 uint32 trainerId = fields[0].GetUInt32();
9320 std::string localeName = fields[1].GetString();
9321
9322 LocaleConstant locale = GetLocaleByName(localeName);
9323 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
9324 continue;
9325
9326 if (Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId))
9327 trainer->AddGreetingLocale(locale, fields[2].GetString());
9328 else
9329 TC_LOG_ERROR("sql.sql", "Table `trainer_locale` references non-existing trainer (TrainerId: {}) for locale {}, ignoring",
9330 trainerId, localeName);
9331
9332 } while (trainerLocalesResult->NextRow());
9333 }
9334
9335 TC_LOG_INFO("server.loading", ">> Loaded {} Trainers in {} ms", _trainers.size(), GetMSTimeDiffToNow(oldMSTime));
9336}
9337
9339{
9340 uint32 oldMSTime = getMSTime();
9341
9342 _creatureDefaultTrainers.clear();
9343
9344 if (QueryResult result = WorldDatabase.Query("SELECT CreatureID, TrainerID, MenuID, OptionID FROM creature_trainer"))
9345 {
9346 do
9347 {
9348 Field* fields = result->Fetch();
9349 uint32 creatureId = fields[0].GetUInt32();
9350 uint32 trainerId = fields[1].GetUInt32();
9351 uint32 gossipMenuId = fields[2].GetUInt32();
9352 uint32 gossipOptionId = fields[3].GetUInt32();
9353
9354 if (!GetCreatureTemplate(creatureId))
9355 {
9356 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing creature template (CreatureID: {}), ignoring", creatureId);
9357 continue;
9358 }
9359
9360 if (!GetTrainer(trainerId))
9361 {
9362 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing trainer (TrainerID: {}) for CreatureID {} MenuID {} OptionID {}, ignoring",
9363 trainerId, creatureId, gossipMenuId, gossipOptionId);
9364 continue;
9365 }
9366
9367 if (gossipMenuId || gossipOptionId)
9368 {
9369 Trinity::IteratorPair<GossipMenuItemsContainer::const_iterator> gossipMenuItems = GetGossipMenuItemsMapBounds(gossipMenuId);
9370 auto gossipOptionItr = std::find_if(gossipMenuItems.begin(), gossipMenuItems.end(), [gossipOptionId](std::pair<uint32 const, GossipMenuItems> const& entry)
9371 {
9372 return entry.second.OrderIndex == gossipOptionId;
9373 });
9374 if (gossipOptionItr == gossipMenuItems.end())
9375 {
9376 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing gossip menu option (MenuID {} OptionID {}) for CreatureID {} and TrainerID {}, ignoring",
9377 gossipMenuId, gossipOptionId, creatureId, trainerId);
9378 continue;
9379 }
9380 }
9381
9382 _creatureDefaultTrainers[std::make_tuple(creatureId, gossipMenuId, gossipOptionId)] = trainerId;
9383 } while (result->NextRow());
9384 }
9385
9386 TC_LOG_INFO("server.loading", ">> Loaded {} default trainers in {} ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
9387}
9388
9389uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
9390{
9391 // find all items from the reference vendor
9393 stmt->setUInt32(0, uint32(item));
9394 PreparedQueryResult result = WorldDatabase.Query(stmt);
9395
9396 if (!result)
9397 return 0;
9398
9399 uint32 count = 0;
9400 do
9401 {
9402 Field* fields = result->Fetch();
9403
9404 int32 item_id = fields[0].GetInt32();
9405
9406 // if item is a negative, its a reference
9407 if (item_id < 0)
9408 count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
9409 else
9410 {
9411 VendorItem vItem;
9412 vItem.item = item_id;
9413 vItem.maxcount = fields[1].GetUInt32();
9414 vItem.incrtime = fields[2].GetUInt32();
9415 vItem.ExtendedCost = fields[3].GetUInt32();
9416 vItem.Type = fields[4].GetUInt8();
9417 vItem.PlayerConditionId = fields[6].GetUInt32();
9418 vItem.IgnoreFiltering = fields[7].GetBool();
9419
9420 for (std::string_view token : Trinity::Tokenize(fields[5].GetStringView(), ' ', false))
9421 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9422 vItem.BonusListIDs.push_back(*bonusListID);
9423
9424 if (!IsVendorItemValid(vendor, vItem, nullptr, skip_vendors))
9425 continue;
9426
9427 VendorItemData& vList = _cacheVendorItemStore[vendor];
9428 vList.AddItem(std::move(vItem));
9429 ++count;
9430 }
9431 } while (result->NextRow());
9432
9433 return count;
9434}
9435
9437{
9438 uint32 oldMSTime = getMSTime();
9439
9440 // For reload case
9441 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
9442 itr->second.Clear();
9443 _cacheVendorItemStore.clear();
9444
9445 std::set<uint32> skip_vendors;
9446
9447 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionID, IgnoreFiltering FROM npc_vendor ORDER BY entry, slot ASC");
9448 if (!result)
9449 {
9450 TC_LOG_ERROR("server.loading", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
9451 return;
9452 }
9453
9454 uint32 count = 0;
9455
9456 do
9457 {
9458 Field* fields = result->Fetch();
9459
9460 uint32 entry = fields[0].GetUInt32();
9461 int32 item_id = fields[1].GetInt32();
9462
9463 // if item is a negative, its a reference
9464 if (item_id < 0)
9465 count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
9466 else
9467 {
9468 VendorItem vItem;
9469 vItem.item = item_id;
9470 vItem.maxcount = fields[2].GetUInt32();
9471 vItem.incrtime = fields[3].GetUInt32();
9472 vItem.ExtendedCost = fields[4].GetUInt32();
9473 vItem.Type = fields[5].GetUInt8();
9474 vItem.PlayerConditionId = fields[7].GetUInt32();
9475 vItem.IgnoreFiltering = fields[8].GetBool();
9476
9477 for (std::string_view token : Trinity::Tokenize(fields[6].GetStringView(), ' ', false))
9478 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9479 vItem.BonusListIDs.push_back(*bonusListID);
9480
9481 if (!IsVendorItemValid(entry, vItem, nullptr, &skip_vendors))
9482 continue;
9483
9484 VendorItemData& vList = _cacheVendorItemStore[entry];
9485 vList.AddItem(std::move(vItem));
9486 ++count;
9487 }
9488 }
9489 while (result->NextRow());
9490
9491 TC_LOG_INFO("server.loading", ">> Loaded {} Vendors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9492}
9493
9495{
9496 uint32 oldMSTime = getMSTime();
9497
9498 _gossipMenusStore.clear();
9499
9500 // 0 1
9501 QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
9502
9503 if (!result)
9504 {
9505 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
9506 return;
9507 }
9508
9509 do
9510 {
9511 Field* fields = result->Fetch();
9512
9513 GossipMenus gMenu;
9514
9515 gMenu.MenuID = fields[0].GetUInt32();
9516 gMenu.TextID = fields[1].GetUInt32();
9517
9518 if (!GetNpcText(gMenu.TextID))
9519 {
9520 TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID {} is using non-existing TextID {}", gMenu.MenuID, gMenu.TextID);
9521 continue;
9522 }
9523
9524 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
9525 } while (result->NextRow());
9526
9527 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu IDs in {} ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
9528}
9529
9531{
9532 uint32 oldMSTime = getMSTime();
9533
9534 _gossipMenuItemsStore.clear();
9535
9536 QueryResult result = WorldDatabase.Query(
9537 // 0 1 2 3 4 5 6 7 8 9 10
9538 "SELECT MenuID, GossipOptionID, OptionID, OptionNpc, OptionText, OptionBroadcastTextID, Language, Flags, ActionMenuID, ActionPoiID, GossipNpcOptionID, "
9539 //11 12 13 14 15 16
9540 "BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID, SpellID, OverrideIconID "
9541 "FROM gossip_menu_option ORDER BY MenuID, OptionID");
9542
9543 if (!result)
9544 {
9545 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
9546 return;
9547 }
9548
9549 std::unordered_map<int32, int32> optionToNpcOption;
9550 for (GossipNPCOptionEntry const* npcOption : sGossipNPCOptionStore)
9551 optionToNpcOption[npcOption->GossipOptionID] = npcOption->ID;
9552
9553 do
9554 {
9555 Field* fields = result->Fetch();
9556
9557 GossipMenuItems gMenuItem;
9558
9559 gMenuItem.MenuID = fields[0].GetUInt32();
9560 gMenuItem.GossipOptionID = fields[1].GetInt32();
9561 gMenuItem.OrderIndex = fields[2].GetUInt32();
9562 gMenuItem.OptionNpc = GossipOptionNpc(fields[3].GetUInt8());
9563 gMenuItem.OptionText = fields[4].GetString();
9564 gMenuItem.OptionBroadcastTextID = fields[5].GetUInt32();
9565 gMenuItem.Language = fields[6].GetUInt32();
9566 gMenuItem.Flags = GossipOptionFlags(fields[7].GetInt32());
9567 gMenuItem.ActionMenuID = fields[8].GetUInt32();
9568 gMenuItem.ActionPoiID = fields[9].GetUInt32();
9569 if (!fields[10].IsNull())
9570 gMenuItem.GossipNpcOptionID = fields[10].GetInt32();
9571
9572 gMenuItem.BoxCoded = fields[11].GetBool();
9573 gMenuItem.BoxMoney = fields[12].GetUInt32();
9574 gMenuItem.BoxText = fields[13].GetString();
9575 gMenuItem.BoxBroadcastTextID = fields[14].GetUInt32();
9576 if (!fields[15].IsNull())
9577 gMenuItem.SpellID = fields[15].GetInt32();
9578
9579 if (!fields[16].IsNull())
9580 gMenuItem.OverrideIconID = fields[16].GetInt32();
9581
9582 if (gMenuItem.OptionNpc >= GossipOptionNpc::Count)
9583 {
9584 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has unknown NPC option id {}. Replacing with GossipOptionNpc::None", gMenuItem.MenuID, gMenuItem.OrderIndex, AsUnderlyingType(gMenuItem.OptionNpc));
9585 gMenuItem.OptionNpc = GossipOptionNpc::None;
9586 }
9587
9588 if (gMenuItem.OptionBroadcastTextID)
9589 {
9590 if (!sBroadcastTextStore.LookupEntry(gMenuItem.OptionBroadcastTextID))
9591 {
9592 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has non-existing or incompatible OptionBroadcastTextID {}, ignoring.", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.OptionBroadcastTextID);
9593 gMenuItem.OptionBroadcastTextID = 0;
9594 }
9595 }
9596
9597 if (gMenuItem.Language && !sLanguagesStore.LookupEntry(gMenuItem.Language))
9598 {
9599 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Language {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.Language);
9600 gMenuItem.Language = 0;
9601 }
9602
9603 if (gMenuItem.ActionMenuID && gMenuItem.OptionNpc != GossipOptionNpc::None)
9604 {
9605 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} can not use ActionMenuID for GossipOptionNpc different from GossipOptionNpc::None, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex);
9606 gMenuItem.ActionMenuID = 0;
9607 }
9608
9609 if (gMenuItem.ActionPoiID)
9610 {
9611 if (gMenuItem.OptionNpc != GossipOptionNpc::None)
9612 {
9613 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} can not use ActionPoiID for GossipOptionNpc different from GossipOptionNpc::None, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex);
9614 gMenuItem.ActionPoiID = 0;
9615 }
9616 else if (!GetPointOfInterest(gMenuItem.ActionPoiID))
9617 {
9618 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing ActionPoiID {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.ActionPoiID);
9619 gMenuItem.ActionPoiID = 0;
9620 }
9621 }
9622
9623 if (gMenuItem.GossipNpcOptionID)
9624 {
9625 if (!sGossipNPCOptionStore.LookupEntry(*gMenuItem.GossipNpcOptionID))
9626 {
9627 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing GossipNPCOption {}, ignoring",
9628 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.GossipNpcOptionID);
9629 gMenuItem.GossipNpcOptionID.reset();
9630 }
9631 }
9632 else if (int32 const* npcOptionId = Trinity::Containers::MapGetValuePtr(optionToNpcOption, gMenuItem.GossipOptionID))
9633 gMenuItem.GossipNpcOptionID = *npcOptionId;
9634
9635 if (gMenuItem.BoxBroadcastTextID)
9636 {
9637 if (!sBroadcastTextStore.LookupEntry(gMenuItem.BoxBroadcastTextID))
9638 {
9639 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has non-existing or incompatible BoxBroadcastTextID {}, ignoring.", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.BoxBroadcastTextID);
9640 gMenuItem.BoxBroadcastTextID = 0;
9641 }
9642 }
9643
9644 if (gMenuItem.SpellID)
9645 {
9646 if (!sSpellMgr->GetSpellInfo(*gMenuItem.SpellID, DIFFICULTY_NONE))
9647 {
9648 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Spell {}, ignoring",
9649 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.SpellID);
9650 gMenuItem.SpellID.reset();
9651 }
9652 }
9653
9654 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
9655 } while (result->NextRow());
9656
9657 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option entries in {} ms", _gossipMenuItemsStore.size(), GetMSTimeDiffToNow(oldMSTime));
9658}
9659
9661{
9662 uint32 oldMSTime = getMSTime();
9663
9664 _gossipMenuAddonStore.clear();
9665
9666 // 0 1
9667 QueryResult result = WorldDatabase.Query("SELECT MenuID, FriendshipFactionID FROM gossip_menu_addon");
9668
9669 if (!result)
9670 {
9671 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_addon IDs. DB table `gossip_menu_addon` is empty!");
9672 return;
9673 }
9674
9675 do
9676 {
9677 Field* fields = result->Fetch();
9678
9679 uint32 menuID = fields[0].GetUInt32();
9680 GossipMenuAddon& addon = _gossipMenuAddonStore[menuID];
9681 addon.FriendshipFactionID = fields[1].GetInt32();
9682
9683 if (FactionEntry const* faction = sFactionStore.LookupEntry(addon.FriendshipFactionID))
9684 {
9685 if (!sFriendshipReputationStore.LookupEntry(faction->FriendshipRepID))
9686 {
9687 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using FriendshipFactionID {} referencing non-existing FriendshipRepID {}",
9688 menuID, addon.FriendshipFactionID, faction->FriendshipRepID);
9689 addon.FriendshipFactionID = 0;
9690 }
9691 }
9692 else
9693 {
9694 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using non-existing FriendshipFactionID {}", menuID, addon.FriendshipFactionID);
9695 addon.FriendshipFactionID = 0;
9696 }
9697
9698 } while (result->NextRow());
9699
9700 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_addon IDs in {} ms", uint32(_gossipMenuAddonStore.size()), GetMSTimeDiffToNow(oldMSTime));
9701}
9702
9704{
9705 return Trinity::Containers::MapGetValuePtr(_trainers, trainerId);
9706}
9707
9708uint32 ObjectMgr::GetCreatureTrainerForGossipOption(uint32 creatureId, uint32 gossipMenuId, uint32 gossipOptionId) const
9709{
9710 auto itr = _creatureDefaultTrainers.find(std::make_tuple(creatureId, gossipMenuId, gossipOptionId));
9711 if (itr != _creatureDefaultTrainers.end())
9712 return itr->second;
9713
9714 return 0;
9715}
9716
9717void ObjectMgr::AddVendorItem(uint32 entry, VendorItem const& vItem, bool persist /*= true*/)
9718{
9719 VendorItemData& vList = _cacheVendorItemStore[entry];
9720 vList.AddItem(vItem);
9721
9722 if (persist)
9723 {
9725
9726 stmt->setUInt32(0, entry);
9727 stmt->setUInt32(1, vItem.item);
9728 stmt->setUInt8(2, vItem.maxcount);
9729 stmt->setUInt32(3, vItem.incrtime);
9730 stmt->setUInt32(4, vItem.ExtendedCost);
9731 stmt->setUInt8(5, vItem.Type);
9732
9733 WorldDatabase.Execute(stmt);
9734 }
9735}
9736
9737bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/)
9738{
9739 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
9740 if (iter == _cacheVendorItemStore.end())
9741 return false;
9742
9743 if (!iter->second.RemoveItem(item, type))
9744 return false;
9745
9746 if (persist)
9747 {
9749
9750 stmt->setUInt32(0, entry);
9751 stmt->setUInt32(1, item);
9752 stmt->setUInt8(2, type);
9753
9754 WorldDatabase.Execute(stmt);
9755 }
9756
9757 return true;
9758}
9759
9760bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
9761{
9762 CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry);
9763 if (!cInfo)
9764 {
9765 if (player)
9767 else
9768 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: {}), ignore", vendor_entry);
9769 return false;
9770 }
9771
9772 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
9773 {
9774 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9775 {
9776 if (player)
9778 else
9779 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: {}) without vendor flag, ignore", vendor_entry);
9780
9781 if (skip_vendors)
9782 skip_vendors->insert(vendor_entry);
9783 }
9784 return false;
9785 }
9786
9787 if ((vItem.Type == ITEM_VENDOR_TYPE_ITEM && !GetItemTemplate(vItem.item)) ||
9788 (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(vItem.item)))
9789 {
9790 if (player)
9792 else
9793 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: {}) have in item list non-existed item ({}, type {}), ignore", vendor_entry, vItem.item, vItem.Type);
9794 return false;
9795 }
9796
9797 if (vItem.PlayerConditionId && !sPlayerConditionStore.LookupEntry(vItem.PlayerConditionId))
9798 {
9799 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: {}) with invalid PlayerConditionId ({}) for vendor ({}), ignore", vItem.item, vItem.PlayerConditionId, vendor_entry);
9800 return false;
9801 }
9802
9803 if (vItem.ExtendedCost && !sItemExtendedCostStore.LookupEntry(vItem.ExtendedCost))
9804 {
9805 if (player)
9807 else
9808 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: {}) with wrong ExtendedCost ({}) for vendor ({}), ignore", vItem.item, vItem.ExtendedCost, vendor_entry);
9809 return false;
9810 }
9811
9812 if (vItem.Type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
9813 {
9814 if (vItem.maxcount > 0 && vItem.incrtime == 0)
9815 {
9816 if (player)
9817 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", vItem.maxcount);
9818 else
9819 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount` ({}) for item {} of vendor (Entry: {}) but `incrtime`=0, ignore", vItem.maxcount, vItem.item, vendor_entry);
9820 return false;
9821 }
9822 else if (vItem.maxcount == 0 && vItem.incrtime > 0)
9823 {
9824 if (player)
9825 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
9826 else
9827 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item {} of vendor (Entry: {}) but `incrtime`<>0, ignore", vItem.item, vendor_entry);
9828 return false;
9829 }
9830
9831 for (int32 bonusListId : vItem.BonusListIDs)
9832 {
9833 if (ItemBonusMgr::GetItemBonuses(bonusListId).empty())
9834 {
9835 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}) with invalid bonus {} for vendor ({}), ignore", vItem.item, bonusListId, vendor_entry);
9836 return false;
9837 }
9838 }
9839 }
9840
9841 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
9842 if (!vItems)
9843 return true; // later checks for non-empty lists
9844
9845 if (vItems->FindItemCostPair(vItem.item, vItem.ExtendedCost, vItem.Type))
9846 {
9847 if (player)
9849 else
9850 TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items {} (with extended cost {}, type {}) for vendor (Entry: {}), ignoring", vItem.item, vItem.ExtendedCost, vItem.Type, vendor_entry);
9851 return false;
9852 }
9853
9854 if (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && vItem.maxcount == 0)
9855 {
9856 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}, type: {}) with missing maxcount for vendor ({}), ignore", vItem.item, vItem.Type, vendor_entry);
9857 return false;
9858 }
9859
9860 return true;
9861}
9862
9864{
9865 // We insert an empty placeholder here so we can use the
9866 // script id 0 as dummy for "no script found".
9867 [[maybe_unused]] uint32 const id = insert("", false);
9868
9869 ASSERT(id == 0);
9870}
9871
9873{
9874 IndexToName.reserve(capacity);
9875}
9876
9877uint32 ObjectMgr::ScriptNameContainer::insert(std::string const& scriptName, bool isScriptNameBound)
9878{
9879 auto result = NameToIndex.try_emplace(scriptName, static_cast<uint32>(NameToIndex.size()), isScriptNameBound);
9880 if (result.second)
9881 {
9882 ASSERT(NameToIndex.size() <= std::numeric_limits<uint32>::max());
9883 IndexToName.emplace_back(result.first);
9884 }
9885
9886 return result.first->second.Id;
9887}
9888
9890{
9891 return IndexToName.size();
9892}
9893
9894ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(size_t index) const
9895{
9896 return index < IndexToName.size() ? IndexToName[index] : end();
9897}
9898
9899ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(std::string const& name) const
9900{
9901 // assume "" is the first element
9902 if (name.empty())
9903 return end();
9904
9905 return NameToIndex.find(name);
9906}
9907
9908ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::end() const
9909{
9910 return NameToIndex.end();
9911}
9912
9913std::unordered_set<std::string> ObjectMgr::ScriptNameContainer::GetAllDBScriptNames() const
9914{
9915 std::unordered_set<std::string> scriptNames;
9916
9917 for (std::pair<std::string const, Entry> const& entry : NameToIndex)
9918 {
9919 if (entry.second.IsScriptDatabaseBound)
9920 {
9921 scriptNames.insert(entry.first);
9922 }
9923 }
9924
9925 return scriptNames;
9926}
9927
9928std::unordered_set<std::string> ObjectMgr::GetAllDBScriptNames() const
9929{
9930 return _scriptNamesStore.GetAllDBScriptNames();
9931}
9932
9933std::string const& ObjectMgr::GetScriptName(uint32 id) const
9934{
9935 auto const itr = _scriptNamesStore.find(id);
9936 if (itr != _scriptNamesStore.end())
9937 {
9938 return itr->first;
9939 }
9940 else
9941 {
9942 static std::string const empty;
9943 return empty;
9944 }
9945}
9946
9948{
9949 auto const itr = _scriptNamesStore.find(id);
9950 if (itr != _scriptNamesStore.end())
9951 {
9952 return itr->second.IsScriptDatabaseBound;
9953 }
9954 else
9955 {
9956 return false;
9957 }
9958}
9959
9960uint32 ObjectMgr::GetScriptId(std::string const& name, bool isDatabaseBound)
9961{
9962 return _scriptNamesStore.insert(name, isDatabaseBound);
9963}
9964
9966{
9967 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
9968
9969 if (it != _creatureBaseStatsStore.end())
9970 return &(it->second);
9971
9972 struct DefaultCreatureBaseStats : public CreatureBaseStats
9973 {
9974 DefaultCreatureBaseStats()
9975 {
9976 BaseMana = 0;
9977 AttackPower = 0;
9978 RangedAttackPower = 0;
9979 }
9980 };
9981 static const DefaultCreatureBaseStats defStats;
9982 return &defStats;
9983}
9984
9986{
9987 uint32 oldMSTime = getMSTime();
9988 // 0 1 2 3 4
9989 QueryResult result = WorldDatabase.Query("SELECT level, class, basemana, attackpower, rangedattackpower FROM creature_classlevelstats");
9990
9991 if (!result)
9992 {
9993 TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
9994 return;
9995 }
9996
9997 uint32 count = 0;
9998 do
9999 {
10000 Field* fields = result->Fetch();
10001
10002 uint8 Level = fields[0].GetUInt8();
10003 uint8 Class = fields[1].GetUInt8();
10004
10005 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
10006 TC_LOG_ERROR("sql.sql", "Creature base stats for level {} has invalid class {}", Level, Class);
10007
10008 CreatureBaseStats stats;
10009
10010 stats.BaseMana = fields[2].GetUInt32();
10011
10012 stats.AttackPower = fields[3].GetUInt16();
10013 stats.RangedAttackPower = fields[4].GetUInt16();
10014
10015 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
10016
10017 ++count;
10018 }
10019 while (result->NextRow());
10020
10021 for (uint8 unitLevel = 1; unitLevel <= DEFAULT_MAX_LEVEL + 3; ++unitLevel)
10022 {
10023 for (uint8 unitClass = 1; unitClass <= MAX_UNIT_CLASSES; ++unitClass)
10024 {
10025 uint8 unitClassMask = 1 << (unitClass - 1);
10026 if (!_creatureBaseStatsStore.count(MAKE_PAIR16(unitLevel, unitClassMask)))
10027 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class {} level {}", unitClassMask, unitLevel);
10028 }
10029 }
10030
10031 TC_LOG_INFO("server.loading", ">> Loaded {} creature base stats in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10032}
10033
10035{
10036 uint32 oldMSTime = getMSTime();
10037
10038 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
10039
10040 if (!result)
10041 {
10042 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
10043 return;
10044 }
10045
10046 uint32 count = 0;
10047
10048 do
10049 {
10050 Field* fields = result->Fetch();
10051
10052 uint32 alliance = fields[0].GetUInt32();
10053 uint32 horde = fields[1].GetUInt32();
10054
10055 if (!sAchievementStore.LookupEntry(alliance))
10056 TC_LOG_ERROR("sql.sql", "Achievement {} (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
10057 else if (!sAchievementStore.LookupEntry(horde))
10058 TC_LOG_ERROR("sql.sql", "Achievement {} (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
10059 else
10060 FactionChangeAchievements[alliance] = horde;
10061
10062 ++count;
10063 }
10064 while (result->NextRow());
10065
10066 TC_LOG_INFO("server.loading", ">> Loaded {} faction change achievement pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10067}
10068
10070{
10071 uint32 oldMSTime = getMSTime();
10072 uint32 count = 0;
10073
10074 for (std::pair<uint32 const, ItemTemplate> const& itemPair : _itemTemplateStore)
10075 {
10076 if (!itemPair.second.GetOtherFactionItemId())
10077 continue;
10078
10079 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_HORDE))
10080 FactionChangeItemsHordeToAlliance[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10081
10082 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
10083 FactionChangeItemsAllianceToHorde[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10084
10085 ++count;
10086 }
10087
10088 TC_LOG_INFO("server.loading", ">> Loaded {} faction change item pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10089}
10090
10092{
10093 uint32 oldMSTime = getMSTime();
10094
10095 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
10096
10097 if (!result)
10098 {
10099 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
10100 return;
10101 }
10102
10103 uint32 count = 0;
10104
10105 do
10106 {
10107 Field* fields = result->Fetch();
10108
10109 uint32 alliance = fields[0].GetUInt32();
10110 uint32 horde = fields[1].GetUInt32();
10111
10112 if (!GetQuestTemplate(alliance))
10113 TC_LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
10114 else if (!GetQuestTemplate(horde))
10115 TC_LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
10116 else
10117 FactionChangeQuests[alliance] = horde;
10118
10119 ++count;
10120 }
10121 while (result->NextRow());
10122
10123 TC_LOG_INFO("server.loading", ">> Loaded {} faction change quest pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10124}
10125
10127{
10128 uint32 oldMSTime = getMSTime();
10129
10130 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
10131
10132 if (!result)
10133 {
10134 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
10135 return;
10136 }
10137
10138 uint32 count = 0;
10139
10140 do
10141 {
10142 Field* fields = result->Fetch();
10143
10144 uint32 alliance = fields[0].GetUInt32();
10145 uint32 horde = fields[1].GetUInt32();
10146
10147 if (!sFactionStore.LookupEntry(alliance))
10148 TC_LOG_ERROR("sql.sql", "Reputation {} (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
10149 else if (!sFactionStore.LookupEntry(horde))
10150 TC_LOG_ERROR("sql.sql", "Reputation {} (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
10151 else
10152 FactionChangeReputation[alliance] = horde;
10153
10154 ++count;
10155 }
10156 while (result->NextRow());
10157
10158 TC_LOG_INFO("server.loading", ">> Loaded {} faction change reputation pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10159}
10160
10162{
10163 uint32 oldMSTime = getMSTime();
10164
10165 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
10166
10167 if (!result)
10168 {
10169 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
10170 return;
10171 }
10172
10173 uint32 count = 0;
10174
10175 do
10176 {
10177 Field* fields = result->Fetch();
10178
10179 uint32 alliance = fields[0].GetUInt32();
10180 uint32 horde = fields[1].GetUInt32();
10181
10182 if (!sSpellMgr->GetSpellInfo(alliance, DIFFICULTY_NONE))
10183 TC_LOG_ERROR("sql.sql", "Spell {} (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
10184 else if (!sSpellMgr->GetSpellInfo(horde, DIFFICULTY_NONE))
10185 TC_LOG_ERROR("sql.sql", "Spell {} (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
10186 else
10187 FactionChangeSpells[alliance] = horde;
10188
10189 ++count;
10190 }
10191 while (result->NextRow());
10192
10193 TC_LOG_INFO("server.loading", ">> Loaded {} faction change spell pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10194}
10195
10197{
10198 uint32 oldMSTime = getMSTime();
10199
10200 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
10201
10202 if (!result)
10203 {
10204 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
10205 return;
10206 }
10207
10208 uint32 count = 0;
10209
10210 do
10211 {
10212 Field* fields = result->Fetch();
10213
10214 uint32 alliance = fields[0].GetUInt32();
10215 uint32 horde = fields[1].GetUInt32();
10216
10217 if (!sCharTitlesStore.LookupEntry(alliance))
10218 TC_LOG_ERROR("sql.sql", "Title {} (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
10219 else if (!sCharTitlesStore.LookupEntry(horde))
10220 TC_LOG_ERROR("sql.sql", "Title {} (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
10221 else
10222 FactionChangeTitles[alliance] = horde;
10223
10224 ++count;
10225 }
10226 while (result->NextRow());
10227
10228 TC_LOG_INFO("server.loading", ">> Loaded {} faction change title pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10229}
10230
10232{
10233 for (PhaseEntry const* phase : sPhaseStore)
10234 _phaseInfoById.emplace(std::make_pair(phase->ID, PhaseInfoStruct{ phase->ID, std::unordered_set<uint32>{} }));
10235
10236 for (MapEntry const* map : sMapStore)
10237 if (map->ParentMapID != -1)
10238 _terrainSwapInfoById.emplace(std::make_pair(map->ID, TerrainSwapInfo{ map->ID, std::vector<uint32>{} }));
10239
10240 TC_LOG_INFO("server.loading", "Loading Terrain World Map definitions...");
10241 LoadTerrainWorldMaps();
10242
10243 TC_LOG_INFO("server.loading", "Loading Terrain Swap Default definitions...");
10244 LoadTerrainSwapDefaults();
10245
10246 TC_LOG_INFO("server.loading", "Loading Phase Area definitions...");
10247 LoadAreaPhases();
10248}
10249
10251{
10252 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10253 for (PhaseAreaInfo& phase : itr->second)
10254 phase.Conditions.clear();
10255}
10256
10258{
10259 uint32 oldMSTime = getMSTime();
10260
10261 // 0 1
10262 QueryResult result = WorldDatabase.Query("SELECT TerrainSwapMap, UiMapPhaseId FROM `terrain_worldmap`");
10263
10264 if (!result)
10265 {
10266 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain world maps. DB table `terrain_worldmap` is empty.");
10267 return;
10268 }
10269
10270 uint32 count = 0;
10271 do
10272 {
10273 Field* fields = result->Fetch();
10274
10275 uint32 mapId = fields[0].GetUInt32();
10276 uint32 uiMapPhaseId = fields[1].GetUInt32();
10277
10278 if (!sMapStore.LookupEntry(mapId))
10279 {
10280 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_worldmap` does not exist, skipped.", mapId);
10281 continue;
10282 }
10283
10284 if (!sDB2Manager.IsUiMapPhase(uiMapPhaseId))
10285 {
10286 TC_LOG_ERROR("sql.sql", "Phase {} defined in `terrain_worldmap` is not a valid terrain swap phase, skipped.", uiMapPhaseId);
10287 continue;
10288 }
10289
10290 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[mapId];
10291 terrainSwapInfo->Id = mapId;
10292 terrainSwapInfo->UiMapPhaseIDs.push_back(uiMapPhaseId);
10293
10294 ++count;
10295 } while (result->NextRow());
10296
10297 TC_LOG_INFO("server.loading", ">> Loaded {} terrain world maps in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10298}
10299
10301{
10302 uint32 oldMSTime = getMSTime();
10303
10304 // 0 1
10305 QueryResult result = WorldDatabase.Query("SELECT MapId, TerrainSwapMap FROM `terrain_swap_defaults`");
10306
10307 if (!result)
10308 {
10309 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain swap defaults. DB table `terrain_swap_defaults` is empty.");
10310 return;
10311 }
10312
10313 uint32 count = 0;
10314 do
10315 {
10316 Field* fields = result->Fetch();
10317
10318 uint32 mapId = fields[0].GetUInt32();
10319
10320 if (!sMapStore.LookupEntry(mapId))
10321 {
10322 TC_LOG_ERROR("sql.sql", "Map {} defined in `terrain_swap_defaults` does not exist, skipped.", mapId);
10323 continue;
10324 }
10325
10326 uint32 terrainSwap = fields[1].GetUInt32();
10327
10328 if (!sMapStore.LookupEntry(terrainSwap))
10329 {
10330 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_swap_defaults` does not exist, skipped.", terrainSwap);
10331 continue;
10332 }
10333
10334 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[terrainSwap];
10335 terrainSwapInfo->Id = terrainSwap;
10336 _terrainSwapInfoByMap[mapId].push_back(terrainSwapInfo);
10337
10338 ++count;
10339 } while (result->NextRow());
10340
10341 TC_LOG_INFO("server.loading", ">> Loaded {} terrain swap defaults in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10342}
10343
10345{
10346 uint32 oldMSTime = getMSTime();
10347
10348 // 0 1
10349 QueryResult result = WorldDatabase.Query("SELECT AreaId, PhaseId FROM `phase_area`");
10350
10351 if (!result)
10352 {
10353 TC_LOG_INFO("server.loading", ">> Loaded 0 phase areas. DB table `phase_area` is empty.");
10354 return;
10355 }
10356
10357 auto getOrCreatePhaseIfMissing = [this](uint32 phaseId)
10358 {
10359 PhaseInfoStruct* phaseInfo = &_phaseInfoById[phaseId];
10360 phaseInfo->Id = phaseId;
10361 return phaseInfo;
10362 };
10363
10364 uint32 count = 0;
10365 do
10366 {
10367 Field* fields = result->Fetch();
10368 uint32 area = fields[0].GetUInt32();
10369 uint32 phaseId = fields[1].GetUInt32();
10370 if (!sAreaTableStore.LookupEntry(area))
10371 {
10372 TC_LOG_ERROR("sql.sql", "Area {} defined in `phase_area` does not exist, skipped.", area);
10373 continue;
10374 }
10375
10376 if (!sPhaseStore.LookupEntry(phaseId))
10377 {
10378 TC_LOG_ERROR("sql.sql", "Phase {} defined in `phase_area` does not exist, skipped.", phaseId);
10379 continue;
10380 }
10381
10382 PhaseInfoStruct* phase = getOrCreatePhaseIfMissing(phaseId);
10383 phase->Areas.insert(area);
10384 _phaseInfoByArea[area].emplace_back(phase);
10385
10386 ++count;
10387 } while (result->NextRow());
10388
10389 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10390 {
10391 for (PhaseAreaInfo& phase : itr->second)
10392 {
10393 uint32 parentAreaId = itr->first;
10394 do
10395 {
10396 AreaTableEntry const* area = sAreaTableStore.LookupEntry(parentAreaId);
10397 if (!area)
10398 break;
10399
10400 parentAreaId = area->ParentAreaID;
10401 if (!parentAreaId)
10402 break;
10403
10404 if (std::vector<PhaseAreaInfo>* parentAreaPhases = Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, parentAreaId))
10405 for (PhaseAreaInfo& parentAreaPhase : *parentAreaPhases)
10406 if (parentAreaPhase.PhaseInfo->Id == phase.PhaseInfo->Id)
10407 parentAreaPhase.SubAreaExclusions.insert(itr->first);
10408
10409 } while (true);
10410 }
10411 }
10412
10413 TC_LOG_INFO("server.loading", ">> Loaded {} phase areas in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10414}
10415
10417{
10418 return std::any_of(Areas.begin(), Areas.end(), [areaId](uint32 areaToCheck)
10419 {
10420 return DB2Manager::IsInArea(areaId, areaToCheck);
10421 });
10422}
10423
10425{
10426 return Trinity::Containers::MapGetValuePtr(_phaseInfoById, phaseId);
10427}
10428
10429std::vector<PhaseAreaInfo> const* ObjectMgr::GetPhasesForArea(uint32 areaId) const
10430{
10431 return Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, areaId);
10432}
10433
10435{
10436 return Trinity::Containers::MapGetValuePtr(_terrainSwapInfoById, terrainSwapId);
10437}
10438
10440{
10441 return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
10442}
10443
10445{
10446 auto itr = _gameObjectTemplateAddonStore.find(entry);
10447 if (itr != _gameObjectTemplateAddonStore.end())
10448 return &itr->second;
10449
10450 return nullptr;
10451}
10452
10454{
10455 return Trinity::Containers::MapGetValuePtr(_gameObjectOverrideStore, spawnId);
10456}
10457
10459{
10460 return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
10461}
10462
10464{
10465 return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
10466}
10467
10469{
10470 return Trinity::Containers::MapGetValuePtr(_vehicleTemplateStore, veh->GetCreatureEntry());
10471}
10472
10474{
10475 if (Creature* cre = veh->GetBase()->ToCreature())
10476 {
10477 // Give preference to GUID-based accessories
10478 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
10479 if (itr != _vehicleAccessoryStore.end())
10480 return &itr->second;
10481 }
10482
10483 // Otherwise return entry-based
10484 VehicleAccessoryTemplateContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
10485 if (itr != _vehicleTemplateAccessoryStore.end())
10486 return &itr->second;
10487 return nullptr;
10488}
10489
10491{
10492 if (race >= MAX_RACES)
10493 return nullptr;
10494 if (class_ >= MAX_CLASSES)
10495 return nullptr;
10496 auto const& info = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
10497 if (!info)
10498 return nullptr;
10499 return info->get();
10500}
10501
10503{
10504 uint32 oldMSTime = getMSTime();
10505 _raceUnlockRequirementStore.clear();
10506
10507 // 0 1 2
10508 QueryResult result = WorldDatabase.Query("SELECT raceID, expansion, achievementId FROM `race_unlock_requirement`");
10509
10510 if (result)
10511 {
10512 do
10513 {
10514 Field* fields = result->Fetch();
10515
10516 uint8 raceID = fields[0].GetUInt8();
10517 uint8 expansion = fields[1].GetUInt8();
10518 uint32 achievementId = fields[2].GetUInt32();
10519
10520 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10521 if (!raceEntry)
10522 {
10523 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` does not exists, skipped.", raceID);
10524 continue;
10525 }
10526
10527 if (expansion >= MAX_ACCOUNT_EXPANSIONS)
10528 {
10529 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect expansion {}, skipped.", raceID, expansion);
10530 continue;
10531 }
10532
10533 if (achievementId && !sAchievementStore.LookupEntry(achievementId))
10534 {
10535 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect achievement {}, skipped.", raceID, achievementId);
10536 continue;
10537 }
10538
10539 RaceUnlockRequirement& raceUnlockRequirement = _raceUnlockRequirementStore[raceID];
10540 raceUnlockRequirement.Expansion = expansion;
10541 raceUnlockRequirement.AchievementId = achievementId;
10542 }
10543 while (result->NextRow());
10544 TC_LOG_INFO("server.loading", ">> Loaded {} race expansion requirements in {} ms.", _raceUnlockRequirementStore.size(), GetMSTimeDiffToNow(oldMSTime));
10545 }
10546 else
10547 TC_LOG_INFO("server.loading", ">> Loaded 0 race expansion requirements. DB table `race_expansion_requirement` is empty.");
10548
10549 oldMSTime = getMSTime();
10550 _classExpansionRequirementStore.clear();
10551
10552 // 0 1 2 3
10553 result = WorldDatabase.Query("SELECT ClassID, RaceID, ActiveExpansionLevel, AccountExpansionLevel FROM `class_expansion_requirement`");
10554
10555 if (result)
10556 {
10557 std::map<uint8, std::map<uint8, std::pair<uint8, uint8>>> temp;
10558 std::array<uint8, MAX_CLASSES> minRequirementForClass = { };
10559 minRequirementForClass.fill(MAX_ACCOUNT_EXPANSIONS);
10560 uint32 count = 0;
10561 do
10562 {
10563 Field* fields = result->Fetch();
10564
10565 uint8 classID = fields[0].GetUInt8();
10566 uint8 raceID = fields[1].GetUInt8();
10567 uint8 activeExpansionLevel = fields[2].GetUInt8();
10568 uint8 accountExpansionLevel = fields[3].GetUInt8();
10569
10570 ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classID);
10571 if (!classEntry)
10572 {
10573 TC_LOG_ERROR("sql.sql", "Class {} (race {}) defined in `class_expansion_requirement` does not exists, skipped.",
10574 uint32(classID), uint32(raceID));
10575 continue;
10576 }
10577
10578 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10579 if (!raceEntry)
10580 {
10581 TC_LOG_ERROR("sql.sql", "Race {} (class {}) defined in `class_expansion_requirement` does not exists, skipped.",
10582 uint32(raceID), uint32(classID));
10583 continue;
10584 }
10585
10586 if (activeExpansionLevel >= MAX_EXPANSIONS)
10587 {
10588 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect ActiveExpansionLevel {}, skipped.",
10589 uint32(classID), uint32(raceID), activeExpansionLevel);
10590 continue;
10591 }
10592
10593 if (accountExpansionLevel >= MAX_ACCOUNT_EXPANSIONS)
10594 {
10595 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect AccountExpansionLevel {}, skipped.",
10596 uint32(classID), uint32(raceID), accountExpansionLevel);
10597 continue;
10598 }
10599
10600 temp[raceID][classID] = { activeExpansionLevel, accountExpansionLevel };
10601 minRequirementForClass[classID] = std::min(minRequirementForClass[classID], activeExpansionLevel);
10602
10603 ++count;
10604 }
10605 while (result->NextRow());
10606
10607 for (auto&& race : temp)
10608 {
10609 RaceClassAvailability& raceClassAvailability = _classExpansionRequirementStore.emplace_back();
10610
10611 raceClassAvailability.RaceID = race.first;
10612
10613 for (auto&& class_ : race.second)
10614 {
10615 ClassAvailability& classAvailability = raceClassAvailability.Classes.emplace_back();
10616
10617 classAvailability.ClassID = class_.first;
10618 classAvailability.ActiveExpansionLevel = class_.second.first;
10619 classAvailability.AccountExpansionLevel = class_.second.second;
10620 classAvailability.MinActiveExpansionLevel = minRequirementForClass[class_.first];
10621 }
10622 }
10623
10624 TC_LOG_INFO("server.loading", ">> Loaded {} class expansion requirements in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10625 }
10626 else
10627 TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
10628}
10629
10631{
10632 auto raceItr = std::find_if(_classExpansionRequirementStore.begin(), _classExpansionRequirementStore.end(), [raceId](RaceClassAvailability const& raceClass)
10633 {
10634 return raceClass.RaceID == raceId;
10635 });
10636 if (raceItr == _classExpansionRequirementStore.end())
10637 return nullptr;
10638
10639 auto classItr = std::find_if(raceItr->Classes.begin(), raceItr->Classes.end(), [classId](ClassAvailability const& classAvailability)
10640 {
10641 return classAvailability.ClassID == classId;
10642 });
10643 if (classItr == raceItr->Classes.end())
10644 return nullptr;
10645
10646 return &(*classItr);
10647}
10648
10650{
10651 for (RaceClassAvailability const& raceClassAvailability : _classExpansionRequirementStore)
10652 for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
10653 if (classAvailability.ClassID == classId)
10654 return &classAvailability;
10655
10656 return nullptr;
10657}
10658
10660{
10661 return Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
10662}
10663
10665{
10666 return Trinity::Containers::MapGetValuePtr(_jumpChargeParams, id);
10667}
10668
10670{
10671 uint32 oldMSTime = getMSTime();
10672
10673 // 0 1 2
10674 QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
10675
10676 if (!result)
10677 {
10678 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
10679 return;
10680 }
10681
10682 uint32 count = 0;
10683 do
10684 {
10685 Field* fields = result->Fetch();
10686
10687 uint32 entry = fields[0].GetUInt32();
10688 uint32 item = fields[1].GetUInt32();
10689 uint32 idx = fields[2].GetUInt32();
10690
10691 GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
10692 if (!goInfo)
10693 {
10694 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
10695 continue;
10696 };
10697
10698 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10699 if (!db2Data)
10700 {
10701 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
10702 continue;
10703 };
10704
10705 _gameObjectQuestItemStore[entry].push_back(item);
10706
10707 ++count;
10708 }
10709 while (result->NextRow());
10710
10711 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10712}
10713
10715{
10716 uint32 oldMSTime = getMSTime();
10717
10718 // 0 1 2 3
10719 QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
10720
10721 if (!result)
10722 {
10723 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
10724 return;
10725 }
10726
10727 uint32 count = 0;
10728 do
10729 {
10730 Field* fields = result->Fetch();
10731
10732 uint32 entry = fields[0].GetUInt32();
10733 Difficulty difficulty = Difficulty(fields[1].GetUInt8());
10734 uint32 item = fields[2].GetUInt32();
10735 uint32 idx = fields[3].GetUInt32();
10736
10737 CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
10738 if (!creatureInfo)
10739 {
10740 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, difficulty: {}, idx: {}), skipped", entry, difficulty, idx);
10741 continue;
10742 }
10743
10744 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10745 if (!db2Data)
10746 {
10747 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, difficulty: {}, idx: {}), skipped", item, entry, difficulty, idx);
10748 continue;
10749 }
10750
10751 _creatureQuestItemStore[std::make_pair(entry, difficulty)].push_back(item);
10752
10753 ++count;
10754 }
10755 while (result->NextRow());
10756
10757 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10758}
10759
10761{
10762 uint32 oldMSTime = getMSTime();
10763
10764 // 0 1
10765 QueryResult result = WorldDatabase.Query("SELECT CreatureId, CurrencyId FROM creature_quest_currency ORDER BY CreatureId, CurrencyId ASC");
10766
10767 if (!result)
10768 {
10769 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest currencies. DB table `creature_quest_currency` is empty.");
10770 return;
10771 }
10772
10773 uint32 count = 0;
10774 do
10775 {
10776 Field* fields = result->Fetch();
10777
10778 uint32 entry = fields[0].GetUInt32();
10779 int32 currency = fields[1].GetInt32();
10780
10781 if (!GetCreatureTemplate(entry))
10782 {
10783 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has data for nonexistent creature (entry: {}, currency: {}), skipped", entry, currency);
10784 continue;
10785 }
10786
10787 if (!sCurrencyTypesStore.HasRecord(currency))
10788 {
10789 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has nonexistent currency (ID: {}) in creature (entry: {}, currency: {}), skipped", currency, entry, currency);
10790 continue;
10791 }
10792
10793 _creatureQuestCurrenciesStore[entry].push_back(currency);
10794
10795 ++count;
10796 }
10797 while (result->NextRow());
10798
10799 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest currencies in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10800}
10801
10803{
10804 uint32 oldMSTime = getMSTime();
10805
10806 // cache disabled
10807 if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
10808 {
10809 TC_LOG_INFO("server.loading", ">> Query data caching is disabled. Skipped initialization.");
10810 return;
10811 }
10812
10814
10815 // Initialize Query data for creatures
10816 if (mask & QUERY_DATA_CREATURES)
10817 for (auto& creatureTemplatePair : _creatureTemplateStore)
10818 pool.PostWork([creature = &creatureTemplatePair.second]() { creature->InitializeQueryData(); });
10819
10820 // Initialize Query Data for gameobjects
10821 if (mask & QUERY_DATA_GAMEOBJECTS)
10822 for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
10823 pool.PostWork([gobj = &gameObjectTemplatePair.second]() { gobj->InitializeQueryData(); });
10824
10825 // Initialize Query Data for quests
10826 if (mask & QUERY_DATA_QUESTS)
10827 for (auto& questTemplatePair : _questTemplates)
10828 pool.PostWork([quest = questTemplatePair.second.get()]() { quest->InitializeQueryData(); });
10829
10830 // Initialize Quest POI data
10831 if (mask & QUERY_DATA_POIS)
10832 for (auto& poiWrapperPair : _questPOIStore)
10833 pool.PostWork([poi = &poiWrapperPair.second]() { poi->InitializeQueryData(); });
10834
10835 pool.Join();
10836
10837 TC_LOG_INFO("server.loading", ">> Initialized query cache data in {} ms", GetMSTimeDiffToNow(oldMSTime));
10838}
10839
10841{
10842 QueryDataBuffer << *this;
10843 QueryDataBuffer.shrink_to_fit();
10844}
10845
10847{
10848 uint32 oldMSTime = getMSTime();
10849 _sceneTemplateStore.clear();
10850
10851 QueryResult templates = WorldDatabase.Query("SELECT SceneId, Flags, ScriptPackageID, Encrypted, ScriptName FROM scene_template");
10852
10853 if (!templates)
10854 {
10855 TC_LOG_INFO("server.loading", ">> Loaded 0 scene templates. DB table `scene_template` is empty.");
10856 return;
10857 }
10858
10859 uint32 count = 0;
10860
10861 do
10862 {
10863 Field* fields = templates->Fetch();
10864
10865 uint32 sceneId = fields[0].GetUInt32();
10866 SceneTemplate& sceneTemplate = _sceneTemplateStore[sceneId];
10867 sceneTemplate.SceneId = sceneId;
10868 sceneTemplate.PlaybackFlags = static_cast<SceneFlag>(fields[1].GetUInt32());
10869 sceneTemplate.ScenePackageId = fields[2].GetUInt32();
10870 sceneTemplate.Encrypted = fields[3].GetUInt8() != 0;
10871 sceneTemplate.ScriptId = GetScriptId(fields[4].GetCString());
10872
10873 } while (templates->NextRow());
10874
10875 TC_LOG_INFO("server.loading", ">> Loaded {} scene templates in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10876}
10877
10879{
10880 uint32 oldMSTime = getMSTime();
10881 _playerChoices.clear();
10882
10883 // 0 1 2 3 4 5 6 7 8
10884 QueryResult choices = WorldDatabase.Query("SELECT ChoiceId, UiTextureKitId, SoundKitId, CloseSoundKitId, Duration, Question, PendingChoiceText, HideWarboardHeader, KeepOpenAfterChoice FROM playerchoice");
10885
10886 if (!choices)
10887 {
10888 TC_LOG_INFO("server.loading", ">> Loaded 0 player choices. DB table `playerchoice` is empty.");
10889 return;
10890 }
10891
10892 uint32 responseCount = 0;
10893 uint32 rewardCount = 0;
10894 uint32 itemRewardCount = 0;
10895 uint32 currencyRewardCount = 0;
10896 uint32 factionRewardCount = 0;
10897 uint32 itemChoiceRewardCount = 0;
10898 uint32 mawPowersCount = 0;
10899
10900 do
10901 {
10902 Field* fields = choices->Fetch();
10903
10904 int32 choiceId = fields[0].GetInt32();
10905
10906 PlayerChoice& choice = _playerChoices[choiceId];
10907 choice.ChoiceId = choiceId;
10908 choice.UiTextureKitId = fields[1].GetInt32();
10909 choice.SoundKitId = fields[2].GetUInt32();
10910 choice.CloseSoundKitId = fields[3].GetUInt32();
10911 choice.Duration = fields[4].GetInt64();
10912 choice.Question = fields[5].GetString();
10913 choice.PendingChoiceText = fields[6].GetString();
10914 choice.HideWarboardHeader = fields[7].GetBool();
10915 choice.KeepOpenAfterChoice = fields[8].GetBool();
10916
10917 } while (choices->NextRow());
10918
10919 // 0 1 2 3 4 5
10920 if (QueryResult responses = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ResponseIdentifier, ChoiceArtFileId, Flags, WidgetSetID, "
10921 // 6 7 8 9 10 11 12 13 14 15 16
10922 "UiTextureAtlasElementID, SoundKitID, GroupID, UiTextureKitID, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation, RewardQuestID "
10923 "FROM playerchoice_response ORDER BY `Index` ASC"))
10924 {
10925 do
10926 {
10927 Field* fields = responses->Fetch();
10928
10929 int32 choiceId = fields[0].GetInt32();
10930 int32 responseId = fields[1].GetInt32();
10931
10932 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
10933 if (!choice)
10934 {
10935 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
10936 continue;
10937 }
10938
10939 choice->Responses.emplace_back();
10940
10941 PlayerChoiceResponse& response = choice->Responses.back();
10942 response.ResponseId = responseId;
10943 response.ResponseIdentifier = fields[2].GetUInt16();
10944 response.ChoiceArtFileId = fields[3].GetInt32();
10945 response.Flags = fields[4].GetInt32();
10946 response.WidgetSetID = fields[5].GetUInt32();
10947 response.UiTextureAtlasElementID = fields[6].GetUInt32();
10948 response.SoundKitID = fields[7].GetUInt32();
10949 response.GroupID = fields[8].GetUInt8();
10950 response.UiTextureKitID = fields[9].GetInt32();
10951 response.Answer = fields[10].GetString();
10952 response.Header = fields[11].GetString();
10953 response.SubHeader = fields[12].GetString();
10954 response.ButtonTooltip = fields[13].GetString();
10955 response.Description = fields[14].GetString();
10956 response.Confirmation = fields[15].GetString();
10957 if (!fields[16].IsNull())
10958 response.RewardQuestID = fields[16].GetUInt32();
10959
10960 ++responseCount;
10961
10962 } while (responses->NextRow());
10963 }
10964
10965 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TitleId, PackageId, SkillLineId, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"))
10966 {
10967 do
10968 {
10969 Field* fields = rewards->Fetch();
10970
10971 int32 choiceId = fields[0].GetInt32();
10972 int32 responseId = fields[1].GetInt32();
10973
10974 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
10975 if (!choice)
10976 {
10977 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
10978 continue;
10979 }
10980
10981 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
10982 [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; });
10983 if (responseItr == choice->Responses.end())
10984 {
10985 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
10986 continue;
10987 }
10988
10989 PlayerChoiceResponseReward* reward = &responseItr->Reward.emplace();
10990 reward->TitleId = fields[2].GetInt32();
10991 reward->PackageId = fields[3].GetInt32();
10992 reward->SkillLineId = fields[4].GetInt32();
10993 reward->SkillPointCount = fields[5].GetUInt32();
10994 reward->ArenaPointCount = fields[6].GetUInt32();
10995 reward->HonorPointCount = fields[7].GetUInt32();
10996 reward->Money = fields[8].GetUInt64();
10997 reward->Xp = fields[9].GetUInt32();
10998 ++rewardCount;
10999
11000 if (reward->TitleId && !sCharTitlesStore.LookupEntry(reward->TitleId))
11001 {
11002 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing Title {} for ChoiceId {}, ResponseId: {}, set to 0",
11003 reward->TitleId, choiceId, responseId);
11004 reward->TitleId = 0;
11005 }
11006
11007 if (reward->PackageId && !sDB2Manager.GetQuestPackageItems(reward->PackageId))
11008 {
11009 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing QuestPackage {} for ChoiceId {}, ResponseId: {}, set to 0",
11010 reward->TitleId, choiceId, responseId);
11011 reward->PackageId = 0;
11012 }
11013
11014 if (reward->SkillLineId && !sSkillLineStore.LookupEntry(reward->SkillLineId))
11015 {
11016 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing SkillLine {} for ChoiceId {}, ResponseId: {}, set to 0",
11017 reward->TitleId, choiceId, responseId);
11018 reward->SkillLineId = 0;
11019 reward->SkillPointCount = 0;
11020 }
11021
11022 } while (rewards->NextRow());
11023 }
11024
11025 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item ORDER BY `Index` ASC"))
11026 {
11027 do
11028 {
11029 Field* fields = rewards->Fetch();
11030
11031 int32 choiceId = fields[0].GetInt32();
11032 int32 responseId = fields[1].GetInt32();
11033 uint32 itemId = fields[2].GetUInt32();
11034 std::vector<int32> bonusListIds;
11035 for (std::string_view token : Trinity::Tokenize(fields[3].GetStringView(), ' ', false))
11036 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11037 bonusListIds.push_back(*bonusListID);
11038 int32 quantity = fields[4].GetInt32();
11039
11040 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11041 if (!choice)
11042 {
11043 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11044 continue;
11045 }
11046
11047 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
11048 [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; });
11049 if (responseItr == choice->Responses.end())
11050 {
11051 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11052 continue;
11053 }
11054
11055 if (!responseItr->Reward)
11056 {
11057 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11058 choiceId, responseId);
11059 continue;
11060 }
11061
11062 if (!GetItemTemplate(itemId))
11063 {
11064 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11065 itemId, choiceId, responseId);
11066 continue;
11067 }
11068
11069 responseItr->Reward->Items.emplace_back(itemId, std::move(bonusListIds), quantity);
11070 ++itemRewardCount;
11071
11072 } while (rewards->NextRow());
11073 }
11074
11075 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, CurrencyId, Quantity FROM playerchoice_response_reward_currency ORDER BY `Index` ASC"))
11076 {
11077 do
11078 {
11079 Field* fields = rewards->Fetch();
11080
11081 int32 choiceId = fields[0].GetInt32();
11082 int32 responseId = fields[1].GetInt32();
11083 uint32 currencyId = fields[2].GetUInt32();
11084 int32 quantity = fields[3].GetInt32();
11085
11086 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11087 if (!choice)
11088 {
11089 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11090 continue;
11091 }
11092
11093 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
11094 [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; });
11095 if (responseItr == choice->Responses.end())
11096 {
11097 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11098 continue;
11099 }
11100
11101 if (!responseItr->Reward)
11102 {
11103 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11104 choiceId, responseId);
11105 continue;
11106 }
11107
11108 if (!sCurrencyTypesStore.LookupEntry(currencyId))
11109 {
11110 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing currency {} for ChoiceId {}, ResponseId: {}, skipped",
11111 currencyId, choiceId, responseId);
11112 continue;
11113 }
11114
11115 responseItr->Reward->Currency.emplace_back(currencyId, quantity);
11116 ++currencyRewardCount;
11117
11118 } while (rewards->NextRow());
11119 }
11120
11121 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, FactionId, Quantity FROM playerchoice_response_reward_faction ORDER BY `Index` ASC"))
11122 {
11123 do
11124 {
11125 Field* fields = rewards->Fetch();
11126
11127 int32 choiceId = fields[0].GetInt32();
11128 int32 responseId = fields[1].GetInt32();
11129 uint32 factionId = fields[2].GetUInt32();
11130 int32 quantity = fields[3].GetInt32();
11131
11132 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11133 if (!choice)
11134 {
11135 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11136 continue;
11137 }
11138
11139 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
11140 [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; });
11141 if (responseItr == choice->Responses.end())
11142 {
11143 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11144 continue;
11145 }
11146
11147 if (!responseItr->Reward)
11148 {
11149 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11150 choiceId, responseId);
11151 continue;
11152 }
11153
11154 if (!sFactionStore.LookupEntry(factionId))
11155 {
11156 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing faction {} for ChoiceId {}, ResponseId: {}, skipped",
11157 factionId, choiceId, responseId);
11158 continue;
11159 }
11160
11161 responseItr->Reward->Faction.emplace_back(factionId, quantity);
11162 ++factionRewardCount;
11163
11164 } while (rewards->NextRow());
11165 }
11166
11167 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item_choice ORDER BY `Index` ASC"))
11168 {
11169 do
11170 {
11171 Field* fields = rewards->Fetch();
11172
11173 int32 choiceId = fields[0].GetInt32();
11174 int32 responseId = fields[1].GetInt32();
11175 uint32 itemId = fields[2].GetUInt32();
11176 std::vector<int32> bonusListIds;
11177 for (std::string_view token : Trinity::Tokenize(fields[3].GetStringView(), ' ', false))
11178 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11179 bonusListIds.push_back(*bonusListID);
11180 int32 quantity = fields[4].GetInt32();
11181
11182 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11183 if (!choice)
11184 {
11185 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11186 continue;
11187 }
11188
11189 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
11190 [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; });
11191 if (responseItr == choice->Responses.end())
11192 {
11193 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11194 continue;
11195 }
11196
11197 if (!responseItr->Reward)
11198 {
11199 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11200 choiceId, responseId);
11201 continue;
11202 }
11203
11204 if (!GetItemTemplate(itemId))
11205 {
11206 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11207 itemId, choiceId, responseId);
11208 continue;
11209 }
11210
11211 responseItr->Reward->ItemChoices.emplace_back(itemId, std::move(bonusListIds), quantity);
11212 ++itemChoiceRewardCount;
11213
11214 } while (rewards->NextRow());
11215 }
11216
11217 if (QueryResult mawPowersResult = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TypeArtFileID, Rarity, RarityColor, SpellID, MaxStacks FROM playerchoice_response_maw_power"))
11218 {
11219 do
11220 {
11221 Field* fields = mawPowersResult->Fetch();
11222 int32 choiceId = fields[0].GetInt32();
11223 int32 responseId = fields[1].GetInt32();
11224
11225 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11226 if (!choice)
11227 {
11228 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11229 continue;
11230 }
11231
11232 auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(),
11233 [responseId](PlayerChoiceResponse const& playerChoiceResponse)
11234 {
11235 return playerChoiceResponse.ResponseId == responseId;
11236 });
11237 if (responseItr == choice->Responses.end())
11238 {
11239 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11240 continue;
11241 }
11242
11243 PlayerChoiceResponseMawPower& mawPower = responseItr->MawPower.emplace();
11244 mawPower.TypeArtFileID = fields[2].GetInt32();
11245 if (!fields[3].IsNull())
11246 mawPower.Rarity = fields[3].GetInt32();
11247 if (!fields[4].IsNull())
11248 mawPower.RarityColor = fields[4].GetUInt32();
11249 mawPower.SpellID = fields[5].GetInt32();
11250 mawPower.MaxStacks = fields[6].GetInt32();
11251
11252 ++mawPowersCount;
11253
11254 } while (mawPowersResult->NextRow());
11255 }
11256
11257 TC_LOG_INFO("server.loading", ">> Loaded {} player choices, {} responses, {} rewards, {} item rewards, {} currency rewards, {} faction rewards, {} item choice rewards and {} maw powers in {} ms.",
11258 _playerChoices.size(), responseCount, rewardCount, itemRewardCount, currencyRewardCount, factionRewardCount, itemChoiceRewardCount, mawPowersCount, GetMSTimeDiffToNow(oldMSTime));
11259}
11260
11262{
11263 uint32 oldMSTime = getMSTime();
11264
11265 // need for reload case
11266 _playerChoiceLocales.clear();
11267
11268 // 0 1 2
11269 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceId, locale, Question FROM playerchoice_locale"))
11270 {
11271 do
11272 {
11273 Field* fields = result->Fetch();
11274
11275 uint32 choiceId = fields[0].GetUInt32();
11276 std::string_view localeName = fields[1].GetStringView();
11277
11278 if (!GetPlayerChoice(choiceId))
11279 {
11280 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for locale {}, skipped", choiceId, localeName);
11281 continue;
11282 }
11283
11284 LocaleConstant locale = GetLocaleByName(localeName);
11285 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11286 continue;
11287
11288 PlayerChoiceLocale& data = _playerChoiceLocales[choiceId];
11289 AddLocaleString(fields[2].GetStringView(), locale, data.Question);
11290 } while (result->NextRow());
11291
11292 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _playerChoiceLocales.size(), GetMSTimeDiffToNow(oldMSTime));
11293 }
11294
11295 oldMSTime = getMSTime();
11296
11297 // 0 1 2 3 4 5 6 7 8
11298 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceID, ResponseID, locale, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation FROM playerchoice_response_locale"))
11299 {
11300 std::size_t count = 0;
11301 do
11302 {
11303 Field* fields = result->Fetch();
11304
11305 int32 choiceId = fields[0].GetInt32();
11306 int32 responseId = fields[1].GetInt32();
11307 std::string_view localeName = fields[2].GetStringView();
11308
11309 auto itr = _playerChoiceLocales.find(choiceId);
11310 if (itr == _playerChoiceLocales.end())
11311 {
11312 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for ResponseId {} locale {}, skipped",
11313 choiceId, responseId, localeName);
11314 continue;
11315 }
11316
11317 PlayerChoice const* playerChoice = ASSERT_NOTNULL(GetPlayerChoice(choiceId));
11318 if (!playerChoice->GetResponse(responseId))
11319 {
11320 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ResponseId: {} for ChoiceId {} locale {}, skipped",
11321 responseId, choiceId, localeName);
11322 continue;
11323 }
11324
11325 LocaleConstant locale = GetLocaleByName(localeName);
11326 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11327 continue;
11328
11329 PlayerChoiceResponseLocale& data = itr->second.Responses[responseId];
11330 AddLocaleString(fields[3].GetStringView(), locale, data.Answer);
11331 AddLocaleString(fields[4].GetStringView(), locale, data.Header);
11332 AddLocaleString(fields[5].GetStringView(), locale, data.SubHeader);
11333 AddLocaleString(fields[6].GetStringView(), locale, data.ButtonTooltip);
11334 AddLocaleString(fields[7].GetStringView(), locale, data.Description);
11335 AddLocaleString(fields[8].GetStringView(), locale, data.Confirmation);
11336 ++count;
11337 } while (result->NextRow());
11338
11339 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice Response locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11340 }
11341}
11342
11344{
11345 uint32 oldMSTime = getMSTime();
11346
11347 // need for reload case
11348 _jumpChargeParams.clear();
11349
11350 // 0 1 2 3 4 5 6
11351 QueryResult result = WorldDatabase.Query("SELECT id, speed, treatSpeedAsMoveTimeSeconds, jumpGravity, spellVisualId, progressCurveId, parabolicCurveId FROM jump_charge_params");
11352 if (!result)
11353 {
11354 return;
11355 }
11356
11357 do
11358 {
11359 Field* fields = result->Fetch();
11360
11361 int32 id = fields[0].GetInt32();
11362 float speed = fields[1].GetFloat();
11363 bool treatSpeedAsMoveTimeSeconds = fields[2].GetBool();
11364 float jumpGravity = fields[3].GetFloat();
11365 Optional<int32> spellVisualId;
11366 Optional<int32> progressCurveId;
11367 Optional<int32> parabolicCurveId;
11368
11369 if (speed <= 0.0f)
11370 {
11371 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid speed {} for id {}, set to default charge speed {}.",
11372 speed, id, SPEED_CHARGE);
11373 speed = SPEED_CHARGE;
11374 }
11375
11376 if (jumpGravity <= 0.0f)
11377 {
11378 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid jump gravity {} for id {}, set to default {}.",
11379 jumpGravity, id, Movement::gravity);
11380 jumpGravity = Movement::gravity;
11381 }
11382
11383 if (!fields[4].IsNull())
11384 {
11385 if (sSpellVisualStore.LookupEntry(fields[4].GetInt32()))
11386 spellVisualId = fields[4].GetInt32();
11387 else
11388 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing SpellVisual: {} for id {}, ignored.",
11389 fields[4].GetInt32(), id);
11390 }
11391
11392 if (!fields[5].IsNull())
11393 {
11394 if (sCurveStore.LookupEntry(fields[5].GetInt32()))
11395 progressCurveId = fields[5].GetInt32();
11396 else
11397 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing progress Curve: {} for id {}, ignored.",
11398 fields[4].GetInt32(), id);
11399 }
11400
11401 if (!fields[6].IsNull())
11402 {
11403 if (sCurveStore.LookupEntry(fields[6].GetInt32()))
11404 parabolicCurveId = fields[6].GetInt32();
11405 else
11406 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing parabolic Curve: {} for id {}, ignored.",
11407 fields[6].GetInt32(), id);
11408 }
11409
11410 JumpChargeParams& params = _jumpChargeParams[id];
11411 params.Speed = speed;
11412 params.TreatSpeedAsMoveTimeSeconds = treatSpeedAsMoveTimeSeconds;
11413 params.JumpGravity = jumpGravity;
11414 params.SpellVisualId = spellVisualId;
11415 params.ProgressCurveId = progressCurveId;
11416 params.ParabolicCurveId = parabolicCurveId;
11417
11418 } while (result->NextRow());
11419
11420 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _jumpChargeParams.size(), GetMSTimeDiffToNow(oldMSTime));
11421}
11422
11424{
11425 uint32 oldMSTime = getMSTime();
11426 _phaseNameStore.clear();
11427
11428 // 0 1
11429 QueryResult result = WorldDatabase.Query("SELECT `ID`, `Name` FROM `phase_name`");
11430
11431 if (!result)
11432 {
11433 TC_LOG_INFO("server.loading", ">> Loaded 0 phase names. DB table `phase_name` is empty.");
11434 return;
11435 }
11436
11437 uint32 count = 0;
11438 do
11439 {
11440 Field* fields = result->Fetch();
11441
11442 uint32 phaseId = fields[0].GetUInt32();
11443 std::string name = fields[1].GetString();
11444
11445 _phaseNameStore[phaseId] = name;
11446
11447 ++count;
11448 } while (result->NextRow());
11449 TC_LOG_INFO("server.loading", ">> Loaded {} phase names in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
11450}
11451
11452std::string ObjectMgr::GetPhaseName(uint32 phaseId) const
11453{
11454 PhaseNameContainer::const_iterator iter = _phaseNameStore.find(phaseId);
11455 return iter != _phaseNameStore.end() ? iter->second : "Unknown Name";
11456}
#define sAreaTriggerDataStore
#define sArenaTeamMgr
Definition: ArenaTeamMgr.h:53
@ CHAR_UPD_ITEM_OWNER
@ CHAR_DEL_EMPTY_EXPIRED_MAIL
@ CHAR_UPD_MAIL_RETURNED
@ CHAR_SEL_EXPIRED_MAIL
@ CHAR_SEL_EXPIRED_MAIL_ITEMS
@ CHAR_DEL_MAIL_BY_ID
@ CHAR_DEL_MAIL_ITEM_BY_ID
@ CHAR_UPD_MAIL_ITEM_RECEIVER
LocaleConstant GetLocaleByName(std::string_view name)
Definition: Common.cpp:36
LocaleConstant
Definition: Common.h:48
@ TOTAL_LOCALES
Definition: Common.h:62
@ LOCALE_enUS
Definition: Common.h:49
constexpr bool IsValidLocale(LocaleConstant locale)
Definition: Common.h:95
#define DEFAULT_LOCALE
Definition: Common.h:66
const uint8 OLD_TOTAL_LOCALES
Definition: Common.h:65
@ DAY
Definition: Common.h:31
#define M_PI
Definition: Common.h:115
#define sConditionMgr
Definition: ConditionMgr.h:365
@ CONDITION_SOURCE_TYPE_GRAVEYARD
Definition: ConditionMgr.h:181
@ CONDITION_TEAM
Definition: ConditionMgr.h:67
#define sCreatureAIRegistry
@ CREATURE_FLAG_EXTRA_DB_ALLOWED
Definition: CreatureData.h:372
@ CREATURE_FLAG_EXTRA_TRIGGER
Definition: CreatureData.h:341
@ CREATURE_FLAG_EXTRA_INSTANCE_BIND
Definition: CreatureData.h:334
CreatureStaticFlags
Definition: CreatureData.h:37
@ CREATURE_STATIC_FLAG_CAN_SWIM
Definition: CreatureData.h:66
CreatureChaseMovementType
Definition: CreatureData.h:376
const uint8 MAX_KILL_CREDIT
Definition: CreatureData.h:416
CreatureStaticFlags4
Definition: CreatureData.h:151
CreatureStaticFlags6
Definition: CreatureData.h:227
CreatureStaticFlags5
Definition: CreatureData.h:189
@ CREATURE_STATIC_FLAG_5_INTERACT_WHILE_HOSTILE
Definition: CreatureData.h:198
CreatureStaticFlags8
Definition: CreatureData.h:283
CreatureStaticFlags7
Definition: CreatureData.h:265
CreatureRandomMovementType
Definition: CreatureData.h:385
CreatureStaticFlags3
Definition: CreatureData.h:113
@ CREATURE_STATIC_FLAG_3_ALLOW_INTERACTION_WHILE_IN_COMBAT
Definition: CreatureData.h:132
@ CREATURE_STATIC_FLAG_3_CANNOT_TURN
Definition: CreatureData.h:139
@ CREATURE_STATIC_FLAG_3_CANNOT_SWIM
Definition: CreatureData.h:122
CreatureStaticFlags2
Definition: CreatureData.h:75
const uint32 MAX_CREATURE_SPELLS
Definition: CreatureData.h:419
#define sCriteriaMgr
std::vector< Criteria const * > CriteriaList
DB2Storage< PhaseEntry > sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance)
DB2Storage< SkillRaceClassInfoEntry > sSkillRaceClassInfoStore("SkillRaceClassInfo.db2", &SkillRaceClassInfoLoadInfo::Instance)
DB2Storage< DifficultyEntry > sDifficultyStore("Difficulty.db2", &DifficultyLoadInfo::Instance)
DB2Storage< GameObjectDisplayInfoEntry > sGameObjectDisplayInfoStore("GameObjectDisplayInfo.db2", &GameobjectDisplayInfoLoadInfo::Instance)
DB2Storage< CharacterLoadoutEntry > sCharacterLoadoutStore("CharacterLoadout.db2", &CharacterLoadoutLoadInfo::Instance)
DB2Storage< SpellVisualEntry > sSpellVisualStore("SpellVisual.db2", &SpellVisualLoadInfo::Instance)
DB2Storage< ItemEntry > sItemStore("Item.db2", &ItemLoadInfo::Instance)
DB2Storage< SkillLineEntry > sSkillLineStore("SkillLine.db2", &SkillLineLoadInfo::Instance)
DB2Storage< FriendshipReputationEntry > sFriendshipReputationStore("FriendshipReputation.db2", &FriendshipReputationLoadInfo::Instance)
DB2Storage< GameObjectsEntry > sGameObjectsStore("GameObjects.db2", &GameobjectsLoadInfo::Instance)
DB2Storage< AchievementEntry > sAchievementStore("Achievement.db2", &AchievementLoadInfo::Instance)
DB2Storage< CurveEntry > sCurveStore("Curve.db2", &CurveLoadInfo::Instance)
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
DB2Storage< TaxiNodesEntry > sTaxiNodesStore("TaxiNodes.db2", &TaxiNodesLoadInfo::Instance)
DB2Storage< CreatureModelDataEntry > sCreatureModelDataStore("CreatureModelData.db2", &CreatureModelDataLoadInfo::Instance)
DB2Storage< CreatureFamilyEntry > sCreatureFamilyStore("CreatureFamily.db2", &CreatureFamilyLoadInfo::Instance)
DB2Storage< CharacterLoadoutItemEntry > sCharacterLoadoutItemStore("CharacterLoadoutItem.db2", &CharacterLoadoutItemLoadInfo::Instance)
DB2Storage< AnimKitEntry > sAnimKitStore("AnimKit.db2", &AnimKitLoadInfo::Instance)
DB2Storage< MapDifficultyEntry > sMapDifficultyStore("MapDifficulty.db2", &MapDifficultyLoadInfo::Instance)
TaxiMask sTaxiNodesMask
Definition: DB2Stores.cpp:379
DB2Storage< CreatureTypeEntry > sCreatureTypeStore("CreatureType.db2", &CreatureTypeLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
Definition: DB2Stores.cpp:383
DB2Storage< ItemXItemEffectEntry > sItemXItemEffectStore("ItemXItemEffect.db2", &ItemXItemEffectLoadInfo::Instance)
DB2Storage< ScenarioEntry > sScenarioStore("Scenario.db2", &ScenarioLoadInfo::Instance)
DB2Storage< ChrClassesEntry > sChrClassesStore("ChrClasses.db2", &ChrClassesLoadInfo::Instance)
DB2Storage< VehicleSeatEntry > sVehicleSeatStore("VehicleSeat.db2", &VehicleSeatLoadInfo::Instance)
DB2Storage< LockEntry > sLockStore("Lock.db2", &LockLoadInfo::Instance)
DB2Storage< CharTitlesEntry > sCharTitlesStore("CharTitles.db2", &CharTitlesLoadInfo::Instance)
DB2Storage< ChrSpecializationEntry > sChrSpecializationStore("ChrSpecialization.db2", &ChrSpecializationLoadInfo::Instance)
DB2Storage< Cfg_CategoriesEntry > sCfgCategoriesStore("Cfg_Categories.db2", &CfgCategoriesLoadInfo::Instance)
DB2Storage< GossipNPCOptionEntry > sGossipNPCOptionStore("GossipNPCOption.db2", &GossipNpcOptionLoadInfo::Instance)
DB2Storage< QuestSortEntry > sQuestSortStore("QuestSort.db2", &QuestSortLoadInfo::Instance)
DB2Storage< ItemEffectEntry > sItemEffectStore("ItemEffect.db2", &ItemEffectLoadInfo::Instance)
DB2Storage< CriteriaTreeEntry > sCriteriaTreeStore("CriteriaTree.db2", &CriteriaTreeLoadInfo::Instance)
DB2Storage< BattlePetSpeciesEntry > sBattlePetSpeciesStore("BattlePetSpecies.db2", &BattlePetSpeciesLoadInfo::Instance)
DB2Storage< AreaTriggerEntry > sAreaTriggerStore("AreaTrigger.db2", &AreaTriggerLoadInfo::Instance)
DB2Storage< MailTemplateEntry > sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance)
DB2Storage< EmotesEntry > sEmotesStore("Emotes.db2", &EmotesLoadInfo::Instance)
DB2Storage< TaxiPathNodeEntry > sTaxiPathNodeStore("TaxiPathNode.db2", &TaxiPathNodeLoadInfo::Instance)
DB2Storage< SpellFocusObjectEntry > sSpellFocusObjectStore("SpellFocusObject.db2", &SpellFocusObjectLoadInfo::Instance)
DB2Storage< BroadcastTextEntry > sBroadcastTextStore("BroadcastText.db2", &BroadcastTextLoadInfo::Instance)
DB2Storage< GameObjectArtKitEntry > sGameObjectArtKitStore("GameObjectArtKit.db2", &GameobjectArtKitLoadInfo::Instance)
DB2Storage< ItemExtendedCostEntry > sItemExtendedCostStore("ItemExtendedCost.db2", &ItemExtendedCostLoadInfo::Instance)
DB2Storage< SpellEffectEntry > sSpellEffectStore("SpellEffect.db2", &SpellEffectLoadInfo::Instance)
DB2Storage< VignetteEntry > sVignetteStore("Vignette.db2", &VignetteLoadInfo::Instance)
DB2Storage< CurrencyTypesEntry > sCurrencyTypesStore("CurrencyTypes.db2", &CurrencyTypesLoadInfo::Instance)
DB2Storage< LanguagesEntry > sLanguagesStore("Languages.db2", &LanguagesLoadInfo::Instance)
DB2Storage< GemPropertiesEntry > sGemPropertiesStore("GemProperties.db2", &GemPropertiesLoadInfo::Instance)
DB2Storage< WorldEffectEntry > sWorldEffectStore("WorldEffect.db2", &WorldEffectLoadInfo::Instance)
DB2Storage< ChrRacesEntry > sChrRacesStore("ChrRaces.db2", &ChrRacesLoadInfo::Instance)
DB2Storage< ParagonReputationEntry > sParagonReputationStore("ParagonReputation.db2", &ParagonReputationLoadInfo::Instance)
DB2Storage< PlayerConditionEntry > sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance)
DB2Storage< CreatureDisplayInfoEntry > sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", &CreatureDisplayInfoLoadInfo::Instance)
DB2Storage< ContentTuningEntry > sContentTuningStore("ContentTuning.db2", &ContentTuningLoadInfo::Instance)
DB2Storage< MovieEntry > sMovieStore("Movie.db2", &MovieLoadInfo::Instance)
DB2Storage< ItemSparseEntry > sItemSparseStore("ItemSparse.db2", &ItemSparseLoadInfo::Instance)
DB2Storage< FactionTemplateEntry > sFactionTemplateStore("FactionTemplate.db2", &FactionTemplateLoadInfo::Instance)
DB2Storage< SoundKitEntry > sSoundKitStore("SoundKit.db2", &SoundKitLoadInfo::Instance)
DB2Storage< ItemSpecEntry > sItemSpecStore("ItemSpec.db2", &ItemSpecLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
DB2Storage< FactionEntry > sFactionStore("Faction.db2", &FactionLoadInfo::Instance)
DB2Storage< VehicleEntry > sVehicleStore("Vehicle.db2", &VehicleLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:538
@ DEFAULT_MAX_LEVEL
Definition: DBCEnums.h:47
@ MAX_LEVEL
Definition: DBCEnums.h:51
@ STRONG_MAX_LEVEL
Definition: DBCEnums.h:55
Difficulty
Definition: DBCEnums.h:873
@ DIFFICULTY_NONE
Definition: DBCEnums.h:874
TaxiNodeFlags
Definition: DBCEnums.h:2137
ItemContext
Definition: DBCEnums.h:1063
ItemSpecStat
Definition: DBCEnums.h:1181
@ ITEM_SPEC_STAT_TWO_HANDED_MACE
Definition: DBCEnums.h:1194
@ ITEM_SPEC_STAT_GUN
Definition: DBCEnums.h:1197
@ ITEM_SPEC_STAT_HASTE
Definition: DBCEnums.h:1207
@ ITEM_SPEC_STAT_CRIT
Definition: DBCEnums.h:1206
@ ITEM_SPEC_STAT_CLOAK
Definition: DBCEnums.h:1209
@ ITEM_SPEC_STAT_DODGE
Definition: DBCEnums.h:1187
@ ITEM_SPEC_STAT_INTELLECT
Definition: DBCEnums.h:1182
@ ITEM_SPEC_STAT_RELIC_FROST
Definition: DBCEnums.h:1216
@ ITEM_SPEC_STAT_RELIC_BLOOD
Definition: DBCEnums.h:1212
@ ITEM_SPEC_STAT_STRENGTH
Definition: DBCEnums.h:1184
@ ITEM_SPEC_STAT_SHIELD
Definition: DBCEnums.h:1204
@ ITEM_SPEC_STAT_NONE
Definition: DBCEnums.h:1223
@ ITEM_SPEC_STAT_ONE_HANDED_AXE
Definition: DBCEnums.h:1189
@ ITEM_SPEC_STAT_BONUS_ARMOR
Definition: DBCEnums.h:1208
@ ITEM_SPEC_STAT_RELIC
Definition: DBCEnums.h:1205
@ ITEM_SPEC_STAT_STAFF
Definition: DBCEnums.h:1200
@ ITEM_SPEC_STAT_DAGGER
Definition: DBCEnums.h:1195
@ ITEM_SPEC_STAT_AGILITY
Definition: DBCEnums.h:1183
@ ITEM_SPEC_STAT_RELIC_HOLY
Definition: DBCEnums.h:1221
@ ITEM_SPEC_STAT_RELIC_WIND
Definition: DBCEnums.h:1220
@ ITEM_SPEC_STAT_THROWN
Definition: DBCEnums.h:1202
@ ITEM_SPEC_STAT_WAND
Definition: DBCEnums.h:1203
@ ITEM_SPEC_STAT_BOW
Definition: DBCEnums.h:1198
@ ITEM_SPEC_STAT_RELIC_FIRE
Definition: DBCEnums.h:1217
@ ITEM_SPEC_STAT_FIST_WEAPON
Definition: DBCEnums.h:1196
@ ITEM_SPEC_STAT_TWO_HANDED_SWORD
Definition: DBCEnums.h:1192
@ ITEM_SPEC_STAT_PARRY
Definition: DBCEnums.h:1188
@ ITEM_SPEC_STAT_POLEARM
Definition: DBCEnums.h:1201
@ ITEM_SPEC_STAT_RELIC_FEL
Definition: DBCEnums.h:1214
@ ITEM_SPEC_STAT_RELIC_WATER
Definition: DBCEnums.h:1218
@ ITEM_SPEC_STAT_ONE_HANDED_MACE
Definition: DBCEnums.h:1193
@ ITEM_SPEC_STAT_RELIC_SHADOW
Definition: DBCEnums.h:1213
@ ITEM_SPEC_STAT_WARGLAIVES
Definition: DBCEnums.h:1210
@ ITEM_SPEC_STAT_RELIC_LIFE
Definition: DBCEnums.h:1219
@ ITEM_SPEC_STAT_RELIC_ARCANE
Definition: DBCEnums.h:1215
@ ITEM_SPEC_STAT_CROSSBOW
Definition: DBCEnums.h:1199
@ ITEM_SPEC_STAT_TWO_HANDED_AXE
Definition: DBCEnums.h:1190
@ ITEM_SPEC_STAT_RELIC_IRON
Definition: DBCEnums.h:1211
@ ITEM_SPEC_STAT_ONE_HANDED_SWORD
Definition: DBCEnums.h:1191
@ ITEM_SPEC_STAT_HIT
Definition: DBCEnums.h:1186
@ PHASE_USE_FLAGS_ALWAYS_VISIBLE
Definition: DBCEnums.h:1746
@ PHASE_USE_FLAGS_ALL
Definition: DBCEnums.h:1749
@ PHASE_USE_FLAGS_INVERSE
Definition: DBCEnums.h:1747
#define MAX_ITEM_PROTO_STATS
Definition: DBCEnums.h:990
CriteriaType
Definition: DBCEnums.h:503
@ PlayerTriggerGameEvent
@ AnyoneTriggerGameEventScenario
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
#define UI64FMTD
Definition: Define.h:126
uint8_t uint8
Definition: Define.h:144
int16_t int16
Definition: Define.h:139
int8_t int8
Definition: Define.h:140
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
#define UI64LIT(N)
Definition: Define.h:127
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
std::unordered_set< uint32 > params[2]
Definition: DisableMgr.cpp:50
uint16 flags
Definition: DisableMgr.cpp:49
@ DISABLE_TYPE_QUEST
Definition: DisableMgr.h:28
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
#define ABORT_MSG
Definition: Errors.h:75
#define ABORT
Definition: Errors.h:74
#define ASSERT_NOTNULL(pointer)
Definition: Errors.h:84
#define ASSERT
Definition: Errors.h:68
#define sGameObjectAIRegistry
GameTable< GtXpEntry > sXpGameTable
Definition: GameTables.cpp:40
GameTable< GtBaseMPEntry > sBaseMPGameTable
Definition: GameTables.cpp:31
float GetGameTableColumnForClass(T const *row, int32 class_)
Definition: GameTables.h:207
GossipOptionNpc
Definition: GossipDef.h:35
GossipOptionFlags
Definition: GossipDef.h:110
#define MAX_NUMBER_OF_GRIDS
Definition: GridDefines.h:38
#define sGroupMgr
Definition: GroupMgr.h:61
#define sGuildMgr
Definition: GuildMgr.h:70
@ TO_BE_DECIDED
@ ITEM_VENDOR_TYPE_ITEM
Definition: ItemDefines.h:203
@ ITEM_VENDOR_TYPE_CURRENCY
Definition: ItemDefines.h:204
@ ITEM_CLASS_GEM
Definition: ItemTemplate.h:423
@ ITEM_CLASS_ARMOR
Definition: ItemTemplate.h:424
@ ITEM_CLASS_WEAPON
Definition: ItemTemplate.h:422
@ ITEM_CLASS_CONSUMABLE
Definition: ItemTemplate.h:420
@ ITEM_FLAG2_FACTION_HORDE
Definition: ItemTemplate.h:212
@ ITEM_FLAG2_FACTION_ALLIANCE
Definition: ItemTemplate.h:213
@ ITEM_SUBCLASS_WEAPON_CROSSBOW
Definition: ItemTemplate.h:498
@ ITEM_SUBCLASS_WEAPON_GUN
Definition: ItemTemplate.h:483
@ ITEM_SUBCLASS_WEAPON_AXE2
Definition: ItemTemplate.h:481
@ ITEM_SUBCLASS_WEAPON_STAFF
Definition: ItemTemplate.h:490
@ ITEM_SUBCLASS_WEAPON_MACE
Definition: ItemTemplate.h:484
@ ITEM_SUBCLASS_WEAPON_WARGLAIVES
Definition: ItemTemplate.h:489
@ ITEM_SUBCLASS_WEAPON_MACE2
Definition: ItemTemplate.h:485
@ ITEM_SUBCLASS_WEAPON_DAGGER
Definition: ItemTemplate.h:495
@ ITEM_SUBCLASS_WEAPON_BOW
Definition: ItemTemplate.h:482
@ ITEM_SUBCLASS_WEAPON_SWORD
Definition: ItemTemplate.h:487
@ ITEM_SUBCLASS_WEAPON_AXE
Definition: ItemTemplate.h:480
@ ITEM_SUBCLASS_WEAPON_FIST_WEAPON
Definition: ItemTemplate.h:493
@ ITEM_SUBCLASS_WEAPON_WAND
Definition: ItemTemplate.h:499
@ ITEM_SUBCLASS_WEAPON_THROWN
Definition: ItemTemplate.h:496
@ ITEM_SUBCLASS_WEAPON_SWORD2
Definition: ItemTemplate.h:488
@ ITEM_SUBCLASS_WEAPON_POLEARM
Definition: ItemTemplate.h:486
@ ITEM_MOD_AGI_STR_INT
Definition: ItemTemplate.h:96
@ ITEM_MOD_AGI_INT
Definition: ItemTemplate.h:98
@ ITEM_MOD_STR_INT
Definition: ItemTemplate.h:99
@ ITEM_MOD_PARRY_RATING
Definition: ItemTemplate.h:39
@ ITEM_MOD_EXTRA_ARMOR
Definition: ItemTemplate.h:75
@ ITEM_MOD_HASTE_RATING
Definition: ItemTemplate.h:61
@ ITEM_MOD_CRIT_SPELL_RATING
Definition: ItemTemplate.h:46
@ ITEM_MOD_CRIT_RANGED_RATING
Definition: ItemTemplate.h:45
@ ITEM_MOD_CRIT_MELEE_RATING
Definition: ItemTemplate.h:44
@ ITEM_MOD_STRENGTH
Definition: ItemTemplate.h:33
@ ITEM_MOD_HIT_RATING
Definition: ItemTemplate.h:56
@ ITEM_MOD_INTELLECT
Definition: ItemTemplate.h:34
@ ITEM_MOD_AGILITY
Definition: ItemTemplate.h:32
@ ITEM_MOD_DODGE_RATING
Definition: ItemTemplate.h:38
@ ITEM_MOD_CRIT_RATING
Definition: ItemTemplate.h:57
@ ITEM_MOD_AGI_STR
Definition: ItemTemplate.h:97
@ ITEM_SUBCLASS_ARMOR_MAIL
Definition: ItemTemplate.h:532
@ ITEM_SUBCLASS_ARMOR_CLOTH
Definition: ItemTemplate.h:530
@ ITEM_SUBCLASS_ARMOR_RELIC
Definition: ItemTemplate.h:540
@ ITEM_SUBCLASS_ARMOR_LEATHER
Definition: ItemTemplate.h:531
@ ITEM_SUBCLASS_ARMOR_SHIELD
Definition: ItemTemplate.h:535
@ ITEM_SUBCLASS_ARMOR_PLATE
Definition: ItemTemplate.h:533
#define MAX_INVTYPE
Definition: ItemTemplate.h:416
@ SOCKET_COLOR_RELIC_FEL
Definition: ItemTemplate.h:356
@ SOCKET_COLOR_RELIC_WIND
Definition: ItemTemplate.h:362
@ SOCKET_COLOR_RELIC_FIRE
Definition: ItemTemplate.h:359
@ SOCKET_COLOR_RELIC_LIFE
Definition: ItemTemplate.h:361
@ SOCKET_COLOR_RELIC_ARCANE
Definition: ItemTemplate.h:357
@ SOCKET_COLOR_RELIC_SHADOW
Definition: ItemTemplate.h:355
@ SOCKET_COLOR_RELIC_FROST
Definition: ItemTemplate.h:358
@ SOCKET_COLOR_RELIC_IRON
Definition: ItemTemplate.h:353
@ SOCKET_COLOR_RELIC_WATER
Definition: ItemTemplate.h:360
@ SOCKET_COLOR_RELIC_HOLY
Definition: ItemTemplate.h:363
@ SOCKET_COLOR_RELIC_BLOOD
Definition: ItemTemplate.h:354
#define MAX_ITEM_SUBCLASS_WEAPON
Definition: ItemTemplate.h:507
@ ITEM_SUBCLASS_FOOD_DRINK
Definition: ItemTemplate.h:451
@ INVTYPE_CLOAK
Definition: ItemTemplate.h:395
@ INVTYPE_ROBE
Definition: ItemTemplate.h:399
@ INVTYPE_HOLDABLE
Definition: ItemTemplate.h:402
@ INVTYPE_RANGED
Definition: ItemTemplate.h:394
@ INVTYPE_THROWN
Definition: ItemTemplate.h:404
@ INVTYPE_RANGEDRIGHT
Definition: ItemTemplate.h:405
@ INVTYPE_WEAPON
Definition: ItemTemplate.h:392
@ INVTYPE_WEAPONMAINHAND
Definition: ItemTemplate.h:400
@ INVTYPE_WEAPONOFFHAND
Definition: ItemTemplate.h:401
@ INVTYPE_2HWEAPON
Definition: ItemTemplate.h:396
@ INVTYPE_SHIELD
Definition: ItemTemplate.h:393
@ LANG_ITEM_ALREADY_IN_LIST
Definition: Language.h:255
@ LANG_COMMAND_VENDORSELECTION
Definition: Language.h:330
@ LANG_ITEM_NOT_FOUND
Definition: Language.h:252
@ LANG_EXTENDED_COST_NOT_EXIST
Definition: Language.h:387
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
@ MAIL_CHECK_MASK_COD_PAYMENT
This mail was copied. Do not allow making a copy of items in mail.
Definition: Mail.h:56
@ MAIL_CHECK_MASK_RETURNED
Definition: Mail.h:54
std::vector< MailItemInfo > MailItemInfoVec
Definition: Mail.h:172
@ MAIL_NORMAL
Definition: Mail.h:39
#define sMapMgr
Definition: MapManager.h:184
@ MAX_DB_MOTION_TYPE
@ IDLE_MOTION_TYPE
@ WAYPOINT_MOTION_TYPE
@ RANDOM_MOTION_TYPE
#define SPEED_CHARGE
#define MAX_NPC_TEXT_OPTIONS
Definition: NPCHandler.h:27
#define DEFAULT_PLAYER_COMBAT_REACH
Definition: ObjectDefines.h:40
#define DEFAULT_VISIBILITY_DISTANCE
Definition: ObjectDefines.h:35
TempSummonType
Definition: ObjectDefines.h:62
@ TEMPSUMMON_MANUAL_DESPAWN
Definition: ObjectDefines.h:70
#define INTERACTION_DISTANCE
Definition: ObjectDefines.h:24
#define DEFAULT_PLAYER_DISPLAY_SCALE
Definition: ObjectDefines.h:41
uint64 MAKE_PAIR64(uint32 l, uint32 h)
Definition: ObjectDefines.h:87
uint16 MAKE_PAIR16(uint8 l, uint8 h)
VisibilityDistanceType
Definition: ObjectDefines.h:50
TypeID
Definition: ObjectGuid.h:34
@ TYPEID_GAMEOBJECT
Definition: ObjectGuid.h:43
@ TYPEID_UNIT
Definition: ObjectGuid.h:40
HighGuid
Definition: ObjectGuid.h:75
void CheckAndFixGOChairHeightId(GameObjectTemplate const *goInfo, uint32 &dataN, uint32 N)
Definition: ObjectMgr.cpp:7501
static EnumFlag< CfgCategoriesCharsets > GetRealmLanguageType(bool create)
Definition: ObjectMgr.cpp:8638
#define ChooseCreatureFlagSource(field)
void CheckGONoDamageImmuneId(GameObjectTemplate *goTemplate, uint32 dataN, uint32 N)
Definition: ObjectMgr.cpp:7513
void CheckGOSpellId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
Definition: ObjectMgr.cpp:7492
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const *rcEntry)
Definition: ObjectMgr.cpp:8986
ScriptMapMap * GetScriptsMapByType(ScriptsType type)
Definition: ObjectMgr.cpp:93
ScriptMapMap sSpellScripts
Definition: ObjectMgr.cpp:78
std::string GetScriptCommandName(ScriptCommands command)
Definition: ObjectMgr.cpp:105
bool isValidString(const std::wstring &wstr, uint32 strictMask, bool numericOrSpace, bool create=false)
Definition: ObjectMgr.cpp:8646
uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
Definition: ObjectMgr.cpp:2948
ScriptMapMap sEventScripts
Definition: ObjectMgr.cpp:79
void CheckGOConsumable(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
Definition: ObjectMgr.cpp:7522
void CheckGOLinkedTrapId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
Definition: ObjectMgr.cpp:7482
void CheckGOLockId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
Definition: ObjectMgr.cpp:7473
std::string GetScriptsTableNameByType(ScriptsType type)
Definition: ObjectMgr.cpp:81
ExtendedPlayerName ExtractExtendedPlayerName(std::string const &name)
Definition: ObjectMgr.cpp:174
bool normalizePlayerName(std::string &name)
Definition: ObjectMgr.cpp:154
std::vector< PlayerCreateInfoItem > PlayerCreateInfoItems
Definition: ObjectMgr.h:628
#define MAX_PLAYER_NAME
Definition: ObjectMgr.h:980
std::pair< GraveyardContainer::const_iterator, GraveyardContainer::const_iterator > GraveyardMapBounds
Definition: ObjectMgr.h:845
std::multimap< uint32, ScriptInfo > ScriptMap
Definition: ObjectMgr.h:418
#define MAX_CHARTER_NAME
Definition: ObjectMgr.h:983
std::pair< SpellScriptsContainer::iterator, SpellScriptsContainer::iterator > SpellScriptsBounds
Definition: ObjectMgr.h:421
ScriptCommands
Definition: ObjectMgr.h:104
@ SCRIPT_COMMAND_EMOTE
Definition: ObjectMgr.h:106
@ SCRIPT_COMMAND_CREATE_ITEM
Definition: ObjectMgr.h:122
@ SCRIPT_COMMAND_DESPAWN_SELF
Definition: ObjectMgr.h:123
@ SCRIPT_COMMAND_CLOSE_DOOR
Definition: ObjectMgr.h:117
@ SCRIPT_COMMAND_CAST_SPELL
Definition: ObjectMgr.h:120
@ SCRIPT_COMMAND_RESPAWN_GAMEOBJECT
Definition: ObjectMgr.h:114
@ SCRIPT_COMMAND_QUEST_EXPLORED
Definition: ObjectMgr.h:112
@ SCRIPT_COMMAND_ACTIVATE_OBJECT
Definition: ObjectMgr.h:118
@ SCRIPT_COMMAND_TALK
Definition: ObjectMgr.h:105
@ SCRIPT_COMMAND_OPEN_DOOR
Definition: ObjectMgr.h:116
@ SCRIPT_COMMAND_EQUIP
Definition: ObjectMgr.h:131
@ SCRIPT_COMMAND_PLAYMOVIE
Definition: ObjectMgr.h:134
@ SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT
Definition: ObjectMgr.h:126
@ SCRIPT_COMMAND_PLAY_ANIMKIT
Definition: ObjectMgr.h:136
@ SCRIPT_COMMAND_TELEPORT_TO
Definition: ObjectMgr.h:111
@ SCRIPT_COMMAND_FIELD_SET_DEPRECATED
Definition: ObjectMgr.h:107
@ SCRIPT_COMMAND_MOVE_TO
Definition: ObjectMgr.h:108
@ SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED
Definition: ObjectMgr.h:110
@ SCRIPT_COMMAND_TEMP_SUMMON_CREATURE
Definition: ObjectMgr.h:115
@ SCRIPT_COMMAND_MOVEMENT
Definition: ObjectMgr.h:135
@ SCRIPT_COMMAND_KILL_CREDIT
Definition: ObjectMgr.h:113
@ SCRIPT_COMMAND_KILL
Definition: ObjectMgr.h:127
@ SCRIPT_COMMAND_LOAD_PATH
Definition: ObjectMgr.h:125
@ SCRIPT_COMMAND_ORIENTATION
Definition: ObjectMgr.h:130
@ SCRIPT_COMMAND_PLAY_SOUND
Definition: ObjectMgr.h:121
@ SCRIPT_COMMAND_MODEL
Definition: ObjectMgr.h:132
@ SCRIPT_COMMAND_CLOSE_GOSSIP
Definition: ObjectMgr.h:133
@ SCRIPT_COMMAND_REMOVE_AURA
Definition: ObjectMgr.h:119
@ SCRIPT_COMMAND_FLAG_SET_DEPRECATED
Definition: ObjectMgr.h:109
QueryDataGroup
Definition: ObjectMgr.h:1041
@ QUERY_DATA_CREATURES
Definition: ObjectMgr.h:1042
@ QUERY_DATA_QUESTS
Definition: ObjectMgr.h:1045
@ QUERY_DATA_GAMEOBJECTS
Definition: ObjectMgr.h:1043
@ QUERY_DATA_POIS
Definition: ObjectMgr.h:1046
std::unordered_map< uint32, CellObjectGuids > CellObjectGuidsMap
Definition: ObjectMgr.h:485
#define SPAWNGROUP_MAP_UNSET
Definition: ObjectMgr.h:986
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::pair< GossipMenusContainer::const_iterator, GossipMenusContainer::const_iterator > GossipMenusMapBounds
Definition: ObjectMgr.h:776
@ CHAT_TYPE_WHISPER
Definition: ObjectMgr.h:145
std::map< uint32, ScriptMap > ScriptMapMap
Definition: ObjectMgr.h:419
SummonerType
Definition: ObjectMgr.h:67
@ SUMMONER_TYPE_MAP
Definition: ObjectMgr.h:70
@ SUMMONER_TYPE_CREATURE
Definition: ObjectMgr.h:68
@ SUMMONER_TYPE_GAMEOBJECT
Definition: ObjectMgr.h:69
ScriptsType
Definition: ObjectMgr.h:175
@ SCRIPTS_EVENT
Definition: ObjectMgr.h:179
@ SCRIPTS_SPELL
Definition: ObjectMgr.h:178
#define MAX_SKILL_STEP
Definition: ObjectMgr.h:968
std::multimap< uint32, uint32 > QuestRelationsReverse
Definition: ObjectMgr.h:560
std::multimap< uint32, uint32 > QuestRelations
Definition: ObjectMgr.h:559
#define MAX_PET_NAME
Definition: ObjectMgr.h:982
SkillRangeType
Definition: ObjectMgr.h:960
@ SKILL_RANGE_MONO
Definition: ObjectMgr.h:963
@ SKILL_RANGE_NONE
Definition: ObjectMgr.h:965
@ SKILL_RANGE_LANGUAGE
Definition: ObjectMgr.h:961
@ SKILL_RANGE_RANK
Definition: ObjectMgr.h:964
@ SKILL_RANGE_LEVEL
Definition: ObjectMgr.h:962
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
#define QUEST_ITEM_DROP_COUNT
Definition: QuestDef.h:46
#define QUEST_REWARD_ITEM_COUNT
Definition: QuestDef.h:48
#define QUEST_REWARD_REPUTATIONS_COUNT
Definition: QuestDef.h:50
@ QUEST_OBJECTIVE_DEFEATBATTLEPET
Definition: QuestDef.h:344
@ QUEST_OBJECTIVE_WINPVPPETBATTLES
Definition: QuestDef.h:345
@ QUEST_OBJECTIVE_INCREASE_REPUTATION
Definition: QuestDef.h:350
@ QUEST_OBJECTIVE_HAVE_CURRENCY
Definition: QuestDef.h:348
@ QUEST_OBJECTIVE_WINPETBATTLEAGAINSTNPC
Definition: QuestDef.h:343
@ QUEST_OBJECTIVE_MONSTER
Definition: QuestDef.h:332
@ QUEST_OBJECTIVE_TALKTO
Definition: QuestDef.h:335
@ QUEST_OBJECTIVE_CRITERIA_TREE
Definition: QuestDef.h:346
@ QUEST_OBJECTIVE_OBTAIN_CURRENCY
Definition: QuestDef.h:349
@ QUEST_OBJECTIVE_PROGRESS_BAR
Definition: QuestDef.h:347
@ QUEST_OBJECTIVE_PLAYERKILLS
Definition: QuestDef.h:341
@ QUEST_OBJECTIVE_ITEM
Definition: QuestDef.h:333
@ QUEST_OBJECTIVE_MONEY
Definition: QuestDef.h:340
@ QUEST_OBJECTIVE_MAX_REPUTATION
Definition: QuestDef.h:339
@ QUEST_OBJECTIVE_AREA_TRIGGER_EXIT
Definition: QuestDef.h:352
@ QUEST_OBJECTIVE_AREATRIGGER
Definition: QuestDef.h:342
@ QUEST_OBJECTIVE_CURRENCY
Definition: QuestDef.h:336
@ QUEST_OBJECTIVE_LEARNSPELL
Definition: QuestDef.h:337
@ QUEST_OBJECTIVE_AREA_TRIGGER_ENTER
Definition: QuestDef.h:351
@ QUEST_OBJECTIVE_GAMEOBJECT
Definition: QuestDef.h:334
@ QUEST_OBJECTIVE_MIN_REPUTATION
Definition: QuestDef.h:338
#define QUEST_REWARD_CURRENCY_COUNT
Definition: QuestDef.h:52
@ QUEST_OBJECTIVE_FLAG_SEQUENCED
Definition: QuestDef.h:361
#define QUEST_REWARD_CHOICES_COUNT
Definition: QuestDef.h:47
@ QUEST_FLAGS_DAILY
Definition: QuestDef.h:206
@ QUEST_FLAGS_WEEKLY
Definition: QuestDef.h:209
@ QUEST_FLAGS_COMPLETION_AREA_TRIGGER
Definition: QuestDef.h:196
@ QUEST_FLAGS_COMPLETION_EVENT
Definition: QuestDef.h:195
@ QUEST_FLAGS_TRACKING_EVENT
Definition: QuestDef.h:204
@ QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES
Definition: QuestDef.h:307
@ QUEST_SPECIAL_FLAGS_REPEATABLE
Definition: QuestDef.h:298
@ QUEST_SPECIAL_FLAGS_DB_ALLOWED
Definition: QuestDef.h:305
@ QUEST_SPECIAL_FLAGS_MONTHLY
Definition: QuestDef.h:302
constexpr Trinity::RaceMask< uint64 > RACEMASK_ALL_PLAYABLE
Definition: RaceMask.h:151
#define MAX_RACES
Definition: RaceMask.h:77
Races
Definition: RaceMask.h:26
@ RACE_PANDAREN_NEUTRAL
Definition: RaceMask.h:51
@ RACE_DRAENEI
Definition: RaceMask.h:38
@ RACE_BLOODELF
Definition: RaceMask.h:37
@ RACE_GOBLIN
Definition: RaceMask.h:36
@ RACE_HUMAN
Definition: RaceMask.h:28
@ RACE_WORGEN
Definition: RaceMask.h:49
@ RACE_PANDAREN_ALLIANCE
Definition: RaceMask.h:52
@ RACE_PANDAREN_HORDE
Definition: RaceMask.h:53
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
SceneFlag
Definition: SceneDefines.h:25
#define sScriptMgr
Definition: ScriptMgr.h:1418
SpellEffIndex
Definition: SharedDefines.h:29
#define MAX_STATS
#define MAX_REPUTATION_RANK
static constexpr uint8 MAX_UNIT_CLASSES
Classes
@ CLASS_HUNTER
@ CLASS_DRUID
@ CLASS_SHAMAN
@ CLASS_PRIEST
@ CLASS_WARRIOR
@ CLASS_WARLOCK
@ CLASS_MAGE
@ CLASS_DEATH_KNIGHT
@ CLASS_DEMON_HUNTER
@ CLASS_PALADIN
@ CLASS_ROGUE
@ CLASS_EVOKER
#define MAX_GO_STATE_TRANSPORT_STOP_FRAMES
@ SKILL_CATEGORY_ARMOR
@ SKILL_CATEGORY_LANGUAGES
@ GAMEOBJECT_TYPE_CAMERA
@ GAMEOBJECT_TYPE_BUTTON
@ GAMEOBJECT_TYPE_SPELL_FOCUS
@ GAMEOBJECT_TYPE_TRANSPORT
@ GAMEOBJECT_TYPE_TRAP
@ GAMEOBJECT_TYPE_GENERIC
@ GAMEOBJECT_TYPE_CHEST
@ GAMEOBJECT_TYPE_FISHINGHOLE
@ GAMEOBJECT_TYPE_FLAGDROP
@ GAMEOBJECT_TYPE_QUESTGIVER
@ GAMEOBJECT_TYPE_SPELLCASTER
@ GAMEOBJECT_TYPE_FLAGSTAND
@ GAMEOBJECT_TYPE_CHAIR
@ GAMEOBJECT_TYPE_AREADAMAGE
@ GAMEOBJECT_TYPE_GOOBER
@ GAMEOBJECT_TYPE_FISHINGNODE
@ GAMEOBJECT_TYPE_BARBER_CHAIR
@ GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
@ GAMEOBJECT_TYPE_DOOR
@ GAMEOBJECT_TYPE_GARRISON_BUILDING
@ GAMEOBJECT_TYPE_RITUAL
@ GAMEOBJECT_TYPE_GATHERING_NODE
@ GENDER_UNKNOWN
@ GENDER_MALE
@ GENDER_FEMALE
@ CREATURE_FAMILY_NONE
ResponseCodes
@ CHAR_NAME_TOO_SHORT
@ CHAR_NAME_THREE_CONSECUTIVE
@ CHAR_NAME_INVALID_CHARACTER
@ CHAR_NAME_TOO_LONG
@ CHAR_NAME_MIXED_LANGUAGES
@ UNIT_CLASS_WARRIOR
@ CREATURE_TYPE_HUMANOID
InvisibilityType
@ TOTAL_INVISIBILITY_TYPES
@ INVISIBILITY_GENERAL
@ MAX_ITEM_QUALITY
@ MAX_DB_ALLOWED_QUEST_TYPES
@ SPELL_EFFECT_DUMMY
@ SPELL_EFFECT_SCRIPT_EFFECT
@ SPELL_EFFECT_SEND_EVENT
constexpr uint32 SkillByQuestSort(int32 QuestSort)
SpellClickUserTypes
@ SPELL_CLICK_USER_PARTY
@ SPELL_CLICK_USER_RAID
@ SPELL_CLICK_USER_MAX
@ SPELL_CLICK_USER_FRIEND
#define MAX_GAMEOBJECT_DATA
@ ALLIANCE
@ HORDE
#define MAX_CLASSES
@ STAT_INTELLECT
@ STAT_AGILITY
@ STAT_STRENGTH
@ STAT_STAMINA
#define CLASSMASK_ALL_PLAYABLE
#define MAX_GO_STATE
CreatureClassifications
@ SPELL_CATEGORY_DRINK
@ SPELL_CATEGORY_FOOD
@ SPELL_SCHOOL_NORMAL
@ SPELL_SCHOOL_HOLY
@ MAX_SPELL_SCHOOL
PetNameInvalidReason
@ PET_NAME_INVALID
@ PET_NAME_SUCCESS
@ PET_NAME_MIXED_LANGUAGES
@ PET_NAME_TOO_SHORT
@ PET_NAME_TOO_LONG
#define CLASSMASK_ALL_CREATURES
@ EXPANSION_THE_BURNING_CRUSADE
Definition: SharedDefines.h:91
@ EXPANSION_LEVEL_CURRENT
Definition: SharedDefines.h:89
@ EXPANSION_MISTS_OF_PANDARIA
Definition: SharedDefines.h:94
@ EXPANSION_DRAGONFLIGHT
Definition: SharedDefines.h:99
@ MAX_ACCOUNT_EXPANSIONS
@ EXPANSION_CATACLYSM
Definition: SharedDefines.h:93
@ MAX_EXPANSIONS
@ EXPANSION_LEGION
Definition: SharedDefines.h:96
@ EXPANSION_WRATH_OF_THE_LICH_KING
Definition: SharedDefines.h:92
@ CHAT_MSG_RAID_BOSS_WHISPER
GOState
@ GO_STATE_TRANSPORT_ACTIVE
@ SKILL_RUNEFORGING
#define MAX_SPILLOVER_FACTIONS
SpawnGroupFlags
Definition: SpawnData.h:52
@ SPAWNGROUP_FLAGS_ALL
Definition: SpawnData.h:61
@ SPAWNGROUP_FLAG_MANUAL_SPAWN
Definition: SpawnData.h:56
@ SPAWNGROUP_FLAG_COMPATIBILITY_MODE
Definition: SpawnData.h:55
@ SPAWNGROUP_FLAG_SYSTEM
Definition: SpawnData.h:54
@ LINKED_RESPAWN_CREATURE_TO_GO
Definition: SpawnData.h:123
@ LINKED_RESPAWN_GO_TO_GO
Definition: SpawnData.h:124
@ LINKED_RESPAWN_CREATURE_TO_CREATURE
Definition: SpawnData.h:122
@ LINKED_RESPAWN_GO_TO_CREATURE
Definition: SpawnData.h:125
SpawnObjectType
Definition: SpawnData.h:33
@ SPAWN_TYPE_GAMEOBJECT
Definition: SpawnData.h:35
@ SPAWN_TYPE_AREATRIGGER
Definition: SpawnData.h:36
@ SPAWN_TYPE_CREATURE
Definition: SpawnData.h:34
@ SPELL_AURA_CONTROL_VEHICLE
#define sSpellMgr
Definition: SpellMgr.h:849
#define sTerrainMgr
Definition: TerrainMgr.h:165
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
#define sTransportMgr
Definition: TransportMgr.h:183
@ UNIT_FLAG2_ALLOWED
Definition: UnitDefines.h:237
@ UNIT_FLAG2_INTERACT_WHILE_HOSTILE
Definition: UnitDefines.h:208
@ UNIT_FLAG2_CANNOT_TURN
Definition: UnitDefines.h:209
@ UNIT_STAND_STATE_SIT_HIGH_CHAIR
Definition: UnitDefines.h:48
@ UNIT_STAND_STATE_SIT_LOW_CHAIR
Definition: UnitDefines.h:46
@ MAX_UNIT_STAND_STATE
Definition: UnitDefines.h:53
#define BASE_ATTACK_TIME
Definition: UnitDefines.h:35
#define MAX_DECLINED_NAME_CASES
Definition: UnitDefines.h:484
@ UNIT_NPC_FLAG_VENDOR
Definition: UnitDefines.h:304
@ UNIT_NPC_FLAG_GOSSIP
Definition: UnitDefines.h:297
@ UNIT_NPC_FLAG_QUESTGIVER
Definition: UnitDefines.h:298
@ UNIT_NPC_FLAG_SPELLCLICK
Definition: UnitDefines.h:321
@ UNIT_FLAG3_ALLOWED
Definition: UnitDefines.h:287
@ UNIT_FLAG3_ALLOW_INTERACTION_WHILE_IN_COMBAT
Definition: UnitDefines.h:266
@ MAX_SHEATH_STATE
Definition: UnitDefines.h:86
#define MAX_EQUIPMENT_ITEMS
Definition: UnitDefines.h:37
AnimTier
Definition: UnitDefines.h:69
@ UNIT_FLAG_ALLOWED
Definition: UnitDefines.h:185
@ UNIT_FLAG_CAN_SWIM
Definition: UnitDefines.h:159
@ UNIT_FLAG_CANT_SWIM
Definition: UnitDefines.h:158
float DegToRad(float degrees)
Definition: Util.cpp:912
std::wstring GetMainPartOfName(std::wstring const &wname, uint32 declension)
Definition: Util.cpp:669
bool WStrToUtf8(wchar_t const *wstr, size_t size, std::string &utf8str)
Definition: Util.cpp:433
void wstrToLower(std::wstring &str)
Definition: Util.cpp:480
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition: Util.cpp:383
bool isKoreanString(std::wstring_view wstr, bool numericOrSpace)
Definition: Util.h:237
bool isCyrillicString(std::wstring_view wstr, bool numericOrSpace)
Definition: Util.h:229
bool isChineseString(std::wstring_view wstr, bool numericOrSpace)
Definition: Util.h:245
bool isBasicLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition: Util.h:213
wchar_t wcharToUpper(wchar_t wchar)
Definition: Util.h:253
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
bool isExtendedLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition: Util.h:221
std::vector< VehicleAccessory > VehicleAccessoryList
@ WORLD_INS_CONDITION
Definition: WorldDatabase.h:87
@ WORLD_DEL_GAME_TELE
Definition: WorldDatabase.h:41
@ WORLD_INS_GRAVEYARD_ZONE
Definition: WorldDatabase.h:38
@ WORLD_SEL_NPC_VENDOR_REF
Definition: WorldDatabase.h:44
@ WORLD_REP_LINKED_RESPAWN
Definition: WorldDatabase.h:33
@ WORLD_INS_GAME_TELE
Definition: WorldDatabase.h:40
@ WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA
Definition: WorldDatabase.h:83
@ WORLD_UPD_CREATURE_ZONE_AREA_DATA
Definition: WorldDatabase.h:82
@ WORLD_DEL_LINKED_RESPAWN
Definition: WorldDatabase.h:31
@ WORLD_SEL_CREATURE_TEMPLATE
Definition: WorldDatabase.h:70
@ WORLD_DEL_NPC_VENDOR
Definition: WorldDatabase.h:43
@ WORLD_INS_NPC_VENDOR
Definition: WorldDatabase.h:42
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
void PSendSysMessage(const char *fmt, Args &&... args)
Definition: Chat.h:57
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition: Chat.cpp:113
bool HasFlag(CreatureStaticFlags flag) const
Definition: CreatureData.h:302
static float GetDamageMod(CreatureClassifications classification)
Definition: Creature.cpp:1686
static char const * GetCreatureFamilyPetName(uint32 petfamily, LocaleConstant locale)
Definition: DB2Stores.cpp:2160
constexpr bool HasFlag(T flag) const
Definition: EnumFlag.h:106
Class used to access individual fields of database query result.
Definition: Field.h:90
uint8 GetUInt8() const
Definition: Field.cpp:30
std::string GetString() const
Definition: Field.cpp:118
bool IsNull() const
Definition: Field.h:125
int64 GetInt64() const
Definition: Field.cpp:86
int8 GetInt8() const
Definition: Field.cpp:38
std::string_view GetStringView() const
Definition: Field.cpp:130
uint64 GetUInt64() const
Definition: Field.cpp:78
int16 GetInt16() const
Definition: Field.cpp:54
uint16 GetUInt16() const
Definition: Field.cpp:46
float GetFloat() const
Definition: Field.cpp:94
bool GetBool() const
Definition: Field.h:98
uint32 GetUInt32() const
Definition: Field.cpp:62
int32 GetInt32() const
Definition: Field.cpp:70
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
Definition: Item.cpp:1088
bool HaveQuestLootFor(uint32 loot_id) const
Definition: LootMgr.cpp:199
static bool IsValidMapCoord(uint32 mapid, float x, float y)
Definition: MapManager.h:82
static bool IsValidMAP(uint32 mapId)
Definition: MapManager.cpp:372
Definition: Map.h:189
void Set(ObjectGuid::LowType val)
Definition: ObjectGuid.h:404
uint64 LowType
Definition: ObjectGuid.h:278
std::unordered_set< std::string > GetAllDBScriptNames() const
Definition: ObjectMgr.cpp:9913
void reserve(size_t capacity)
Definition: ObjectMgr.cpp:9872
NameMap::const_iterator find(size_t index) const
Definition: ObjectMgr.cpp:9894
uint32 insert(std::string const &scriptName, bool isScriptNameBound=true)
Definition: ObjectMgr.cpp:9877
NameMap::const_iterator end() const
Definition: ObjectMgr.cpp:9908
static void ChooseCreatureFlags(CreatureTemplate const *cInfo, uint64 *npcFlags, uint32 *unitFlags, uint32 *unitFlags2, uint32 *unitFlags3, CreatureStaticFlagsHolder const &staticFlags, CreatureData const *data=nullptr)
Definition: ObjectMgr.cpp:1630
std::set< uint32 > EventContainer
Definition: ObjectMgr.h:1080
CreatureMovementData const * GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
Definition: ObjectMgr.cpp:1429
QuestOfferRewardLocaleContainer _questOfferRewardLocaleStore
Definition: ObjectMgr.h:1918
uint32 LoadReferenceVendor(int32 vendor, int32 item_id, std::set< uint32 > *skip_vendors)
Definition: ObjectMgr.cpp:9389
Trainer::Trainer const * GetTrainer(uint32 trainerId) const
Definition: ObjectMgr.cpp:9703
std::unordered_set< std::string > GetAllDBScriptNames() const
Definition: ObjectMgr.cpp:9928
AccessRequirement const * GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
Definition: ObjectMgr.cpp:7049
void LoadQuestRelationsHelper(QuestRelations &map, QuestRelationsReverse *reverseMap, std::string const &table)
Definition: ObjectMgr.cpp:8485
EquipmentInfo const * GetEquipmentInfo(uint32 entry, int8 &id) const
Definition: ObjectMgr.cpp:1434
TempSummonDataContainer _tempSummonDataStore
Stores temp summon data grouped by summoner's entry, summoner's type and group id.
Definition: ObjectMgr.h:1912
QuestRelations _creatureQuestInvolvedRelations
Definition: ObjectMgr.h:1809
PetLevelInfo const * GetPetLevelInfo(uint32 creature_id, uint8 level) const
Definition: ObjectMgr.cpp:3687
void LoadCreatureTemplateResistances()
Definition: ObjectMgr.cpp:515
VehicleAccessoryList const * GetVehicleAccessoryList(Vehicle *veh) const
VehicleAccessoryContainer _vehicleAccessoryStore
Definition: ObjectMgr.h:1828
int32 GetFishingBaseSkillLevel(AreaTableEntry const *areaEntry) const
Definition: ObjectMgr.cpp:7913
bool HasPersonalSpawns(uint32 mapid, Difficulty spawnMode, uint32 phaseId) const
Definition: ObjectMgr.cpp:2405
uint32 GetXPForLevel(uint8 level) const
Definition: ObjectMgr.cpp:7906
void LoadCreatureTemplate(Field *fields)
Definition: ObjectMgr.cpp:399
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore
Definition: ObjectMgr.h:1905
CreatureBaseStats const * GetCreatureBaseStats(uint8 level, uint8 unitClass)
Definition: ObjectMgr.cpp:9965
void LoadCreatureTemplateSpells()
Definition: ObjectMgr.cpp:560
ObjectGuid::LowType GenerateGameObjectSpawnId()
Definition: ObjectMgr.cpp:7431
InstanceTemplate const * GetInstanceTemplate(uint32 mapId) const
Definition: ObjectMgr.cpp:6229
uint64 GenerateMailID()
Definition: ObjectMgr.cpp:7391
TrinityString const * GetTrinityString(uint32 entry) const
Definition: ObjectMgr.h:1581
QuestObjectivesByIdContainer _questObjectives
Definition: ObjectMgr.h:1773
MapPersonalObjectGuids _mapPersonalObjectGuidsStore
Definition: ObjectMgr.h:1886
void LoadQuestTemplateLocale()
Definition: ObjectMgr.cpp:5346
void LoadReputationOnKill()
Definition: ObjectMgr.cpp:8103
std::vector< PhaseAreaInfo > const * GetPhasesForArea(uint32 areaId) const
void LoadEventScripts()
Definition: ObjectMgr.cpp:5934
void LoadGameObjectTemplate()
Definition: ObjectMgr.cpp:7532
CreatureAddon const * GetCreatureTemplateAddon(uint32 entry) const
Definition: ObjectMgr.cpp:1415
void LoadFactionChangeSpells()
void LoadFactionChangeItems()
void LoadAreaTriggerTeleports()
Definition: ObjectMgr.cpp:7106
void LoadNPCText()
Definition: ObjectMgr.cpp:6246
WorldSafeLocsEntry const * GetClosestGraveyardInZone(WorldLocation const &location, uint32 team, WorldObject *conditionObject, uint32 zoneId) const
Definition: ObjectMgr.cpp:6851
QuestGreetingLocale const * GetQuestGreetingLocale(TypeID type, uint32 id) const
Definition: ObjectMgr.cpp:6516
std::unordered_map< std::pair< Races, Classes >, std::unique_ptr< PlayerInfo > > _playerInfo
Definition: ObjectMgr.h:1869
CreatureQuestCurrenciesMap _creatureQuestCurrenciesStore
Definition: ObjectMgr.h:1898
AreaTriggerStruct const * GetAreaTrigger(uint32 trigger) const
Definition: ObjectMgr.cpp:7041
void LoadPageTextLocales()
Definition: ObjectMgr.cpp:6160
HalfNameContainer _petHalfName1
Definition: ObjectMgr.h:1883
void LoadVehicleTemplate()
Definition: ObjectMgr.cpp:3465
void LoadInstanceTemplate()
Definition: ObjectMgr.cpp:6189
CreatureTemplateSparringContainer _creatureTemplateSparringStore
Definition: ObjectMgr.h:1893
void LoadItemScriptNames()
Definition: ObjectMgr.cpp:3378
uint32 GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team=false)
Definition: ObjectMgr.cpp:6707
void LoadSpellScripts()
Definition: ObjectMgr.cpp:5850
SpellScriptsContainer _spellScriptsStore
Definition: ObjectMgr.h:1824
SpawnGroupDataContainer _spawnGroupDataStore
Definition: ObjectMgr.h:1907
QuestPOIData const * GetQuestPOIData(int32 questId)
WorldSafeLocsEntry const * GetDefaultGraveyard(uint32 team) const
Definition: ObjectMgr.cpp:6802
void RemoveSpawnDataFromGrid(SpawnData const *data)
Definition: ObjectMgr.cpp:2436
void LoadQuestGreetingLocales()
Definition: ObjectMgr.cpp:5414
CreatureDataContainer _creatureDataStore
Definition: ObjectMgr.h:1887
void LoadPageTexts()
Definition: ObjectMgr.cpp:6114
void ValidateSpellScripts()
Definition: ObjectMgr.cpp:6049
void LoadCreatureQuestEnders()
Definition: ObjectMgr.cpp:8563
QuestRelations _goQuestRelations
Definition: ObjectMgr.h:1805
void LoadSceneTemplates()
void LoadGameobjectQuestEnders()
Definition: ObjectMgr.cpp:8535
void LoadCreatureMovementOverrides()
Definition: ObjectMgr.cpp:1552
void LoadCreatureLocales()
Definition: ObjectMgr.cpp:251
TavernAreaTriggerContainer _tavernAreaTriggerStore
Definition: ObjectMgr.h:1781
void LoadCreatureClassLevelStats()
Definition: ObjectMgr.cpp:9985
PlayerInfo const * GetPlayerInfo(uint32 race, uint32 class_) const
void LoadCreatureTemplates()
Definition: ObjectMgr.cpp:345
GameObjectTemplateAddon const * GetGameObjectTemplateAddon(uint32 entry) const
void LoadEventSet()
Definition: ObjectMgr.cpp:5879
Trinity::IteratorPair< std::unordered_map< uint32, WorldSafeLocsEntry >::const_iterator > GetWorldSafeLocs() const
Definition: ObjectMgr.cpp:7036
PageTextLocaleContainer _pageTextLocaleStore
Definition: ObjectMgr.h:1920
EquipmentInfoContainer _equipmentInfoStore
Definition: ObjectMgr.h:1899
ObjectGuid::LowType _gameObjectSpawnId
Definition: ObjectMgr.h:1764
void GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32 &baseMana) const
Definition: ObjectMgr.cpp:4372
void LoadTrainers()
Definition: ObjectMgr.cpp:9226
AccessRequirementContainer _accessRequirementStore
Definition: ObjectMgr.h:1788
JumpChargeParams const * GetJumpChargeParams(int32 id) const
void LoadQuestAreaTriggers()
Definition: ObjectMgr.cpp:6443
QuestGreetingLocaleContainer _questGreetingLocaleStore
Definition: ObjectMgr.h:1785
SpawnData const * GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition: ObjectMgr.cpp:2902
std::unordered_map< uint32, SkillTiersEntry > _skillTiers
Definition: ObjectMgr.h:1879
CreatureTemplateContainer _creatureTemplateStore
Definition: ObjectMgr.h:1888
bool AddGameTele(GameTele &data)
Definition: ObjectMgr.cpp:9106
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
Definition: ObjectMgr.cpp:8921
void LoadQuestOfferRewardLocale()
Definition: ObjectMgr.cpp:5468
void LoadFactionChangeAchievements()
static void AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector< std::string > &data)
Definition: ObjectMgr.cpp:240
void LoadGameObjectOverrides()
Definition: ObjectMgr.cpp:7836
SpellScriptsBounds GetSpellScriptsBounds(uint32 spellId)
Definition: ObjectMgr.cpp:8953
void LoadGossipMenuAddon()
Definition: ObjectMgr.cpp:9660
QuestRequestItemsLocaleContainer _questRequestItemsLocaleStore
Definition: ObjectMgr.h:1919
PlayerChoice const * GetPlayerChoice(int32 choiceId) const
void LoadPhases()
QuestGreetingContainer _questGreetingStore
Definition: ObjectMgr.h:1784
SpawnGroupTemplateData const * GetLegacySpawnGroup() const
Definition: ObjectMgr.h:1444
void LoadFactionChangeReputations()
void RemoveCreatureFromGrid(CreatureData const *data)
Definition: ObjectMgr.cpp:2457
std::set< uint32 > _transportMaps
Definition: ObjectMgr.h:1942
void LoadPetNumber()
Definition: ObjectMgr.cpp:7980
std::unordered_map< ObjectGuid::LowType, CreatureMovementData > _creatureMovementOverrides
Definition: ObjectMgr.h:1894
GraveyardContainer GraveyardStore
Definition: ObjectMgr.h:1679
std::string const & GetScriptName(uint32 id) const
Definition: ObjectMgr.cpp:9933
GameObjectTemplateContainer _gameObjectTemplateStore
Definition: ObjectMgr.h:1904
void LoadReputationSpilloverTemplate()
Definition: ObjectMgr.cpp:8175
void LoadExplorationBaseXP()
Definition: ObjectMgr.cpp:7874
void LoadFishingBaseSkillLevel()
Definition: ObjectMgr.cpp:8858
InstanceSpawnGroupContainer _instanceSpawnGroupStore
Definition: ObjectMgr.h:1910
void LoadQuestRequestItemsLocale()
Definition: ObjectMgr.cpp:5496
EventScriptContainer _eventScriptStore
Definition: ObjectMgr.h:1792
void LoadPetLevelInfo()
Definition: ObjectMgr.cpp:3599
uint64 GenerateVoidStorageItemId()
Definition: ObjectMgr.cpp:7411
void LoadQuestObjectivesLocale()
Definition: ObjectMgr.cpp:5385
void UnloadPhaseConditions()
void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo *info) const
Definition: ObjectMgr.cpp:4390
std::vector< int32 > const * GetCreatureQuestCurrencyList(uint32 creatureId) const
Definition: ObjectMgr.cpp:4485
PhaseInfoStruct const * GetPhaseInfo(uint32 phaseId) const
CreatureAddon const * GetCreatureAddon(ObjectGuid::LowType lowguid) const
Definition: ObjectMgr.cpp:1406
void LoadLinkedRespawn()
Definition: ObjectMgr.cpp:1776
void LoadEquipmentTemplates()
Definition: ObjectMgr.cpp:1460
GameObjectData const * GetGameObjectData(ObjectGuid::LowType spawnId) const
Definition: ObjectMgr.h:1519
bool IsValidEvent(uint32 eventId) const
Definition: ObjectMgr.h:1205
void LoadSpawnGroups()
Definition: ObjectMgr.cpp:2768
NpcText const * GetNpcText(uint32 textID) const
Definition: ObjectMgr.cpp:6238
void LoadGossipMenu()
Definition: ObjectMgr.cpp:9494
QuestRelationsReverse _creatureQuestInvolvedRelationsReverse
Definition: ObjectMgr.h:1810
ItemTemplateContainer _itemTemplateStore
Definition: ObjectMgr.h:1915
void LoadQuestStartersAndEnders()
Definition: ObjectMgr.cpp:5334
GameObjectOverride const * GetGameObjectOverride(ObjectGuid::LowType spawnId) const
static PetNameInvalidReason CheckPetName(std::string_view name)
Definition: ObjectMgr.cpp:8730
GameObjectDataContainer _gameObjectDataStore
Definition: ObjectMgr.h:1902
void LoadSpawnGroupTemplates()
Definition: ObjectMgr.cpp:2709
void LoadGraveyardZones()
Definition: ObjectMgr.cpp:6755
uint32 GeneratePetNumber()
Definition: ObjectMgr.cpp:7401
CreatureSummonedData const * GetCreatureSummonedData(uint32 entryId) const
Definition: ObjectMgr.cpp:1611
uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
Definition: ObjectMgr.cpp:6653
void LoadTerrainWorldMaps()
void LoadPlayerInfo()
Definition: ObjectMgr.cpp:3725
void LoadGossipMenuItemsLocales()
Definition: ObjectMgr.cpp:284
void LoadQuests()
Definition: ObjectMgr.cpp:4490
CreatureLocaleContainer _creatureLocaleStore
Definition: ObjectMgr.h:1901
void CheckCreatureTemplate(CreatureTemplate const *cInfo)
Definition: ObjectMgr.cpp:1006
std::atomic< uint32 > _hiPetNumber
Definition: ObjectMgr.h:1762
bool IsReservedName(std::string_view name) const
Definition: ObjectMgr.cpp:8627
void AddCreatureToGrid(CreatureData const *data)
Definition: ObjectMgr.cpp:2452
std::unordered_map< uint32, std::vector< uint32 > > _spawnGroupsByMap
Definition: ObjectMgr.h:1908
std::atomic< uint64 > _mailId
Definition: ObjectMgr.h:1761
void RemoveGameobjectFromGrid(GameObjectData const *data)
Definition: ObjectMgr.cpp:2943
GameTele const * GetGameTeleExactName(std::string_view name) const
Definition: ObjectMgr.cpp:9087
void LoadGameTele()
Definition: ObjectMgr.cpp:9009
int32 GetBaseReputationOf(FactionEntry const *factionEntry, uint8 race, uint8 playerClass) const
Definition: ObjectMgr.cpp:8967
void LoadCreatureSummonedData()
Definition: ObjectMgr.cpp:658
PointOfInterestLocaleContainer _pointOfInterestLocaleStore
Definition: ObjectMgr.h:1922
InstanceTemplateContainer _instanceTemplateStore
Definition: ObjectMgr.h:1833
void LoadTerrainSwapDefaults()
bool DeleteGameTele(std::string_view name)
Definition: ObjectMgr.cpp:9139
ObjectGuid::LowType GenerateCreatureSpawnId()
Definition: ObjectMgr.cpp:7421
QuestRelations _goQuestInvolvedRelations
Definition: ObjectMgr.h:1806
void LoadCreatureQuestItems()
void LoadNPCSpellClickSpells()
Definition: ObjectMgr.cpp:8394
QuestAreaTriggerContainer _questAreaTriggerStore
Definition: ObjectMgr.h:1780
FishingBaseSkillContainer _fishingBaseForAreaStore
Definition: ObjectMgr.h:1878
bool IsTransportMap(uint32 mapId) const
Definition: ObjectMgr.h:1716
ObjectGuid::LowType _creatureSpawnId
Definition: ObjectMgr.h:1763
AreaTriggerContainer _areaTriggerStore
Definition: ObjectMgr.h:1786
void LoadAccessRequirements()
Definition: ObjectMgr.cpp:7160
MapObjectGuids _mapObjectGuidsStore
Definition: ObjectMgr.h:1885
CreatureAddonContainer _creatureAddonStore
Definition: ObjectMgr.h:1891
PetLevelInfoContainer _petInfoStore
Definition: ObjectMgr.h:1865
static CreatureModel const * ChooseDisplayId(CreatureTemplate const *cinfo, CreatureData const *data=nullptr)
Definition: ObjectMgr.cpp:1616
VehicleAccessoryTemplateContainer _vehicleTemplateAccessoryStore
Definition: ObjectMgr.h:1827
AreaTriggerStruct const * GetGoBackTrigger(uint32 Map) const
Definition: ObjectMgr.cpp:7263
static ObjectMgr * instance()
Definition: ObjectMgr.cpp:230
void LoadVehicleTemplateAccessories()
Definition: ObjectMgr.cpp:3410
void DeleteGameObjectData(ObjectGuid::LowType spawnId)
Definition: ObjectMgr.cpp:8472
void LoadJumpChargeParams()
WorldSafeLocsEntry const * GetWorldSafeLoc(uint32 id) const
Definition: ObjectMgr.cpp:7031
TerrainSwapInfo const * GetTerrainSwapInfo(uint32 terrainSwapId) const
HalfNameContainer _petHalfName0
Definition: ObjectMgr.h:1882
void LoadGameObjectLocales()
Definition: ObjectMgr.cpp:7441
void LoadAreaPhases()
void LoadTempSummons()
Definition: ObjectMgr.cpp:2015
void LoadQuestGreetings()
Definition: ObjectMgr.cpp:6529
NpcTextContainer _npcTextStore
Definition: ObjectMgr.h:1783
QuestContainer _questTemplates
Definition: ObjectMgr.h:1771
void PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
Definition: ObjectMgr.cpp:3699
CreatureModelInfo const * GetCreatureModelRandomGender(CreatureModel *model, CreatureTemplate const *creatureTemplate) const
Definition: ObjectMgr.cpp:1667
PageText const * GetPageText(uint32 pageEntry)
Definition: ObjectMgr.cpp:6151
void LoadCreatureTemplateAddons()
Definition: ObjectMgr.cpp:746
static ResponseCodes CheckPlayerName(std::string_view name, LocaleConstant locale, bool create=false)
Definition: ObjectMgr.cpp:8687
void LoadItemTemplates()
Definition: ObjectMgr.cpp:3247
uint32 GetCreatureTrainerForGossipOption(uint32 creatureId, uint32 gossipMenuId, uint32 gossipOptionId) const
Definition: ObjectMgr.cpp:9708
std::map< HighGuid, std::unique_ptr< ObjectGuidGenerator > > _guidGenerators
Definition: ObjectMgr.h:1770
bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist=true)
Definition: ObjectMgr.cpp:9737
std::vector< Quest const * > _questTemplatesAutoPush
Definition: ObjectMgr.h:1772
ObjectGuidGenerator & GetGuidSequenceGenerator(HighGuid high)
Definition: ObjectMgr.cpp:7362
uint32 GetBaseXP(uint8 level)
Definition: ObjectMgr.cpp:7901
CreatureTemplateAddonContainer _creatureTemplateAddonStore
Definition: ObjectMgr.h:1892
static bool IsValidCharterName(std::string_view name)
Definition: ObjectMgr.cpp:8712
std::unordered_map< uint32, VehicleTemplate > _vehicleTemplateStore
Definition: ObjectMgr.h:1826
void LoadPetNames()
Definition: ObjectMgr.cpp:7949
GameObjectTemplate const * GetGameObjectTemplate(uint32 entry) const
GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore
Definition: ObjectMgr.h:1921
BaseXPContainer _baseXPTable
Definition: ObjectMgr.h:1875
void LoadQuestPOI()
Definition: ObjectMgr.cpp:8317
void LoadGameObjectTemplateAddons()
Definition: ObjectMgr.cpp:7750
void LoadTavernAreaTriggers()
Definition: ObjectMgr.cpp:6583
void LoadReputationRewardRate()
Definition: ObjectMgr.cpp:8015
AreaTriggerScriptContainer _areaTriggerScriptStore
Definition: ObjectMgr.h:1787
void CheckCreatureMovement(char const *table, uint64 id, CreatureMovementData &creatureMovement)
Definition: ObjectMgr.cpp:1163
QuestRelations _creatureQuestRelations
Definition: ObjectMgr.h:1808
ClassAvailability const * GetClassExpansionRequirementFallback(uint8 classId) const
void AddSpawnDataToGrid(SpawnData const *data)
Definition: ObjectMgr.cpp:2419
SceneTemplate const * GetSceneTemplate(uint32 sceneId) const
Definition: ObjectMgr.h:1744
Quest const * GetQuestTemplate(uint32 quest_id) const
Definition: ObjectMgr.cpp:6749
void LoadCreatureQuestCurrencies()
VehicleTemplate const * GetVehicleTemplate(Vehicle *veh) const
GameObjectAddon const * GetGameObjectAddon(ObjectGuid::LowType lowguid) const
Definition: ObjectMgr.cpp:1397
void LoadGameobjectQuestStarters()
Definition: ObjectMgr.cpp:8521
void LoadScripts(ScriptsType type)
Definition: ObjectMgr.cpp:5524
RepOnKillContainer _repOnKillStore
Definition: ObjectMgr.h:1795
void LoadGameObjectForQuests()
Definition: ObjectMgr.cpp:8750
AreaTriggerStruct const * GetMapEntranceTrigger(uint32 Map) const
Definition: ObjectMgr.cpp:7290
void LoadFactionChangeTitles()
uint32 GetAreaTriggerScriptId(uint32 trigger_id) const
Definition: ObjectMgr.cpp:8945
void LoadPlayerChoices()
SpawnGroupLinkContainer _spawnGroupMapStore
Definition: ObjectMgr.h:1909
std::string GetPhaseName(uint32 phaseId) const
uint64 _equipmentSetGuid
Definition: ObjectMgr.h:1760
RepSpilloverTemplateContainer _repSpilloverTemplateStore
Definition: ObjectMgr.h:1796
void LoadAreaTriggerScripts()
Definition: ObjectMgr.cpp:6620
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid)
Definition: ObjectMgr.cpp:1964
SpawnMetadata const * GetSpawnMetadata(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition: ObjectMgr.h:1487
void AddVendorItem(uint32 entry, VendorItem const &vItem, bool persist=true)
Definition: ObjectMgr.cpp:9717
void ReturnOrDeleteOldMails(bool serverUp)
Definition: ObjectMgr.cpp:6318
void LoadGameObjectAddons()
Definition: ObjectMgr.cpp:1325
uint32 GetScriptId(std::string const &name, bool isDatabaseBound=true)
Definition: ObjectMgr.cpp:9960
SpawnGroupTemplateData const * GetDefaultSpawnGroup() const
Definition: ObjectMgr.h:1443
CreatureModelInfo const * GetCreatureModelInfo(uint32 modelId) const
Definition: ObjectMgr.cpp:1602
CreatureModelContainer _creatureModelStore
Definition: ObjectMgr.h:1889
LinkedRespawnContainer _linkedRespawnStore
Definition: ObjectMgr.h:1900
uint64 GenerateEquipmentSetGuid()
Definition: ObjectMgr.cpp:7381
void LoadSkillTiers()
Definition: ObjectMgr.cpp:8895
uint32 _auctionId
Definition: ObjectMgr.h:1759
void GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
Definition: ObjectMgr.cpp:6692
void DeleteCreatureData(ObjectGuid::LowType spawnId)
Definition: ObjectMgr.cpp:8459
void LoadCreatureTemplateSparring()
Definition: ObjectMgr.cpp:884
CellObjectGuids const * GetCellPersonalObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 phaseId, uint32 cell_id) const
Definition: ObjectMgr.cpp:2410
CellObjectGuidsMap const * GetMapObjectGuids(uint32 mapid, Difficulty spawnMode)
Definition: ObjectMgr.cpp:2400
GameObjectLocaleContainer _gameObjectLocaleStore
Definition: ObjectMgr.h:1903
CellObjectGuids const * GetCellObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 cell_id)
Definition: ObjectMgr.cpp:2392
GameObjectAddonContainer _gameObjectAddonStore
Definition: ObjectMgr.h:1895
QuestObjectivesLocaleContainer _questObjectivesLocaleStore
Definition: ObjectMgr.h:1917
void LoadCreatureQuestStarters()
Definition: ObjectMgr.cpp:8549
bool IsScriptDatabaseBound(uint32 id) const
Definition: ObjectMgr.cpp:9947
GameTele const * GetGameTele(uint32 id) const
Definition: ObjectMgr.h:1610
void LoadInstanceSpawnGroups()
Definition: ObjectMgr.cpp:2834
void LoadItemTemplateAddon()
Definition: ObjectMgr.cpp:3339
PlayerXPperLevel _playerXPperLevel
Definition: ObjectMgr.h:1872
QuestRelationsReverse _goQuestInvolvedRelationsReverse
Definition: ObjectMgr.h:1807
void LoadSpellScriptNames()
Definition: ObjectMgr.cpp:5980
CreatureTemplate const * GetCreatureTemplate(uint32 entry) const
void LoadMailLevelRewards()
Definition: ObjectMgr.cpp:9167
void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo *plinfo) const
Definition: ObjectMgr.cpp:4405
CreatureData const * GetCreatureData(ObjectGuid::LowType spawnId) const
Definition: ObjectMgr.h:1498
void AddGameobjectToGrid(GameObjectData const *data)
Definition: ObjectMgr.cpp:2938
PointOfInterestContainer _pointsOfInterestStore
Definition: ObjectMgr.h:1801
void LoadGameObjectQuestItems()
std::vector< Difficulty > ParseSpawnDifficulties(std::string_view difficultyString, std::string_view table, ObjectGuid::LowType spawnId, uint32 mapId, std::set< Difficulty > const &mapDifficulties)
Definition: ObjectMgr.cpp:2103
void LoadPointsOfInterest()
Definition: ObjectMgr.cpp:8270
void LoadFactionChangeQuests()
RepRewardRateContainer _repRewardRateStore
Definition: ObjectMgr.h:1794
EventContainer _eventStore
Definition: ObjectMgr.h:1791
uint32 GenerateAuctionID()
Definition: ObjectMgr.cpp:7371
std::string GeneratePetName(uint32 entry)
Definition: ObjectMgr.cpp:7994
void LoadCreatureTemplateGossip()
Definition: ObjectMgr.cpp:469
void LoadPointOfInterestLocales()
Definition: ObjectMgr.cpp:316
VehicleSeatAddonContainer _vehicleSeatAddonStore
Definition: ObjectMgr.h:1943
QuestTemplateLocaleContainer _questTemplateLocaleStore
Definition: ObjectMgr.h:1916
bool LoadTrinityStrings()
Definition: ObjectMgr.cpp:8813
std::vector< float > const * GetCreatureTemplateSparringValues(uint32 entry) const
Definition: ObjectMgr.cpp:1424
void LoadCreatureTemplateModels()
Definition: ObjectMgr.cpp:605
std::vector< uint32 > const * GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
Definition: ObjectMgr.cpp:4473
PageTextContainer _pageTextStore
Definition: ObjectMgr.h:1832
void LoadGossipMenuItems()
Definition: ObjectMgr.cpp:9530
void LoadCreatureTrainers()
Definition: ObjectMgr.cpp:9338
ExclusiveQuestGroups _exclusiveQuestGroups
Definition: ObjectMgr.h:1812
void LoadVehicleAccessories()
Definition: ObjectMgr.cpp:3500
void LoadVendors()
Definition: ObjectMgr.cpp:9436
void LoadCreatureModelInfo()
Definition: ObjectMgr.cpp:1699
ClassAvailability const * GetClassExpansionRequirement(uint8 raceId, uint8 classId) const
void LoadVehicleSeatAddon()
Definition: ObjectMgr.cpp:3543
GraveyardData const * FindGraveyardData(uint32 id, uint32 zone) const
Definition: ObjectMgr.cpp:6989
uint32 GetEventScriptId(uint32 eventId) const
Definition: ObjectMgr.cpp:8958
void SetHighestGuids()
Definition: ObjectMgr.cpp:7304
void LoadCreatureTemplateDifficulty()
Definition: ObjectMgr.cpp:925
void InitializeQueriesData(QueryDataGroup mask)
void LoadReservedPlayersNames()
Definition: ObjectMgr.cpp:8588
CreatureQuestItemMap _creatureQuestItemStore
Definition: ObjectMgr.h:1897
void LoadRaceAndClassExpansionRequirements()
QuestPOIContainer _questPOIStore
Definition: ObjectMgr.h:1803
void OnDeleteSpawnData(SpawnData const *data)
Definition: ObjectMgr.cpp:2920
void LoadGameObjects()
Definition: ObjectMgr.cpp:2462
SpellClickInfoContainer _spellClickInfoStore
Definition: ObjectMgr.h:1822
void LoadPlayerChoicesLocale()
QuestGreeting const * GetQuestGreeting(TypeID type, uint32 id) const
Definition: ObjectMgr.cpp:6503
void LoadPhaseNames()
void LoadWorldSafeLocs()
Definition: ObjectMgr.cpp:7001
uint64 _voidItemId
Definition: ObjectMgr.h:1765
std::unordered_map< uint32, WorldSafeLocsEntry > _worldSafeLocs
Definition: ObjectMgr.h:1789
bool IsVendorItemValid(uint32 vendor_entry, VendorItem const &vItem, Player *player=nullptr, std::set< uint32 > *skip_vendors=nullptr, uint32 ORnpcflag=0) const
Definition: ObjectMgr.cpp:9760
void LoadCreatureAddons()
Definition: ObjectMgr.cpp:1180
bool AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist=true)
Definition: ObjectMgr.cpp:7054
GameObjectOverrideContainer _gameObjectOverrideStore
Definition: ObjectMgr.h:1906
void LoadCreatures()
Definition: ObjectMgr.cpp:2132
std::unordered_map< uint32, CreatureSummonedData > _creatureSummonedDataStore
Definition: ObjectMgr.h:1890
SkillTiersEntry const * GetSkillTier(uint32 skillTierId) const
Definition: ObjectMgr.cpp:7932
ItemTemplate const * GetItemTemplate(uint32 entry) const
Definition: ObjectMgr.cpp:3405
WorldSafeLocsEntry const * GetClosestGraveyard(WorldLocation const &location, uint32 team, WorldObject *conditionObject) const
Definition: ObjectMgr.cpp:6817
static Creature * ToCreature(Object *o)
Definition: Object.h:219
static Player * ToPlayer(Object *o)
Definition: Object.h:213
bool HasVisibleMapId(uint32 visibleMapId) const
Definition: PhaseShift.h:105
static bool IsPersonalPhase(uint32 phaseId)
static PhaseShift const & GetEmptyPhaseShift()
static void InitDbVisibleMapId(PhaseShift &phaseShift, int32 visibleMapId)
WorldSession * GetSession() const
Definition: Player.h:2101
void setFloat(const uint8 index, const float value)
void setUInt8(const uint8 index, const uint8 value)
void setInt64(const uint8 index, const int64 value)
void setUInt32(const uint8 index, const uint32 value)
void setUInt16(const uint8 index, const uint16 value)
void setString(const uint8 index, const std::string &value)
void setUInt64(const uint8 index, const uint64 value)
uint32 _soundAccept
Definition: QuestDef.h:772
int32 _prevQuestID
Definition: QuestDef.h:817
int32 _questSortID
Definition: QuestDef.h:740
uint32 _contentTuningID
Definition: QuestDef.h:739
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionValue
Definition: QuestDef.h:697
uint32 _requiredSkillPoints
Definition: QuestDef.h:824
std::array< LootItemType, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemType
Definition: QuestDef.h:692
std::vector< uint32 > DependentBreadcrumbQuests
Definition: QuestDef.h:726
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyId
Definition: QuestDef.h:700
int32 _breadcrumbForQuestId
Definition: QuestDef.h:820
void LoadQuestDetails(Field *fields)
Definition: QuestDef.cpp:173
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemCount
Definition: QuestDef.h:694
bool HasQuestObjectiveType(QuestObjectiveType type) const
Definition: QuestDef.h:575
void LoadRewardDisplaySpell(Field *fields)
Definition: QuestDef.cpp:140
uint32 _sourceItemIdCount
Definition: QuestDef.h:829
Trinity::RaceMask< uint64 > _allowableRaces
Definition: QuestDef.h:776
static bool IsTakingQuestEnabled(uint32 questId)
Definition: QuestDef.cpp:428
void LoadConditionalConditionalRequestItemsText(Field *fields)
Definition: QuestDef.cpp:326
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemId
Definition: QuestDef.h:693
uint32 _rewardSkillPoints
Definition: QuestDef.h:766
void LoadQuestRequestItems(Field *fields)
Definition: QuestDef.cpp:190
uint32 _requiredMinRepFaction
Definition: QuestDef.h:825
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemId
Definition: QuestDef.h:688
uint32 _requiredSkillId
Definition: QuestDef.h:823
int32 _requiredMinRepValue
Definition: QuestDef.h:826
uint32 _flags
Definition: QuestDef.h:756
void LoadConditionalConditionalQuestDescription(Field *fields)
Definition: QuestDef.cpp:306
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemCount
Definition: QuestDef.h:689
void LoadQuestMailSender(Field *fields)
Definition: QuestDef.cpp:250
uint32 _nextQuestInChain
Definition: QuestDef.h:743
void LoadQuestObjective(Field *fields)
Definition: QuestDef.cpp:255
int32 _exclusiveGroup
Definition: QuestDef.h:819
uint32 _sourceItemId
Definition: QuestDef.h:755
int32 GetPrevQuestId() const
Definition: QuestDef.h:604
void LoadConditionalConditionalOfferRewardText(Field *fields)
Definition: QuestDef.cpp:346
int32 _requiredMaxRepValue
Definition: QuestDef.h:828
uint32 GetQuestType() const
Definition: QuestDef.h:588
uint32 _requiredMaxRepFaction
Definition: QuestDef.h:827
std::vector< uint32 > DependentPreviousQuests
Definition: QuestDef.h:725
uint32 GetQuestId() const
Definition: QuestDef.h:587
void LoadQuestOfferReward(Field *fields)
Definition: QuestDef.cpp:206
uint32 _rewardMailDelay
Definition: QuestDef.h:822
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDropQuantity
Definition: QuestDef.h:691
uint32 _sourceSpellID
Definition: QuestDef.h:816
uint32 _rewardSkillId
Definition: QuestDef.h:765
uint32 _soundTurnIn
Definition: QuestDef.h:773
int32 GetBreadcrumbForQuestId() const
Definition: QuestDef.h:607
uint32 _specialFlags
Definition: QuestDef.h:831
void SetSpecialFlag(QuestSpecialFlags flag)
Definition: QuestDef.h:574
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionOverride
Definition: QuestDef.h:698
std::array< uint32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionId
Definition: QuestDef.h:696
uint32 _rewardSpell
Definition: QuestDef.h:749
uint32 _allowableClasses
Definition: QuestDef.h:815
QuestObjectives const & GetObjectives() const
Definition: QuestDef.h:631
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyCount
Definition: QuestDef.h:701
uint32 _id
Definition: QuestDef.h:736
uint32 _rewardMailSenderEntry
Definition: QuestDef.h:830
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDrop
Definition: QuestDef.h:690
void LoadConditionalConditionalQuestCompletionLog(Field *fields)
Definition: QuestDef.cpp:366
uint32 _rewardTitleId
Definition: QuestDef.h:763
bool HasFlag(QuestFlags flag) const
Definition: QuestDef.h:569
uint32 _rewardMailTemplateId
Definition: QuestDef.h:821
void LoadRewardChoiceItems(Field *fields)
Definition: QuestDef.cpp:167
uint32 _nextQuestID
Definition: QuestDef.h:818
void LoadQuestTemplateAddon(Field *fields)
Definition: QuestDef.cpp:225
static const int32 Reputation_Cap
Definition: ReputationMgr.h:79
SpellEffectName Effect
Definition: SpellInfo.h:218
SpellInfo const * GetFirstRankSpell() const
Definition: SpellInfo.cpp:4272
uint32 const Id
Definition: SpellInfo.h:325
bool IsRanked() const
Definition: SpellInfo.cpp:4260
int32 GetDuration() const
Definition: SpellInfo.cpp:3791
SpellEffectInfo const & GetEffect(SpellEffIndex index) const
Definition: SpellInfo.h:577
std::vector< SpellEffectInfo > const & GetEffects() const
Definition: SpellInfo.h:576
SpellInfo const * GetNextRankSpell() const
Definition: SpellInfo.cpp:4286
bool HasAura(AuraType aura) const
Definition: SpellInfo.cpp:1400
static bool IsSpellValid(SpellInfo const *spellInfo, Player *player=nullptr, bool msg=true)
Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book,...
Definition: SpellMgr.cpp:142
Unit * GetSummonerUnit() const
Utility class to enable range for loop syntax for multimap.equal_range uses.
Definition: IteratorPair.h:32
constexpr end_iterator end() const
Definition: IteratorPair.h:39
constexpr iterator begin() const
Definition: IteratorPair.h:38
decltype(auto) PostWork(T &&work)
Definition: ThreadPool.h:33
Definition: Unit.h:627
TempSummon * ToTempSummon()
Definition: Unit.h:1756
bool IsSummon() const
Definition: Unit.h:738
bool IsInRaidWith(Unit const *unit) const
Definition: Unit.cpp:11546
bool IsInPartyWith(Unit const *unit) const
Definition: Unit.cpp:11527
static VMapManager2 * createOrGetVMapManager()
Definition: VMapFactory.cpp:27
Unit * GetBase() const
Definition: Vehicle.h:49
uint32 GetCreatureEntry() const
Definition: Vehicle.h:51
constexpr void WorldRelocate(WorldLocation const &loc)
Definition: Position.h:187
constexpr uint32 GetMapId() const
Definition: Position.h:201
PhaseShift & GetPhaseShift()
Definition: Object.h:523
bool IsFriendlyTo(WorldObject const *target) const
Definition: Object.cpp:2865
static void StopNow(uint8 exitcode)
Definition: World.h:670
#define sWorld
Definition: World.h:931
Realm realm
Definition: World.cpp:3966
@ CONFIG_STRICT_PET_NAMES
Definition: World.h:247
@ CONFIG_MIN_PET_NAME
Definition: World.h:250
@ CONFIG_MAX_PLAYER_LEVEL
Definition: World.h:259
@ CONFIG_MIN_CHARTER_NAME
Definition: World.h:249
@ CONFIG_STRICT_CHARTER_NAMES
Definition: World.h:246
@ CONFIG_STRICT_PLAYER_NAMES
Definition: World.h:245
@ CONFIG_EXPANSION
Definition: World.h:308
@ CONFIG_MIN_PLAYER_NAME
Definition: World.h:248
@ CONFIG_CREATURE_CHECK_INVALID_POSITION
Definition: World.h:192
@ CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA
Definition: World.h:176
@ CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA
Definition: World.h:175
@ CONFIG_CACHE_DATA_QUERIES
Definition: World.h:191
@ CONFIG_GAME_OBJECT_CHECK_INVALID_POSITION
Definition: World.h:193
@ ERROR_EXIT_CODE
Definition: World.h:75
bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const *ref, uint8 flags)
Definition: DisableMgr.cpp:285
time_t GetGameTime()
Definition: GameTime.cpp:44
std::span< ItemBonusEntry const * > GetItemBonuses(uint32 bonusListId)
float constexpr gravity
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
bool Intersects(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Definition: Containers.h:201
TC_GAME_API char const * GetTrinityString(ChatHandler const *handler, TrinityStrings which)
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
bool IsValidMapCoord(float c)
Definition: GridDefines.h:231
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
Definition: StringFormat.h:38
GridCoord ComputeGridCoord(float x, float y)
Definition: GridDefines.h:194
Optional< Result > StringTo(std::string_view str, Params &&... params)
CellCoord ComputeCellCoord(float x, float y)
Definition: GridDefines.h:206
LoadResult
Definition: IVMapManager.h:35
std::string questFailedText
Definition: ObjectMgr.h:476
uint32 achievement
Definition: ObjectMgr.h:475
uint16 ParentAreaID
Definition: DB2Structure.h:131
uint32 target_mapId
Definition: ObjectMgr.h:460
float target_Orientation
Definition: ObjectMgr.h:464
Definition: Player.h:375
uint8 MinActiveExpansionLevel
Definition: ObjectMgr.h:1025
uint8 AccountExpansionLevel
Definition: ObjectMgr.h:1024
uint8 ActiveExpansionLevel
Definition: ObjectMgr.h:1023
std::weak_ptr< ConditionContainer > Conditions
Definition: ConditionMgr.h:397
bool Meets(WorldObject const *object) const
Definition: ConditionMgr.h:369
uint32 x_coord
Definition: GridDefines.h:173
uint32 y_coord
Definition: GridDefines.h:174
uint32 GetId() const
Definition: GridDefines.h:166
std::vector< uint32 > auras
Definition: CreatureData.h:646
uint16 meleeAnimKit
Definition: CreatureData.h:645
uint16 movementAnimKit
Definition: CreatureData.h:644
uint16 aiAnimKit
Definition: CreatureData.h:643
VisibilityDistanceType visibilityDistanceType
Definition: CreatureData.h:647
uint32 RangedAttackPower
Definition: CreatureData.h:561
float wander_distance
Definition: CreatureData.h:593
Optional< uint64 > npcflag
Definition: CreatureData.h:598
uint8 movementType
Definition: CreatureData.h:597
Optional< CreatureModel > display
Definition: CreatureData.h:591
uint32 curhealth
Definition: CreatureData.h:595
uint32 curmana
Definition: CreatureData.h:596
Optional< uint32 > unit_flags2
Definition: CreatureData.h:600
uint32 currentwaypoint
Definition: CreatureData.h:594
Optional< uint32 > unit_flags3
Definition: CreatureData.h:601
Optional< uint32 > unit_flags
Definition: CreatureData.h:599
CreatureStaticFlagsHolder StaticFlags
Definition: CreatureData.h:456
int32 HealthScalingExpansion
Definition: CreatureData.h:442
std::vector< std::string > Title
Definition: CreatureData.h:571
std::vector< std::string > Name
Definition: CreatureData.h:569
std::vector< std::string > TitleAlt
Definition: CreatureData.h:572
std::vector< std::string > NameAlt
Definition: CreatureData.h:570
uint32 displayId_other_gender
Definition: CreatureData.h:609
uint32 CreatureDisplayID
Definition: CreatureData.h:432
CreatureRandomMovementType Random
Definition: CreatureData.h:399
CreatureChaseMovementType Chase
Definition: CreatureData.h:398
Optional< uint32 > CreatureIDVisibleToSummoner
Definition: CreatureData.h:615
Optional< uint32 > GroundMountDisplayID
Definition: CreatureData.h:616
Optional< std::vector< uint32 > > DespawnOnQuestsRemoved
Definition: CreatureData.h:618
Optional< uint32 > FlyingMountDisplayID
Definition: CreatureData.h:617
CreatureModel const * GetRandomValidModel() const
Definition: Creature.cpp:107
uint32 RangeAttackTime
Definition: CreatureData.h:500
std::string TitleAlt
Definition: CreatureData.h:486
CreatureClassifications Classification
Definition: CreatureData.h:497
CreatureModel const * GetFirstInvisibleModel() const
Definition: Creature.cpp:142
std::string SubName
Definition: CreatureData.h:485
int32 CreatureImmunitiesId
Definition: CreatureData.h:522
CreatureFamily family
Definition: CreatureData.h:507
std::string Name
Definition: CreatureData.h:483
uint32 spells[MAX_CREATURE_SPELLS]
Definition: CreatureData.h:511
int32 resistance[MAX_SPELL_SCHOOL]
Definition: CreatureData.h:510
std::string StringId
Definition: CreatureData.h:525
std::vector< uint32 > GossipMenuIds
Definition: CreatureData.h:488
CreatureMovementData Movement
Definition: CreatureData.h:515
uint32 KillCredit[MAX_KILL_CREDIT]
Definition: CreatureData.h:481
uint32 RequiredExpansion
Definition: CreatureData.h:490
int32 WidgetSetUnitConditionID
Definition: CreatureData.h:520
std::vector< CreatureModel > Models
Definition: CreatureData.h:482
std::string FemaleName
Definition: CreatureData.h:484
std::string AIName
Definition: CreatureData.h:513
std::string IconName
Definition: CreatureData.h:487
std::string name[MAX_DECLINED_NAME_CASES]
Definition: UnitDefines.h:491
EquipmentItem Items[MAX_EQUIPMENT_ITEMS]
Definition: CreatureData.h:584
uint16 AppearanceModId
Definition: CreatureData.h:578
uint16 ItemVisual
Definition: CreatureData.h:579
std::array< int32, 4 > ReputationBase
uint16 ParentFactionID
std::array< int16, 4 > ReputationClassMask
bool CanHaveReputation() const
std::array< Trinity::RaceMask< int64 >, 4 > ReputationRaceMask
QuaternionData ParentRotation
InvisibilityType invisibilityType
QuaternionData rotation
std::vector< std::string > Name
std::vector< std::string > Unk1
std::vector< std::string > CastBarCaption
std::array< uint32, 5 > ArtKits
struct GameObjectTemplate::@213::@239 flagStand
struct GameObjectTemplate::@213::@278 raw
std::string StringId
uint32 data[MAX_GAMEOBJECT_DATA]
struct GameObjectTemplate::@213::@227 areaDamage
struct GameObjectTemplate::@213::@241 flagDrop
struct GameObjectTemplate::@213::@218 chest
struct GameObjectTemplate::@213::@237 spellCaster
struct GameObjectTemplate::@213::@216 button
struct GameObjectTemplate::@213::@215 door
struct GameObjectTemplate::@213::@265 gatheringNode
bool IsDespawnAtAction() const
struct GameObjectTemplate::@213::@223 spellFocus
struct GameObjectTemplate::@213::@230 moTransport
struct GameObjectTemplate::@213::@247 barberChair
std::string castBarCaption
struct GameObjectTemplate::@213::@217 questgiver
struct GameObjectTemplate::@213::@225 goober
std::string IconName
struct GameObjectTemplate::@213::@240 fishingHole
struct GameObjectTemplate::@213::@222 chair
struct GameObjectTemplate::@213::@253 garrisonBuilding
struct GameObjectTemplate::@213::@221 trap
float position_y
Definition: ObjectMgr.h:164
float orientation
Definition: ObjectMgr.h:166
float position_x
Definition: ObjectMgr.h:163
uint32 mapId
Definition: ObjectMgr.h:167
std::string name
Definition: ObjectMgr.h:168
float position_z
Definition: ObjectMgr.h:165
std::wstring wnameLow
Definition: ObjectMgr.h:169
int32 FriendshipFactionID
Definition: ObjectMgr.h:772
std::vector< std::string > BoxText
Definition: ObjectMgr.h:529
std::vector< std::string > OptionText
Definition: ObjectMgr.h:528
Optional< int32 > SpellID
Definition: ObjectMgr.h:758
uint32 BoxBroadcastTextID
Definition: ObjectMgr.h:757
std::string OptionText
Definition: ObjectMgr.h:747
int32 GossipOptionID
Definition: ObjectMgr.h:744
uint32 ActionMenuID
Definition: ObjectMgr.h:751
uint32 OrderIndex
Definition: ObjectMgr.h:745
Optional< int32 > GossipNpcOptionID
Definition: ObjectMgr.h:753
Optional< int32 > OverrideIconID
Definition: ObjectMgr.h:759
uint32 Language
Definition: ObjectMgr.h:749
uint32 ActionPoiID
Definition: ObjectMgr.h:752
uint32 BoxMoney
Definition: ObjectMgr.h:755
GossipOptionNpc OptionNpc
Definition: ObjectMgr.h:746
std::string BoxText
Definition: ObjectMgr.h:756
GossipOptionFlags Flags
Definition: ObjectMgr.h:750
uint32 OptionBroadcastTextID
Definition: ObjectMgr.h:748
uint32 TextID
Definition: ObjectMgr.h:766
uint32 MenuID
Definition: ObjectMgr.h:765
uint32 safeLocId
Definition: ObjectMgr.h:840
ConditionsReference Conditions
Definition: ObjectMgr.h:841
uint8 SubclassID
uint8 ClassID
int8 InventoryType
std::array< int8, MAX_ITEM_PROTO_STATS > StatModifierBonusStat
ItemSpecStats(ItemEntry const *item, ItemSparseEntry const *sparse)
Definition: ObjectMgr.cpp:3043
void AddModStat(int32 itemStatType)
Definition: ObjectMgr.cpp:3192
uint32 ItemSpecStatTypes[MAX_ITEM_PROTO_STATS]
Definition: ObjectMgr.cpp:3040
uint32 ItemSpecStatCount
Definition: ObjectMgr.cpp:3041
void AddStat(ItemSpecStat statType)
Definition: ObjectMgr.cpp:3180
std::bitset< MAX_CLASSES *MAX_SPECIALIZATIONS > Specializations[3]
Definition: ItemTemplate.h:838
uint32 MinMoneyLoot
Definition: ItemTemplate.h:833
uint32 MaxMoneyLoot
Definition: ItemTemplate.h:834
ItemEntry const * BasicData
Definition: ItemTemplate.h:773
uint32 FoodType
Definition: ItemTemplate.h:832
static std::size_t CalculateItemSpecBit(ChrSpecializationEntry const *spec)
ItemSparseEntry const * ExtendedData
Definition: ItemTemplate.h:774
int32 QuestLogItemId
Definition: ItemTemplate.h:840
uint32 RandomBonusListTemplateId
Definition: ItemTemplate.h:837
uint32 ScriptId
Definition: ItemTemplate.h:831
uint32 MaxDurability
Definition: ItemTemplate.h:827
float SpellPPMRate
Definition: ItemTemplate.h:836
uint32 ItemSpecClassMask
Definition: ItemTemplate.h:839
uint32 FlagsCu
Definition: ItemTemplate.h:835
ObjectGuid::LowType item_guid
Definition: Mail.h:169
uint32 item_template
Definition: Mail.h:170
Definition: Mail.h:175
uint64 messageID
Definition: Mail.h:176
ObjectGuid::LowType receiver
Definition: Mail.h:181
uint8 messageType
Definition: Mail.h:177
uint64 COD
Definition: Mail.h:189
time_t expire_time
Definition: Mail.h:186
ObjectGuid::LowType sender
Definition: Mail.h:180
std::vector< MailItemInfo > items
Definition: Mail.h:184
time_t deliver_time
Definition: Mail.h:187
uint32 checked
Definition: Mail.h:190
uint16 mailTemplateId
Definition: Mail.h:179
DBCPosition2D Corpse
bool IsBattlegroundOrArena() const
int16 CorpseMapID
int16 ParentMapID
bool IsDungeon() const
bool Instanceable() const
float Probability
Definition: NPCHandler.h:23
uint32 BroadcastTextID
Definition: NPCHandler.h:24
NpcTextData Data[MAX_NPC_TEXT_OPTIONS]
Definition: NPCHandler.h:31
std::vector< std::string > Text
Definition: NPCHandler.h:36
uint8 Flags
Definition: ObjectMgr.h:63
std::string Text
Definition: ObjectMgr.h:60
uint32 NextPageID
Definition: ObjectMgr.h:61
int32 PlayerConditionID
Definition: ObjectMgr.h:62
uint16 health
Definition: ObjectMgr.h:680
uint16 armor
Definition: ObjectMgr.h:682
uint16 stats[MAX_STATS]
Definition: ObjectMgr.h:679
uint16 mana
Definition: ObjectMgr.h:681
ConditionContainer Conditions
Definition: ObjectMgr.h:1017
PhaseInfoStruct const * PhaseInfo
Definition: ObjectMgr.h:1015
std::unordered_set< uint32 > Areas
Definition: ObjectMgr.h:1006
bool IsAllowedInArea(uint32 areaId) const
std::vector< std::string > Question
Definition: ObjectMgr.h:553
std::vector< std::string > Confirmation
Definition: ObjectMgr.h:548
std::vector< std::string > SubHeader
Definition: ObjectMgr.h:545
std::vector< std::string > Header
Definition: ObjectMgr.h:544
std::vector< std::string > Description
Definition: ObjectMgr.h:547
std::vector< std::string > Answer
Definition: ObjectMgr.h:543
std::vector< std::string > ButtonTooltip
Definition: ObjectMgr.h:546
Optional< uint32 > RarityColor
Definition: ObjectMgr.h:904
Optional< int32 > Rarity
Definition: ObjectMgr.h:903
std::string Confirmation
Definition: ObjectMgr.h:925
uint16 ResponseIdentifier
Definition: ObjectMgr.h:912
std::string ButtonTooltip
Definition: ObjectMgr.h:923
uint32 UiTextureAtlasElementID
Definition: ObjectMgr.h:916
Optional< uint32 > RewardQuestID
Definition: ObjectMgr.h:927
std::string Answer
Definition: ObjectMgr.h:920
std::string Description
Definition: ObjectMgr.h:924
std::string SubHeader
Definition: ObjectMgr.h:922
std::string Header
Definition: ObjectMgr.h:921
int32 ChoiceId
Definition: ObjectMgr.h:933
uint32 SoundKitId
Definition: ObjectMgr.h:935
bool HideWarboardHeader
Definition: ObjectMgr.h:941
uint32 CloseSoundKitId
Definition: ObjectMgr.h:936
bool KeepOpenAfterChoice
Definition: ObjectMgr.h:942
std::vector< PlayerChoiceResponse > Responses
Definition: ObjectMgr.h:940
std::string Question
Definition: ObjectMgr.h:938
int32 UiTextureKitId
Definition: ObjectMgr.h:934
int64 Duration
Definition: ObjectMgr.h:937
std::string PendingChoiceText
Definition: ObjectMgr.h:939
PlayerChoiceResponse const * GetResponse(int32 responseId) const
Definition: ObjectMgr.h:944
uint16 stats[MAX_STATS]
Definition: ObjectMgr.h:632
std::vector< std::string > Name
Definition: ObjectMgr.h:536
std::string Name
Definition: ObjectMgr.h:738
uint32 Importance
Definition: ObjectMgr.h:736
int32 WMOGroupID
Definition: ObjectMgr.h:737
Position Pos
Definition: ObjectMgr.h:733
constexpr float GetPositionX() const
Definition: Position.h:76
constexpr float GetPositionY() const
Definition: Position.h:77
std::string ToString() const
Definition: Position.cpp:128
constexpr void GetPosition(float &x, float &y) const
Definition: Position.h:81
constexpr void Relocate(float x, float y)
Definition: Position.h:63
constexpr float GetOrientation() const
Definition: Position.h:79
constexpr float GetPositionZ() const
Definition: Position.h:78
bool isUnit() const
Definition: GameObject.cpp:108
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
Definition: GameObject.cpp:118
std::vector< std::string > Greeting
Definition: QuestDef.h:403
int32 ObjectID
Definition: QuestDef.h:449
uint32 QuestID
Definition: QuestDef.h:446
std::vector< std::string > Description
Definition: QuestDef.h:426
std::vector< std::string > RewardText
Definition: QuestDef.h:431
int32 QuestID
Definition: ObjectMgr.h:820
void InitializeQueryData()
std::vector< QuestPOIBlobData > Blobs
Definition: ObjectMgr.h:821
QuestRelations::const_iterator _it
Definition: ObjectMgr.h:591
QuestRelations::const_iterator _end
Definition: ObjectMgr.h:591
bool HasQuest(uint32 questId) const
Definition: ObjectMgr.cpp:8583
QuestRelations::const_iterator _end
Definition: ObjectMgr.h:605
QuestRelations::const_iterator _begin
Definition: ObjectMgr.h:605
Iterator end() const
Definition: ObjectMgr.h:600
std::vector< std::string > CompletionText
Definition: QuestDef.h:421
std::vector< std::string > PortraitTurnInText
Definition: QuestDef.h:414
std::vector< std::string > QuestCompletionLog
Definition: QuestDef.h:416
std::vector< std::string > LogTitle
Definition: QuestDef.h:408
std::vector< std::string > PortraitGiverName
Definition: QuestDef.h:413
std::vector< std::string > PortraitTurnInName
Definition: QuestDef.h:415
std::vector< std::string > QuestDescription
Definition: QuestDef.h:410
std::vector< std::string > LogDescription
Definition: QuestDef.h:409
std::vector< std::string > PortraitGiverText
Definition: QuestDef.h:412
std::vector< std::string > AreaDescription
Definition: QuestDef.h:411
std::vector< ClassAvailability > Classes
Definition: ObjectMgr.h:1031
uint8 Timezone
Definition: Realm.h:90
float questMonthlyRate
Definition: ObjectMgr.h:704
float questWeeklyRate
Definition: ObjectMgr.h:703
float questDailyRate
Definition: ObjectMgr.h:702
float questRepeatableRate
Definition: ObjectMgr.h:705
float spellRate
Definition: ObjectMgr.h:707
float questRate
Definition: ObjectMgr.h:701
float creatureRate
Definition: ObjectMgr.h:706
uint32 faction_rank[MAX_SPILLOVER_FACTIONS]
Definition: ObjectMgr.h:727
uint32 faction[MAX_SPILLOVER_FACTIONS]
Definition: ObjectMgr.h:725
float faction_rate[MAX_SPILLOVER_FACTIONS]
Definition: ObjectMgr.h:726
uint32 SceneId
Definition: ObjectMgr.h:854
EnumFlag< SceneFlag > PlaybackFlags
Definition: ObjectMgr.h:855
uint32 ScriptId
Definition: ObjectMgr.h:858
uint32 ScenePackageId
Definition: ObjectMgr.h:856
bool Encrypted
Definition: ObjectMgr.h:857
float Orientation
Definition: ObjectMgr.h:279
uint32 Flags
Definition: ObjectMgr.h:236
ScriptsType type
Definition: ObjectMgr.h:220
struct ScriptInfo::@284::@296 TempSummonCreature
uint32 QuestID
Definition: ObjectMgr.h:284
int32 TextID
Definition: ObjectMgr.h:237
struct ScriptInfo::@284::@311 PlayAnimKit
struct ScriptInfo::@284::@295 RespawnGameobject
float DestX
Definition: ObjectMgr.h:258
uint32 ItemEntry
Definition: ObjectMgr.h:344
uint32 ChatType
Definition: ObjectMgr.h:235
struct ScriptInfo::@284::@292 TeleportTo
uint32 id
Definition: ObjectMgr.h:221
uint32 delay
Definition: ObjectMgr.h:222
float fData[4]
Definition: ObjectMgr.h:230
float PosY
Definition: ObjectMgr.h:307
ScriptCommands command
Definition: ObjectMgr.h:223
float DestY
Definition: ObjectMgr.h:259
struct ScriptInfo::@284::@297 ToggleDoor
uint32 AnimKitID
Definition: ObjectMgr.h:411
uint32 MapID
Definition: ObjectMgr.h:272
uint32 nData[3]
Definition: ObjectMgr.h:229
struct ScriptInfo::@284::@286 Raw
float PosZ
Definition: ObjectMgr.h:308
struct ScriptInfo::@284::@301 CreateItem
struct ScriptInfo::@284::@288 Emote
struct ScriptInfo::@284::@294 KillCredit
float PosX
Definition: ObjectMgr.h:306
struct ScriptInfo::@284::@299 CastSpell
uint32 Distance
Definition: ObjectMgr.h:285
uint32 SpellID
Definition: ObjectMgr.h:323
std::string GetDebugInfo() const
Definition: ObjectMgr.cpp:149
float DestZ
Definition: ObjectMgr.h:260
struct ScriptInfo::@284::@287 Talk
struct ScriptInfo::@284::@293 QuestExplored
uint32 GOGuid
Definition: ObjectMgr.h:296
struct ScriptInfo::@284::@298 RemoveAura
uint32 CreatureEntry
Definition: ObjectMgr.h:290
uint32 Amount
Definition: ObjectMgr.h:345
uint32 EmoteID
Definition: ObjectMgr.h:242
uint32 GetValueForTierIndex(uint32 tierIndex) const
Definition: ObjectMgr.cpp:7938
uint32 Value[MAX_SKILL_STEP]
Definition: ObjectMgr.h:973
uint32 scriptId
Definition: SpawnData.h:113
uint8 phaseUseFlags
Definition: SpawnData.h:106
uint32 id
Definition: SpawnData.h:104
uint32 phaseId
Definition: SpawnData.h:107
Position spawnPoint
Definition: SpawnData.h:105
int32 spawntimesecs
Definition: SpawnData.h:111
int32 terrainSwapMap
Definition: SpawnData.h:109
uint32 phaseGroup
Definition: SpawnData.h:108
uint32 poolId
Definition: SpawnData.h:110
std::string StringId
Definition: SpawnData.h:114
std::vector< Difficulty > spawnDifficulties
Definition: SpawnData.h:112
std::string name
Definition: SpawnData.h:68
SpawnGroupFlags flags
Definition: SpawnData.h:70
SpawnObjectType const type
Definition: SpawnData.h:92
static constexpr bool TypeHasData(SpawnObjectType type)
Definition: SpawnData.h:85
SpawnGroupTemplateData const * spawnGroupData
Definition: SpawnData.h:96
static constexpr bool TypeIsValid(SpawnObjectType type)
Definition: SpawnData.h:86
uint64 spawnId
Definition: SpawnData.h:93
uint32 mapId
Definition: SpawnData.h:94
uint8 castFlags
Definition: ObjectMgr.h:449
uint32 spellId
Definition: ObjectMgr.h:448
bool IsFitToRequirements(Unit const *clicker, Unit const *clickee) const
Definition: ObjectMgr.cpp:183
SpellClickUserTypes userType
Definition: ObjectMgr.h:450
std::array< int32, 2 > MountCreatureID
Stores data for temp summons.
Definition: ObjectMgr.h:93
Milliseconds time
Despawn time, usable only with certain temp summon types.
Definition: ObjectMgr.h:97
TempSummonType type
Summon type, see TempSummonType for available types.
Definition: ObjectMgr.h:96
uint32 entry
Entry of summoned creature.
Definition: ObjectMgr.h:94
Position pos
Position, where should be creature spawned.
Definition: ObjectMgr.h:95
Key for storing temp summon data in TempSummonDataContainer.
Definition: ObjectMgr.h:77
std::vector< uint32 > UiMapPhaseIDs
Definition: ObjectMgr.h:1000
uint32 ReqSkillLine
Definition: Trainer.h:57
std::array< uint32, 3 > ReqAbility
Definition: Trainer.h:59
uint8 ReqLevel
Definition: Trainer.h:60
uint32 ReqSkillRank
Definition: Trainer.h:58
uint32 SpellId
Definition: Trainer.h:55
uint32 MoneyCost
Definition: Trainer.h:56
std::vector< std::string > Content
Definition: ObjectMgr.h:491
constexpr bool HasRace(uint8 raceId) const
Definition: RaceMask.h:88
constexpr bool IsEmpty() const
Definition: RaceMask.h:143
Milliseconds DespawnDelay
VendorItem const * FindItemCostPair(uint32 item_id, uint32 extendedCost, uint8 type) const
Definition: Creature.cpp:91
void AddItem(VendorItem vItem)
Definition: CreatureData.h:678
uint32 ExtendedCost
Definition: CreatureData.h:658
bool IgnoreFiltering
Definition: CreatureData.h:662
uint32 PlayerConditionId
Definition: CreatureData.h:661
uint32 item
Definition: CreatureData.h:655
uint32 maxcount
Definition: CreatureData.h:656
std::vector< int32 > BonusListIDs
Definition: CreatureData.h:660
uint32 incrtime
Definition: CreatureData.h:657
WorldLocation Loc
Definition: ObjectMgr.h:835