TrinityCore
Loading...
Searching...
No Matches
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"
20#include "AreaTriggerTemplate.h"
21#include "ArenaTeamMgr.h"
23#include "AzeriteItem.h"
24#include "Chat.h"
25#include "Containers.h"
26#include "CreatureAIFactory.h"
27#include "CriteriaHandler.h"
28#include "DB2Stores.h"
29#include "DatabaseEnv.h"
30#include "DisableMgr.h"
31#include "GameObject.h"
32#include "GameObjectAIFactory.h"
33#include "GameTables.h"
34#include "GameTime.h"
35#include "GossipDef.h"
36#include "GridDefines.h"
37#include "GroupMgr.h"
38#include "GuildMgr.h"
39#include "InstanceScript.h"
40#include "Item.h"
41#include "ItemBonusMgr.h"
42#include "LFGMgr.h"
43#include "Language.h"
44#include "Log.h"
45#include "LootMgr.h"
46#include "Mail.h"
47#include "MapManager.h"
48#include "MapUtils.h"
49#include "MotionMaster.h"
50#include "MovementTypedefs.h"
51#include "ObjectAccessor.h"
52#include "ObjectDefines.h"
53#include "PhasingHandler.h"
54#include "Player.h"
55#include "PlayerChoice.h"
56#include "QueryPackets.h"
58#include "QuestDef.h"
59#include "QuestMgr.h"
60#include "Random.h"
61#include "RealmList.h"
62#include "ReputationMgr.h"
63#include "ScriptMgr.h"
64#include "ScriptReloadMgr.h"
65#include "SpellInfo.h"
66#include "SpellMgr.h"
67#include "SpellScript.h"
68#include "StringConvert.h"
69#include "TemporarySummon.h"
70#include "TerrainMgr.h"
71#include "ThreadPool.h"
72#include "Timer.h"
73#include "TransmogMgr.h"
74#include "TransportMgr.h"
75#include "VMapFactory.h"
76#include "VMapManager.h"
77#include "Vehicle.h"
78#include "World.h"
79#include "advstd.h"
80#include <G3D/g3dmath.h>
81#include <limits>
82#include <numeric>
83
85
87{
88 std::string res = "";
89 switch (type)
90 {
91 case SCRIPTS_EVENT: res = "event_scripts"; break;
92 default: break;
93 }
94 return res;
95}
96
98{
99 ScriptMapMap* res = nullptr;
100 switch (type)
101 {
102 case SCRIPTS_EVENT: res = &sEventScripts; break;
103 default: break;
104 }
105 return res;
106}
107
109{
110 std::string res = "";
111 switch (command)
112 {
113 case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
114 case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
115 case SCRIPT_COMMAND_FIELD_SET_DEPRECATED: res = "SCRIPT_COMMAND_FIELD_SET_DEPRECATED"; break;
116 case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
117 case SCRIPT_COMMAND_FLAG_SET_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_SET_DEPRECATED"; break;
118 case SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED"; break;
119 case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
120 case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
121 case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
122 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
123 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
124 case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
125 case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
126 case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
127 case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
128 case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
129 case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
130 case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
131 case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
132 case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
133 case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
134 case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
135 // TrinityCore only
136 case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
137 case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
138 case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
139 case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
140 case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
141 case SCRIPT_COMMAND_MOVEMENT: res = "SCRIPT_COMMAND_MOVEMENT"; break;
142 case SCRIPT_COMMAND_PLAY_ANIMKIT: res = "SCRIPT_COMMAND_PLAY_ANIMKIT"; break;
143 default:
144 {
145 res = Trinity::StringFormat("Unknown command: {}", command);
146 break;
147 }
148 }
149 return res;
150}
151
152std::string ScriptInfo::GetDebugInfo() const
153{
154 return Trinity::StringFormat("{} ('{}' script id: {})", GetScriptCommandName(command), GetScriptsTableNameByType(type), id);
155}
156
157bool normalizePlayerName(std::string& name)
158{
159 if (name.empty())
160 return false;
161
162 std::wstring tmp;
163 if (!Utf8toWStr(name, tmp))
164 return false;
165
166 wstrToLower(tmp);
167 if (!tmp.empty())
168 tmp[0] = wcharToUpper(tmp[0]);
169
170 if (!WStrToUtf8(tmp, name))
171 return false;
172
173 return true;
174}
175
176// Extracts player and realm names delimited by -
178{
179 size_t pos = name.find('-');
180 if (pos != std::string::npos)
181 return ExtendedPlayerName(name.substr(0, pos), name.substr(pos + 1));
182 else
183 return ExtendedPlayerName(name, "");
184}
185
186bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
187{
188 Player const* playerClicker = clicker->ToPlayer();
189 if (!playerClicker)
190 return true;
191
192 Unit const* summoner = nullptr;
193 // Check summoners for party
194 if (clickee->IsSummon())
195 summoner = clickee->ToTempSummon()->GetSummonerUnit();
196 if (!summoner)
197 summoner = clickee;
198
199 // This only applies to players
200 switch (userType)
201 {
203 if (!playerClicker->IsFriendlyTo(summoner))
204 return false;
205 break;
207 if (!playerClicker->IsInRaidWith(summoner))
208 return false;
209 break;
211 if (!playerClicker->IsInPartyWith(summoner))
212 return false;
213 break;
214 default:
215 break;
216 }
217
218 return true;
219}
220
222 _auctionId(1),
223 _equipmentSetGuid(1),
224 _mailId(1),
225 _hiPetNumber(1),
226 _creatureSpawnId(1),
227 _gameObjectSpawnId(1),
228 DBCLocaleIndex(LOCALE_enUS)
229{
230}
231
233{
234 static ObjectMgr instance;
235 return &instance;
236}
237
241
242void ObjectMgr::AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector<std::string>& data)
243{
244 if (data.size() <= size_t(localeConstant))
245 {
246 data.reserve(TOTAL_LOCALES);
247 data.resize(localeConstant + 1);
248 }
249
250 data[localeConstant] = value.empty() ? "" : value;
251}
252
254{
255 uint32 oldMSTime = getMSTime();
256
257 _creatureLocaleStore.clear(); // need for reload case
258
259 // 0 1 2 3 4 5
260 QueryResult result = WorldDatabase.Query("SELECT entry, locale, Name, NameAlt, Title, TitleAlt FROM creature_template_locale");
261 if (!result)
262 return;
263
264 do
265 {
266 Field* fields = result->Fetch();
267
268 uint32 id = fields[0].GetUInt32();
269 std::string_view localeName = fields[1].GetStringView();
270
271 LocaleConstant locale = GetLocaleByName(localeName);
272 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
273 continue;
274
276 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
277 AddLocaleString(fields[3].GetStringView(), locale, data.NameAlt);
278 AddLocaleString(fields[4].GetStringView(), locale, data.Title);
279 AddLocaleString(fields[5].GetStringView(), locale, data.TitleAlt);
280
281 } while (result->NextRow());
282
283 TC_LOG_INFO("server.loading", ">> Loaded {} creature locale strings in {} ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
284}
285
287{
288 uint32 oldMSTime = getMSTime();
289
290 _gossipMenuItemsLocaleStore.clear(); // need for reload case
291
292 // 0 1 2 3 4
293 QueryResult result = WorldDatabase.Query("SELECT MenuID, OptionID, Locale, OptionText, BoxText FROM gossip_menu_option_locale");
294
295 if (!result)
296 return;
297
298 do
299 {
300 Field* fields = result->Fetch();
301
302 uint32 menuId = fields[0].GetUInt32();
303 uint32 optionId = fields[1].GetUInt32();
304 std::string_view localeName = fields[2].GetStringView();
305
306 LocaleConstant locale = GetLocaleByName(localeName);
307 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
308 continue;
309
310 GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[std::make_pair(menuId, optionId)];
311 AddLocaleString(fields[3].GetStringView(), locale, data.OptionText);
312 AddLocaleString(fields[4].GetStringView(), locale, data.BoxText);
313 } while (result->NextRow());
314
315 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option locale strings in {} ms", _gossipMenuItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
316}
317
319{
320 uint32 oldMSTime = getMSTime();
321
322 _pointOfInterestLocaleStore.clear(); // need for reload case
323
324 // 0 1 2
325 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM points_of_interest_locale");
326 if (!result)
327 return;
328
329 do
330 {
331 Field* fields = result->Fetch();
332
333 uint32 id = fields[0].GetUInt32();
334 std::string_view localeName = fields[1].GetStringView();
335
336 LocaleConstant locale = GetLocaleByName(localeName);
337 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
338 continue;
339
341 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
342 } while (result->NextRow());
343
344 TC_LOG_INFO("server.loading", ">> Loaded {} points_of_interest locale strings in {} ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
345}
346
348{
349 uint32 oldMSTime = getMSTime();
350
351 // 0 1 2 3 4 5
352 // "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, "
353 // 6 7 8 9
354 // "TitleAlt, IconName, RequiredExpansion, VignetteID, "
355 // 10 11 12 13 14 15 16 17 18 19 20
356 // "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, "
357 // 21 22 23 24
358 // "unit_class, unit_flags, unit_flags2, unit_flags3, "
359 // 25 26 27 28 29 30
360 // "family, trainer_class, type, VehicleId, AIName, MovementType, "
361 // 31 32 33 34 35
362 // "ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, "
363 // 36 37 38 39 40
364 // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
365 // 41 42
366 // "CreatureImmunitiesId, flags_extra, "
367 // 43 44
368 // "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
369
371 stmt->setUInt32(0, 0);
372 stmt->setUInt32(1, 1);
373
374 PreparedQueryResult result = WorldDatabase.Query(stmt);
375 if (!result)
376 {
377 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
378 return;
379 }
380
381 _creatureTemplateStore.reserve(result->GetRowCount());
382 do
383 {
384 Field* fields = result->Fetch();
385 LoadCreatureTemplate(fields);
386 } while (result->NextRow());
387
390
391 // We load the creature models after loading but before checking
393
394 // Checking needs to be done after loading because of the difficulty self referencing
395 for (auto const& ctPair : _creatureTemplateStore)
396 CheckCreatureTemplate(&ctPair.second);
397
398 TC_LOG_INFO("server.loading", ">> Loaded {} creature definitions in {} ms", _creatureTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
399}
400
402{
403 uint32 entry = fields[0].GetUInt32();
404 CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
405
406 creatureTemplate.Entry = entry;
407
408 for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
409 creatureTemplate.KillCredit[i] = fields[1 + i].GetUInt32();
410
411 creatureTemplate.Name = fields[3].GetString();
412 creatureTemplate.FemaleName = fields[4].GetString();
413 creatureTemplate.SubName = fields[5].GetString();
414 creatureTemplate.TitleAlt = fields[6].GetString();
415 creatureTemplate.IconName = fields[7].GetString();
416 creatureTemplate.RequiredExpansion = fields[8].GetUInt32();
417 creatureTemplate.VignetteID = fields[9].GetUInt32();
418 creatureTemplate.faction = fields[10].GetUInt16();
419 creatureTemplate.npcflag = fields[11].GetUInt64();
420 creatureTemplate.speed_walk = fields[12].GetFloat();
421 creatureTemplate.speed_run = fields[13].GetFloat();
422 creatureTemplate.scale = fields[14].GetFloat();
423 creatureTemplate.Classification = CreatureClassifications(fields[15].GetUInt8());
424 creatureTemplate.dmgschool = uint32(fields[16].GetInt8());
425 creatureTemplate.BaseAttackTime = fields[17].GetUInt32();
426 creatureTemplate.RangeAttackTime = fields[18].GetUInt32();
427 creatureTemplate.BaseVariance = fields[19].GetFloat();
428 creatureTemplate.RangeVariance = fields[20].GetFloat();
429 creatureTemplate.unit_class = uint32(fields[21].GetUInt8());
430 creatureTemplate.unit_flags = fields[22].GetUInt32();
431 creatureTemplate.unit_flags2 = fields[23].GetUInt32();
432 creatureTemplate.unit_flags3 = fields[24].GetUInt32();
433 creatureTemplate.family = CreatureFamily(fields[25].GetInt32());
434 creatureTemplate.trainer_class = uint32(fields[26].GetUInt8());
435 creatureTemplate.type = uint32(fields[27].GetUInt8());
436
437 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
438 creatureTemplate.resistance[i] = 0;
439
440 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
441 creatureTemplate.spells[i] = 0;
442
443 creatureTemplate.VehicleId = fields[28].GetUInt32();
444 creatureTemplate.AIName = fields[29].GetString();
445 creatureTemplate.MovementType = uint32(fields[30].GetUInt8());
446
447 if (!fields[31].IsNull())
448 creatureTemplate.Movement.HoverInitiallyEnabled = fields[31].GetBool();
449
450 if (Optional<uint8> chaseMovementType = fields[32].GetUInt8OrNull())
451 creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(*chaseMovementType);
452
453 if (Optional<uint8> randomMovementType = fields[33].GetUInt8OrNull())
454 creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(*randomMovementType);
455
456 if (Optional<uint32> interactionPauseTimer = fields[34].GetUInt32OrNull())
457 creatureTemplate.Movement.InteractionPauseTimer = *interactionPauseTimer;
458
459 creatureTemplate.ModExperience = fields[35].GetFloat();
460 creatureTemplate.RacialLeader = fields[36].GetBool();
461 creatureTemplate.movementId = fields[37].GetUInt32();
462 creatureTemplate.WidgetSetID = fields[38].GetInt32();
463 creatureTemplate.WidgetSetUnitConditionID = fields[39].GetInt32();
464 creatureTemplate.RegenHealth = fields[40].GetBool();
465 creatureTemplate.CreatureImmunitiesId = fields[41].GetInt32();
466 creatureTemplate.flags_extra = fields[42].GetUInt32();
467 creatureTemplate.ScriptID = GetScriptId(fields[43].GetStringView());
468 creatureTemplate.StringId = fields[44].GetString();
469}
470
472{
473 uint32 oldMSTime = getMSTime();
474
475 // 0 1
476 QueryResult result = WorldDatabase.Query("SELECT CreatureID, MenuID FROM creature_template_gossip");
477
478 if (!result)
479 {
480 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template gossip definitions. DB table `creature_template_gossip` is empty.");
481 return;
482 }
483
484 uint32 count = 0;
485
486 do
487 {
488 Field* fields = result->Fetch();
489
490 uint32 creatureID = fields[0].GetUInt32();
491 uint32 menuID = fields[1].GetUInt32();
492
493 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
494 if (itr == _creatureTemplateStore.end())
495 {
496 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for creature {} but this creature doesn't exist", creatureID);
497 continue;
498 }
499
500 GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuID);
501 if (menuBounds.first == menuBounds.second)
502 {
503 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for menu id {} but this menu doesn't exist", menuID);
504 continue;
505 }
506
507 CreatureTemplate& creatureTemplate = itr->second;
508 creatureTemplate.GossipMenuIds.push_back(menuID);
509
510 ++count;
511
512 } while (result->NextRow());
513
514 TC_LOG_INFO("server.loading", ">> Loaded {} creature template gossip menus in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
515}
516
518{
519 uint32 oldMSTime = getMSTime();
520
521 // 0 1 2
522 QueryResult result = WorldDatabase.Query("SELECT CreatureID, School, Resistance FROM creature_template_resistance");
523
524 if (!result)
525 {
526 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template resistance definitions. DB table `creature_template_resistance` is empty.");
527 return;
528 }
529
530 uint32 count = 0;
531
532 do
533 {
534 Field* fields = result->Fetch();
535
536 uint32 creatureID = fields[0].GetUInt32();
537 uint8 school = fields[1].GetUInt8();
538
539 if (school == SPELL_SCHOOL_NORMAL || school >= MAX_SPELL_SCHOOL)
540 {
541 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this school {} doesn't exist", creatureID, school);
542 continue;
543 }
544
545 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
546 if (itr == _creatureTemplateStore.end())
547 {
548 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this creature doesn't exist", creatureID);
549 continue;
550 }
551
552 CreatureTemplate& creatureTemplate = itr->second;
553 creatureTemplate.resistance[school] = fields[2].GetInt16();
554
555 ++count;
556
557 } while (result->NextRow());
558
559 TC_LOG_INFO("server.loading", ">> Loaded {} creature template resistances in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
560}
561
563{
564 uint32 oldMSTime = getMSTime();
565
566 // 0 1 2
567 QueryResult result = WorldDatabase.Query("SELECT CreatureID, `Index`, Spell FROM creature_template_spell");
568
569 if (!result)
570 {
571 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template spell definitions. DB table `creature_template_spell` is empty.");
572 return;
573 }
574
575 uint32 count = 0;
576
577 do
578 {
579 Field* fields = result->Fetch();
580
581 uint32 creatureID = fields[0].GetUInt32();
582 uint8 index = fields[1].GetUInt8();
583
584 if (index >= MAX_CREATURE_SPELLS)
585 {
586 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} with a incorrect index {}", creatureID, index);
587 continue;
588 }
589
590 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
591 if (itr == _creatureTemplateStore.end())
592 {
593 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} but this creature doesn't exist", creatureID);
594 continue;
595 }
596
597 CreatureTemplate& creatureTemplate = itr->second;
598 creatureTemplate.spells[index] = fields[2].GetUInt32();;
599
600 ++count;
601
602 } while (result->NextRow());
603
604 TC_LOG_INFO("server.loading", ">> Loaded {} creature template spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
605}
606
608{
609 uint32 oldMSTime = getMSTime();
610
611 // 0 1 2 3
612 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureDisplayID, DisplayScale, Probability FROM creature_template_model ORDER BY Idx ASC");
613
614 if (!result)
615 {
616 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template model definitions. DB table `creature_template_model` is empty.");
617 return;
618 }
619
620 uint32 count = 0;
621 do
622 {
623 Field* fields = result->Fetch();
624
625 uint32 creatureId = fields[0].GetUInt32();
626 uint32 creatureDisplayId = fields[1].GetUInt32();
627 float displayScale = fields[2].GetFloat();
628 float probability = fields[3].GetFloat();
629
630 CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
631 if (!cInfo)
632 {
633 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_model`", creatureId);
634 continue;
635 }
636
637 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(creatureDisplayId);
638 if (!displayEntry)
639 {
640 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing CreatureDisplayID id ({}), this can crash the client.", creatureId, creatureDisplayId);
641 continue;
642 }
643
644 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(creatureDisplayId);
645 if (!modelInfo)
646 TC_LOG_ERROR("sql.sql", "No model data exist for `CreatureDisplayID` = {} listed by creature (Entry: {}).", creatureDisplayId, creatureId);
647
648 if (displayScale <= 0.0f)
649 displayScale = 1.0f;
650
651 const_cast<CreatureTemplate*>(cInfo)->Models.emplace_back(creatureDisplayId, displayScale, probability);
652
653 ++count;
654 }
655 while (result->NextRow());
656
657 TC_LOG_INFO("server.loading", ">> Loaded {} creature template models in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
658}
659
661{
662 uint32 oldMSTime = getMSTime();
663
664 // 0 1 2 3 4
665 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureIDVisibleToSummoner, GroundMountDisplayID, FlyingMountDisplayID, DespawnOnQuestsRemoved FROM creature_summoned_data");
666
667 if (!result)
668 {
669 TC_LOG_INFO("server.loading", ">> Loaded 0 creature summoned data definitions. DB table `creature_summoned_data` is empty.");
670 return;
671 }
672
673 do
674 {
675 Field* fields = result->Fetch();
676
677 uint32 creatureId = fields[0].GetUInt32();
678 if (!GetCreatureTemplate(creatureId))
679 {
680 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {}, skipped", creatureId);
681 continue;
682 }
683
684 CreatureSummonedData& summonedData = _creatureSummonedDataStore[creatureId];
685 summonedData.CreatureIDVisibleToSummoner = fields[1].GetUInt32OrNull();
686 summonedData.GroundMountDisplayID = fields[2].GetUInt32OrNull();
687 summonedData.FlyingMountDisplayID = fields[3].GetUInt32OrNull();
688
690 {
691 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {} in CreatureIDVisibleToSummoner for creature {}, set to 0",
692 *summonedData.CreatureIDVisibleToSummoner, creatureId);
693 summonedData.CreatureIDVisibleToSummoner.reset();
694 }
695
696 if (summonedData.GroundMountDisplayID && !sCreatureDisplayInfoStore.LookupEntry(*summonedData.GroundMountDisplayID))
697 {
698 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in GroundMountDisplayID for creature {}, set to 0",
699 *summonedData.GroundMountDisplayID, creatureId);
700 summonedData.CreatureIDVisibleToSummoner.reset();
701 }
702
703 if (summonedData.FlyingMountDisplayID && !sCreatureDisplayInfoStore.LookupEntry(*summonedData.FlyingMountDisplayID))
704 {
705 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in FlyingMountDisplayID for creature {}, set to 0",
706 *summonedData.FlyingMountDisplayID, creatureId);
707 summonedData.GroundMountDisplayID.reset();
708 }
709
710 if (Optional<std::string_view> despawnOnQuestsRemoved = fields[4].GetStringViewOrNull())
711 {
712 summonedData.DespawnOnQuestsRemoved.emplace();
713 for (std::string_view questStr : Trinity::Tokenize(*despawnOnQuestsRemoved, ',', false))
714 {
715 Optional<uint32> questId = Trinity::StringTo<uint32>(questStr);
716 if (!questId)
717 continue;
718
719 Quest const* quest = GetQuestTemplate(*questId);
720 if (!quest)
721 {
722 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing quest {} in DespawnOnQuestsRemoved for creature {}, skipping",
723 *questId, creatureId);
724 continue;
725 }
726
727 summonedData.DespawnOnQuestsRemoved->push_back(*questId);
728 }
729
730 if (summonedData.DespawnOnQuestsRemoved->empty())
731 summonedData.DespawnOnQuestsRemoved.reset();
732 }
733
734 } while (result->NextRow());
735
736 TC_LOG_INFO("server.loading", ">> Loaded {} creature summoned data definitions in {} ms", _creatureSummonedDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
737}
738
740{
741 uint32 oldMSTime = getMSTime();
742
743 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
744 QueryResult result = WorldDatabase.Query("SELECT entry, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon");
745
746 if (!result)
747 {
748 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
749 return;
750 }
751
752 uint32 count = 0;
753 do
754 {
755 Field* fields = result->Fetch();
756
757 uint32 entry = fields[0].GetUInt32();
758
759 if (!GetCreatureTemplate(entry))
760 {
761 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
762 continue;
763 }
764
765 CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
766
767 creatureAddon.PathId = fields[1].GetUInt32();
768 creatureAddon.mount = fields[2].GetUInt32();
769 creatureAddon.standState = fields[3].GetUInt8();
770 creatureAddon.animTier = fields[4].GetUInt8();
771 creatureAddon.visFlags = fields[5].GetUInt8();
772 creatureAddon.sheathState = fields[6].GetUInt8();
773 creatureAddon.pvpFlags = fields[7].GetUInt8();
774 creatureAddon.emote = fields[8].GetUInt32();
775 creatureAddon.aiAnimKit = fields[9].GetUInt16();
776 creatureAddon.movementAnimKit = fields[10].GetUInt16();
777 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
778 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
779
780 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
781 {
782 SpellInfo const* spellInfo = nullptr;
783 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
784 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
785
786 if (!spellInfo)
787 {
788 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong spell '{}' defined in `auras` field in `creature_template_addon`.", entry, aura);
789 continue;
790 }
791
792 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
793 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
794
795 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
796 {
797 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has duplicate aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
798 continue;
799 }
800
801 if (spellInfo->GetDuration() > 0)
802 {
803 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has temporary aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
804 continue;
805 }
806
807 creatureAddon.auras.push_back(spellInfo->Id);
808 }
809
810 if (creatureAddon.mount)
811 {
812 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
813 {
814 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid displayInfoId ({}) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
815 creatureAddon.mount = 0;
816 }
817 }
818
819 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
820 {
821 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit stand state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState);
822 creatureAddon.standState = 0;
823 }
824
825 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
826 {
827 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid animation tier ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier);
828 creatureAddon.animTier = 0;
829 }
830
831 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
832 {
833 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid sheath state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState);
834 creatureAddon.sheathState = 0;
835 }
836
837 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
838
839 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
840 {
841 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid emote ({}) defined in `creature_template_addon`.", entry, creatureAddon.emote);
842 creatureAddon.emote = 0;
843 }
844
845 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
846 {
847 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid aiAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.aiAnimKit);
848 creatureAddon.aiAnimKit = 0;
849 }
850
851 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
852 {
853 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid movementAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.movementAnimKit);
854 creatureAddon.movementAnimKit = 0;
855 }
856
857 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
858 {
859 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid meleeAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.meleeAnimKit);
860 creatureAddon.meleeAnimKit = 0;
861 }
862
864 {
865 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid visibilityDistanceType ({}) defined in `creature_template_addon`.",
866 entry, AsUnderlyingType(creatureAddon.visibilityDistanceType));
868 }
869
870 ++count;
871 }
872 while (result->NextRow());
873
874 TC_LOG_INFO("server.loading", ">> Loaded {} creature template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
875}
876
878{
879 uint32 oldMSTime = getMSTime();
880
881 // 0 1
882 QueryResult result = WorldDatabase.Query("SELECT Entry, NoNPCDamageBelowHealthPct FROM creature_template_sparring");
883
884 if (!result)
885 {
886 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template sparring definitions. DB table `creature_template_sparring` is empty.");
887 return;
888 }
889
890 uint32 count = 0;
891 do
892 {
893 Field* fields = result->Fetch();
894
895 uint32 entry = fields[0].GetUInt32();
896 float noNPCDamageBelowHealthPct = fields[1].GetFloat();
897
898 if (!sObjectMgr->GetCreatureTemplate(entry))
899 {
900 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_sparring`", entry);
901 continue;
902 }
903
904 if (noNPCDamageBelowHealthPct <= 0 || noNPCDamageBelowHealthPct > 100)
905 {
906 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid NoNPCDamageBelowHealthPct ({}) defined in `creature_template_sparring`. Skipping",
907 entry, noNPCDamageBelowHealthPct);
908 continue;
909 }
910 _creatureTemplateSparringStore[entry].push_back(noNPCDamageBelowHealthPct);
911
912 ++count;
913 } while (result->NextRow());
914
915 TC_LOG_INFO("server.loading", ">> Loaded {} creature template sparring rows in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
916}
917
919{
920 uint32 oldMSTime = getMSTime();
921
922 // 0 1 2 3 4 5
923 QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, "
924 // 6 7 8 9 10 11 12 13
925 "HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, TypeFlags3, "
926 // 14 15 16 17 18
927 "LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax,"
928 // 19 20 21 22 23 24 25 26
929 "StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 "
930 "FROM creature_template_difficulty ORDER BY Entry");
931
932 if (!result)
933 {
934 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
935 return;
936 }
937
938 uint32 count = 0;
939 do
940 {
941 Field* fields = result->Fetch();
942
943 uint32 entry = fields[0].GetUInt32();
944 Difficulty difficulty = Difficulty(fields[1].GetInt32());
945
946 auto itr = _creatureTemplateStore.find(entry);
947 if (itr == _creatureTemplateStore.end())
948 {
949 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_difficulty`", entry);
950 continue;
951 }
952
953 CreatureDifficulty& creatureDifficulty = itr->second.difficultyStore.try_emplace(difficulty).first->second;
954 creatureDifficulty.DeltaLevelMin = fields[2].GetInt16();
955 creatureDifficulty.DeltaLevelMax = fields[3].GetInt16();
956 creatureDifficulty.ContentTuningID = fields[4].GetInt32();
957 creatureDifficulty.HealthScalingExpansion = fields[5].GetInt32();
958 creatureDifficulty.HealthModifier = fields[6].GetFloat();
959 creatureDifficulty.ManaModifier = fields[7].GetFloat();
960 creatureDifficulty.ArmorModifier = fields[8].GetFloat();
961 creatureDifficulty.DamageModifier = fields[9].GetFloat();
962 creatureDifficulty.CreatureDifficultyID = fields[10].GetInt32();
963 creatureDifficulty.TypeFlags = fields[11].GetUInt32();
964 creatureDifficulty.TypeFlags2 = fields[12].GetUInt32();
965 creatureDifficulty.TypeFlags3 = fields[13].GetUInt32();
966 creatureDifficulty.LootID = fields[14].GetUInt32();
967 creatureDifficulty.PickPocketLootID = fields[15].GetUInt32();
968 creatureDifficulty.SkinLootID = fields[16].GetUInt32();
969 creatureDifficulty.GoldMin = fields[17].GetUInt32();
970 creatureDifficulty.GoldMax = fields[18].GetUInt32();
971 creatureDifficulty.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[19].GetUInt32()), CreatureStaticFlags2(fields[20].GetUInt32()),
972 CreatureStaticFlags3(fields[21].GetUInt32()), CreatureStaticFlags4(fields[22].GetUInt32()), CreatureStaticFlags5(fields[23].GetUInt32()),
973 CreatureStaticFlags6(fields[24].GetUInt32()), CreatureStaticFlags7(fields[25].GetUInt32()), CreatureStaticFlags8(fields[26].GetUInt32()));
974
975 // TODO: Check if this still applies
976 creatureDifficulty.DamageModifier *= Creature::GetDamageMod(itr->second.Classification);
977
978 if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS)
979 {
980 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.",
981 entry, creatureDifficulty.HealthScalingExpansion);
982 creatureDifficulty.HealthScalingExpansion = 0;
983 }
984
985 if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
986 {
987 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with `GoldMin` {} greater than `GoldMax` {}, setting `GoldMax` to {}.",
988 entry, creatureDifficulty.GoldMin, creatureDifficulty.GoldMax, creatureDifficulty.GoldMin);
989 creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
990 }
991
992 ++count;
993 } while (result->NextRow());
994
995 TC_LOG_INFO("server.loading", ">> Loaded {} creature template difficulty data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
996}
997
999{
1000 if (!cInfo)
1001 return;
1002
1003 if (!cInfo->AIName.empty())
1004 {
1005 auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
1006 if (!registryItem)
1007 {
1008 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-registered `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1009 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1010 }
1011 else
1012 {
1013 DBPermit const* permit = dynamic_cast<DBPermit const*>(registryItem);
1014 if (!ASSERT_NOTNULL(permit)->IsScriptNameAllowedInDB())
1015 {
1016 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has not-allowed `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1017 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1018 }
1019 }
1020 }
1021
1022 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
1023 if (!factionTemplate)
1024 {
1025 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);
1026 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
1027 }
1028
1029 for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
1030 {
1031 if (cInfo->KillCredit[k])
1032 {
1033 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
1034 {
1035 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing creature entry {} in `KillCredit{}`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
1036 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
1037 }
1038 }
1039 }
1040
1041 if (cInfo->Models.empty())
1042 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in creature_template_model.", cInfo->Entry);
1043
1044 if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
1045 {
1046 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);
1047 const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
1048 }
1049
1050 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
1051 {
1052 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid spell school value ({}) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
1053 const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
1054 }
1055
1056 if (cInfo->BaseAttackTime == 0)
1057 const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime = BASE_ATTACK_TIME;
1058
1059 if (cInfo->RangeAttackTime == 0)
1060 const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
1061
1062 if (cInfo->speed_walk == 0.0f)
1063 {
1064 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
1065 const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
1066 }
1067
1068 if (cInfo->speed_run == 0.0f)
1069 {
1070 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
1071 const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
1072 }
1073
1074 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
1075 {
1076 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature type ({}) in `type`.", cInfo->Entry, cInfo->type);
1077 const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
1078 }
1079
1080 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family))
1081 {
1082 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature family ({}) in `family`.", cInfo->Entry, cInfo->family);
1083 const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
1084 }
1085
1086 CheckCreatureMovement("creature_template_movement", cInfo->Entry, const_cast<CreatureTemplate*>(cInfo)->Movement);
1087
1088 if (cInfo->VehicleId)
1089 {
1090 VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
1091 if (!vehId)
1092 {
1093 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has a non-existing VehicleId ({}). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
1094 const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
1095 }
1096 }
1097
1098 for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
1099 {
1100 if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j], DIFFICULTY_NONE))
1101 {
1102 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing Spell{} ({}), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
1103 const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
1104 }
1105 }
1106
1107 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
1108 {
1109 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong movement generator type ({}), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
1110 const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
1111 }
1112
1113 if (cInfo->RequiredExpansion >= MAX_EXPANSIONS)
1114 {
1115 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `RequiredExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->RequiredExpansion);
1116 const_cast<CreatureTemplate*>(cInfo)->RequiredExpansion = 0;
1117 }
1118
1119 if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
1120 {
1121 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `flags_extra` {}, removing incorrect flag.", cInfo->Entry, badFlags);
1122 const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
1123 }
1124
1125 if (uint32 disallowedUnitFlags = (cInfo->unit_flags & ~UNIT_FLAG_ALLOWED))
1126 {
1127 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags);
1128 const_cast<CreatureTemplate*>(cInfo)->unit_flags &= UNIT_FLAG_ALLOWED;
1129 }
1130
1131 if (uint32 disallowedUnitFlags2 = (cInfo->unit_flags2 & ~UNIT_FLAG2_ALLOWED))
1132 {
1133 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags2);
1134 const_cast<CreatureTemplate*>(cInfo)->unit_flags2 &= UNIT_FLAG2_ALLOWED;
1135 }
1136
1137 if (uint32 disallowedUnitFlags3 = (cInfo->unit_flags3 & ~UNIT_FLAG3_ALLOWED))
1138 {
1139 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags3);
1140 const_cast<CreatureTemplate*>(cInfo)->unit_flags3 &= UNIT_FLAG3_ALLOWED;
1141 }
1142
1143 if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP))
1144 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry);
1145 else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)
1146 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry);
1147
1148 if (cInfo->VignetteID && !sVignetteStore.HasRecord(cInfo->VignetteID))
1149 {
1150 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has non-existing Vignette {}, set to 0.", cInfo->Entry, cInfo->VignetteID);
1151 const_cast<CreatureTemplate*>(cInfo)->VignetteID = 0;
1152 }
1153}
1154
1155void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement)
1156{
1157 if (creatureMovement.Chase >= CreatureChaseMovementType::Max)
1158 {
1159 TC_LOG_ERROR("sql.sql", "`{}`.`Chase` wrong value ({}) for Id {}, setting to Run.",
1160 table, uint32(creatureMovement.Chase), id);
1161 creatureMovement.Chase = CreatureChaseMovementType::Run;
1162 }
1163
1164 if (creatureMovement.Random >= CreatureRandomMovementType::Max)
1165 {
1166 TC_LOG_ERROR("sql.sql", "`{}`.`Random` wrong value ({}) for Id {}, setting to Walk.",
1167 table, uint32(creatureMovement.Random), id);
1168 creatureMovement.Random = CreatureRandomMovementType::Walk;
1169 }
1170}
1171
1173{
1174 uint32 oldMSTime = getMSTime();
1175
1176 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1177 QueryResult result = WorldDatabase.Query("SELECT guid, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon");
1178
1179 if (!result)
1180 {
1181 TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
1182 return;
1183 }
1184
1185 uint32 count = 0;
1186 do
1187 {
1188 Field* fields = result->Fetch();
1189
1190 ObjectGuid::LowType guid = fields[0].GetUInt64();
1191
1192 CreatureData const* creData = GetCreatureData(guid);
1193 if (!creData)
1194 {
1195 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_addon`", guid);
1196 continue;
1197 }
1198
1199 CreatureAddon& creatureAddon = _creatureAddonStore[guid];
1200
1201 creatureAddon.PathId = fields[1].GetUInt32();
1202 if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.PathId)
1203 {
1204 const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
1205 TC_LOG_ERROR("sql.sql", "Creature (GUID {}) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
1206 }
1207
1208 creatureAddon.mount = fields[2].GetUInt32();
1209 creatureAddon.standState = fields[3].GetUInt8();
1210 creatureAddon.animTier = fields[4].GetUInt8();
1211 creatureAddon.visFlags = fields[5].GetUInt8();
1212 creatureAddon.sheathState = fields[6].GetUInt8();
1213 creatureAddon.pvpFlags = fields[7].GetUInt8();
1214 creatureAddon.emote = fields[8].GetUInt32();
1215 creatureAddon.aiAnimKit = fields[9].GetUInt16();
1216 creatureAddon.movementAnimKit = fields[10].GetUInt16();
1217 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
1218 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
1219
1220 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
1221 {
1222 SpellInfo const* spellInfo = nullptr;
1223 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
1224 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
1225
1226 if (!spellInfo)
1227 {
1228 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has wrong spell '{}' defined in `auras` field in `creature_addon`.", guid, aura);
1229 continue;
1230 }
1231
1232 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
1233 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1234
1235 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
1236 {
1237 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has duplicate aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1238 continue;
1239 }
1240
1241 if (spellInfo->GetDuration() > 0)
1242 {
1243 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has temporary aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1244 continue;
1245 }
1246
1247 creatureAddon.auras.push_back(spellInfo->Id);
1248 }
1249
1250 if (creatureAddon.mount)
1251 {
1252 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
1253 {
1254 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid displayInfoId ({}) for mount defined in `creature_addon`", guid, creatureAddon.mount);
1255 creatureAddon.mount = 0;
1256 }
1257 }
1258
1259 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
1260 {
1261 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid unit stand state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState);
1262 creatureAddon.standState = 0;
1263 }
1264
1265 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
1266 {
1267 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid animation tier ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier);
1268 creatureAddon.animTier = 0;
1269 }
1270
1271 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
1272 {
1273 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid sheath state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState);
1274 creatureAddon.sheathState = 0;
1275 }
1276
1277 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
1278
1279 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
1280 {
1281 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid emote ({}) defined in `creature_addon`.", guid, creatureAddon.emote);
1282 creatureAddon.emote = 0;
1283 }
1284
1285 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
1286 {
1287 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid aiAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.aiAnimKit);
1288 creatureAddon.aiAnimKit = 0;
1289 }
1290
1291 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
1292 {
1293 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid movementAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.movementAnimKit);
1294 creatureAddon.movementAnimKit = 0;
1295 }
1296
1297 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
1298 {
1299 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid meleeAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.meleeAnimKit);
1300 creatureAddon.meleeAnimKit = 0;
1301 }
1302
1304 {
1305 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid visibilityDistanceType ({}) defined in `creature_addon`.",
1306 guid, AsUnderlyingType(creatureAddon.visibilityDistanceType));
1308 }
1309
1310 ++count;
1311 }
1312 while (result->NextRow());
1313
1314 TC_LOG_INFO("server.loading", ">> Loaded {} creature addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1315}
1316
1318{
1319 uint32 oldMSTime = getMSTime();
1320
1321 // 0 1 2 3 4 5 6 7 8
1322 QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue, WorldEffectID, AIAnimKitID FROM gameobject_addon");
1323
1324 if (!result)
1325 {
1326 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
1327 return;
1328 }
1329
1330 uint32 count = 0;
1331 do
1332 {
1333 Field* fields = result->Fetch();
1334
1335 ObjectGuid::LowType guid = fields[0].GetUInt64();
1336
1337 GameObjectData const* goData = GetGameObjectData(guid);
1338 if (!goData)
1339 {
1340 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) does not exist but has a record in `gameobject_addon`", guid);
1341 continue;
1342 }
1343
1344 GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
1345 gameObjectAddon.ParentRotation = QuaternionData(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
1346 gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
1347 gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
1348 gameObjectAddon.WorldEffectID = fields[7].GetUInt32();
1349 gameObjectAddon.AIAnimKitID = fields[8].GetUInt32();
1350
1351 if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
1352 {
1353 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
1354 gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
1355 gameObjectAddon.InvisibilityValue = 0;
1356 }
1357
1358 if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue)
1359 {
1360 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid);
1361 gameObjectAddon.InvisibilityValue = 1;
1362 }
1363
1364 if (!gameObjectAddon.ParentRotation.isUnit())
1365 {
1366 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid parent rotation in `gameobject_addon`, set to default", guid);
1367 gameObjectAddon.ParentRotation = QuaternionData();
1368 }
1369
1370 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
1371 {
1372 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid WorldEffectID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.WorldEffectID);
1373 gameObjectAddon.WorldEffectID = 0;
1374 }
1375
1376 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
1377 {
1378 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid AIAnimKitID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.AIAnimKitID);
1379 gameObjectAddon.AIAnimKitID = 0;
1380 }
1381
1382 ++count;
1383 }
1384 while (result->NextRow());
1385
1386 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1387}
1388
1390{
1391 GameObjectAddonContainer::const_iterator itr = _gameObjectAddonStore.find(lowguid);
1392 if (itr != _gameObjectAddonStore.end())
1393 return &(itr->second);
1394
1395 return nullptr;
1396}
1397
1399{
1400 CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
1401 if (itr != _creatureAddonStore.end())
1402 return &(itr->second);
1403
1404 return nullptr;
1405}
1406
1408{
1409 CreatureTemplateAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
1410 if (itr != _creatureTemplateAddonStore.end())
1411 return &(itr->second);
1412
1413 return nullptr;
1414}
1415
1420
1425
1427{
1428 EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
1429 if (itr == _equipmentInfoStore.end())
1430 return nullptr;
1431
1432 if (itr->second.empty())
1433 return nullptr;
1434
1435 if (id == -1) // select a random element
1436 {
1437 EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
1438 std::advance(ritr, urand(0u, itr->second.size() - 1));
1439 id = std::distance(itr->second.begin(), ritr) + 1;
1440 return &ritr->second;
1441 }
1442 else
1443 {
1444 EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
1445 if (itr2 != itr->second.end())
1446 return &itr2->second;
1447 }
1448
1449 return nullptr;
1450}
1451
1453{
1454 uint32 oldMSTime = getMSTime();
1455
1456 // 0 1 2 3 4
1457 QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, AppearanceModID1, ItemVisual1, "
1458 // 5 6 7
1459 "ItemID2, AppearanceModID2, ItemVisual2, "
1460 // 8 9 10
1461 "ItemID3, AppearanceModID3, ItemVisual3 "
1462 "FROM creature_equip_template");
1463
1464 if (!result)
1465 {
1466 TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
1467 return;
1468 }
1469
1470 uint32 count = 0;
1471 do
1472 {
1473 Field* fields = result->Fetch();
1474
1475 uint32 entry = fields[0].GetUInt32();
1476
1477 if (!GetCreatureTemplate(entry))
1478 {
1479 TC_LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry);
1480 continue;
1481 }
1482
1483 uint8 id = fields[1].GetUInt8();
1484 if (!id)
1485 {
1486 TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature {}, skipped.", entry);
1487 continue;
1488 }
1489
1490 EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
1491 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1492 {
1493 equipmentInfo.Items[i].ItemId = fields[2 + i * 3].GetUInt32();
1494 equipmentInfo.Items[i].AppearanceModId = fields[3 + i * 3].GetUInt16();
1495 equipmentInfo.Items[i].ItemVisual = fields[4 + i * 3].GetUInt16();
1496
1497 if (!equipmentInfo.Items[i].ItemId)
1498 continue;
1499
1500 ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.Items[i].ItemId);
1501 if (!dbcItem)
1502 {
1503 TC_LOG_ERROR("sql.sql", "Unknown item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID={}, forced to 0.",
1504 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1505 equipmentInfo.Items[i].ItemId = 0;
1506 continue;
1507 }
1508
1509 // AppearanceModId 0 is always valid
1510 if (equipmentInfo.Items[i].AppearanceModId && !TransmogMgr::GetItemModifiedAppearance(equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId))
1511 {
1512 TC_LOG_ERROR("sql.sql", "Unknown item appearance for (ID={}, AppearanceModID={}) pair in creature_equip_template.ItemID{} creature_equip_template.AppearanceModID{} "
1513 "for CreatureID = {} and ID={}, forced to default.",
1514 equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId, i + 1, i + 1, entry, id);
1515 if (ItemModifiedAppearanceEntry const* defaultAppearance = TransmogMgr::GetDefaultItemModifiedAppearance(equipmentInfo.Items[i].ItemId))
1516 equipmentInfo.Items[i].AppearanceModId = defaultAppearance->ItemAppearanceModifierID;
1517 else
1518 equipmentInfo.Items[i].AppearanceModId = 0;
1519 continue;
1520 }
1521
1523 {
1524 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.",
1525 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1526 equipmentInfo.Items[i].ItemId = 0;
1527 }
1528 }
1529
1530 ++count;
1531 }
1532 while (result->NextRow());
1533
1534 TC_LOG_INFO("server.loading", ">> Loaded {} equipment templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1535}
1536
1538{
1539 uint32 oldMSTime = getMSTime();
1540
1542
1543 // Load the data from creature_movement_override and if NULL fallback to creature_template_movement
1544 QueryResult result = WorldDatabase.Query(
1545 "SELECT cmo.SpawnId,"
1546 "COALESCE(cmo.HoverInitiallyEnabled, ctm.HoverInitiallyEnabled),"
1547 "COALESCE(cmo.Chase, ctm.Chase),"
1548 "COALESCE(cmo.Random, ctm.Random),"
1549 "COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
1550 "FROM creature_movement_override AS cmo "
1551 "LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
1552 "LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
1553
1554 if (!result)
1555 {
1556 TC_LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
1557 return;
1558 }
1559
1560 do
1561 {
1562 Field* fields = result->Fetch();
1563 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
1564 if (!GetCreatureData(spawnId))
1565 {
1566 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_movement_override`", spawnId);
1567 continue;
1568 }
1569
1571 if (!fields[1].IsNull())
1572 movement.HoverInitiallyEnabled = fields[1].GetBool();
1573 if (Optional<uint8> chaseMovementType = fields[2].GetUInt8OrNull())
1574 movement.Chase = static_cast<CreatureChaseMovementType>(*chaseMovementType);
1575 if (Optional<uint8> randomMovementType = fields[3].GetUInt8OrNull())
1576 movement.Random = static_cast<CreatureRandomMovementType>(*randomMovementType);
1577 if (Optional<uint32> interactionPauseTimer = fields[4].GetUInt32OrNull())
1578 movement.InteractionPauseTimer = *interactionPauseTimer;
1579
1580 CheckCreatureMovement("creature_movement_override", spawnId, movement);
1581 }
1582 while (result->NextRow());
1583
1584 TC_LOG_INFO("server.loading", ">> Loaded {} movement overrides in {} ms", _creatureMovementOverrides.size(), GetMSTimeDiffToNow(oldMSTime));
1585}
1586
1588{
1589 CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
1590 if (itr != _creatureModelStore.end())
1591 return &(itr->second);
1592
1593 return nullptr;
1594}
1595
1600
1601CreatureModel const* ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
1602{
1603 // Load creature model (display id)
1604 if (data && data->display)
1605 return &*data->display;
1606
1608 if (CreatureModel const* model = cinfo->GetRandomValidModel())
1609 return model;
1610
1611 // Triggers by default receive the invisible model
1612 return cinfo->GetFirstInvisibleModel();
1613}
1614
1615void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cInfo, uint64* npcFlags, uint32* unitFlags, uint32* unitFlags2, uint32* unitFlags3, CreatureStaticFlagsHolder const& staticFlags, CreatureData const* data /*= nullptr*/)
1616{
1617#define ChooseCreatureFlagSource(field) ((data && data->field.has_value()) ? *data->field : cInfo->field)
1618
1619 if (npcFlags)
1620 *npcFlags = ChooseCreatureFlagSource(npcflag);
1621
1622 if (unitFlags)
1623 {
1624 *unitFlags = ChooseCreatureFlagSource(unit_flags);
1625 if (staticFlags.HasFlag(CREATURE_STATIC_FLAG_CAN_SWIM))
1626 *unitFlags |= UNIT_FLAG_CAN_SWIM;
1627
1629 *unitFlags |= UNIT_FLAG_CANT_SWIM;
1630 }
1631
1632 if (unitFlags2)
1633 {
1634 *unitFlags2 = ChooseCreatureFlagSource(unit_flags2);
1636 *unitFlags2 |= UNIT_FLAG2_CANNOT_TURN;
1637
1640
1642 *unitFlags2 |= UNIT_FLAG2_INTERACT_WHILE_HOSTILE;
1643 }
1644
1645 if (unitFlags3)
1646 {
1647 *unitFlags3 = ChooseCreatureFlagSource(unit_flags3);
1650 }
1651
1652#undef ChooseCreatureFlagSource
1653}
1654
1656{
1657 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(model->CreatureDisplayID);
1658 if (!modelInfo)
1659 return nullptr;
1660
1661 // If a model for another gender exists, 50% chance to use it
1662 if (modelInfo->displayId_other_gender != 0 && urand(0, 1) == 0)
1663 {
1664 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->displayId_other_gender);
1665 if (!minfo_tmp)
1666 TC_LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", model->CreatureDisplayID, modelInfo->displayId_other_gender);
1667 else
1668 {
1669 // DisplayID changed
1670 model->CreatureDisplayID = modelInfo->displayId_other_gender;
1671 if (creatureTemplate)
1672 {
1673 auto itr = std::find_if(creatureTemplate->Models.begin(), creatureTemplate->Models.end(), [&](CreatureModel const& templateModel)
1674 {
1675 return templateModel.CreatureDisplayID == modelInfo->displayId_other_gender;
1676 });
1677 if (itr != creatureTemplate->Models.end())
1678 *model = *itr;
1679 }
1680 return minfo_tmp;
1681 }
1682 }
1683
1684 return modelInfo;
1685}
1686
1688{
1689 uint32 oldMSTime = getMSTime();
1690
1691 QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, DisplayID_Other_Gender FROM creature_model_info");
1692
1693 if (!result)
1694 {
1695 TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
1696 return;
1697 }
1698
1699 _creatureModelStore.reserve(result->GetRowCount());
1700 uint32 count = 0;
1701
1702 // List of model FileDataIDs that the client treats as invisible stalker
1703 uint32 trigggerCreatureModelFileID[5] = { 124640, 124641, 124642, 343863, 439302 };
1704
1705 do
1706 {
1707 Field* fields = result->Fetch();
1708
1709 uint32 displayId = fields[0].GetUInt32();
1710
1711 CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(displayId);
1712 if (!creatureDisplay)
1713 {
1714 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has a non-existent DisplayID (ID: {}). Skipped.", displayId);
1715 continue;
1716 }
1717
1718 CreatureModelInfo& modelInfo = _creatureModelStore[displayId];
1719
1720 modelInfo.bounding_radius = fields[1].GetFloat();
1721 modelInfo.combat_reach = fields[2].GetFloat();
1722 modelInfo.displayId_other_gender = fields[3].GetUInt32();
1723 modelInfo.gender = creatureDisplay->Gender;
1724 modelInfo.is_trigger = false;
1725
1726 // Checks
1727
1728 // to remove when the purpose of GENDER_UNKNOWN is known
1729 if (modelInfo.gender == GENDER_UNKNOWN)
1730 {
1731 // We don't need more errors
1732 //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);
1733 modelInfo.gender = GENDER_MALE;
1734 }
1735
1736 if (modelInfo.displayId_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.displayId_other_gender))
1737 {
1738 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);
1739 modelInfo.displayId_other_gender = 0;
1740 }
1741
1742 if (modelInfo.combat_reach < 0.1f)
1744
1745 if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelID))
1746 {
1747 for (uint32 i = 0; i < 5; ++i)
1748 {
1749 if (modelData->FileDataID == trigggerCreatureModelFileID[i])
1750 {
1751 modelInfo.is_trigger = true;
1752 break;
1753 }
1754 }
1755 }
1756
1757 ++count;
1758 }
1759 while (result->NextRow());
1760
1761 TC_LOG_INFO("server.loading", ">> Loaded {} creature model based info in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1762}
1763
1765{
1766 uint32 oldMSTime = getMSTime();
1767
1768 _linkedRespawnStore.clear();
1769 // 0 1 2
1770 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1771
1772 if (!result)
1773 {
1774 TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1775 return;
1776 }
1777
1778 do
1779 {
1780 Field* fields = result->Fetch();
1781
1782 ObjectGuid::LowType guidLow = fields[0].GetUInt64();
1783 ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt64();
1784 uint8 linkType = fields[2].GetUInt8();
1785
1786 ObjectGuid guid, linkedGuid;
1787 bool error = false;
1788 switch (linkType)
1789 {
1791 {
1792 CreatureData const* slave = GetCreatureData(guidLow);
1793 if (!slave)
1794 {
1795 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1796 error = true;
1797 break;
1798 }
1799
1800 CreatureData const* master = GetCreatureData(linkedGuidLow);
1801 if (!master)
1802 {
1803 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1804 error = true;
1805 break;
1806 }
1807
1808 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1809 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1810 {
1811 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1812 error = true;
1813 break;
1814 }
1815
1816 // they must have a possibility to meet (normal/heroic difficulty)
1817 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1818 {
1819 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1820 error = true;
1821 break;
1822 }
1823
1824 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1825 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1826 break;
1827 }
1829 {
1830 CreatureData const* slave = GetCreatureData(guidLow);
1831 if (!slave)
1832 {
1833 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1834 error = true;
1835 break;
1836 }
1837
1838 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1839 if (!master)
1840 {
1841 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1842 error = true;
1843 break;
1844 }
1845
1846 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1847 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1848 {
1849 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1850 error = true;
1851 break;
1852 }
1853
1854 // they must have a possibility to meet (normal/heroic difficulty)
1855 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1856 {
1857 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1858 error = true;
1859 break;
1860 }
1861
1862 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1863 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1864 break;
1865 }
1867 {
1868 GameObjectData const* slave = GetGameObjectData(guidLow);
1869 if (!slave)
1870 {
1871 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1872 error = true;
1873 break;
1874 }
1875
1876 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1877 if (!master)
1878 {
1879 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1880 error = true;
1881 break;
1882 }
1883
1884 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1885 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1886 {
1887 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1888 error = true;
1889 break;
1890 }
1891
1892 // they must have a possibility to meet (normal/heroic difficulty)
1893 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1894 {
1895 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1896 error = true;
1897 break;
1898 }
1899
1900 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1901 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1902 break;
1903 }
1905 {
1906 GameObjectData const* slave = GetGameObjectData(guidLow);
1907 if (!slave)
1908 {
1909 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1910 error = true;
1911 break;
1912 }
1913
1914 CreatureData const* master = GetCreatureData(linkedGuidLow);
1915 if (!master)
1916 {
1917 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1918 error = true;
1919 break;
1920 }
1921
1922 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1923 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1924 {
1925 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1926 error = true;
1927 break;
1928 }
1929
1930 // they must have a possibility to meet (normal/heroic difficulty)
1931 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1932 {
1933 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1934 error = true;
1935 break;
1936 }
1937
1938 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1939 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1940 break;
1941 }
1942 }
1943
1944 if (!error)
1945 _linkedRespawnStore[guid] = linkedGuid;
1946 }
1947 while (result->NextRow());
1948
1949 TC_LOG_INFO("server.loading", ">> Loaded {} linked respawns in {} ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
1950}
1951
1953{
1954 if (!guidLow)
1955 return false;
1956
1957 CreatureData const* master = GetCreatureData(guidLow);
1958 ASSERT(master);
1959 ObjectGuid guid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, guidLow);
1960
1961 if (!linkedGuidLow) // we're removing the linking
1962 {
1963 _linkedRespawnStore.erase(guid);
1965 stmt->setUInt64(0, guidLow);
1967 WorldDatabase.Execute(stmt);
1968 return true;
1969 }
1970
1971 CreatureData const* slave = GetCreatureData(linkedGuidLow);
1972 if (!slave)
1973 {
1974 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to non-existent creature '{}'.", guidLow, linkedGuidLow);
1975 return false;
1976 }
1977
1978 MapEntry const* map = sMapStore.LookupEntry(master->mapId);
1979 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1980 {
1981 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1982 return false;
1983 }
1984
1985 // they must have a possibility to meet (normal/heroic difficulty)
1986 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1987 {
1988 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1989 return false;
1990 }
1991
1992 ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, linkedGuidLow);
1993
1994 _linkedRespawnStore[guid] = linkedGuid;
1996 stmt->setUInt64(0, guidLow);
1997 stmt->setUInt64(1, linkedGuidLow);
1999 WorldDatabase.Execute(stmt);
2000 return true;
2001}
2002
2004{
2005 uint32 oldMSTime = getMSTime();
2006
2007 _tempSummonDataStore.clear(); // needed for reload case
2008
2009 // 0 1 2 3 4 5 6 7 8 9
2010 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
2011
2012 if (!result)
2013 {
2014 TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
2015 return;
2016 }
2017
2018 uint32 count = 0;
2019 do
2020 {
2021 Field* fields = result->Fetch();
2022
2023 uint32 summonerId = fields[0].GetUInt32();
2024 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
2025 uint8 group = fields[2].GetUInt8();
2026
2027 switch (summonerType)
2028 {
2030 if (!GetCreatureTemplate(summonerId))
2031 {
2032 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for creature summoner type, skipped.", summonerId);
2033 continue;
2034 }
2035 break;
2037 if (!GetGameObjectTemplate(summonerId))
2038 {
2039 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for gameobject summoner type, skipped.", summonerId);
2040 continue;
2041 }
2042 break;
2043 case SUMMONER_TYPE_MAP:
2044 if (!sMapStore.LookupEntry(summonerId))
2045 {
2046 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for map summoner type, skipped.", summonerId);
2047 continue;
2048 }
2049 break;
2050 default:
2051 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type {} for summoner {}, skipped.", summonerType, summonerId);
2052 continue;
2053 }
2054
2055 TempSummonData data;
2056 data.entry = fields[3].GetUInt32();
2057
2058 if (!GetCreatureTemplate(data.entry))
2059 {
2060 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);
2061 continue;
2062 }
2063
2064 float posX = fields[4].GetFloat();
2065 float posY = fields[5].GetFloat();
2066 float posZ = fields[6].GetFloat();
2067 float orientation = fields[7].GetFloat();
2068
2069 data.pos.Relocate(posX, posY, posZ, orientation);
2070
2071 data.type = TempSummonType(fields[8].GetUInt8());
2072
2074 {
2075 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);
2076 continue;
2077 }
2078
2079 data.time = Milliseconds(fields[9].GetUInt32());
2080
2081 TempSummonGroupKey key{ .SummonerEntry = summonerId, .Type = summonerType, .SummonGroup = group };
2082 _tempSummonDataStore[key].push_back(data);
2083
2084 ++count;
2085
2086 } while (result->NextRow());
2087
2088 TC_LOG_INFO("server.loading", ">> Loaded {} temp summons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
2089}
2090
2091std::vector<Difficulty> ObjectMgr::ParseSpawnDifficulties(std::string_view difficultyString, std::string_view table, ObjectGuid::LowType spawnId, uint32 mapId,
2092 std::set<Difficulty> const& mapDifficulties)
2093{
2094 std::vector<Difficulty> difficulties;
2095 bool isTransportMap = sObjectMgr->IsTransportMap(mapId);
2096 for (std::string_view token : Trinity::Tokenize(difficultyString, ',', false))
2097 {
2098 Difficulty difficultyId = Difficulty(Trinity::StringTo<std::underlying_type_t<Difficulty>>(token).value_or(DIFFICULTY_NONE));
2099 if (difficultyId && !sDifficultyStore.LookupEntry(difficultyId))
2100 {
2101 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) with non invalid difficulty id {}, skipped.",
2102 table, table, spawnId, uint32(difficultyId));
2103 continue;
2104 }
2105
2106 if (!isTransportMap && mapDifficulties.find(difficultyId) == mapDifficulties.end())
2107 {
2108 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) has unsupported difficulty {} for map (Id: {}).",
2109 table, table, spawnId, uint32(difficultyId), mapId);
2110 continue;
2111 }
2112
2113 difficulties.push_back(difficultyId);
2114 }
2115
2116 std::sort(difficulties.begin(), difficulties.end());
2117 return difficulties;
2118}
2119
2121{
2122 uint32 oldMSTime = getMSTime();
2123
2124 // 0 1 2 3 4 5 6 7 8 9 10
2125 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
2126 // 11 12 13 14 15 16 17 18 19 20
2127 "currentwaypoint, curHealthPct, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, "
2128 // 21 22 23 24 25 26
2129 "creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId "
2130 "FROM creature "
2131 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
2132 "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
2133
2134 if (!result)
2135 {
2136 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
2137 return;
2138 }
2139
2140 // Build single time for check spawnmask
2141 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2142 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2143 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2144
2145 PhaseShift phaseShift;
2146
2147 _creatureDataStore.reserve(result->GetRowCount());
2148
2149 do
2150 {
2151 Field* fields = result->Fetch();
2152
2153 ObjectGuid::LowType guid = fields[0].GetUInt64();
2154 uint32 entry = fields[1].GetUInt32();
2155
2156 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2157 if (!cInfo)
2158 {
2159 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, entry);
2160 continue;
2161 }
2162
2163 CreatureData& data = _creatureDataStore[guid];
2164 data.spawnId = guid;
2165 data.id = entry;
2166 data.mapId = fields[2].GetUInt16();
2167 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2168 if (uint32 displayId = fields[7].GetUInt32())
2169 data.display.emplace(displayId, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
2170 data.equipmentId = fields[8].GetInt8();
2171 data.spawntimesecs = fields[9].GetUInt32();
2172 data.wander_distance = fields[10].GetFloat();
2173 data.currentwaypoint = fields[11].GetUInt32();
2174 data.curHealthPct = fields[12].GetUInt32OrNull();
2175 data.movementType = fields[13].GetUInt8();
2176 data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "creature", guid, data.mapId, spawnMasks[data.mapId]);
2177 int16 gameEvent = fields[15].GetInt8();
2178 data.poolId = fields[16].GetUInt32();
2179 data.npcflag = fields[17].GetUInt64OrNull();
2180 data.unit_flags = fields[18].GetUInt32OrNull();
2181 data.unit_flags2 = fields[19].GetUInt32OrNull();
2182 data.unit_flags3 = fields[20].GetUInt32OrNull();
2183 data.phaseUseFlags = fields[21].GetUInt8();
2184 data.phaseId = fields[22].GetUInt32();
2185 data.phaseGroup = fields[23].GetUInt32();
2186 data.terrainSwapMap = fields[24].GetInt32();
2187 data.scriptId = GetScriptId(fields[25].GetStringView());
2188 data.StringId = fields[26].GetString();
2189 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2190
2191 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2192 if (!mapEntry)
2193 {
2194 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, data.mapId);
2195 continue;
2196 }
2197
2199 {
2201 {
2202 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2203 {
2205 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2206 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2207
2208 VMAP::LoadResult result = vmgr->existsMap(sWorld->GetDataPath() + "vmaps", data.mapId, gx, gy);
2209 if (result != VMAP::LoadResult::Success)
2210 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2211 guid, data.id, data.mapId, data.spawnPoint);
2212 }
2213 }
2214 }
2215
2216 if (data.spawnDifficulties.empty())
2217 {
2218 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2219 continue;
2220 }
2221
2222 if (data.display.has_value())
2223 {
2224 if (!GetCreatureModelInfo(data.display->CreatureDisplayID))
2225 {
2226 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with invalid `modelid` {}, ignoring.", guid, data.id, data.display->CreatureDisplayID);
2227 data.display.reset();
2228 }
2229 }
2230
2231 // -1 random, 0 no equipment
2232 if (data.equipmentId != 0)
2233 {
2234 if (!GetEquipmentInfo(data.id, data.equipmentId))
2235 {
2236 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);
2237 data.equipmentId = 0;
2238 }
2239 }
2240
2242 {
2243 if (!mapEntry->IsDungeon())
2244 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);
2245 }
2246
2247 if (data.movementType >= MAX_DB_MOTION_TYPE)
2248 {
2249 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);
2251 }
2252
2253 if (data.wander_distance < 0.0f)
2254 {
2255 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance`< 0, set to 0.", guid, data.id);
2256 data.wander_distance = 0.0f;
2257 }
2258 else if (data.wander_distance > 0.0f && data.wander_distance < 0.1f)
2259 {
2260 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);
2261 data.wander_distance = 0.0f;
2262 }
2263 else if (data.movementType == RANDOM_MOTION_TYPE)
2264 {
2265 if (G3D::fuzzyEq(data.wander_distance, 0.0f))
2266 {
2267 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);
2269 }
2270 }
2271 else if (data.movementType == IDLE_MOTION_TYPE)
2272 {
2273 if (data.wander_distance != 0.0f)
2274 {
2275 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);
2276 data.wander_distance = 0.0f;
2277 }
2278 }
2279
2281 {
2282 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2284 }
2285
2287 {
2288 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2289 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2290 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2291 }
2292
2293 if (data.phaseGroup && data.phaseId)
2294 {
2295 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2296 data.phaseGroup = 0;
2297 }
2298
2299 if (data.phaseId)
2300 {
2301 if (!sPhaseStore.LookupEntry(data.phaseId))
2302 {
2303 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2304 data.phaseId = 0;
2305 }
2306 }
2307
2308 if (data.phaseGroup)
2309 {
2310 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2311 {
2312 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phasegroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2313 data.phaseGroup = 0;
2314 }
2315 }
2316
2317 if (data.terrainSwapMap != -1)
2318 {
2319 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2320 if (!terrainSwapEntry)
2321 {
2322 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2323 data.terrainSwapMap = -1;
2324 }
2325 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2326 {
2327 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);
2328 data.terrainSwapMap = -1;
2329 }
2330 }
2331
2332 if (data.unit_flags.has_value())
2333 {
2334 if (uint32 disallowedUnitFlags = (*data.unit_flags & ~UNIT_FLAG_ALLOWED))
2335 {
2336 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags);
2338 }
2339 }
2340
2341 if (data.unit_flags2.has_value())
2342 {
2343 if (uint32 disallowedUnitFlags2 = (*data.unit_flags2 & ~UNIT_FLAG2_ALLOWED))
2344 {
2345 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags2);
2347 }
2348
2350 {
2351 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) has UNIT_FLAG2_FEIGN_DEATH set without IMMUNE_TO_PC | IMMUNE_TO_NPC, removing incorrect flag.", guid, data.id);
2352 *data.unit_flags2 &= ~UNIT_FLAG2_FEIGN_DEATH;
2353 }
2354 }
2355
2356 if (data.unit_flags3.has_value())
2357 {
2358 if (uint32 disallowedUnitFlags3 = (*data.unit_flags3 & ~UNIT_FLAG3_ALLOWED))
2359 {
2360 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags3);
2362 }
2363
2365 {
2366 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) has UNIT_FLAG3_FAKE_DEAD set without IMMUNE_TO_PC | IMMUNE_TO_NPC, removing incorrect flag.", guid, data.id);
2367 *data.unit_flags3 &= ~UNIT_FLAG3_FAKE_DEAD;
2368 }
2369 }
2370
2371 if (data.curHealthPct)
2372 {
2373 uint32 healthPct = std::clamp<uint32>(*data.curHealthPct, 1, 100);
2374 if (*data.curHealthPct != healthPct)
2375 {
2376 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with invalid `curHealthPct` {}, set to {}.", guid, data.id, *data.curHealthPct, healthPct);
2377 data.curHealthPct = healthPct;
2378 }
2379
2380 if (cInfo->RegenHealth)
2381 {
2382 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `curHealthPct` {}, but health regeneration is not disabled in `creature_template`, set to 100.", guid, data.id, *data.curHealthPct);
2383 data.curHealthPct.reset();
2384 }
2385 }
2386
2388 {
2389 uint32 zoneId = 0;
2390 uint32 areaId = 0;
2392 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2393
2395
2396 stmt->setUInt32(0, zoneId);
2397 stmt->setUInt32(1, areaId);
2398 stmt->setUInt64(2, guid);
2399
2400 WorldDatabase.Execute(stmt);
2401 }
2402
2403 // Add to grid if not managed by the game event
2404 if (gameEvent == 0)
2405 AddCreatureToGrid(&data);
2406 }
2407 while (result->NextRow());
2408
2409 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in {} ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2410}
2411
2413{
2414 if (GridObjectGuidsMap const* mapGuids = Trinity::Containers::MapGetValuePtr(_mapObjectGuidsStore, { mapid, spawnMode }))
2415 return Trinity::Containers::MapGetValuePtr(*mapGuids, gridId);
2416
2417 return nullptr;
2418}
2419
2424
2425bool ObjectMgr::HasPersonalSpawns(uint32 mapid, Difficulty spawnMode, uint32 phaseId) const
2426{
2427 return Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }) != nullptr;
2428}
2429
2431{
2432 if (GridObjectGuidsMap const* guids = Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }))
2433 return Trinity::Containers::MapGetValuePtr(*guids, gridId);
2434
2435 return nullptr;
2436}
2437
2438template<GridGuidSet GridObjectGuids::*guids>
2440{
2443 {
2444 for (Difficulty difficulty : data->spawnDifficulties)
2445 (_mapObjectGuidsStore[{ data->mapId, difficulty }][gridId].*guids).insert(data->spawnId);
2446 }
2447 else
2448 {
2449 for (Difficulty difficulty : data->spawnDifficulties)
2450 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][gridId].*guids).insert(data->spawnId);
2451 }
2452}
2453
2454template<GridGuidSet GridObjectGuids::*guids>
2456{
2459 {
2460 for (Difficulty difficulty : data->spawnDifficulties)
2461 (_mapObjectGuidsStore[{ data->mapId, difficulty }][gridId].*guids).erase(data->spawnId);
2462 }
2463 else
2464 {
2465 for (Difficulty difficulty : data->spawnDifficulties)
2466 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][gridId].*guids).erase(data->spawnId);
2467 }
2468}
2469
2471{
2472 AddSpawnDataToGrid<&GridObjectGuids::creatures>(data);
2473}
2474
2476{
2477 RemoveSpawnDataFromGrid<&GridObjectGuids::creatures>(data);
2478}
2479
2481{
2482 uint32 oldMSTime = getMSTime();
2483
2484 // 0 1 2 3 4 5 6
2485 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
2486 // 7 8 9 10 11 12 13 14 15 16
2487 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnDifficulties, eventEntry, poolSpawnId, "
2488 // 17 18 19 20 21 22
2489 "phaseUseFlags, phaseid, phasegroup, terrainSwapMap, ScriptName, StringId "
2490 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
2491 "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId");
2492
2493 if (!result)
2494 {
2495 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
2496 return;
2497 }
2498
2499 // build single time for check spawnmask
2500 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2501 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2502 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2503
2504 PhaseShift phaseShift;
2505
2506 _gameObjectDataStore.reserve(result->GetRowCount());
2507
2508 do
2509 {
2510 Field* fields = result->Fetch();
2511
2512 ObjectGuid::LowType guid = fields[0].GetUInt64();
2513 uint32 entry = fields[1].GetUInt32();
2514
2515 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
2516 if (!gInfo)
2517 {
2518 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non existing gameobject entry {}, skipped.", guid, entry);
2519 continue;
2520 }
2521
2522 if (!gInfo->displayId)
2523 {
2524 switch (gInfo->type)
2525 {
2528 break;
2529 default:
2530 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) doesn't have a displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2531 break;
2532 }
2533 }
2534
2535 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
2536 {
2537 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) has an invalid displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2538 continue;
2539 }
2540
2542
2543 data.spawnId = guid;
2544 data.id = entry;
2545 data.mapId = fields[2].GetUInt16();
2546 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2547 data.rotation.x = fields[7].GetFloat();
2548 data.rotation.y = fields[8].GetFloat();
2549 data.rotation.z = fields[9].GetFloat();
2550 data.rotation.w = fields[10].GetFloat();
2551 data.spawntimesecs = fields[11].GetInt32();
2552 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2553
2554 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2555 if (!mapEntry)
2556 {
2557 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existed map (Id: {}), skip", guid, data.id, data.mapId);
2558 continue;
2559 }
2560
2562 {
2564 {
2565 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2566 {
2568 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2569 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2570
2571 VMAP::LoadResult result = vmgr->existsMap(sWorld->GetDataPath() + "vmaps", data.mapId, gx, gy);
2572 if (result != VMAP::LoadResult::Success)
2573 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2574 guid, data.id, data.mapId, data.spawnPoint);
2575 }
2576 }
2577 }
2578
2579 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
2580 {
2581 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);
2582 }
2583
2584 data.animprogress = fields[12].GetUInt8();
2585 data.artKit = 0;
2586
2587 uint32 go_state = fields[13].GetUInt8();
2588 if (go_state >= MAX_GO_STATE)
2589 {
2591 {
2592 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skip", guid, data.id, go_state);
2593 continue;
2594 }
2595 }
2596 data.goState = GOState(go_state);
2597
2598 data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "gameobject", guid, data.mapId, spawnMasks[data.mapId]);
2599 if (data.spawnDifficulties.empty())
2600 {
2601 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2602 continue;
2603 }
2604
2605 int16 gameEvent = fields[15].GetInt8();
2606 data.poolId = fields[16].GetUInt32();
2607 data.phaseUseFlags = fields[17].GetUInt8();
2608 data.phaseId = fields[18].GetUInt32();
2609 data.phaseGroup = fields[19].GetUInt32();
2610
2612 {
2613 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2615 }
2616
2618 {
2619 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2620 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2621 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2622 }
2623
2624 if (data.phaseGroup && data.phaseId)
2625 {
2626 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2627 data.phaseGroup = 0;
2628 }
2629
2630 if (data.phaseId)
2631 {
2632 if (!sPhaseStore.LookupEntry(data.phaseId))
2633 {
2634 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2635 data.phaseId = 0;
2636 }
2637 }
2638
2639 if (data.phaseGroup)
2640 {
2641 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2642 {
2643 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseGroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2644 data.phaseGroup = 0;
2645 }
2646 }
2647
2648 data.terrainSwapMap = fields[20].GetInt32();
2649 if (data.terrainSwapMap != -1)
2650 {
2651 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2652 if (!terrainSwapEntry)
2653 {
2654 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2655 data.terrainSwapMap = -1;
2656 }
2657 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2658 {
2659 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);
2660 data.terrainSwapMap = -1;
2661 }
2662 }
2663
2664 data.scriptId = GetScriptId(fields[21].GetStringView());
2665 data.StringId = fields[22].GetString();
2666
2667 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
2668 {
2669 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skip", guid, data.id, data.rotation.x);
2670 continue;
2671 }
2672
2673 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
2674 {
2675 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skip", guid, data.id, data.rotation.y);
2676 continue;
2677 }
2678
2679 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
2680 {
2681 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skip", guid, data.id, data.rotation.z);
2682 continue;
2683 }
2684
2685 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
2686 {
2687 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skip", guid, data.id, data.rotation.w);
2688 continue;
2689 }
2690
2692 {
2693 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id);
2694 continue;
2695 }
2696
2697 if (!data.rotation.isUnit())
2698 {
2699 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);
2701 }
2702
2704 {
2705 uint32 zoneId = 0;
2706 uint32 areaId = 0;
2708 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2709
2711
2712 stmt->setUInt32(0, zoneId);
2713 stmt->setUInt32(1, areaId);
2714 stmt->setUInt64(2, guid);
2715
2716 WorldDatabase.Execute(stmt);
2717 }
2718
2719 if (gameEvent == 0) // if not this is to be managed by GameEvent System
2720 AddGameobjectToGrid(&data);
2721 }
2722 while (result->NextRow());
2723
2724 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in {} ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2725}
2726
2728{
2729 uint32 oldMSTime = getMSTime();
2730
2731 // 0 1 2
2732 QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
2733
2734 if (result)
2735 {
2736 do
2737 {
2738 Field* fields = result->Fetch();
2739 uint32 groupId = fields[0].GetUInt32();
2741 group.groupId = groupId;
2742 group.name = fields[1].GetString();
2744 uint32 flags = fields[2].GetUInt32();
2746 {
2748 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag {} on group ID {} ({}), reduced to valid flag {}.", flags, groupId, group.name, uint32(group.flags));
2749 }
2751 {
2752 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
2753 TC_LOG_ERROR("sql.sql", "System spawn group {} ({}) has invalid manual spawn flag. Ignored.", groupId, group.name);
2754 }
2755 group.flags = SpawnGroupFlags(flags);
2756 } while (result->NextRow());
2757 }
2758
2759 if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
2760 {
2761 TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
2763 data.groupId = 0;
2764 data.name = "Default Group";
2765 data.mapId = 0;
2767 }
2768 if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
2769 {
2770 TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
2772 data.groupId = 1;
2773 data.name = "Legacy Group";
2774 data.mapId = 0;
2776 }
2777
2778 if (result)
2779 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group templates in {} ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2780 else
2781 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
2782
2783 return;
2784}
2785
2787{
2788 uint32 oldMSTime = getMSTime();
2789
2790 // 0 1 2
2791 QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
2792
2793 if (!result)
2794 {
2795 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
2796 return;
2797 }
2798
2799 uint32 numMembers = 0;
2800 do
2801 {
2802 Field* fields = result->Fetch();
2803 uint32 groupId = fields[0].GetUInt32();
2804 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
2805 if (!SpawnData::TypeIsValid(spawnType))
2806 {
2807 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type {} listed for spawn group {}. Skipped.", uint32(spawnType), groupId);
2808 continue;
2809 }
2810 ObjectGuid::LowType spawnId = fields[2].GetUInt64();
2811
2812 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
2813 if (!data)
2814 {
2815 TC_LOG_ERROR("sql.sql", "Spawn data with ID ({},{}) not found, but is listed as a member of spawn group {}!", uint32(spawnType), spawnId, groupId);
2816 continue;
2817 }
2818 else if (data->spawnGroupData->groupId)
2819 {
2820 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);
2821 continue;
2822 }
2823 auto it = _spawnGroupDataStore.find(groupId);
2824 if (it == _spawnGroupDataStore.end())
2825 {
2826 TC_LOG_ERROR("sql.sql", "Spawn group {} assigned to spawn ID ({},{}), but group is found!", groupId, uint32(spawnType), spawnId);
2827 continue;
2828 }
2829 else
2830 {
2831 SpawnGroupTemplateData& groupTemplate = it->second;
2832 if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
2833 {
2834 groupTemplate.mapId = data->mapId;
2835 _spawnGroupsByMap[data->mapId].push_back(groupId);
2836 }
2837 else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2838 {
2839 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);
2840 continue;
2841 }
2842 const_cast<SpawnMetadata*>(data)->spawnGroupData = &groupTemplate;
2843 if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2844 _spawnGroupMapStore.emplace(groupId, data);
2845 ++numMembers;
2846 }
2847 } while (result->NextRow());
2848
2849 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group members in {} ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
2850}
2851
2853{
2854 uint32 oldMSTime = getMSTime();
2855
2856 // 0 1 2 3 4
2857 QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
2858
2859 if (!result)
2860 {
2861 TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
2862 return;
2863 }
2864
2865 uint32 n = 0;
2866 do
2867 {
2868 Field* fields = result->Fetch();
2869 uint32 const spawnGroupId = fields[3].GetUInt32();
2870 auto it = _spawnGroupDataStore.find(spawnGroupId);
2871 if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
2872 {
2873 TC_LOG_ERROR("sql.sql", "Invalid spawn group {} specified for instance {}. Skipped.", spawnGroupId, fields[0].GetUInt16());
2874 continue;
2875 }
2876
2877 uint16 const instanceMapId = fields[0].GetUInt16();
2878 if (it->second.mapId != instanceMapId)
2879 {
2880 TC_LOG_ERROR("sql.sql", "Instance spawn group {} specified for instance {} has spawns on a different map {}. Skipped.",
2881 spawnGroupId, instanceMapId, it->second.mapId);
2882 continue;
2883 }
2884
2885 InstanceSpawnGroupInfo& info = _instanceSpawnGroupStore[instanceMapId].emplace_back();
2886 info.SpawnGroupId = spawnGroupId;
2887 info.BossStateId = fields[1].GetUInt8();
2888
2889 uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
2890 uint8 const states = fields[2].GetUInt8();
2891 if (states & ~ALL_STATES)
2892 {
2893 info.BossStates = states & ALL_STATES;
2894 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid boss state mask {} - truncated to {}.", instanceMapId, spawnGroupId, states, info.BossStates);
2895 }
2896 else
2897 info.BossStates = states;
2898
2899 uint8 const flags = fields[4].GetUInt8();
2901 {
2903 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid flags {} - truncated to {}.", instanceMapId, spawnGroupId, flags, info.Flags);
2904 }
2905 else
2906 info.Flags = flags;
2907
2909 {
2911 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);
2912 }
2913
2914 ++n;
2915 } while (result->NextRow());
2916
2917 TC_LOG_INFO("server.loading", ">> Loaded {} instance spawn groups in {} ms", n, GetMSTimeDiffToNow(oldMSTime));
2918}
2919
2921{
2922 if (!SpawnData::TypeHasData(type))
2923 return nullptr;
2924 switch (type)
2925 {
2927 return GetCreatureData(spawnId);
2929 return GetGameObjectData(spawnId);
2931 return sAreaTriggerDataStore->GetAreaTriggerSpawn(spawnId);
2932 default:
2933 ABORT_MSG("Invalid spawn object type %u", uint32(type));
2934 return nullptr;
2935 }
2936}
2937
2939{
2940 if (data->spawnTrackingData)
2941 {
2943 ASSERT(spawnTrackingData, "Creature data for (%u," UI64FMTD ") is being deleted and has invalid spawn tracking id %u!", uint32(data->type), data->spawnId, data->spawnTrackingData->SpawnTrackingId);
2944
2945 auto pair = _spawnTrackingMapStore.equal_range(data->spawnTrackingData->SpawnTrackingId);
2946 bool erased = false;
2947 for (auto it = pair.first; it != pair.second; ++it)
2948 {
2949 if (it->second != data)
2950 continue;
2951 _spawnTrackingMapStore.erase(it);
2952 erased = true;
2953 }
2954
2955 if (!erased)
2956 ABORT_MSG("Spawn data (%u," UI64FMTD ") being removed is member of spawn tracking %u, but not actually listed in the lookup table for that spawn tracking!", uint32(data->type), data->spawnId, data->spawnTrackingData->SpawnTrackingId);
2957 }
2958
2959 auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
2960 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);
2961 if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
2962 return;
2963
2964 auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
2965 for (auto it = pair.first; it != pair.second; ++it)
2966 {
2967 if (it->second != data)
2968 continue;
2969 _spawnGroupMapStore.erase(it);
2970 return;
2971 }
2972 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);
2973}
2974
2976{
2977 AddSpawnDataToGrid<&GridObjectGuids::gameobjects>(data);
2978}
2979
2981{
2982 RemoveSpawnDataFromGrid<&GridObjectGuids::gameobjects>(data);
2983}
2984
2985uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
2986{
2987 if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON)
2988 return 0;
2989
2990 static float const qualityMultipliers[MAX_ITEM_QUALITY] =
2991 {
2992 0.92f, 0.92f, 0.92f, 1.11f, 1.32f, 1.61f, 0.0f, 0.0f
2993 };
2994
2995 static float const armorMultipliers[MAX_INVTYPE] =
2996 {
2997 0.00f, // INVTYPE_NON_EQUIP
2998 0.60f, // INVTYPE_HEAD
2999 0.00f, // INVTYPE_NECK
3000 0.60f, // INVTYPE_SHOULDERS
3001 0.00f, // INVTYPE_BODY
3002 1.00f, // INVTYPE_CHEST
3003 0.33f, // INVTYPE_WAIST
3004 0.72f, // INVTYPE_LEGS
3005 0.48f, // INVTYPE_FEET
3006 0.33f, // INVTYPE_WRISTS
3007 0.33f, // INVTYPE_HANDS
3008 0.00f, // INVTYPE_FINGER
3009 0.00f, // INVTYPE_TRINKET
3010 0.00f, // INVTYPE_WEAPON
3011 0.72f, // INVTYPE_SHIELD
3012 0.00f, // INVTYPE_RANGED
3013 0.00f, // INVTYPE_CLOAK
3014 0.00f, // INVTYPE_2HWEAPON
3015 0.00f, // INVTYPE_BAG
3016 0.00f, // INVTYPE_TABARD
3017 1.00f, // INVTYPE_ROBE
3018 0.00f, // INVTYPE_WEAPONMAINHAND
3019 0.00f, // INVTYPE_WEAPONOFFHAND
3020 0.00f, // INVTYPE_HOLDABLE
3021 0.00f, // INVTYPE_AMMO
3022 0.00f, // INVTYPE_THROWN
3023 0.00f, // INVTYPE_RANGEDRIGHT
3024 0.00f, // INVTYPE_QUIVER
3025 0.00f, // INVTYPE_RELIC
3026 0.00f, // INVTYPE_PROFESSION_TOOL
3027 0.00f, // INVTYPE_PROFESSION_GEAR
3028 0.00f, // INVTYPE_EQUIPABLE_SPELL_OFFENSIVE
3029 0.00f, // INVTYPE_EQUIPABLE_SPELL_UTILITY
3030 0.00f, // INVTYPE_EQUIPABLE_SPELL_DEFENSIVE
3031 0.00f, // INVTYPE_EQUIPABLE_SPELL_MOBILITY
3032 };
3033
3034 static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] =
3035 {
3036 0.91f, // ITEM_SUBCLASS_WEAPON_AXE
3037 1.00f, // ITEM_SUBCLASS_WEAPON_AXE2
3038 1.00f, // ITEM_SUBCLASS_WEAPON_BOW
3039 1.00f, // ITEM_SUBCLASS_WEAPON_GUN
3040 0.91f, // ITEM_SUBCLASS_WEAPON_MACE
3041 1.00f, // ITEM_SUBCLASS_WEAPON_MACE2
3042 1.00f, // ITEM_SUBCLASS_WEAPON_POLEARM
3043 0.91f, // ITEM_SUBCLASS_WEAPON_SWORD
3044 1.00f, // ITEM_SUBCLASS_WEAPON_SWORD2
3045 1.00f, // ITEM_SUBCLASS_WEAPON_WARGLAIVES
3046 1.00f, // ITEM_SUBCLASS_WEAPON_STAFF
3047 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC
3048 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2
3049 0.66f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON
3050 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
3051 0.66f, // ITEM_SUBCLASS_WEAPON_DAGGER
3052 0.00f, // ITEM_SUBCLASS_WEAPON_THROWN
3053 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR
3054 1.00f, // ITEM_SUBCLASS_WEAPON_CROSSBOW
3055 0.66f, // ITEM_SUBCLASS_WEAPON_WAND
3056 0.66f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE
3057 };
3058
3059 float levelPenalty = 1.0f;
3060 if (itemLevel <= 28)
3061 levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f;
3062
3063 if (itemClass == ITEM_CLASS_ARMOR)
3064 {
3065 if (inventoryType > INVTYPE_ROBE)
3066 return 0;
3067
3068 return 5 * uint32(round(25.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty));
3069 }
3070
3071 return 5 * uint32(round(18.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty));
3072}
3073
3075{
3079
3081 {
3082 memset(ItemSpecStatTypes, -1, sizeof(ItemSpecStatTypes));
3083
3084 if (item->ClassID == ITEM_CLASS_WEAPON)
3085 {
3086 ItemType = 5;
3087 switch (item->SubclassID)
3088 {
3091 break;
3094 break;
3097 break;
3100 break;
3103 break;
3106 break;
3109 break;
3112 break;
3115 break;
3118 break;
3121 break;
3124 break;
3127 break;
3130 break;
3133 break;
3136 break;
3137 default:
3138 break;
3139 }
3140 }
3141 else if (item->ClassID == ITEM_CLASS_ARMOR)
3142 {
3143 switch (item->SubclassID)
3144 {
3146 if (sparse->InventoryType != INVTYPE_CLOAK)
3147 {
3148 ItemType = 1;
3149 break;
3150 }
3151
3152 ItemType = 0;
3154 break;
3156 ItemType = 2;
3157 break;
3159 ItemType = 3;
3160 break;
3162 ItemType = 4;
3163 break;
3164 default:
3166 {
3167 ItemType = 6;
3169 }
3171 {
3172 ItemType = 6;
3174 }
3175 else
3176 ItemType = 0;
3177 break;
3178 }
3179 }
3180 else if (item->ClassID == ITEM_CLASS_GEM)
3181 {
3182 ItemType = 7;
3183 if (GemPropertiesEntry const* gem = sGemPropertiesStore.LookupEntry(sparse->GemProperties))
3184 {
3185 if (gem->Type & SOCKET_COLOR_RELIC_IRON)
3187 if (gem->Type & SOCKET_COLOR_RELIC_BLOOD)
3189 if (gem->Type & SOCKET_COLOR_RELIC_SHADOW)
3191 if (gem->Type & SOCKET_COLOR_RELIC_FEL)
3193 if (gem->Type & SOCKET_COLOR_RELIC_ARCANE)
3195 if (gem->Type & SOCKET_COLOR_RELIC_FROST)
3197 if (gem->Type & SOCKET_COLOR_RELIC_FIRE)
3199 if (gem->Type & SOCKET_COLOR_RELIC_WATER)
3201 if (gem->Type & SOCKET_COLOR_RELIC_LIFE)
3203 if (gem->Type & SOCKET_COLOR_RELIC_WIND)
3205 if (gem->Type & SOCKET_COLOR_RELIC_HOLY)
3207 }
3208 }
3209 else
3210 ItemType = 0;
3211
3212 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3213 if (sparse->StatModifierBonusStat[i] != -1)
3215 }
3216
3217 void AddStat(ItemSpecStat statType)
3218 {
3220 return;
3221
3222 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3223 if (ItemSpecStatTypes[i] == uint32(statType))
3224 return;
3225
3227 }
3228
3229 void AddModStat(int32 itemStatType)
3230 {
3231 switch (itemStatType)
3232 {
3233 case ITEM_MOD_AGILITY:
3235 break;
3236 case ITEM_MOD_STRENGTH:
3238 break;
3239 case ITEM_MOD_INTELLECT:
3241 break;
3244 break;
3247 break;
3253 break;
3256 break;
3259 break;
3262 break;
3267 break;
3268 case ITEM_MOD_AGI_STR:
3271 break;
3272 case ITEM_MOD_AGI_INT:
3275 break;
3276 case ITEM_MOD_STR_INT:
3279 break;
3280 }
3281 }
3282};
3283
3285{
3286 uint32 oldMSTime = getMSTime();
3287
3288 for (ItemSparseEntry const* sparse : sItemSparseStore)
3289 {
3290 ItemEntry const* db2Data = sItemStore.LookupEntry(sparse->ID);
3291 if (!db2Data)
3292 continue;
3293
3294 ItemTemplate& itemTemplate = _itemTemplateStore[sparse->ID];
3295
3296 itemTemplate.BasicData = db2Data;
3297 itemTemplate.ExtendedData = sparse;
3298
3299 itemTemplate.MaxDurability = FillMaxDurability(db2Data->ClassID, db2Data->SubclassID, sparse->InventoryType, sparse->OverallQualityID, sparse->ItemLevel);
3300 itemTemplate.ScriptId = 0;
3301 itemTemplate.FoodType = 0;
3302 itemTemplate.MinMoneyLoot = 0;
3303 itemTemplate.MaxMoneyLoot = 0;
3304 itemTemplate.FlagsCu = 0;
3305 itemTemplate.SpellPPMRate = 0.0f;
3306 itemTemplate.RandomBonusListTemplateId = 0;
3307 itemTemplate.ItemSpecClassMask = 0;
3308 itemTemplate.QuestLogItemId = 0;
3309
3310 if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID))
3311 {
3312 for (ItemSpecOverrideEntry const* itemSpecOverride : *itemSpecOverrides)
3313 {
3314 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpecOverride->SpecID))
3315 {
3316 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3317 itemTemplate.Specializations[0].set(ItemTemplate::CalculateItemSpecBit(specialization));
3318 itemTemplate.Specializations[1] |= itemTemplate.Specializations[0];
3319 itemTemplate.Specializations[2] |= itemTemplate.Specializations[0];
3320 }
3321 }
3322 }
3323 else
3324 {
3325 ItemSpecStats itemSpecStats(db2Data, sparse);
3326
3327 for (ItemSpecEntry const* itemSpec : sItemSpecStore)
3328 {
3329 if (itemSpecStats.ItemType != itemSpec->ItemType)
3330 continue;
3331
3332 bool hasPrimary = itemSpec->PrimaryStat == ITEM_SPEC_STAT_NONE;
3333 bool hasSecondary = itemSpec->SecondaryStat == ITEM_SPEC_STAT_NONE;
3334 for (uint32 i = 0; i < itemSpecStats.ItemSpecStatCount; ++i)
3335 {
3336 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->PrimaryStat)
3337 hasPrimary = true;
3338 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->SecondaryStat)
3339 hasSecondary = true;
3340 }
3341
3342 if (!hasPrimary || !hasSecondary)
3343 continue;
3344
3345 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpec->SpecializationID))
3346 {
3347 if ((1 << (specialization->ClassID - 1)) & sparse->AllowableClass)
3348 {
3349 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3350 std::size_t specBit = ItemTemplate::CalculateItemSpecBit(specialization);
3351 itemTemplate.Specializations[0].set(specBit);
3352 if (itemSpec->MaxLevel > 40)
3353 itemTemplate.Specializations[1].set(specBit);
3354 if (itemSpec->MaxLevel >= 110)
3355 itemTemplate.Specializations[2].set(specBit);
3356 }
3357 }
3358 }
3359 }
3360
3361 // Items that have no specializations set can be used by everyone
3362 for (auto& specs : itemTemplate.Specializations)
3363 if (specs.count() == 0)
3364 specs.set();
3365 }
3366
3367 // Load item effects (spells)
3368 for (ItemXItemEffectEntry const* effectEntry : sItemXItemEffectStore)
3369 {
3371 {
3372 if (ItemEffectEntry const* effect = sItemEffectStore.LookupEntry(effectEntry->ItemEffectID))
3373 {
3374 auto itr = std::ranges::lower_bound(item->Effects, effect->LegacySlotIndex, {}, &ItemEffectEntry::LegacySlotIndex);
3375 item->Effects.insert(itr, effect);
3376 }
3377 }
3378 }
3379
3380 TC_LOG_INFO("server.loading", ">> Loaded {} item templates in {} ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3381}
3382
3384{
3385 uint32 oldMSTime = getMSTime();
3386 uint32 count = 0;
3387
3388 QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId, QuestLogItemId FROM item_template_addon");
3389 if (result)
3390 {
3391 do
3392 {
3393 Field* fields = result->Fetch();
3394 uint32 itemId = fields[0].GetUInt32();
3395 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3396 if (!itemTemplate)
3397 {
3398 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_template_addon` does not exist, skipped.", itemId);
3399 continue;
3400 }
3401
3402 uint32 minMoneyLoot = fields[3].GetUInt32();
3403 uint32 maxMoneyLoot = fields[4].GetUInt32();
3404 if (minMoneyLoot > maxMoneyLoot)
3405 {
3406 TC_LOG_ERROR("sql.sql", "Minimum money loot specified in `item_template_addon` for item {} was greater than maximum amount, swapping.", itemId);
3407 std::swap(minMoneyLoot, maxMoneyLoot);
3408 }
3409 itemTemplate->FlagsCu = fields[1].GetUInt32();
3410 itemTemplate->FoodType = fields[2].GetUInt8();
3411 itemTemplate->MinMoneyLoot = minMoneyLoot;
3412 itemTemplate->MaxMoneyLoot = maxMoneyLoot;
3413 itemTemplate->SpellPPMRate = fields[5].GetFloat();
3414 itemTemplate->RandomBonusListTemplateId = fields[6].GetUInt32();
3415 itemTemplate->QuestLogItemId = fields[7].GetInt32();
3416 ++count;
3417 } while (result->NextRow());
3418 }
3419 TC_LOG_INFO("server.loading", ">> Loaded {} item addon templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3420}
3421
3423{
3424 uint32 oldMSTime = getMSTime();
3425 uint32 count = 0;
3426
3427 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names");
3428 if (result)
3429 {
3430 do
3431 {
3432 Field* fields = result->Fetch();
3433 uint32 itemId = fields[0].GetUInt32();
3434 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3435 if (!itemTemplate)
3436 {
3437 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_script_names` does not exist, skipped.", itemId);
3438 continue;
3439 }
3440
3441 itemTemplate->ScriptId = GetScriptId(fields[1].GetStringView());
3442 ++count;
3443 } while (result->NextRow());
3444 }
3445
3446 TC_LOG_INFO("server.loading", ">> Loaded {} item script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3447}
3448
3453
3455{
3456 uint32 oldMSTime = getMSTime();
3457
3458 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
3459
3460 uint32 count = 0;
3461
3462 // 0 1 2 3 4 5 6
3463 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_template_accessory`");
3464
3465 if (!result)
3466 {
3467 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
3468 return;
3469 }
3470
3471 do
3472 {
3473 Field* fields = result->Fetch();
3474
3475 uint32 entry = fields[0].GetUInt32();
3476 uint32 accessory = fields[1].GetUInt32();
3477 int8 seatId = fields[2].GetInt8();
3478 bool isMinion = fields[3].GetBool();
3479 uint8 summonType = fields[4].GetUInt8();
3480 uint32 summonTimer = fields[5].GetUInt32();
3481 Optional<uint32> rideSpellId = fields[6].GetUInt32OrNull();
3482
3483 if (rideSpellId && !sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
3484 {
3485 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: rideSpellId {} does not exist for entry {}.", *rideSpellId, entry);
3486 continue;
3487 }
3488
3489 if (!GetCreatureTemplate(entry))
3490 {
3491 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
3492 continue;
3493 }
3494
3495 if (!GetCreatureTemplate(accessory))
3496 {
3497 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", accessory);
3498 continue;
3499 }
3500
3501 if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
3502 {
3503 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} has no data in npc_spellclick_spells", entry);
3504 continue;
3505 }
3506
3507 _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer, rideSpellId));
3508
3509 ++count;
3510 }
3511 while (result->NextRow());
3512
3513 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3514}
3515
3517{
3518 uint32 oldMSTime = getMSTime();
3519
3520 _vehicleTemplateStore.clear();
3521
3522 // 0 1 2 3
3523 QueryResult result = WorldDatabase.Query("SELECT creatureId, despawnDelayMs, Pitch, CustomFlags FROM vehicle_template");
3524
3525 if (!result)
3526 {
3527 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template. DB table `vehicle_template` is empty.");
3528 return;
3529 }
3530
3531 do
3532 {
3533 Field* fields = result->Fetch();
3534
3535 uint32 creatureId = fields[0].GetUInt32();
3536
3537 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creatureId);
3538 if (!creatureInfo)
3539 {
3540 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) does not exist.", creatureId);
3541 continue;
3542 }
3543
3544 if (!creatureInfo->VehicleId)
3545 {
3546 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) is not a vehicle.", creatureId);
3547 continue;
3548 }
3549
3550 VehicleTemplate& vehicleTemplate = _vehicleTemplateStore[creatureId];
3551 vehicleTemplate.DespawnDelay = Milliseconds(fields[1].GetInt32());
3552 vehicleTemplate.Pitch = fields[2].GetFloatOrNull();
3553 vehicleTemplate.CustomFlags = VehicleCustomFlags(fields[3].GetInt32());
3554
3555 if (vehicleTemplate.DespawnDelay < 0ms)
3556 {
3557 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) has negative despawnDelayMs ({}).`. Ignoring",
3558 creatureId, vehicleTemplate.DespawnDelay.count());
3559 vehicleTemplate.DespawnDelay = 1ms;
3560 }
3561 else if (vehicleTemplate.DespawnDelay == 0ms)
3562 vehicleTemplate.DespawnDelay = 1ms;
3563
3564 if (vehicleTemplate.Pitch)
3565 {
3566 if (VehicleEntry const* vehicle = sVehicleStore.LookupEntry(creatureInfo->VehicleId))
3567 {
3568 if (*vehicleTemplate.Pitch < vehicle->PitchMin || *vehicleTemplate.Pitch > vehicle->PitchMax)
3569 {
3570 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) has invalid Pitch ({}).`. Ignoring",
3571 creatureId, *vehicleTemplate.Pitch);
3572 vehicleTemplate.Pitch.reset();
3573 }
3574 }
3575
3576 }
3577 } while (result->NextRow());
3578
3579 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template entries in {} ms", _vehicleTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3580}
3581
3583{
3584 uint32 oldMSTime = getMSTime();
3585
3586 _vehicleAccessoryStore.clear(); // needed for reload case
3587
3588 uint32 count = 0;
3589
3590 // 0 1 2 3 4 5 6
3591 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_accessory`");
3592
3593 if (!result)
3594 {
3595 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle accessories. DB table `vehicle_accessory` is empty.");
3596 return;
3597 }
3598
3599 do
3600 {
3601 Field* fields = result->Fetch();
3602
3603 ObjectGuid::LowType uiGUID = fields[0].GetUInt64();
3604 uint32 uiAccessory = fields[1].GetUInt32();
3605 int8 uiSeat = int8(fields[2].GetInt16());
3606 bool bMinion = fields[3].GetBool();
3607 uint8 uiSummonType = fields[4].GetUInt8();
3608 uint32 uiSummonTimer= fields[5].GetUInt32();
3609 Optional<uint32> rideSpellId = fields[6].GetUInt32OrNull();
3610
3611 if (rideSpellId && !sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
3612 {
3613 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: rideSpellId {} does not exist for guid {}.", *rideSpellId, uiGUID);
3614 continue;
3615 }
3616
3617 if (!GetCreatureTemplate(uiAccessory))
3618 {
3619 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
3620 continue;
3621 }
3622
3623 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer, rideSpellId));
3624
3625 ++count;
3626 }
3627 while (result->NextRow());
3628
3629 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3630}
3631
3633{
3634 uint32 oldMSTime = getMSTime();
3635
3636 _vehicleSeatAddonStore.clear(); // needed for reload case
3637
3638 uint32 count = 0;
3639
3640 // 0 1 2 3 4 5 6
3641 QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
3642
3643 if (!result)
3644 {
3645 TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
3646 return;
3647 }
3648
3649 do
3650 {
3651 Field* fields = result->Fetch();
3652
3653 uint32 seatID = fields[0].GetUInt32();
3654 float orientation = fields[1].GetFloat();
3655 float exitX = fields[2].GetFloat();
3656 float exitY = fields[3].GetFloat();
3657 float exitZ = fields[4].GetFloat();
3658 float exitO = fields[5].GetFloat();
3659 uint8 exitParam = fields[6].GetUInt8();
3660
3661 if (!sVehicleSeatStore.LookupEntry(seatID))
3662 {
3663 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} does not exist in VehicleSeat.dbc. Skipping entry.", seatID);
3664 continue;
3665 }
3666
3667 // Sanitizing values
3668 if (orientation > float(M_PI * 2))
3669 {
3670 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid angle offset value ({}). Set Value to 0.", seatID, orientation);
3671 orientation = 0.0f;
3672 }
3673
3675 {
3676 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid exit parameter value ({}). Setting to 0 (none).", seatID, exitParam);
3677 continue;
3678 }
3679
3680 _vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
3681
3682 ++count;
3683 } while (result->NextRow());
3684
3685 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Seat Addon entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3686}
3687
3689{
3690 uint32 oldMSTime = getMSTime();
3691
3692 // 0 1 2 3 4 5 6 7 8 9
3693 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
3694
3695 if (!result)
3696 {
3697 TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
3698 return;
3699 }
3700
3701 uint32 count = 0;
3702
3703 do
3704 {
3705 Field* fields = result->Fetch();
3706
3707 uint32 creature_id = fields[0].GetUInt32();
3708 if (!GetCreatureTemplate(creature_id))
3709 {
3710 TC_LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
3711 continue;
3712 }
3713
3714 uint32 current_level = fields[1].GetUInt8();
3715 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3716 {
3717 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3718 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3719 else
3720 {
3721 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `pet_levelstats` table, ignoring.", current_level);
3722 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3723 }
3724 continue;
3725 }
3726 else if (current_level < 1)
3727 {
3728 TC_LOG_ERROR("sql.sql", "Wrong (<1) level {} in `pet_levelstats` table, ignoring.", current_level);
3729 continue;
3730 }
3731
3732 auto& pInfoMapEntry = _petInfoStore[creature_id];
3733 if (!pInfoMapEntry)
3734 pInfoMapEntry = std::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3735
3736 // data for level 1 stored in [0] array element, ...
3737 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
3738
3739 pLevelInfo->health = fields[2].GetUInt16();
3740 pLevelInfo->mana = fields[3].GetUInt16();
3741 pLevelInfo->armor = fields[9].GetUInt32();
3742
3743 for (uint8 i = 0; i < MAX_STATS; i++)
3744 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
3745
3746 ++count;
3747 }
3748 while (result->NextRow());
3749
3750 // Fill gaps and check integrity
3751 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
3752 {
3753 auto& pInfo = itr->second;
3754
3755 // fatal error if no level 1 data
3756 if (!pInfo || pInfo[0].health == 0)
3757 {
3758 TC_LOG_ERROR("sql.sql", "Creature {} does not have pet stats data for Level 1!", itr->first);
3759 ABORT();
3760 }
3761
3762 // fill level gaps
3763 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3764 {
3765 if (pInfo[level].health == 0)
3766 {
3767 TC_LOG_ERROR("sql.sql", "Creature {} has no data for Level {} pet stats data, using data of Level {}.", itr->first, level + 1, level);
3768 pInfo[level] = pInfo[level - 1];
3769 }
3770 }
3771 }
3772
3773 TC_LOG_INFO("server.loading", ">> Loaded {} level pet stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3774}
3775
3776PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
3777{
3778 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3779 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3780
3781 auto itr = _petInfoStore.find(creature_id);
3782 if (itr == _petInfoStore.end())
3783 return nullptr;
3784
3785 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
3786}
3787
3789{
3790 PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race_), Classes(class_) });
3791 if (!playerInfo)
3792 return;
3793
3794 if (count > 0)
3795 playerInfo->item.emplace_back(itemId, count);
3796 else
3797 {
3798 if (count < -1)
3799 TC_LOG_ERROR("sql.sql", "Invalid count {} specified on item {} be removed from original player create info (use -1)!", count, itemId);
3800
3801 PlayerCreateInfoItems& items = playerInfo->item;
3802
3803 auto erased = std::remove_if(items.begin(), items.end(), [itemId](PlayerCreateInfoItem const& item) { return item.item_id == itemId; });
3804 if (erased == items.end())
3805 {
3806 TC_LOG_ERROR("sql.sql", "Item {} specified to be removed from original create info not found in db2!", itemId);
3807 return;
3808 }
3809
3810 items.erase(erased, items.end());
3811 }
3812}
3813
3815{
3816 // Load playercreate
3817 {
3818 uint32 oldMSTime = getMSTime();
3819 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3820 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");
3821
3822 if (!result)
3823 {
3824 TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
3825 ABORT();
3826 }
3827 else
3828 {
3829 uint32 count = 0;
3830
3831 do
3832 {
3833 Field* fields = result->Fetch();
3834
3835 uint32 current_race = fields[0].GetUInt8();
3836 uint32 current_class = fields[1].GetUInt8();
3837 uint32 mapId = fields[2].GetUInt16();
3838 float positionX = fields[3].GetFloat();
3839 float positionY = fields[4].GetFloat();
3840 float positionZ = fields[5].GetFloat();
3841 float orientation = fields[6].GetFloat();
3842
3843 if (!sChrRacesStore.LookupEntry(current_race))
3844 {
3845 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3846 continue;
3847 }
3848
3849 if (!sChrClassesStore.LookupEntry(current_class))
3850 {
3851 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3852 continue;
3853 }
3854
3855 // accept DB data only for valid position (and non instanceable)
3856 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3857 {
3858 TC_LOG_ERROR("sql.sql", "Wrong home position for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3859 continue;
3860 }
3861
3862 if (sMapStore.LookupEntry(mapId)->Instanceable())
3863 {
3864 TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3865 continue;
3866 }
3867
3868 if (!sDB2Manager.GetChrModel(current_race, GENDER_MALE))
3869 {
3870 TC_LOG_ERROR("sql.sql", "Missing male model for race {}, ignoring.", current_race);
3871 continue;
3872 }
3873
3874 if (!sDB2Manager.GetChrModel(current_race, GENDER_FEMALE))
3875 {
3876 TC_LOG_ERROR("sql.sql", "Missing female model for race {}, ignoring.", current_race);
3877 continue;
3878 }
3879
3880 std::unique_ptr<PlayerInfo> info = std::make_unique<PlayerInfo>();
3881 info->createPosition.Loc.WorldRelocate(mapId, positionX, positionY, positionZ, orientation);
3882
3883 if (std::none_of(fields + 7, fields + 12, [](Field const& field) { return field.IsNull(); }))
3884 {
3885 info->createPositionNPE.emplace();
3886
3887 info->createPositionNPE->Loc.WorldRelocate(fields[7].GetUInt32(), fields[8].GetFloat(), fields[9].GetFloat(), fields[10].GetFloat(), fields[11].GetFloat());
3888 info->createPositionNPE->TransportGuid = fields[12].GetUInt64OrNull();
3889
3890 if (!sMapStore.LookupEntry(info->createPositionNPE->Loc.GetMapId()))
3891 {
3892 TC_LOG_ERROR("sql.sql", "Invalid NPE map id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3893 info->createPositionNPE->Loc.GetMapId(), current_class, current_race);
3894 info->createPositionNPE.reset();
3895 }
3896
3897 if (info->createPositionNPE && info->createPositionNPE->TransportGuid && !sTransportMgr->GetTransportSpawn(*info->createPositionNPE->TransportGuid))
3898 {
3899 TC_LOG_ERROR("sql.sql", "Invalid NPE transport spawn id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3900 *info->createPositionNPE->TransportGuid, current_class, current_race);
3901 info->createPositionNPE.reset(); // remove entire NPE data - assume user put transport offsets into npe_position fields
3902 }
3903 }
3904
3905 info->introMovieId = fields[13].GetUInt32OrNull();
3906 if (info->introMovieId && !sMovieStore.LookupEntry(*info->introMovieId))
3907 {
3908 TC_LOG_ERROR("sql.sql", "Invalid intro movie id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3909 *info->introMovieId, current_class, current_race);
3910 info->introMovieId.reset();
3911 }
3912
3913 info->introSceneId = fields[14].GetUInt32OrNull();
3914 if (info->introSceneId && !GetSceneTemplate(*info->introSceneId))
3915 {
3916 TC_LOG_ERROR("sql.sql", "Invalid intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3917 *info->introSceneId, current_class, current_race);
3918 info->introSceneId.reset();
3919 }
3920
3921 info->introSceneIdNPE = fields[15].GetUInt32OrNull();
3922 if (info->introSceneIdNPE && !GetSceneTemplate(*info->introSceneIdNPE))
3923 {
3924 TC_LOG_ERROR("sql.sql", "Invalid NPE intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3925 *info->introSceneIdNPE, current_class, current_race);
3926 info->introSceneIdNPE.reset();
3927 }
3928
3929 _playerInfo[{ Races(current_race), Classes(current_class) }] = std::move(info);
3930
3931 ++count;
3932 }
3933 while (result->NextRow());
3934
3935 TC_LOG_INFO("server.loading", ">> Loaded {} player create definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3936 }
3937 }
3938
3939 // Load playercreate items
3940 TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
3941 {
3942 std::unordered_map<uint32, std::vector<ItemTemplate const*>> itemsByCharacterLoadout;
3943 for (CharacterLoadoutItemEntry const* characterLoadoutItem : sCharacterLoadoutItemStore)
3944 if (ItemTemplate const* itemTemplate = GetItemTemplate(characterLoadoutItem->ItemID))
3945 itemsByCharacterLoadout[characterLoadoutItem->CharacterLoadoutID].push_back(itemTemplate);
3946
3947 for (CharacterLoadoutEntry const* characterLoadout : sCharacterLoadoutStore)
3948 {
3949 if (!characterLoadout->IsForNewCharacter())
3950 continue;
3951
3952 std::vector<ItemTemplate const*> const* items = Trinity::Containers::MapGetValuePtr(itemsByCharacterLoadout, characterLoadout->ID);
3953 if (!items)
3954 continue;
3955
3956 for (ChrRacesEntry const* race : sChrRacesStore)
3957 {
3958 if (!characterLoadout->RaceMask.HasRace(race->ID))
3959 continue;
3960
3961 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(characterLoadout->ChrClassID) }))
3962 {
3963 playerInfo->itemContext = ItemContext(characterLoadout->ItemContext);
3964
3965 for (ItemTemplate const* itemTemplate : *items)
3966 {
3967 // BuyCount by default
3968 uint32 count = itemTemplate->GetBuyCount();
3969
3970 // special amount for food/drink
3971 if (itemTemplate->GetClass() == ITEM_CLASS_CONSUMABLE && itemTemplate->GetSubClass() == ITEM_SUBCLASS_FOOD_DRINK)
3972 {
3973 if (!itemTemplate->Effects.empty())
3974 {
3975 switch (itemTemplate->Effects[0]->SpellCategoryID)
3976 {
3977 case SPELL_CATEGORY_FOOD: // food
3978 count = characterLoadout->ChrClassID == CLASS_DEATH_KNIGHT ? 10 : 4;
3979 break;
3980 case SPELL_CATEGORY_DRINK: // drink
3981 count = 2;
3982 break;
3983 }
3984 }
3985 if (itemTemplate->GetMaxStackSize() < count)
3986 count = itemTemplate->GetMaxStackSize();
3987 }
3988
3989 playerInfo->item.emplace_back(itemTemplate->GetId(), count);
3990 }
3991 }
3992 }
3993 }
3994 }
3995
3996 TC_LOG_INFO("server.loading", "Loading Player Create Items Override Data...");
3997 {
3998 uint32 oldMSTime = getMSTime();
3999 // 0 1 2 3
4000 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
4001
4002 if (!result)
4003 {
4004 TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
4005 }
4006 else
4007 {
4008 uint32 count = 0;
4009
4010 do
4011 {
4012 Field* fields = result->Fetch();
4013
4014 uint32 current_race = fields[0].GetUInt8();
4015 if (current_race && !sChrRacesStore.HasRecord(current_race))
4016 {
4017 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
4018 continue;
4019 }
4020
4021 uint32 current_class = fields[1].GetUInt8();
4022 if (current_class && !sChrClassesStore.HasRecord(current_class))
4023 {
4024 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_item` table, ignoring.", current_class);
4025 continue;
4026 }
4027
4028 uint32 item_id = fields[2].GetUInt32();
4029
4030 if (!GetItemTemplate(item_id))
4031 {
4032 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);
4033 continue;
4034 }
4035
4036 int32 amount = fields[3].GetInt8();
4037
4038 if (!amount)
4039 {
4040 TC_LOG_ERROR("sql.sql", "Item id {} (class {} race {}) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
4041 continue;
4042 }
4043
4044 if (!current_race || !current_class)
4045 {
4046 uint32 min_race = current_race ? current_race : 1;
4047 uint32 max_race = current_race ? current_race + 1 : sChrRacesStore.GetNumRows();
4048 uint32 min_class = current_class ? current_class : 1;
4049 uint32 max_class = current_class ? current_class + 1 : sChrClassesStore.GetNumRows();
4050 for (uint32 r = min_race; r < max_race; ++r)
4051 for (uint32 c = min_class; c < max_class; ++c)
4052 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
4053 }
4054 else
4055 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
4056
4057 ++count;
4058 }
4059 while (result->NextRow());
4060
4061 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4062 }
4063 }
4064
4065 // Load playercreate skills
4066 TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
4067 {
4068 uint32 oldMSTime = getMSTime();
4069
4071 if (rcInfo->Availability == 1)
4072 for (ChrRacesEntry const* race : sChrRacesStore)
4073 if (rcInfo->RaceMask.IsEmpty() || rcInfo->RaceMask.HasRace(race->ID))
4074 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4075 if (rcInfo->ClassMask == -1 || rcInfo->ClassMask == 0 || ((1 << (classIndex - 1)) & rcInfo->ClassMask))
4076 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(classIndex) }))
4077 playerInfo->skills.push_back(rcInfo);
4078
4079 TC_LOG_INFO("server.loading", ">> Loaded player create skills in {} ms", GetMSTimeDiffToNow(oldMSTime));
4080 }
4081
4082 // Load playercreate custom spells
4083 TC_LOG_INFO("server.loading", "Loading Player Create Custom Spell Data...");
4084 {
4085 uint32 oldMSTime = getMSTime();
4086
4087 QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
4088
4089 if (!result)
4090 {
4091 TC_LOG_INFO("server.loading", ">> Loaded 0 player create custom spells. DB table `playercreateinfo_spell_custom` is empty.");
4092 }
4093 else
4094 {
4095 uint32 count = 0;
4096
4097 do
4098 {
4099 Field* fields = result->Fetch();
4100 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4101 uint32 classMask = fields[1].GetUInt32();
4102 uint32 spellId = fields[2].GetUInt32();
4103
4104 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4105 {
4106 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask.RawValue[0]);
4107 continue;
4108 }
4109
4110 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4111 {
4112 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_spell_custom` table, ignoring.", classMask);
4113 continue;
4114 }
4115
4116 for (ChrRacesEntry const* race : sChrRacesStore)
4117 {
4118 if (raceMask.IsEmpty() || raceMask.HasRace(race->ID))
4119 {
4120 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4121 {
4122 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4123 {
4124 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, {Races(race->ID), Classes(classIndex)}))
4125 {
4126 playerInfo->customSpells.push_back(spellId);
4127 ++count;
4128 }
4129 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
4130 // Either split the masks per class, or per race, which kind of kills the point yet.
4131 // else if (raceMask != 0 && classMask != 0)
4132 // 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);
4133 }
4134 }
4135 }
4136 }
4137 }
4138 while (result->NextRow());
4139
4140 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4141 }
4142 }
4143
4144 // Load playercreate cast spell
4145 TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
4146 {
4147 uint32 oldMSTime = getMSTime();
4148
4149 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell, createMode FROM playercreateinfo_cast_spell");
4150
4151 if (!result)
4152 TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
4153 else
4154 {
4155 uint32 count = 0;
4156
4157 do
4158 {
4159 Field* fields = result->Fetch();
4160 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4161 uint32 classMask = fields[1].GetUInt32();
4162 uint32 spellId = fields[2].GetUInt32();
4163 int8 playerCreateMode = fields[3].GetInt8();
4164
4165 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4166 {
4167 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask.RawValue[0]);
4168 continue;
4169 }
4170
4171 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4172 {
4173 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_cast_spell` table, ignoring.", classMask);
4174 continue;
4175 }
4176
4177 if (playerCreateMode < 0 || playerCreateMode >= AsUnderlyingType(PlayerCreateMode::Max))
4178 {
4179 TC_LOG_ERROR("sql.sql", "Uses invalid createMode {} in `playercreateinfo_cast_spell` table, ignoring.", playerCreateMode);
4180 continue;
4181 }
4182
4183 for (ChrRacesEntry const* race : sChrRacesStore)
4184 {
4185 if (raceMask.IsEmpty() || raceMask.HasRace(race->ID))
4186 {
4187 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4188 {
4189 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4190 {
4191 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(classIndex) }))
4192 {
4193 playerInfo->castSpells[playerCreateMode].push_back(spellId);
4194 ++count;
4195 }
4196 }
4197 }
4198 }
4199 }
4200 } while (result->NextRow());
4201
4202 TC_LOG_INFO("server.loading", ">> Loaded {} player create cast spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4203 }
4204 }
4205
4206 // Load playercreate actions
4207 TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
4208 {
4209 uint32 oldMSTime = getMSTime();
4210
4211 // 0 1 2 3 4
4212 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
4213
4214 if (!result)
4215 {
4216 TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
4217 }
4218 else
4219 {
4220 uint32 count = 0;
4221
4222 do
4223 {
4224 Field* fields = result->Fetch();
4225
4226 uint32 current_race = fields[0].GetUInt8();
4227 if (!sChrRacesStore.HasRecord(current_race))
4228 {
4229 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
4230 continue;
4231 }
4232
4233 uint32 current_class = fields[1].GetUInt8();
4234 if (!sChrClassesStore.HasRecord(current_class))
4235 {
4236 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_action` table, ignoring.", current_class);
4237 continue;
4238 }
4239
4240 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(current_race), Classes(current_class) }))
4241 playerInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
4242
4243 ++count;
4244 }
4245 while (result->NextRow());
4246
4247 TC_LOG_INFO("server.loading", ">> Loaded {} player create actions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4248 }
4249 }
4250
4251 // Loading levels data (class/race dependent)
4252 TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
4253 {
4254 struct RaceStats
4255 {
4256 Races Race;
4257 std::array<int16, MAX_STATS> StatModifier = { };
4258
4259 explicit RaceStats(Races race) : Race(race) { }
4260 std::strong_ordering operator<=>(RaceStats const& right) const { return Race <=> right.Race; }
4261 bool operator==(RaceStats const& right) const { return Race == right.Race; }
4262 };
4263
4265
4266 uint32 oldMSTime = getMSTime();
4267
4268 QueryResult raceStatsResult = WorldDatabase.Query("SELECT race, str, agi, sta, inte, spi FROM player_racestats");
4269
4270 if (!raceStatsResult)
4271 {
4272 TC_LOG_ERROR("server.loading", ">> Loaded 0 race stats definitions. DB table `player_racestats` is empty.");
4273 ABORT();
4274 }
4275
4276 do
4277 {
4278 Field* fields = raceStatsResult->Fetch();
4279
4280 uint32 current_race = fields[0].GetUInt8();
4281 if (!sChrRacesStore.HasRecord(current_race))
4282 {
4283 TC_LOG_ERROR("sql.sql", "Wrong race {} in `player_racestats` table, ignoring.", current_race);
4284 continue;
4285 }
4286
4287 RaceStats& stats = *raceStatModifiers.emplace(Races(current_race)).first;
4288 for (uint32 i = 0; i < MAX_STATS; ++i)
4289 stats.StatModifier[i] = fields[i + 1].GetInt16();
4290
4291 } while (raceStatsResult->NextRow());
4292
4293 // 0 1 2 3 4 5 6
4294 QueryResult result = WorldDatabase.Query("SELECT class, level, str, agi, sta, inte, spi FROM player_classlevelstats");
4295
4296 if (!result)
4297 {
4298 TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_classlevelstats` is empty.");
4299 ABORT();
4300 }
4301
4302 uint32 count = 0;
4303
4304 do
4305 {
4306 Field* fields = result->Fetch();
4307
4308 uint32 current_class = fields[0].GetUInt8();
4309 if (!sChrClassesStore.HasRecord(current_class))
4310 {
4311 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
4312 continue;
4313 }
4314
4315 uint32 current_level = fields[1].GetUInt8();
4316 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4317 {
4318 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4319 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_classlevelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4320 else
4321 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
4322
4323 continue;
4324 }
4325
4326 for (RaceStats const& raceStats : raceStatModifiers)
4327 {
4328 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { raceStats.Race, Classes(current_class) }))
4329 {
4330 if (!playerInfo->levelInfo)
4331 playerInfo->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4332
4333 PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1];
4334 for (uint8 i = 0; i < MAX_STATS; ++i)
4335 levelInfo.stats[i] = fields[i + 2].GetInt32() + raceStats.StatModifier[i];
4336 }
4337 }
4338
4339 ++count;
4340 }
4341 while (result->NextRow());
4342
4343 // Fill gaps and check integrity
4344 for (auto const& [raceClass, playerInfo] : _playerInfo)
4345 {
4346 auto [race, class_] = raceClass;
4347
4348 // fatal error if no level 1 data
4349 if (!playerInfo->levelInfo || playerInfo->levelInfo[0].stats[0] == 0)
4350 {
4351 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level 1 does not have stats data!", race, class_);
4352 ABORT();
4353 }
4354
4355 // fill level gaps
4356 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4357 {
4358 if (playerInfo->levelInfo[level].stats[0] == 0)
4359 {
4360 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level {} does not have stats data. Using stats data of level {}.", race, class_, level + 1, level);
4361 playerInfo->levelInfo[level] = playerInfo->levelInfo[level - 1];
4362 }
4363 }
4364 }
4365
4366 TC_LOG_INFO("server.loading", ">> Loaded {} level stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4367 }
4368
4369 // Loading xp per level data
4370 TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
4371 {
4372 uint32 oldMSTime = getMSTime();
4373
4374 _playerXPperLevel.resize(sXpGameTable.GetTableRowCount(), 0);
4375
4376 // 0 1
4377 QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
4378
4379 // load the DBC's levels at first...
4380 for (uint32 level = 1; level < sXpGameTable.GetTableRowCount(); ++level)
4381 _playerXPperLevel[level] = sXpGameTable.GetRow(level)->Total;
4382
4383 uint32 count = 0;
4384
4385 // ...overwrite if needed (custom values)
4386 if (result)
4387 {
4388 do
4389 {
4390 Field* fields = result->Fetch();
4391
4392 uint32 current_level = fields[0].GetUInt8();
4393 uint32 current_xp = fields[1].GetUInt32();
4394
4395 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4396 {
4397 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4398 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4399 else
4400 {
4401 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_xp_for_level` table, ignoring.", current_level);
4402 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4403 }
4404 continue;
4405 }
4406 //PlayerXPperLevel
4407 _playerXPperLevel[current_level] = current_xp;
4408 ++count;
4409 } while (result->NextRow());
4410 }
4411
4412 // fill level gaps - only accounting levels > MAX_LEVEL
4413 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4414 {
4415 if (_playerXPperLevel[level] == 0)
4416 {
4417 TC_LOG_ERROR("sql.sql", "Level {} does not have XP for level data. Using data of level [{}] + 12000.", level + 1, level);
4418 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 12000;
4419 }
4420 }
4421
4422 TC_LOG_INFO("server.loading", ">> Loaded {} xp for level definition(s) from database in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4423 }
4424}
4425
4426void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseMana) const
4427{
4428 if (level < 1 || class_ >= MAX_CLASSES)
4429 return;
4430
4431 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4432 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
4433
4434 GtBaseMPEntry const* mp = sBaseMPGameTable.GetRow(level);
4435 if (!mp)
4436 {
4437 TC_LOG_ERROR("misc", "Tried to get non-existant Class-Level combination data for base hp/mp. Class {} Level {}", class_, level);
4438 return;
4439 }
4440
4441 baseMana = uint32(GetGameTableColumnForClass(mp, class_));
4442}
4443
4445{
4446 if (level < 1)
4447 return;
4448
4449 PlayerInfo const* pInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
4450 if (!pInfo)
4451 return;
4452
4453 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4454 *info = pInfo->levelInfo[level - 1];
4455 else
4456 BuildPlayerLevelInfo(race, class_, level, info);
4457}
4458
4460{
4461 // base data (last known level)
4462 *info = ASSERT_NOTNULL(Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(_class) }))->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
4463
4464 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
4465 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
4466 {
4467 switch (_class)
4468 {
4469 case CLASS_WARRIOR:
4470 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4471 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4472 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4473 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4474 break;
4475 case CLASS_PALADIN:
4476 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
4477 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4478 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
4479 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
4480 break;
4481 case CLASS_HUNTER:
4482 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
4483 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4484 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4485 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
4486 break;
4487 case CLASS_ROGUE:
4488 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
4489 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4490 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
4491 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
4492 break;
4493 case CLASS_PRIEST:
4494 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4495 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4496 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
4497 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
4498 break;
4499 case CLASS_SHAMAN:
4500 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4501 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4502 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
4503 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
4504 break;
4505 case CLASS_MAGE:
4506 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4507 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4508 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4509 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
4510 break;
4511 case CLASS_WARLOCK:
4512 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4513 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4514 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4515 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4516 break;
4517 case CLASS_DRUID:
4518 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
4519 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
4520 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
4521 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
4522 break;
4523 }
4524 }
4525}
4526
4527std::vector<uint32> const* ObjectMgr::GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
4528{
4529 if (std::vector<uint32> const* items = Trinity::Containers::MapGetValuePtr(_creatureQuestItemStore, { creatureEntry, difficulty }))
4530 return items;
4531
4532 // If there is no data for the difficulty, try to get data for the fallback difficulty
4533 if (DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty))
4534 return GetCreatureQuestItemList(creatureEntry, Difficulty(difficultyEntry->FallbackDifficultyID));
4535
4536 return nullptr;
4537}
4538
4539std::vector<int32> const* ObjectMgr::GetCreatureQuestCurrencyList(uint32 creatureId) const
4540{
4542}
4543
4545{
4546 uint32 oldMSTime = getMSTime();
4547
4548 _questTemplates.clear();
4550 _questObjectives.clear();
4551
4552 _exclusiveQuestGroups.clear();
4553
4554 QueryResult result = WorldDatabase.Query("SELECT "
4555 "ID, QuestType, QuestPackageID, ContentTuningID, QuestSortID, QuestInfoID, SuggestedGroupNum, RewardNextQuest, RewardXPDifficulty, RewardXPMultiplier, "
4556 "RewardMoneyDifficulty, RewardMoneyMultiplier, RewardBonusMoney, RewardSpell, RewardHonor, RewardKillHonor, RewardFavor, StartItem, "
4557 "RewardArtifactXPDifficulty, RewardArtifactXPMultiplier, RewardArtifactCategoryID, Flags, FlagsEx, FlagsEx2, FlagsEx3, "
4558 "RewardItem1, RewardAmount1, ItemDrop1, ItemDropQuantity1, RewardItem2, RewardAmount2, ItemDrop2, ItemDropQuantity2, "
4559 "RewardItem3, RewardAmount3, ItemDrop3, ItemDropQuantity3, RewardItem4, RewardAmount4, ItemDrop4, ItemDropQuantity4, "
4560 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemDisplayID1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemDisplayID2, "
4561 "RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemDisplayID3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemDisplayID4, "
4562 "RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemDisplayID5, RewardChoiceItemID6, RewardChoiceItemQuantity6, RewardChoiceItemDisplayID6, "
4563 "POIContinent, POIx, POIy, POIPriority, RewardTitle, RewardArenaPoints, RewardSkillLineID, RewardNumSkillUps, "
4564 "PortraitGiver, PortraitGiverMount, PortraitGiverModelSceneID, PortraitTurnIn, "
4565 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionCapIn1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionCapIn2, "
4566 "RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionCapIn3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionCapIn4, "
4567 "RewardFactionID5, RewardFactionValue5, RewardFactionOverride5, RewardFactionCapIn5, RewardFactionFlags, "
4568 "RewardCurrencyID1, RewardCurrencyQty1, RewardCurrencyID2, RewardCurrencyQty2, RewardCurrencyID3, RewardCurrencyQty3, RewardCurrencyID4, RewardCurrencyQty4, "
4569 "AcceptedSoundKitID, CompleteSoundKitID, AreaGroupID, TimeAllowed, AllowableRaces, ResetByScheduler, Expansion, ManagedWorldStateID, QuestSessionBonus, "
4570 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog "
4571 "FROM quest_template");
4572 if (!result)
4573 {
4574 TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
4575 return;
4576 }
4577
4578 _questTemplates.reserve(result->GetRowCount());
4579
4580 // create multimap previous quest for each existed quest
4581 // some quests can have many previous maps set by NextQuestId in previous quest
4582 // for example set of race quests can lead to single not race specific quest
4583 do
4584 {
4585 Field* fields = result->Fetch();
4586
4587 uint32 questId = fields[0].GetUInt32();
4588 auto itr = _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(new Quest(result))).first;
4589 itr->second->_weakRef = itr->second;
4590 if (itr->second->IsAutoPush())
4591 _questTemplatesAutoPush.push_back(itr->second.get());
4592 } while (result->NextRow());
4593
4594 struct QuestLoaderHelper
4595 {
4596 typedef void(Quest::* QuestLoaderFunction)(Field* fields);
4597
4598 char const* QueryFields;
4599 char const* TableName;
4600 char const* QueryExtra;
4601 char const* TableDesc;
4602 QuestLoaderFunction LoaderFunction;
4603 };
4604
4605 // QuestID needs to be fields[0]
4606 QuestLoaderHelper const QuestLoaderHelpers[] =
4607 {
4608 // 0 1 2 3 4 5 6
4609 { "QuestID, Type1, Type2, Type3, Type4, Type5, Type6", "quest_reward_choice_items", "", "reward choice items", &Quest::LoadRewardChoiceItems },
4610
4611 // 0 1 2 3
4612 { "QuestID, SpellID, PlayerConditionID, Type", "quest_reward_display_spell", "ORDER BY QuestID ASC, Idx ASC", "reward display spells", &Quest::LoadRewardDisplaySpell },
4613
4614 // 0 1 2 3 4 5 6 7 8
4615 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4", "quest_details", "", "details", &Quest::LoadQuestDetails },
4616
4617 // 0 1 2 3 4 5
4618 { "ID, EmoteOnComplete, EmoteOnIncomplete, EmoteOnCompleteDelay, EmoteOnIncompleteDelay, CompletionText", "quest_request_items", "", "request items", &Quest::LoadQuestRequestItems },
4619
4620 // 0 1 2 3 4 5 6 7 8 9
4621 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "", "reward emotes", &Quest::LoadQuestOfferReward },
4622
4623 // 0 1 2 3 4 5 6 7 8 9
4624 { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
4625 // 10 11 12 13 14 15 16 17
4626 " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags,"
4627 // 18
4628 " ScriptName", "quest_template_addon", "", "template addons", &Quest::LoadQuestTemplateAddon },
4629
4630 // 0 1
4631 { "QuestId, RewardMailSenderEntry", "quest_mail_sender", "", "mail sender entries", &Quest::LoadQuestMailSender },
4632
4633 // 0 1 2 3 4 5 6 7 8 9 10 11 12
4634 { "qo.QuestID, qo.ID, qo.Type, qo.StorageIndex, qo.ObjectID, qo.Amount, qo.ConditionalAmount, qo.Flags, qo.Flags2, qo.ProgressBarWeight, qo.ParentObjectiveID, qo.Visible, qo.Description, "
4635 // 13 14 15 16 17
4636 "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 },
4637
4638 // 0 1 2 3 4
4639 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_description_conditional", "ORDER BY OrderIndex", "conditional details", &Quest::LoadConditionalConditionalQuestDescription },
4640
4641 // 0 1 2 3 4
4642 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_request_items_conditional", "ORDER BY OrderIndex", "conditional request items", &Quest::LoadConditionalConditionalRequestItemsText },
4643
4644 // 0 1 2 3 4
4645 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_offer_reward_conditional", "ORDER BY OrderIndex", "conditional reward", &Quest::LoadConditionalConditionalOfferRewardText },
4646
4647 // 0 1 2 3 4
4648 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_completion_log_conditional", "ORDER BY OrderIndex", "conditional completion log", &Quest::LoadConditionalConditionalQuestCompletionLog },
4649
4650 // 0 1
4651 { "QuestID, TreasurePickerID", "quest_treasure_pickers", "ORDER BY OrderIndex", "treasure pickers", &Quest::LoadTreasurePickers },
4652
4653 // 0 1
4654 { "QuestID, HouseRoomID", "quest_reward_house_room", "ORDER BY OrderIndex", "house room rewards", &Quest::LoadRewardHouseRoom },
4655
4656 // 0 1
4657 { "QuestID, HouseDecorID", "quest_reward_house_decor", "ORDER BY OrderIndex", "house decor rewards", &Quest::LoadRewardHouseDecor }
4658 };
4659
4660 for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
4661 {
4662 result = WorldDatabase.PQuery("SELECT {} FROM {} {}", loader.QueryFields, loader.TableName, loader.QueryExtra);
4663
4664 if (!result)
4665 TC_LOG_INFO("server.loading", ">> Loaded 0 quest {}. DB table `{}` is empty.", loader.TableDesc, loader.TableName);
4666 else
4667 {
4668 do
4669 {
4670 Field* fields = result->Fetch();
4671 uint32 questId = fields[0].GetUInt32();
4672
4673 auto itr = _questTemplates.find(questId);
4674 if (itr != _questTemplates.end())
4675 (itr->second.get()->*loader.LoaderFunction)(fields);
4676 else
4677 TC_LOG_ERROR("sql.sql", "Table `{}` has data for quest {} but such quest does not exist", loader.TableName, questId);
4678 } while (result->NextRow());
4679 }
4680 }
4681
4682 // 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)
4683 // 0 1 2 3 4
4684 result = WorldDatabase.Query("SELECT v.ID AS vID, o.ID AS oID, 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");
4685
4686 if (!result)
4687 TC_LOG_INFO("server.loading", ">> Loaded 0 quest visual effects. DB table `quest_visual_effect` is empty.");
4688 else
4689 {
4690 do
4691 {
4692 Field* fields = result->Fetch();
4693 uint32 vID = fields[0].GetUInt32();
4694 uint32 oID = fields[1].GetUInt32();
4695
4696 if (!vID)
4697 {
4698 TC_LOG_ERROR("sql.sql", "Table `quest_visual_effect` has visual effect for null objective id");
4699 continue;
4700 }
4701
4702 // objID will be null if match for table join is not found
4703 if (vID != oID)
4704 {
4705 TC_LOG_ERROR("sql.sql", "Table `quest_visual_effect` has visual effect for objective {} but such objective does not exist.", vID);
4706 continue;
4707 }
4708
4709 uint32 questId = fields[2].GetUInt32();
4710
4711 // Do not throw error here because error for non existing quest is thrown while loading quest objectives. we do not need duplication
4712 auto itr = _questTemplates.find(questId);
4713 if (itr != _questTemplates.end())
4714 itr->second->LoadQuestObjectiveVisualEffect(fields);
4715 } while (result->NextRow());
4716 }
4717
4718 std::map<uint32, uint32> usedMailTemplates;
4719
4720 // Post processing
4721 for (auto& questPair : _questTemplates)
4722 {
4723 // skip post-loading checks for disabled quests
4724 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
4725 continue;
4726
4727 Quest* qinfo = questPair.second.get();
4728
4729 // additional quest integrity checks (GO, creature_template and items must be loaded already)
4730
4732 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestType` = {}, expected values are 0, 1, 2 or 3.", qinfo->GetQuestId(), qinfo->GetQuestType());
4733
4735 {
4736 TC_LOG_ERROR("sql.sql", "Quest {} has `SpecialFlags` = {} > max allowed value. Correct `SpecialFlags` to value <= {}",
4739 }
4740
4741 if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
4742 {
4743 TC_LOG_ERROR("sql.sql", "Weekly Quest {} is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
4744 qinfo->_flags &= ~QUEST_FLAGS_DAILY;
4745 }
4746
4747 if (qinfo->_flags & QUEST_FLAGS_DAILY)
4748 {
4750 {
4751 TC_LOG_DEBUG("sql.sql", "Daily Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4753 }
4754 }
4755
4756 if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
4757 {
4759 {
4760 TC_LOG_DEBUG("sql.sql", "Weekly Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4762 }
4763 }
4764
4766 {
4768 {
4769 TC_LOG_DEBUG("sql.sql", "Monthly quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4771 }
4772 }
4773
4775 {
4776 // at auto-reward can be rewarded only RewardChoiceItemId[0]
4777 for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
4778 {
4779 if (uint32 id = qinfo->RewardChoiceItemId[j])
4780 {
4781 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item from `RewardChoiceItemId{}` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
4782 qinfo->GetQuestId(), j + 1, id, j + 1);
4783 // no changes, quest ignore this data
4784 }
4785 }
4786 }
4787
4788 if (qinfo->_contentTuningID && !sContentTuningStore.LookupEntry(qinfo->_contentTuningID))
4789 {
4790 TC_LOG_ERROR("sql.sql", "Quest {} has `ContentTuningID` = {} but content tuning with this id does not exist.",
4791 qinfo->GetQuestId(), qinfo->_contentTuningID);
4792 }
4793
4794 // client quest log visual (area case)
4795 if (qinfo->_questSortID > 0)
4796 {
4797 if (!sAreaTableStore.LookupEntry(qinfo->_questSortID))
4798 {
4799 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (zone case) but zone with this id does not exist.",
4800 qinfo->GetQuestId(), qinfo->_questSortID);
4801 // no changes, quest not dependent from this value but can have problems at client
4802 }
4803 }
4804 // client quest log visual (sort case)
4805 if (qinfo->_questSortID < 0)
4806 {
4807 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_questSortID));
4808 if (!qSort)
4809 {
4810 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (sort case) but quest sort with this id does not exist.",
4811 qinfo->GetQuestId(), qinfo->_questSortID);
4812 // 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)
4813 }
4814 //check for proper RequiredSkillId value (skill case)
4815 if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->_questSortID)))
4816 {
4817 if (qinfo->_requiredSkillId != skill_id)
4818 {
4819 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} but `RequiredSkillId` does not have a corresponding value ({}).",
4820 qinfo->GetQuestId(), qinfo->_questSortID, skill_id);
4821 //override, and force proper value here?
4822 }
4823 }
4824 }
4825
4826 // AllowableClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
4827 if (qinfo->_allowableClasses)
4828 {
4830 {
4831 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable classes in `AllowableClasses` ({}), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_allowableClasses);
4832 qinfo->_allowableClasses = 0;
4833 }
4834 }
4835 // AllowableRaces, can be -1/RACEMASK_ALL_PLAYABLE to allow any race
4836 if (qinfo->_allowableRaces != RACEMASK_ALL_v<int32, 2>)
4837 {
4838 if (!qinfo->_allowableRaces.IsEmpty() && (qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE_v<int32, 2>).IsEmpty())
4839 {
4840 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` (0x{:X}{:08X}), value set to -1 (all races).",
4841 qinfo->GetQuestId(), qinfo->_allowableRaces.RawValue[1], qinfo->_allowableRaces.RawValue[0]);
4842 qinfo->_allowableRaces = RACEMASK_ALL_v<int32, 2>;
4843 }
4844 }
4845 // RequiredSkillId, can be 0
4846 if (qinfo->_requiredSkillId)
4847 {
4848 if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
4849 {
4850 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillId` = {} but this skill does not exist",
4851 qinfo->GetQuestId(), qinfo->_requiredSkillId);
4852 }
4853 }
4854
4855 if (qinfo->_requiredSkillPoints)
4856 {
4857 if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
4858 {
4859 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillPoints` = {} but max possible skill is {}, quest can't be done.",
4860 qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
4861 // no changes, quest can't be done for this requirement
4862 }
4863 }
4864 // else Skill quests can have 0 skill level, this is ok
4865
4866 if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
4867 {
4868 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4870 // no changes, quest can't be done for this requirement
4871 }
4872
4873 if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
4874 {
4875 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4877 // no changes, quest can't be done for this requirement
4878 }
4879
4881 {
4882 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but max reputation is {}, quest can't be done.",
4884 // no changes, quest can't be done for this requirement
4885 }
4886
4888 {
4889 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} and `RequiredMinRepValue` = {}, quest can't be done.",
4890 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
4891 // no changes, quest can't be done for this requirement
4892 }
4893
4894 if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
4895 {
4896 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but `RequiredMinRepFaction` is 0, value has no effect",
4897 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
4898 // warning
4899 }
4900
4901 if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
4902 {
4903 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} but `RequiredMaxRepFaction` is 0, value has no effect",
4904 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
4905 // warning
4906 }
4907
4908 if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
4909 {
4910 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardTitleId` = {} but CharTitle Id {} does not exist, quest can't be rewarded with title.",
4911 qinfo->GetQuestId(), qinfo->_rewardTitleId, qinfo->_rewardTitleId);
4912 qinfo->_rewardTitleId = 0;
4913 // quest can't reward this title
4914 }
4915
4916 if (qinfo->_sourceItemId)
4917 {
4918 if (!GetItemTemplate(qinfo->_sourceItemId))
4919 {
4920 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = {} but item with entry {} does not exist, quest can't be done.",
4921 qinfo->GetQuestId(), qinfo->_sourceItemId, qinfo->_sourceItemId);
4922 qinfo->_sourceItemId = 0; // quest can't be done for this requirement
4923 }
4924 else if (qinfo->_sourceItemIdCount == 0)
4925 {
4926 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but `ProvidedItemCount` = 0, set to 1 but need fix in DB.",
4927 qinfo->GetQuestId(), qinfo->_sourceItemId);
4928 qinfo->_sourceItemIdCount = 1; // update to 1 for allow quest work for backward compatibility with DB
4929 }
4930 }
4931 else if (qinfo->_sourceItemIdCount > 0)
4932 {
4933 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = 0 but `SourceItemIdCount` = {}, useless value.",
4934 qinfo->GetQuestId(), qinfo->_sourceItemIdCount);
4935 qinfo->_sourceItemIdCount = 0; // no quest work changes in fact
4936 }
4937
4938 if (qinfo->_sourceSpellID)
4939 {
4940 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellID, DIFFICULTY_NONE);
4941 if (!spellInfo)
4942 {
4943 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} doesn't exist, quest can't be done.",
4944 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4945 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4946 }
4947 else if (!SpellMgr::IsSpellValid(spellInfo))
4948 {
4949 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} is broken, quest can't be done.",
4950 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4951 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4952 }
4953 }
4954
4955 for (QuestObjective const& obj : qinfo->GetObjectives())
4956 {
4957 // Store objective for lookup by id
4958 _questObjectives[obj.ID] = &obj;
4959
4960 // Check storage index for objectives which store data
4961 if (obj.IsStoringValue() && obj.StorageIndex < 0)
4962 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid StorageIndex = {} for objective type {}", qinfo->GetQuestId(), obj.ID, obj.StorageIndex, obj.Type);
4963
4964 switch (obj.Type)
4965 {
4967 if (!GetItemTemplate(obj.ObjectID))
4968 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing item entry {}, quest can't be done.",
4969 qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4970 break;
4972 if (!GetCreatureTemplate(obj.ObjectID))
4973 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4974 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4975 break;
4977 if (!GetGameObjectTemplate(obj.ObjectID))
4978 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing gameobject entry {}, quest can't be done.",
4979 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4980 break;
4982 if (!GetCreatureTemplate(obj.ObjectID))
4983 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4984 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4985 break;
4989 if (!sFactionStore.LookupEntry(obj.ObjectID))
4990 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing faction id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4991 break;
4993 if (obj.Amount <= 0)
4994 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid player kills count {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
4995 break;
4999 if (!sCurrencyTypesStore.LookupEntry(obj.ObjectID))
5000 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing currency {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5001 if (obj.Amount <= 0)
5002 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid currency amount {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
5003 break;
5005 if (!sSpellMgr->GetSpellInfo(obj.ObjectID, DIFFICULTY_NONE))
5006 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing spell id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5007 break;
5009 if (obj.ObjectID && !GetCreatureTemplate(obj.ObjectID))
5010 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
5011 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
5012 break;
5014 if (!sBattlePetSpeciesStore.LookupEntry(obj.ObjectID))
5015 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing battlepet species id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5016 break;
5018 if (!sCriteriaTreeStore.LookupEntry(obj.ObjectID))
5019 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing criteria tree id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5020 break;
5022 if (!sAreaTriggerStore.LookupEntry(uint32(obj.ObjectID)) && obj.ObjectID != -1)
5023 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing AreaTrigger.db2 id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5024 break;
5027 if (!sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), false }) && !sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), true }))
5028 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing areatrigger id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5029 break;
5034 break;
5035 default:
5036 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has unhandled type {}", qinfo->GetQuestId(), obj.ID, obj.Type);
5037 break;
5038 }
5039
5040 if (obj.Flags & QUEST_OBJECTIVE_FLAG_SEQUENCED)
5042 }
5043
5044 for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
5045 {
5046 uint32 id = qinfo->ItemDrop[j];
5047 if (id)
5048 {
5049 if (!GetItemTemplate(id))
5050 {
5051 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
5052 qinfo->GetQuestId(), j+1, id, id);
5053 // no changes, quest can't be done for this requirement
5054 }
5055 }
5056 else
5057 {
5058 if (qinfo->ItemDropQuantity[j]>0)
5059 {
5060 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = 0 but `ItemDropQuantity{}` = {}.",
5061 qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
5062 // no changes, quest ignore this data
5063 }
5064 }
5065 }
5066
5067 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
5068 {
5069 if (uint32 id = qinfo->RewardChoiceItemId[j])
5070 {
5071 switch (qinfo->RewardChoiceItemType[j])
5072 {
5073 case LootItemType::Item:
5074 if (!GetItemTemplate(id))
5075 {
5076 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5077 qinfo->GetQuestId(), j + 1, id, id);
5078 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5079 }
5080 break;
5082 if (!sCurrencyTypesStore.HasRecord(id))
5083 {
5084 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but currency with id {} does not exist, quest will not reward this currency.",
5085 qinfo->GetQuestId(), j + 1, id, id);
5086 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5087 }
5088 break;
5089 default:
5090 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemType{}` = {} but it is not a valid item type, reward removed.",
5091 qinfo->GetQuestId(), j + 1, uint32(qinfo->RewardChoiceItemType[j]));
5092 qinfo->RewardChoiceItemId[j] = 0;
5093 break;
5094 }
5095
5096 if (!qinfo->RewardChoiceItemCount[j])
5097 {
5098 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but `RewardChoiceItemCount{}` = 0.",
5099 qinfo->GetQuestId(), j + 1, id, j + 1);
5100 }
5101 }
5102 else if (qinfo->RewardChoiceItemCount[j] > 0)
5103 {
5104 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = 0 but `RewardChoiceItemCount{}` = {}.",
5105 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardChoiceItemCount[j]);
5106 // no changes, quest ignore this data
5107 }
5108 }
5109
5110 for (uint8 j = 0; j < QUEST_REWARD_ITEM_COUNT; ++j)
5111 {
5112 uint32 id = qinfo->RewardItemId[j];
5113 if (id)
5114 {
5115 if (!GetItemTemplate(id))
5116 {
5117 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5118 qinfo->GetQuestId(), j+1, id, id);
5119 qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
5120 }
5121
5122 if (!qinfo->RewardItemCount[j])
5123 {
5124 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but `RewardItemCount{}` = 0, quest will not reward this item.",
5125 qinfo->GetQuestId(), j+1, id, j+1);
5126 // no changes
5127 }
5128 }
5129 else if (qinfo->RewardItemCount[j]>0)
5130 {
5131 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = 0 but `RewardItemCount{}` = {}.",
5132 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemCount[j]);
5133 // no changes, quest ignore this data
5134 }
5135 }
5136
5137 for (uint8 j = 0; j < QUEST_REWARD_REPUTATIONS_COUNT; ++j)
5138 {
5139 if (qinfo->RewardFactionId[j])
5140 {
5141 if (std::abs(qinfo->RewardFactionValue[j]) > 9)
5142 {
5143 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]);
5144 }
5145 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
5146 {
5147 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]);
5148 qinfo->RewardFactionId[j] = 0; // quest will not reward this
5149 }
5150 }
5151
5152 else if (qinfo->RewardFactionOverride[j] != 0)
5153 {
5154 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = 0 but `RewardFactionValueIdOverride{}` = {}.",
5155 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionOverride[j]);
5156 // no changes, quest ignore this data
5157 }
5158 }
5159
5160 if (qinfo->_rewardSpell > 0)
5161 {
5162 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell, DIFFICULTY_NONE);
5163
5164 if (!spellInfo)
5165 {
5166 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} does not exist, quest will not have a spell reward.",
5167 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5168 qinfo->_rewardSpell = 0; // no spell will be cast on player
5169 }
5170
5171 else if (!SpellMgr::IsSpellValid(spellInfo))
5172 {
5173 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} is broken, quest will not have a spell reward.",
5174 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5175 qinfo->_rewardSpell = 0; // no spell will be cast on player
5176 }
5177 }
5178
5179 if (qinfo->_rewardMailTemplateId)
5180 {
5181 if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
5182 {
5183 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} does not exist, quest will not have a mail reward.",
5184 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
5185 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5186 qinfo->_rewardMailDelay = 0; // no mail will send to player
5187 qinfo->_rewardMailSenderEntry = 0;
5188 }
5189 else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
5190 {
5191 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
5192 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} already used for quest {}, quest will not have a mail reward.",
5193 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
5194 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5195 qinfo->_rewardMailDelay = 0; // no mail will send to player
5196 qinfo->_rewardMailSenderEntry = 0;
5197 }
5198 else
5199 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
5200 }
5201
5202 if (uint32 rewardNextQuest = qinfo->_rewardNextQuest)
5203 {
5204 if (!_questTemplates.count(rewardNextQuest))
5205 {
5206 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardNextQuest` = {} but quest {} does not exist, quest chain will not work.",
5207 qinfo->GetQuestId(), qinfo->_rewardNextQuest, qinfo->_rewardNextQuest);
5208 qinfo->_rewardNextQuest = 0;
5209 }
5210 }
5211
5212 for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
5213 {
5214 if (qinfo->RewardCurrencyId[j])
5215 {
5216 if (qinfo->RewardCurrencyCount[j] == 0)
5217 {
5218 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but `RewardCurrencyCount{}` = 0, quest can't be done.",
5219 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], j+1);
5220 // no changes, quest can't be done for this requirement
5221 }
5222
5223 if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j]))
5224 {
5225 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but currency with entry {} does not exist, quest can't be done.",
5226 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]);
5227 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5228 }
5229 }
5230 else if (qinfo->RewardCurrencyCount[j] > 0)
5231 {
5232 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = 0 but `RewardCurrencyCount{}` = {}, quest can't be done.",
5233 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardCurrencyCount[j]);
5234 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5235 }
5236 }
5237
5238 if (qinfo->_soundAccept)
5239 {
5240 if (!sSoundKitStore.LookupEntry(qinfo->_soundAccept))
5241 {
5242 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundAccept` = {} but sound {} does not exist, set to 0.",
5243 qinfo->GetQuestId(), qinfo->_soundAccept, qinfo->_soundAccept);
5244 qinfo->_soundAccept = 0; // no sound will be played
5245 }
5246 }
5247
5248 if (qinfo->_soundTurnIn)
5249 {
5250 if (!sSoundKitStore.LookupEntry(qinfo->_soundTurnIn))
5251 {
5252 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundTurnIn` = {} but sound {} does not exist, set to 0.",
5253 qinfo->GetQuestId(), qinfo->_soundTurnIn, qinfo->_soundTurnIn);
5254 qinfo->_soundTurnIn = 0; // no sound will be played
5255 }
5256 }
5257
5258 if (qinfo->_rewardSkillId)
5259 {
5260 if (!sSkillLineStore.LookupEntry(qinfo->_rewardSkillId))
5261 {
5262 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but this skill does not exist",
5263 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5264 }
5265 if (!qinfo->_rewardSkillPoints)
5266 {
5267 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but `RewardSkillPoints` is 0",
5268 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5269 }
5270 }
5271
5272 if (qinfo->_rewardSkillPoints)
5273 {
5274 if (qinfo->_rewardSkillPoints > sWorld->GetConfigMaxSkillValue())
5275 {
5276 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but max possible skill is {}, quest can't be done.",
5277 qinfo->GetQuestId(), qinfo->_rewardSkillPoints, sWorld->GetConfigMaxSkillValue());
5278 // no changes, quest can't be done for this requirement
5279 }
5280 if (!qinfo->_rewardSkillId)
5281 {
5282 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but `RewardSkillId` is 0",
5283 qinfo->GetQuestId(), qinfo->_rewardSkillPoints);
5284 }
5285 }
5286
5287 // fill additional data stores
5288 if (uint32 prevQuestId = std::abs(qinfo->_prevQuestID))
5289 {
5290 auto prevQuestItr = _questTemplates.find(prevQuestId);
5291 if (prevQuestItr == _questTemplates.end())
5292 TC_LOG_ERROR("sql.sql", "Quest {} has PrevQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
5293 else if (prevQuestItr->second->_breadcrumbForQuestId)
5294 TC_LOG_ERROR("sql.sql", "Quest {} should not be unlocked by breadcrumb quest {}", qinfo->_id, prevQuestId);
5295 else if (qinfo->_prevQuestID > 0)
5296 qinfo->DependentPreviousQuests.push_back(prevQuestId);
5297 }
5298
5299 if (uint32 nextQuestId = qinfo->_nextQuestID)
5300 {
5301 auto nextQuestItr = _questTemplates.find(nextQuestId);
5302 if (nextQuestItr == _questTemplates.end())
5303 TC_LOG_ERROR("sql.sql", "Quest {} has NextQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestID);
5304 else
5305 nextQuestItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId());
5306 }
5307
5308 if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
5309 {
5310 if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
5311 {
5312 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb for quest {}, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
5313 qinfo->_breadcrumbForQuestId = 0;
5314 }
5315 if (qinfo->_nextQuestID)
5316 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb, should not unlock quest {}", qinfo->_id, qinfo->_nextQuestID);
5317 }
5318
5319 if (qinfo->_exclusiveGroup)
5320 _exclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->_exclusiveGroup, qinfo->GetQuestId()));
5321 }
5322
5323 // Disallow any breadcrumb loops and inform quests of their breadcrumbs
5324 for (auto& questPair : _questTemplates)
5325 {
5326 // skip post-loading checks for disabled quests
5327 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
5328 continue;
5329
5330 Quest* qinfo = questPair.second.get();
5331 uint32 qid = qinfo->GetQuestId();
5332 uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
5333 std::set<uint32> questSet;
5334
5335 while(breadcrumbForQuestId)
5336 {
5337 //a previously visited quest was found as a breadcrumb quest
5338 //breadcrumb loop found!
5339 if (!questSet.insert(qinfo->_id).second)
5340 {
5341 TC_LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} are in a loop", qid, breadcrumbForQuestId);
5342 qinfo->_breadcrumbForQuestId = 0;
5343 break;
5344 }
5345
5346 qinfo = const_cast<Quest*>(GetQuestTemplate(breadcrumbForQuestId));
5347
5348 //every quest has a list of every breadcrumb towards it
5349 qinfo->DependentBreadcrumbQuests.push_back(qid);
5350
5351 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
5352 }
5353 }
5354
5355 // don't check spells with SPELL_EFFECT_QUEST_COMPLETE, a lot of invalid db2 data
5356
5357 // Make all paragon reward quests repeatable
5358 for (ParagonReputationEntry const* paragonReputation : sParagonReputationStore)
5359 if (Quest const* quest = GetQuestTemplate(paragonReputation->QuestID))
5361
5362 TC_LOG_INFO("server.loading", ">> Loaded {} quests definitions in {} ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
5363}
5364
5366{
5367 TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
5369 TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
5371 TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
5373 TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
5375}
5376
5378{
5379 uint32 oldMSTime = getMSTime();
5380
5381 _questTemplateLocaleStore.clear(); // need for reload case
5382 // 0 1
5383 QueryResult result = WorldDatabase.Query("SELECT Id, locale, "
5384 // 2 3 4 5 6 7 8 9 10
5385 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog"
5386 " FROM quest_template_locale");
5387 if (!result)
5388 return;
5389
5390 do
5391 {
5392 Field* fields = result->Fetch();
5393
5394 uint32 id = fields[0].GetUInt32();
5395 std::string_view localeName = fields[1].GetStringView();
5396
5397 LocaleConstant locale = GetLocaleByName(localeName);
5398 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5399 continue;
5400
5402 AddLocaleString(fields[2].GetStringView(), locale, data.LogTitle);
5403 AddLocaleString(fields[3].GetStringView(), locale, data.LogDescription);
5404 AddLocaleString(fields[4].GetStringView(), locale, data.QuestDescription);
5405 AddLocaleString(fields[5].GetStringView(), locale, data.AreaDescription);
5406 AddLocaleString(fields[6].GetStringView(), locale, data.PortraitGiverText);
5407 AddLocaleString(fields[7].GetStringView(), locale, data.PortraitGiverName);
5408 AddLocaleString(fields[8].GetStringView(), locale, data.PortraitTurnInText);
5409 AddLocaleString(fields[9].GetStringView(), locale, data.PortraitTurnInName);
5410 AddLocaleString(fields[10].GetStringView(), locale, data.QuestCompletionLog);
5411 } while (result->NextRow());
5412
5413 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Template locale strings in {} ms", _questTemplateLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5414}
5415
5417{
5418 uint32 oldMSTime = getMSTime();
5419
5420 _questObjectivesLocaleStore.clear(); // need for reload case
5421 // 0 1 2
5422 QueryResult result = WorldDatabase.Query("SELECT Id, locale, Description FROM quest_objectives_locale");
5423 if (!result)
5424 return;
5425
5426 do
5427 {
5428 Field* fields = result->Fetch();
5429
5430 uint32 id = fields[0].GetUInt32();
5431 std::string_view localeName = fields[1].GetStringView();
5432
5433 LocaleConstant locale = GetLocaleByName(localeName);
5434 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5435 continue;
5436
5438 AddLocaleString(fields[2].GetStringView(), locale, data.Description);
5439 }
5440 while (result->NextRow());
5441
5442 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Objectives locale strings in {} ms", _questObjectivesLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5443}
5444
5446{
5447 uint32 oldMSTime = getMSTime();
5448
5449 for (std::size_t i = 0; i < _questGreetingLocaleStore.size(); ++i)
5450 _questGreetingLocaleStore[i].clear();
5451
5452 // 0 1 2 3
5453 QueryResult result = WorldDatabase.Query("SELECT Id, type, locale, Greeting FROM quest_greeting_locale");
5454 if (!result)
5455 return;
5456
5457 uint32 count = 0;
5458 do
5459 {
5460 Field* fields = result->Fetch();
5461
5462 uint32 id = fields[0].GetUInt32();
5463 uint8 type = fields[1].GetUInt8();
5464 switch (type)
5465 {
5466 case 0: // Creature
5467 if (!GetCreatureTemplate(id))
5468 {
5469 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id);
5470 continue;
5471 }
5472 break;
5473 case 1: // GameObject
5474 if (!GetGameObjectTemplate(id))
5475 {
5476 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id);
5477 continue;
5478 }
5479 break;
5480 default:
5481 continue;
5482 }
5483
5484 std::string_view localeName = fields[2].GetStringView();
5485
5486 LocaleConstant locale = GetLocaleByName(localeName);
5487 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5488 continue;
5489
5491 AddLocaleString(fields[3].GetStringView(), locale, data.Greeting);
5492 ++count;
5493 }
5494 while (result->NextRow());
5495
5496 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Greeting locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5497}
5498
5500{
5501 uint32 oldMSTime = getMSTime();
5502
5503 _questOfferRewardLocaleStore.clear(); // need for reload case
5504 // 0 1 2
5505 QueryResult result = WorldDatabase.Query("SELECT Id, locale, RewardText FROM quest_offer_reward_locale");
5506 if (!result)
5507 return;
5508
5509 do
5510 {
5511 Field* fields = result->Fetch();
5512
5513 uint32 id = fields[0].GetUInt32();
5514 std::string_view localeName = fields[1].GetStringView();
5515
5516 LocaleConstant locale = GetLocaleByName(localeName);
5517 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5518 continue;
5519
5521 AddLocaleString(fields[2].GetStringView(), locale, data.RewardText);
5522 } while (result->NextRow());
5523
5524 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Offer Reward locale strings in {} ms", _questOfferRewardLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5525}
5526
5528{
5529 uint32 oldMSTime = getMSTime();
5530
5531 _questRequestItemsLocaleStore.clear(); // need for reload case
5532 // 0 1 2
5533 QueryResult result = WorldDatabase.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale");
5534 if (!result)
5535 return;
5536
5537 do
5538 {
5539 Field* fields = result->Fetch();
5540
5541 uint32 id = fields[0].GetUInt32();
5542 std::string_view localeName = fields[1].GetStringView();
5543
5544 LocaleConstant locale = GetLocaleByName(localeName);
5545 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5546 continue;
5547
5549 AddLocaleString(fields[2].GetStringView(), locale, data.CompletionText);
5550 } while (result->NextRow());
5551
5552 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Request Items locale strings in {} ms", _questRequestItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5553}
5554
5556{
5557 uint32 oldMSTime = getMSTime();
5558
5559 ScriptMapMap* scripts = GetScriptsMapByType(type);
5560 if (!scripts)
5561 return;
5562
5563 std::string tableName = GetScriptsTableNameByType(type);
5564 if (tableName.empty())
5565 return;
5566
5567 if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
5568 return;
5569
5570 TC_LOG_INFO("server.loading", "Loading {}...", tableName);
5571
5572 scripts->clear(); // need for reload support
5573
5574 // 0 1 2 3 4 5 6 7 8 9
5575 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o FROM {}", tableName);
5576
5577 if (!result)
5578 {
5579 TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `{}` is empty!", tableName);
5580 return;
5581 }
5582
5583 uint32 count = 0;
5584
5585 do
5586 {
5587 Field* fields = result->Fetch();
5588 ScriptInfo tmp;
5589 tmp.type = type;
5590 tmp.id = fields[0].GetUInt32();
5591 tmp.delay = fields[1].GetUInt32();
5592 tmp.command = ScriptCommands(fields[2].GetUInt32());
5593 tmp.Raw.nData[0] = fields[3].GetUInt32();
5594 tmp.Raw.nData[1] = fields[4].GetUInt32();
5595 tmp.Raw.nData[2] = fields[5].GetInt32();
5596 tmp.Raw.fData[0] = fields[6].GetFloat();
5597 tmp.Raw.fData[1] = fields[7].GetFloat();
5598 tmp.Raw.fData[2] = fields[8].GetFloat();
5599 tmp.Raw.fData[3] = fields[9].GetFloat();
5600
5601 // generic command args check
5602 switch (tmp.command)
5603 {
5605 {
5607 {
5608 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk type (datalong = {}) in SCRIPT_COMMAND_TALK for script id {}",
5609 tableName, tmp.Talk.ChatType, tmp.id);
5610 continue;
5611 }
5612 if (!sBroadcastTextStore.LookupEntry(uint32(tmp.Talk.TextID)))
5613 {
5614 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk text id (dataint = {}) in SCRIPT_COMMAND_TALK for script id {}",
5615 tableName, tmp.Talk.TextID, tmp.id);
5616 continue;
5617 }
5618
5619 break;
5620 }
5621
5623 {
5624 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
5625 {
5626 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid emote id (datalong = {}) in SCRIPT_COMMAND_EMOTE for script id {}",
5627 tableName, tmp.Emote.EmoteID, tmp.id);
5628 continue;
5629 }
5630 break;
5631 }
5632
5634 {
5635 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
5636 {
5637 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid map (Id: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5638 tableName, tmp.TeleportTo.MapID, tmp.id);
5639 continue;
5640 }
5641
5643 {
5644 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5645 tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
5646 continue;
5647 }
5648 break;
5649 }
5650
5652 {
5653 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
5654 if (!quest)
5655 {
5656 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5657 tableName, tmp.QuestExplored.QuestID, tmp.id);
5658 continue;
5659 }
5660
5662 {
5663 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.",
5664 tableName, tmp.QuestExplored.QuestID, tmp.id);
5665 continue;
5666 }
5667
5669 {
5670 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 {}",
5671 tableName, tmp.QuestExplored.Distance, tmp.id);
5672 continue;
5673 }
5674
5676 {
5677 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",
5679 continue;
5680 }
5681
5683 {
5684 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",
5685 tableName, tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
5686 continue;
5687 }
5688
5689 break;
5690 }
5691
5693 {
5695 {
5696 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_KILL_CREDIT for script id {}",
5697 tableName, tmp.KillCredit.CreatureEntry, tmp.id);
5698 continue;
5699 }
5700 break;
5701 }
5702
5704 {
5706 if (!data)
5707 {
5708 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5709 tableName, tmp.RespawnGameobject.GOGuid, tmp.id);
5710 continue;
5711 }
5712
5713 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5714 if (!info)
5715 {
5716 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5717 tableName, tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
5718 continue;
5719 }
5720
5721 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
5723 info->type == GAMEOBJECT_TYPE_DOOR ||
5724 info->type == GAMEOBJECT_TYPE_BUTTON ||
5725 info->type == GAMEOBJECT_TYPE_TRAP)
5726 {
5727 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5728 tableName, info->entry, tmp.id);
5729 continue;
5730 }
5731 break;
5732 }
5733
5735 {
5737 {
5738 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5740 continue;
5741 }
5742
5744 {
5745 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5746 tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id);
5747 continue;
5748 }
5749 break;
5750 }
5751
5754 {
5756 if (!data)
5757 {
5758 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in {} for script id {}",
5759 tableName, tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command), tmp.id);
5760 continue;
5761 }
5762
5763 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5764 if (!info)
5765 {
5766 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in {} for script id {}",
5767 tableName, tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command), tmp.id);
5768 continue;
5769 }
5770
5771 if (info->type != GAMEOBJECT_TYPE_DOOR)
5772 {
5773 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command {} for script id {}",
5774 tableName, info->entry, GetScriptCommandName(tmp.command), tmp.id);
5775 continue;
5776 }
5777
5778 break;
5779 }
5780
5782 {
5783 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID, DIFFICULTY_NONE))
5784 {
5785 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5786 tableName, tmp.RemoveAura.SpellID, tmp.id);
5787 continue;
5788 }
5789 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
5790 {
5791 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in datalong2 ({}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5792 tableName, tmp.RemoveAura.Flags, tmp.id);
5793 continue;
5794 }
5795 break;
5796 }
5797
5799 {
5800 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID, DIFFICULTY_NONE))
5801 {
5802 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5803 tableName, tmp.CastSpell.SpellID, tmp.id);
5804 continue;
5805 }
5806 if (tmp.CastSpell.Flags > 4) // targeting type
5807 {
5808 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown target in datalong2 ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5809 tableName, tmp.CastSpell.Flags, tmp.id);
5810 continue;
5811 }
5812 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
5813 {
5814 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5815 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5816 continue;
5817 }
5818 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
5819 {
5820 TC_LOG_ERROR("sql.sql", "Table `{}` using invalid creature entry in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5821 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5822 continue;
5823 }
5824 break;
5825 }
5826
5828 {
5830 {
5831 TC_LOG_ERROR("sql.sql", "Table `{}` has nonexistent item (entry: {}) in SCRIPT_COMMAND_CREATE_ITEM for script id {}",
5832 tableName, tmp.CreateItem.ItemEntry, tmp.id);
5833 continue;
5834 }
5835 if (!tmp.CreateItem.Amount)
5836 {
5837 TC_LOG_ERROR("sql.sql", "Table `{}` SCRIPT_COMMAND_CREATE_ITEM but amount is {} for script id {}",
5838 tableName, tmp.CreateItem.Amount, tmp.id);
5839 continue;
5840 }
5841 break;
5842 }
5844 {
5845 if (!sAnimKitStore.LookupEntry(tmp.PlayAnimKit.AnimKitID))
5846 {
5847 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid AnimKid id (datalong = {}) in SCRIPT_COMMAND_PLAY_ANIMKIT for script id {}",
5848 tableName, tmp.PlayAnimKit.AnimKitID, tmp.id);
5849 continue;
5850 }
5851 break;
5852 }
5856 {
5857 TC_LOG_ERROR("sql.sql", "Table `{}` uses deprecated direct updatefield modify command {} for script id {}", tableName, GetScriptCommandName(tmp.command), tmp.id);
5858 continue;
5859 }
5860 default:
5861 break;
5862 }
5863
5864 if (scripts->find(tmp.id) == scripts->end())
5865 {
5866 ScriptMap emptyMap;
5867 (*scripts)[tmp.id] = emptyMap;
5868 }
5869 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
5870
5871 ++count;
5872 }
5873 while (result->NextRow());
5874
5875 TC_LOG_INFO("server.loading", ">> Loaded {} script definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5876}
5877
5879{
5880 _eventStore.clear();
5881
5882 // Load all possible event ids from gameobjects
5883 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
5884 {
5885 EventContainer eventSet = gameObjectTemplatePair.second.GetEventScriptSet();
5886 _eventStore.insert(eventSet.begin(), eventSet.end());
5887 }
5888
5889 // Load all possible event ids from spells
5890 for (SpellEffectEntry const* spellEffect : sSpellEffectStore)
5891 if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT && spellEffect->EffectMiscValue[0])
5892 _eventStore.insert(spellEffect->EffectMiscValue[0]);
5893
5894 // Load all possible event ids from taxi path nodes
5895 for (TaxiPathNodeEntry const* node : sTaxiPathNodeStore)
5896 {
5897 if (node->ArrivalEventID)
5898 _eventStore.insert(node->ArrivalEventID);
5899
5900 if (node->DepartureEventID)
5901 _eventStore.insert(node->DepartureEventID);
5902 }
5903
5904 // Load all possible event ids from criterias
5905 for (CriteriaEntry const* criteria : sCriteriaStore)
5906 {
5907 switch (CriteriaType(criteria->Type))
5908 {
5911 if (criteria->Asset.EventID)
5912 _eventStore.insert(criteria->Asset.EventID);
5913 break;
5914 default:
5915 break;
5916 }
5917
5918 if (CriteriaStartEvent(criteria->StartEvent) == CriteriaStartEvent::SendEvent && criteria->StartAsset)
5919 _eventStore.insert(criteria->StartAsset);
5920
5921 if (CriteriaFailEvent(criteria->FailEvent) == CriteriaFailEvent::SendEvent && criteria->FailAsset)
5922 _eventStore.insert(criteria->FailAsset);
5923 }
5924}
5925
5927{
5928 // Set of valid events referenced in several sources
5929 LoadEventSet();
5930
5931 // Deprecated
5933
5934 // Then check if all scripts are in above list of possible script entries
5935 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
5936 {
5937 if (!IsValidEvent(itr->first))
5938 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 {}",
5939 itr->first, SPELL_EFFECT_SEND_EVENT);
5940 }
5941
5942 uint32 oldMSTime = getMSTime();
5943
5944 _eventScriptStore.clear(); // Reload case
5945
5946 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM event_script_names");
5947 if (!result)
5948 {
5949 TC_LOG_INFO("server.loading", ">> Loaded 0 event scripts. DB table `event_script_names` is empty.");
5950 return;
5951 }
5952
5953 do
5954 {
5955 Field* fields = result->Fetch();
5956
5957 uint32 eventId = fields[0].GetUInt32();
5958 std::string_view scriptName = fields[1].GetStringView();
5959
5960 if (!IsValidEvent(eventId))
5961 {
5962 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 {}",
5963 eventId, SPELL_EFFECT_SEND_EVENT);
5964 continue;
5965 }
5966 _eventScriptStore[eventId] = GetScriptId(scriptName);
5967 } while (result->NextRow());
5968
5969 TC_LOG_INFO("server.loading", ">> Loaded {} event scripts in {} ms", _eventScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
5970}
5971
5973{
5974 uint32 oldMSTime = getMSTime();
5975
5976 _spellScriptsStore.clear(); // need for reload case
5977
5978 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
5979
5980 if (!result)
5981 {
5982 TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
5983 return;
5984 }
5985
5986 uint32 count = 0;
5987
5988 do
5989 {
5990
5991 Field* fields = result->Fetch();
5992
5993 int32 spellId = fields[0].GetInt32();
5994 std::string_view scriptName = fields[1].GetStringView();
5995
5996 bool allRanks = false;
5997 if (spellId < 0)
5998 {
5999 allRanks = true;
6000 spellId = -spellId;
6001 }
6002
6003 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
6004 if (!spellInfo)
6005 {
6006 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) does not exist.", scriptName, fields[0].GetInt32());
6007 continue;
6008 }
6009
6010 if (allRanks)
6011 {
6012 if (!spellInfo->IsRanked())
6013 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) has no ranks of spell.", scriptName, fields[0].GetInt32());
6014
6015 if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
6016 {
6017 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is not first rank of spell.", scriptName, fields[0].GetInt32());
6018 continue;
6019 }
6020
6021 while (spellInfo)
6022 {
6023 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6024 spellInfo = spellInfo->GetNextRankSpell();
6025 }
6026 }
6027 else
6028 {
6029 if (spellInfo->IsRanked())
6030 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
6031
6032 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6033 }
6034
6035 ++count;
6036 }
6037 while (result->NextRow());
6038
6039 TC_LOG_INFO("server.loading", ">> Loaded {} spell script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6040}
6041
6043{
6044 uint32 oldMSTime = getMSTime();
6045
6046 if (_spellScriptsStore.empty())
6047 {
6048 TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
6049 return;
6050 }
6051
6052 uint32 count = 0;
6053
6054 for (auto& spell : _spellScriptsStore)
6055 {
6056 SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spell.first, DIFFICULTY_NONE);
6057
6058 if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(spell.second.first))
6059 {
6060 ++count;
6061
6062 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
6063 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
6064
6065 if (!spellScript && !auraScript)
6066 {
6067 TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `{}` do not return objects - script skipped", GetScriptName(spell.second.first));
6068
6069 spell.second.second = false;
6070 continue;
6071 }
6072
6073 if (spellScript)
6074 {
6075 spellScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6076 spellScript->_Register();
6077
6078 if (!spellScript->_Validate(spellEntry))
6079 {
6080 spell.second.second = false;
6081 continue;
6082 }
6083 }
6084
6085 if (auraScript)
6086 {
6087 auraScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6088 auraScript->_Register();
6089
6090 if (!auraScript->_Validate(spellEntry))
6091 {
6092 spell.second.second = false;
6093 continue;
6094 }
6095 }
6096
6097 // Enable the script when all checks passed
6098 spell.second.second = true;
6099 }
6100 else
6101 spell.second.second = false;
6102 }
6103
6104 TC_LOG_INFO("server.loading", ">> Validated {} scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6105}
6106
6108{
6109 uint32 oldMSTime = getMSTime();
6110
6111 // 0 1 2 3 4
6112 QueryResult result = WorldDatabase.Query("SELECT ID, `Text`, NextPageID, PlayerConditionID, Flags FROM page_text");
6113 if (!result)
6114 {
6115 TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
6116 return;
6117 }
6118
6119 uint32 count = 0;
6120 do
6121 {
6122 Field* fields = result->Fetch();
6123
6124 uint32 id = fields[0].GetUInt32();
6125
6126 PageText& pageText = _pageTextStore[id];
6127 pageText.Text = fields[1].GetString();
6128 pageText.NextPageID = fields[2].GetUInt32();
6129 pageText.PlayerConditionID = fields[3].GetInt32();
6130 pageText.Flags = fields[4].GetUInt8();
6131
6132 ++count;
6133 }
6134 while (result->NextRow());
6135
6136 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
6137 if (itr->second.NextPageID)
6138 if (_pageTextStore.find(itr->second.NextPageID) == _pageTextStore.end())
6139 TC_LOG_ERROR("sql.sql", "Page text (ID: {}) has non-existing `NextPageID` ({})", itr->first, itr->second.NextPageID);
6140
6141 TC_LOG_INFO("server.loading", ">> Loaded {} page texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6142}
6143
6145{
6146 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
6147 if (itr != _pageTextStore.end())
6148 return &(itr->second);
6149
6150 return nullptr;
6151}
6152
6154{
6155 uint32 oldMSTime = getMSTime();
6156
6157 _pageTextLocaleStore.clear(); // needed for reload case
6158
6159 // 0 1 2
6160 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text` FROM page_text_locale");
6161 if (!result)
6162 return;
6163
6164 do
6165 {
6166 Field* fields = result->Fetch();
6167
6168 uint32 id = fields[0].GetUInt32();
6169 std::string_view localeName = fields[1].GetStringView();
6170
6171 LocaleConstant locale = GetLocaleByName(localeName);
6172 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
6173 continue;
6174
6176 AddLocaleString(fields[2].GetStringView(), locale, data.Text);
6177 } while (result->NextRow());
6178
6179 TC_LOG_INFO("server.loading", ">> Loaded {} PageText locale strings in {} ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6180}
6181
6183{
6184 uint32 oldMSTime = getMSTime();
6185
6186 // 0 1 2
6187 QueryResult result = WorldDatabase.Query("SELECT map, parent, script FROM instance_template");
6188
6189 if (!result)
6190 {
6191 TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
6192 return;
6193 }
6194
6195 uint32 count = 0;
6196 do
6197 {
6198 Field* fields = result->Fetch();
6199
6200 uint16 mapID = fields[0].GetUInt16();
6201
6202 if (!MapManager::IsValidMAP(mapID))
6203 {
6204 TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid {} for template!", mapID);
6205 continue;
6206 }
6207
6208 InstanceTemplate instanceTemplate;
6209
6210 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
6211 instanceTemplate.ScriptId = GetScriptId(fields[2].GetStringView());
6212
6213 _instanceTemplateStore[mapID] = instanceTemplate;
6214
6215 ++count;
6216 }
6217 while (result->NextRow());
6218
6219 TC_LOG_INFO("server.loading", ">> Loaded {} instance templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6220}
6221
6223{
6224 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
6225 if (itr != _instanceTemplateStore.end())
6226 return &(itr->second);
6227
6228 return nullptr;
6229}
6230
6232{
6233 NpcTextContainer::const_iterator itr = _npcTextStore.find(Text_ID);
6234 if (itr != _npcTextStore.end())
6235 return &itr->second;
6236 return nullptr;
6237}
6238
6240{
6241 uint32 oldMSTime = getMSTime();
6242
6243 QueryResult result = WorldDatabase.Query("SELECT ID, "
6244 "Probability0, Probability1, Probability2, Probability3, Probability4, Probability5, Probability6, Probability7, "
6245 "BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7"
6246 " FROM npc_text");
6247 if (!result)
6248 {
6249 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6250 return;
6251 }
6252
6253 _npcTextStore.reserve(result->GetRowCount());
6254
6255 do
6256 {
6257 Field* fields = result->Fetch();
6258
6259 uint32 textID = fields[0].GetUInt32();
6260 if (!textID)
6261 {
6262 TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
6263 continue;
6264 }
6265
6266 NpcText npcText;
6267
6268 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; ++i)
6269 {
6270 npcText.Data[i].Probability = fields[1 + i].GetFloat();
6271 npcText.Data[i].BroadcastTextID = fields[9 + i].GetUInt32();
6272 }
6273
6274 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6275 {
6276 if (npcText.Data[i].BroadcastTextID)
6277 {
6278 if (!sBroadcastTextStore.LookupEntry(npcText.Data[i].BroadcastTextID))
6279 {
6280 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a non-existing BroadcastText (ID: {}, Index: {})", textID, npcText.Data[i].BroadcastTextID, i);
6281 npcText.Data[i].Probability = 0.0f;
6282 npcText.Data[i].BroadcastTextID = 0;
6283 }
6284 }
6285 }
6286
6287 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6288 {
6289 if (npcText.Data[i].Probability > 0 && npcText.Data[i].BroadcastTextID == 0)
6290 {
6291 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability (Index: {}) set, but no BroadcastTextID to go with it", textID, i);
6292 npcText.Data[i].Probability = 0;
6293 }
6294 }
6295
6296 float probabilitySum = std::accumulate(std::begin(npcText.Data), std::end(npcText.Data), 0.0f, [](float sum, NpcTextData const& data) { return sum + data.Probability; });
6297 if (probabilitySum <= 0.0f)
6298 {
6299 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability sum 0, no text can be selected from it, skipped.", textID);
6300 continue;
6301 }
6302
6303 _npcTextStore[textID] = npcText;
6304 }
6305 while (result->NextRow());
6306
6307 TC_LOG_INFO("server.loading", ">> Loaded {} npc texts in {} ms", uint32(_npcTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
6308}
6309
6310//not very fast function but it is called only once a day, or on starting-up
6312{
6313 uint32 oldMSTime = getMSTime();
6314
6315 time_t curTime = GameTime::GetGameTime();
6316 tm lt;
6317 localtime_r(&curTime, &lt);
6318 TC_LOG_INFO("misc", "Returning mails current time: hour: {}, minute: {}, second: {} ", lt.tm_hour, lt.tm_min, lt.tm_sec);
6319
6320 // Delete all old mails without item and without body immediately, if starting server
6321 if (!serverUp)
6322 {
6324 stmt->setInt64(0, curTime);
6325 CharacterDatabase.Execute(stmt);
6326 }
6328 stmt->setInt64(0, curTime);
6329 PreparedQueryResult result = CharacterDatabase.Query(stmt);
6330 if (!result)
6331 {
6332 TC_LOG_INFO("server.loading", ">> No expired mails found.");
6333 return; // any mails need to be returned or deleted
6334 }
6335
6336 std::map<uint64 /*messageId*/, MailItemInfoVec> itemsCache;
6337 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
6338 stmt->setUInt32(0, curTime);
6339 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
6340 {
6341 MailItemInfo item;
6342 do
6343 {
6344 Field* fields = items->Fetch();
6345 item.item_guid = fields[0].GetUInt64();
6346 item.item_template = fields[1].GetUInt32();
6347 uint64 mailId = fields[2].GetUInt64();
6348 itemsCache[mailId].push_back(item);
6349 } while (items->NextRow());
6350 }
6351
6352 uint32 deletedCount = 0;
6353 uint32 returnedCount = 0;
6354 do
6355 {
6356 Field* fields = result->Fetch();
6357 ObjectGuid::LowType receiver = fields[3].GetUInt64();
6358 if (serverUp && ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(receiver)))
6359 continue;
6360
6361 Mail* m = new Mail;
6362 m->messageID = fields[0].GetUInt64();
6363 m->messageType = fields[1].GetUInt8();
6364 m->sender = fields[2].GetUInt64();
6365 m->receiver = receiver;
6366 bool has_items = fields[4].GetBool();
6367 m->expire_time = fields[5].GetInt64();
6368 m->deliver_time = 0;
6369 m->COD = fields[6].GetUInt64();
6370 m->checked = fields[7].GetUInt32();
6371 m->mailTemplateId = fields[8].GetInt16();
6372
6373 // Delete or return mail
6374 if (has_items)
6375 {
6376 // read items from cache
6377 m->items.swap(itemsCache[m->messageID]);
6378
6379 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
6381 {
6382 CharacterDatabaseTransaction nonTransactional(nullptr);
6383 // mail open and then not returned
6384 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6385 {
6386 Item::DeleteFromDB(nonTransactional, itr2->item_guid);
6387 AzeriteItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6388 AzeriteEmpoweredItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6389 }
6390
6391 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
6392 stmt->setUInt64(0, m->messageID);
6393 CharacterDatabase.Execute(stmt);
6394 }
6395 else
6396 {
6397 // Mail will be returned
6398 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
6399 stmt->setUInt64(0, m->receiver);
6400 stmt->setUInt64(1, m->sender);
6401 stmt->setInt64 (2, curTime + 30 * DAY);
6402 stmt->setInt64 (3, curTime);
6404 stmt->setUInt64(5, m->messageID);
6405 CharacterDatabase.Execute(stmt);
6406 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6407 {
6408 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
6409 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
6410 stmt->setUInt64(0, m->sender);
6411 stmt->setUInt64(1, itr2->item_guid);
6412 CharacterDatabase.Execute(stmt);
6413
6414 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
6415 stmt->setUInt64(0, m->sender);
6416 stmt->setUInt64(1, itr2->item_guid);
6417 CharacterDatabase.Execute(stmt);
6418 }
6419 delete m;
6420 ++returnedCount;
6421 continue;
6422 }
6423 }
6424
6425 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
6426 stmt->setUInt64(0, m->messageID);
6427 CharacterDatabase.Execute(stmt);
6428 delete m;
6429 ++deletedCount;
6430 }
6431 while (result->NextRow());
6432
6433 TC_LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
6434}
6435
6437{
6438 uint32 oldMSTime = getMSTime();
6439
6440 _questAreaTriggerStore.clear(); // need for reload case
6441
6442 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
6443
6444 if (!result)
6445 {
6446 TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
6447 return;
6448 }
6449
6450 uint32 count = 0;
6451
6452 do
6453 {
6454 ++count;
6455
6456 Field* fields = result->Fetch();
6457
6458 uint32 trigger_ID = fields[0].GetUInt32();
6459 uint32 quest_ID = fields[1].GetUInt32();
6460
6461 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
6462 if (!atEntry)
6463 {
6464 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
6465 continue;
6466 }
6467
6468 Quest const* quest = GetQuestTemplate(quest_ID);
6469
6470 if (!quest)
6471 {
6472 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not existing quest {}", trigger_ID, quest_ID);
6473 continue;
6474 }
6475
6477 {
6478 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);
6479 continue;
6480 }
6481
6482 _questAreaTriggerStore[trigger_ID].insert(quest_ID);
6483
6484 } while (result->NextRow());
6485
6486 for (auto const& pair : _questObjectives)
6487 {
6488 QuestObjective const* objective = pair.second;
6489 if (objective->Type == QUEST_OBJECTIVE_AREATRIGGER)
6490 _questAreaTriggerStore[objective->ObjectID].insert(objective->QuestID);
6491 }
6492
6493 TC_LOG_INFO("server.loading", ">> Loaded {} quest trigger points in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6494}
6495
6497{
6498 uint32 typeIndex;
6499 if (type == TYPEID_UNIT)
6500 typeIndex = 0;
6501 else if (type == TYPEID_GAMEOBJECT)
6502 typeIndex = 1;
6503 else
6504 return nullptr;
6505
6507}
6508
6510{
6511 uint32 typeIndex;
6512 if (type == TYPEID_UNIT)
6513 typeIndex = 0;
6514 else if (type == TYPEID_GAMEOBJECT)
6515 typeIndex = 1;
6516 else
6517 return nullptr;
6518
6520}
6521
6523{
6524 uint32 oldMSTime = getMSTime();
6525
6526 for (std::size_t i = 0; i < _questGreetingStore.size(); ++i)
6527 _questGreetingStore[i].clear();
6528
6529 // 0 1 2 3
6530 QueryResult result = WorldDatabase.Query("SELECT ID, type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
6531 if (!result)
6532 {
6533 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6534 return;
6535 }
6536
6537 uint32 count = 0;
6538 do
6539 {
6540 Field* fields = result->Fetch();
6541
6542 uint32 id = fields[0].GetUInt32();
6543 uint8 type = fields[1].GetUInt8();
6544 switch (type)
6545 {
6546 case 0: // Creature
6547 if (!GetCreatureTemplate(id))
6548 {
6549 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id);
6550 continue;
6551 }
6552 break;
6553 case 1: // GameObject
6554 if (!GetGameObjectTemplate(id))
6555 {
6556 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id);
6557 continue;
6558 }
6559 break;
6560 default:
6561 continue;
6562 }
6563
6564 uint16 greetEmoteType = fields[2].GetUInt16();
6565 uint32 greetEmoteDelay = fields[3].GetUInt32();
6566 std::string greeting = fields[4].GetString();
6567
6568 _questGreetingStore[type].emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(greetEmoteType, greetEmoteDelay, std::move(greeting)));
6569 ++count;
6570 }
6571 while (result->NextRow());
6572
6573 TC_LOG_INFO("server.loading", ">> Loaded {} quest_greeting in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6574}
6575
6577{
6578 uint32 oldMSTime = getMSTime();
6579
6580 _tavernAreaTriggerStore.clear(); // need for reload case
6581
6582 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
6583
6584 if (!result)
6585 {
6586 TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
6587 return;
6588 }
6589
6590 uint32 count = 0;
6591
6592 do
6593 {
6594 ++count;
6595
6596 Field* fields = result->Fetch();
6597
6598 uint32 Trigger_ID = fields[0].GetUInt32();
6599
6600 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6601 if (!atEntry)
6602 {
6603 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6604 continue;
6605 }
6606
6607 _tavernAreaTriggerStore.insert(Trigger_ID);
6608 } while (result->NextRow());
6609
6610 TC_LOG_INFO("server.loading", ">> Loaded {} tavern triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6611}
6612
6614{
6615 uint32 oldMSTime = getMSTime();
6616
6617 _areaTriggerScriptStore.clear(); // need for reload case
6618
6619 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
6620 if (!result)
6621 {
6622 TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
6623 return;
6624 }
6625
6626 do
6627 {
6628 Field* fields = result->Fetch();
6629
6630 uint32 triggerId = fields[0].GetUInt32();
6631 std::string_view scriptName = fields[1].GetStringView();
6632
6633 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
6634 if (!atEntry)
6635 {
6636 TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: {}) does not exist in `AreaTrigger.dbc`.", triggerId);
6637 continue;
6638 }
6639 _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
6640 }
6641 while (result->NextRow());
6642
6643 TC_LOG_INFO("server.loading", ">> Loaded {} areatrigger scripts in {} ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6644}
6645
6646uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
6647{
6648 bool found = false;
6649 float dist = 10000;
6650 uint32 id = 0;
6651
6652 auto isVisibleForFaction = [&](TaxiNodesEntry const* node)
6653 {
6654 switch (team)
6655 {
6656 case HORDE: return node->GetFlags().HasFlag(TaxiNodeFlags::ShowOnHordeMap);
6657 case ALLIANCE: return node->GetFlags().HasFlag(TaxiNodeFlags::ShowOnAllianceMap);
6658 default: break;
6659 }
6660 return false;
6661 };
6662
6663 for (TaxiNodesEntry const* node : sTaxiNodesStore)
6664 {
6665 if (!node || node->ContinentID != mapid || !isVisibleForFaction(node) || node->GetFlags().HasFlag(TaxiNodeFlags::IgnoreForFindNearest))
6666 continue;
6667
6668 uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
6669 TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
6670
6671 // skip not taxi network nodes
6672 if ((sTaxiNodesMask[field] & submask) == 0)
6673 continue;
6674
6675 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);
6676 if (found)
6677 {
6678 if (dist2 < dist)
6679 {
6680 dist = dist2;
6681 id = node->ID;
6682 }
6683 }
6684 else
6685 {
6686 found = true;
6687 dist = dist2;
6688 id = node->ID;
6689 }
6690 }
6691
6692 return id;
6693}
6694
6695void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
6696{
6697 TaxiPathEntry const* taxiPath = sDB2Manager.GetTaxiPath(source, destination);
6698 if (taxiPath)
6699 {
6700 path = taxiPath->ID;
6701 cost = taxiPath->Cost;
6702 }
6703 else
6704 {
6705 path = 0;
6706 cost = 0;
6707 }
6708}
6709
6710uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
6711{
6712 CreatureModel mountModel;
6713 CreatureTemplate const* mount_info = nullptr;
6714
6715 // select mount creature id
6716 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
6717 if (node)
6718 {
6719 uint32 mount_entry = 0;
6720 if (team == ALLIANCE)
6721 mount_entry = node->MountCreatureID[1];
6722 else
6723 mount_entry = node->MountCreatureID[0];
6724
6725 // Fix for Alliance not being able to use Acherus taxi
6726 // only one mount type for both sides
6727 if (mount_entry == 0 && allowed_alt_team)
6728 {
6729 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
6730 mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
6731 }
6732
6733 mount_info = GetCreatureTemplate(mount_entry);
6734 if (mount_info)
6735 {
6736 CreatureModel const* model = mount_info->GetRandomValidModel();
6737 if (!model)
6738 {
6739 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
6740 return 0;
6741 }
6742 mountModel = *model;
6743 }
6744 }
6745
6746 // minfo is not actually used but the mount_id was updated
6747 GetCreatureModelRandomGender(&mountModel, mount_info);
6748
6749 return mountModel.CreatureDisplayID;
6750}
6751
6753{
6754 auto itr = _questTemplates.find(quest_id);
6755 return itr != _questTemplates.end() ? itr->second.get() : nullptr;
6756}
6757
6762
6764{
6765 uint32 oldMSTime = getMSTime();
6766
6767 GraveyardStore.clear(); // needed for reload case
6768
6769 // 0 1
6770 QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone FROM graveyard_zone");
6771
6772 if (!result)
6773 {
6774 TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
6775 return;
6776 }
6777
6778 uint32 count = 0;
6779
6780 do
6781 {
6782 ++count;
6783
6784 Field* fields = result->Fetch();
6785
6786 uint32 safeLocId = fields[0].GetUInt32();
6787 uint32 zoneId = fields[1].GetUInt32();
6788
6789 WorldSafeLocsEntry const* entry = GetWorldSafeLoc(safeLocId);
6790 if (!entry)
6791 {
6792 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: {}), skipped.", safeLocId);
6793 continue;
6794 }
6795
6796 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
6797 if (!areaEntry)
6798 {
6799 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: {}), skipped.", zoneId);
6800 continue;
6801 }
6802
6803 if (!AddGraveyardLink(safeLocId, zoneId, 0, false))
6804 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
6805 } while (result->NextRow());
6806
6807 TC_LOG_INFO("server.loading", ">> Loaded {} graveyard-zone links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6808}
6809
6811{
6812 enum DefaultGraveyard
6813 {
6814 HORDE_GRAVEYARD = 10, // Crossroads
6815 ALLIANCE_GRAVEYARD = 4 // Westfall
6816 };
6817
6818 if (team == HORDE)
6819 return GetWorldSafeLoc(HORDE_GRAVEYARD);
6820 else if (team == ALLIANCE)
6821 return GetWorldSafeLoc(ALLIANCE_GRAVEYARD);
6822 else return nullptr;
6823}
6824
6825WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(WorldLocation const& location, uint32 team, WorldObject* conditionObject) const
6826{
6827 float x, y, z;
6828 location.GetPosition(x, y, z);
6829 uint32 MapId = location.GetMapId();
6830
6831 // search for zone associated closest graveyard
6832 uint32 zoneId = sTerrainMgr.GetZoneId(conditionObject ? conditionObject->GetPhaseShift() : PhasingHandler::GetEmptyPhaseShift(), MapId, x, y, z);
6833
6834 if (!zoneId)
6835 {
6836 if (z > -500)
6837 {
6838 TC_LOG_ERROR("misc", "ZoneId not found for map {} coords ({}, {}, {}), object name: {} {}", MapId, x, y, z,
6839 conditionObject ? std::string_view(conditionObject->GetName()) : "", Object::GetGUID(conditionObject));
6840 return GetDefaultGraveyard(team);
6841 }
6842 }
6843
6844 WorldSafeLocsEntry const* graveyard = GetClosestGraveyardInZone(location, team, conditionObject, zoneId);
6845 AreaTableEntry const* zoneEntry = sAreaTableStore.AssertEntry(zoneId);
6846 AreaTableEntry const* parentEntry = sAreaTableStore.LookupEntry(zoneEntry->ParentAreaID);
6847
6848 while (!graveyard && parentEntry)
6849 {
6850 graveyard = GetClosestGraveyardInZone(location, team, conditionObject, parentEntry->ID);
6851 if (!graveyard && parentEntry->ParentAreaID != 0)
6852 parentEntry = sAreaTableStore.LookupEntry(parentEntry->ParentAreaID);
6853 else // nothing found, cant look further, give up.
6854 parentEntry = nullptr;
6855 }
6856
6857 if (!graveyard && !sMapStore.LookupEntry(MapId)->IsBattlegroundOrArena())
6858 {
6859 if (zoneId != 0)
6860 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
6861
6862 graveyard = GetDefaultGraveyard(team);
6863 }
6864
6865 return graveyard;
6866}
6867
6868WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyardInZone(WorldLocation const& location, uint32 team, WorldObject* conditionObject, uint32 zoneId) const
6869{
6870 uint32 MapId = location.GetMapId();
6871
6872 // Simulate std. algorithm:
6873 // found some graveyard associated to (ghost_zone, ghost_map)
6874 //
6875 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
6876 // then check faction
6877 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
6878 // then check faction
6879 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6880 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
6881
6882 // at corpse map
6883 Optional<float> distNear;
6884 WorldSafeLocsEntry const* entryNear = nullptr;
6885
6886 // at entrance map for corpse map
6887 Optional<float> distEntr;
6888 WorldSafeLocsEntry const* entryEntr = nullptr;
6889
6890 // some where other
6891 WorldSafeLocsEntry const* entryFar = nullptr;
6892
6893 ConditionSourceInfo conditionSource(conditionObject);
6894
6895 for (; range.first != range.second; ++range.first)
6896 {
6897 GraveyardData const& data = range.first->second;
6898
6900
6901 if (conditionObject)
6902 {
6903 if (!data.Conditions.Meets(conditionSource))
6904 continue;
6905
6906 if (int16(entry->Loc.GetMapId()) == mapEntry->ParentMapID && !conditionObject->GetPhaseShift().HasVisibleMapId(entry->Loc.GetMapId()))
6907 continue;
6908 }
6909 else if (team != 0)
6910 {
6911 bool teamConditionMet = true;
6912 if (std::shared_ptr<std::vector<Condition>> conditions = data.Conditions.Conditions.lock())
6913 {
6914 for (Condition const& cond : *conditions)
6915 {
6916 if (cond.ConditionType != CONDITION_TEAM)
6917 continue;
6918
6919 if (cond.ConditionValue1 == team)
6920 continue;
6921
6922 teamConditionMet = false;
6923 }
6924 }
6925
6926 if (!teamConditionMet)
6927 continue;
6928 }
6929
6930 // find now nearest graveyard at other map
6931 if (MapId != entry->Loc.GetMapId() && int16(entry->Loc.GetMapId()) != mapEntry->ParentMapID)
6932 {
6933 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
6934 if (!mapEntry
6935 || mapEntry->CorpseMapID < 0
6936 || uint32(mapEntry->CorpseMapID) != entry->Loc.GetMapId()
6937 || (mapEntry->Corpse.X == 0 && mapEntry->Corpse.Y == 0)) // Check X and Y
6938 {
6939 // not have any corrdinates for check distance anyway
6940 entryFar = entry;
6941 continue;
6942 }
6943
6944 // at entrance map calculate distance (2D);
6945 float dist2 = entry->Loc.GetExactDist2dSq(mapEntry->Corpse.X, mapEntry->Corpse.Y);
6946 if (!distEntr || dist2 < *distEntr)
6947 {
6948 distEntr = dist2;
6949 entryEntr = entry;
6950 }
6951 }
6952 // find now nearest graveyard at same map
6953 else
6954 {
6955 float dist2 = entry->Loc.GetExactDistSq(location);
6956 if (!distNear || dist2 < *distNear)
6957 {
6958 distNear = dist2;
6959 entryNear = entry;
6960 }
6961 }
6962 }
6963
6964 if (entryNear)
6965 return entryNear;
6966
6967 if (entryEntr)
6968 return entryEntr;
6969
6970 return entryFar;
6971}
6972
6974{
6975 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6976 for (; range.first != range.second; ++range.first)
6977 {
6978 GraveyardData const& data = range.first->second;
6979 if (data.safeLocId == id)
6980 return &data;
6981 }
6982 return nullptr;
6983}
6984
6986{
6987 uint32 oldMSTime = getMSTime();
6988
6989 // 0 1 2 3 4 5 6
6990 if (QueryResult result = WorldDatabase.Query("SELECT ID, MapID, LocX, LocY, LocZ, Facing, TransportSpawnId FROM world_safe_locs"))
6991 {
6992 do
6993 {
6994 Field* fields = result->Fetch();
6995 uint32 id = fields[0].GetUInt32();
6996 WorldLocation loc(fields[1].GetUInt32(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat(), DegToRad(fields[5].GetFloat()));
6998 {
6999 TC_LOG_ERROR("sql.sql", "World location (ID: {}) has a invalid position {}, skipped", id, loc);
7000 continue;
7001 }
7002
7003 Optional<ObjectGuid::LowType> transportSpawnId = fields[6].GetUInt64OrNull();
7004 if (transportSpawnId && !sTransportMgr->GetTransportSpawn(*transportSpawnId))
7005 {
7006 TC_LOG_ERROR("sql.sql", "World location (ID: {}) has a invalid transportSpawnID {}, skipped.", id, *transportSpawnId);
7007 continue;
7008 }
7009
7010 WorldSafeLocsEntry& worldSafeLocs = _worldSafeLocs[id];
7011 worldSafeLocs.ID = id;
7012 worldSafeLocs.Loc.WorldRelocate(loc);
7013 worldSafeLocs.TransportSpawnId = transportSpawnId;
7014
7015 } while (result->NextRow());
7016
7017 TC_LOG_INFO("server.loading", ">> Loaded {} world locations {} ms", _worldSafeLocs.size(), GetMSTimeDiffToNow(oldMSTime));
7018 }
7019 else
7020 TC_LOG_INFO("server.loading", ">> Loaded 0 world locations. DB table `world_safe_locs` is empty.");
7021}
7022
7027
7032
7037
7042
7043bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
7044{
7045 if (FindGraveyardData(id, zoneId))
7046 return false;
7047
7048 // add link to loaded data
7049 GraveyardData data;
7050 data.safeLocId = id;
7051
7052 GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
7053
7054 // add link to DB
7055 if (persist)
7056 {
7058
7059 stmt->setUInt32(0, id);
7060 stmt->setUInt32(1, zoneId);
7061
7062 WorldDatabase.Execute(stmt);
7063
7064 // Store graveyard condition if team is set
7065 if (team != 0)
7066 {
7067 WorldDatabasePreparedStatement* conditionStmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CONDITION);
7068 conditionStmt->setUInt32(0, CONDITION_SOURCE_TYPE_GRAVEYARD); // SourceTypeOrReferenceId
7069 conditionStmt->setUInt32(1, zoneId); // SourceGroup
7070 conditionStmt->setUInt32(2, id); // SourceEntry
7071 conditionStmt->setUInt32(3, 0); // SourceId
7072 conditionStmt->setUInt32(4, 0); // ElseGroup
7073 conditionStmt->setUInt32(5, CONDITION_TEAM); // ConditionTypeOrReference
7074 conditionStmt->setUInt8(6, 0); // ConditionTarget
7075 conditionStmt->setUInt32(7, team); // ConditionValue1
7076 conditionStmt->setUInt32(8, 0); // ConditionValue2
7077 conditionStmt->setUInt32(9, 0); // ConditionValue3
7078 conditionStmt->setUInt8(10, 0); // NegativeCondition
7079 conditionStmt->setUInt32(11, 0); // ErrorType
7080 conditionStmt->setUInt32(12, 0); // ErrorTextId
7081 conditionStmt->setString(13, ""sv); // ScriptName
7082 conditionStmt->setString(14, ""sv); // Comment
7083
7084 WorldDatabase.Execute(conditionStmt);
7085
7086 // reload conditions to make sure everything is loaded as it should be
7087 sConditionMgr->LoadConditions(true);
7088 sScriptMgr->NotifyScriptIDUpdate();
7089 }
7090 }
7091
7092 return true;
7093}
7094
7096{
7097 uint32 oldMSTime = getMSTime();
7098
7099 _areaTriggerStore.clear(); // needed for reload case
7100
7101 // 0 1
7102 QueryResult result = WorldDatabase.Query("SELECT ID, PortLocID FROM areatrigger_teleport");
7103 if (!result)
7104 {
7105 TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
7106 return;
7107 }
7108
7109 uint32 count = 0;
7110
7111 do
7112 {
7113 Field* fields = result->Fetch();
7114
7115 ++count;
7116
7117 uint32 Trigger_ID = fields[0].GetUInt32();
7118 uint32 PortLocID = fields[1].GetUInt32();
7119
7120 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
7121 if (!atEntry)
7122 {
7123 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) does not exist in AreaTrigger.dbc.", Trigger_ID);
7124 continue;
7125 }
7126
7127 WorldSafeLocsEntry const* portLoc = GetWorldSafeLoc(PortLocID);
7128 if (!portLoc)
7129 {
7130 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) has a non-existing Port Loc (ID: {}) in WorldSafeLocs.dbc, skipped", Trigger_ID, PortLocID);
7131 continue;
7132 }
7133
7134 _areaTriggerStore[Trigger_ID] = portLoc;
7135
7136 } while (result->NextRow());
7137
7138 TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7139}
7140
7142{
7143 for (AreaTriggerEntry const* areaTrigger : sAreaTriggerStore)
7144 {
7145 if (areaTrigger->GetShapeType() != AreaTriggerShapeType::Polygon)
7146 continue;
7147
7148 PathDb2 const* path = sDB2Manager.GetPath(areaTrigger->ShapeID);
7149 if (!path || path->Locations.size() < 4)
7150 continue;
7151
7152 AreaTriggerPolygon& polygon = _areaTriggerPolygons[areaTrigger->ID];
7153 polygon.Vertices.resize(path->Locations.size() - 1);
7154 std::ranges::transform(path->Locations.begin() + 1, path->Locations.end(), polygon.Vertices.begin(), [](DBCPosition3D const& pos)
7155 {
7156 return Position(pos.X, pos.Y, pos.Z);
7157 });
7158
7159 for (PathPropertyEntry const* pathProperty : path->Properties)
7160 if (pathProperty->GetPropertyIndex() == PathPropertyIndex::VolumeHeight)
7161 polygon.Height = pathProperty->Value * 0.001f + 0.02f;
7162 }
7163}
7164
7166{
7167 uint32 oldMSTime = getMSTime();
7168
7169 _accessRequirementStore.clear(); // need for reload case
7170
7171 // 0 1 2 3 4 5 6 7 8 9
7172 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");
7173
7174 if (!result)
7175 {
7176 TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
7177 return;
7178 }
7179
7180 uint32 count = 0;
7181
7182 do
7183 {
7184 Field* fields = result->Fetch();
7185
7186 uint32 mapid = fields[0].GetUInt32();
7187 if (!sMapStore.LookupEntry(mapid))
7188 {
7189 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not exist, skipped.", mapid);
7190 continue;
7191 }
7192
7193 Difficulty difficulty = Difficulty(fields[1].GetInt32());
7194 if (!sDB2Manager.GetMapDifficultyData(mapid, difficulty))
7195 {
7196 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not have difficulty {}, skipped", mapid, difficulty);
7197 continue;
7198 }
7199
7200 uint64 requirement_ID = MAKE_PAIR64(mapid, difficulty);
7201
7202 AccessRequirement* ar = &_accessRequirementStore[requirement_ID];
7203 ar->levelMin = fields[2].GetUInt8();
7204 ar->levelMax = fields[3].GetUInt8();
7205 ar->item = fields[4].GetUInt32();
7206 ar->item2 = fields[5].GetUInt32();
7207 ar->quest_A = fields[6].GetUInt32();
7208 ar->quest_H = fields[7].GetUInt32();
7209 ar->achievement = fields[8].GetUInt32();
7210 ar->questFailedText = fields[9].GetString();
7211
7212 if (ar->item)
7213 {
7214 ItemTemplate const* pProto = GetItemTemplate(ar->item);
7215 if (!pProto)
7216 {
7217 TC_LOG_ERROR("sql.sql", "Key item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item, mapid, difficulty);
7218 ar->item = 0;
7219 }
7220 }
7221
7222 if (ar->item2)
7223 {
7224 ItemTemplate const* pProto = GetItemTemplate(ar->item2);
7225 if (!pProto)
7226 {
7227 TC_LOG_ERROR("sql.sql", "Second item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item2, mapid, difficulty);
7228 ar->item2 = 0;
7229 }
7230 }
7231
7232 if (ar->quest_A)
7233 {
7234 if (!GetQuestTemplate(ar->quest_A))
7235 {
7236 TC_LOG_ERROR("sql.sql", "Required Alliance Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_A, mapid, difficulty);
7237 ar->quest_A = 0;
7238 }
7239 }
7240
7241 if (ar->quest_H)
7242 {
7243 if (!GetQuestTemplate(ar->quest_H))
7244 {
7245 TC_LOG_ERROR("sql.sql", "Required Horde Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_H, mapid, difficulty);
7246 ar->quest_H = 0;
7247 }
7248 }
7249
7250 if (ar->achievement)
7251 {
7252 if (!sAchievementStore.LookupEntry(ar->achievement))
7253 {
7254 TC_LOG_ERROR("sql.sql", "Required Achievement {} not exist for map {} difficulty {}, remove quest done requirement.", ar->achievement, mapid, difficulty);
7255 ar->achievement = 0;
7256 }
7257 }
7258 ++count;
7259
7260 } while (result->NextRow());
7261
7262 TC_LOG_INFO("server.loading", ">> Loaded {} access requirement definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7263}
7264
7265/*
7266 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
7267 */
7269{
7270 Optional<uint32> parentId;
7271 MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
7272 if (!mapEntry || mapEntry->CorpseMapID < 0)
7273 return nullptr;
7274
7275 if (mapEntry->IsDungeon())
7276 if (InstanceTemplate const* iTemplate = GetInstanceTemplate(Map))
7277 parentId = iTemplate->Parent;
7278
7279 uint32 entrance_map = parentId.value_or(mapEntry->CorpseMapID);
7280 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7281 {
7282 if (itr->second->Loc.GetMapId() == entrance_map)
7283 {
7284 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7285 if (atEntry && atEntry->ContinentID == Map)
7286 return itr->second;
7287 }
7288 }
7289 return nullptr;
7290}
7291
7296{
7297 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7298 {
7299 if (itr->second->Loc.GetMapId() == Map)
7300 {
7301 if (sAreaTriggerStore.HasRecord(itr->first))
7302 return itr->second;
7303 }
7304 }
7305 return nullptr;
7306}
7307
7309{
7310 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
7311 if (result)
7312 GetGuidSequenceGenerator(HighGuid::Player).Set((*result)[0].GetUInt64() + 1);
7313
7314 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
7315 if (result)
7316 GetGenerator<HighGuid::Item>().Set((*result)[0].GetUInt64() + 1);
7317
7318 // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
7319 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7320 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7321 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 >= '{}'",
7322 GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7323 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7324
7325 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
7326 if (result)
7327 GetGuidSequenceGenerator(HighGuid::Transport).Set((*result)[0].GetUInt64() + 1);
7328
7329 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
7330 if (result)
7331 _auctionId = (*result)[0].GetUInt32()+1;
7332
7333 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
7334 if (result)
7335 _mailId = (*result)[0].GetUInt64()+1;
7336
7337 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
7338 if (result)
7339 sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
7340
7341 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");
7342 if (result)
7343 _equipmentSetGuid = (*result)[0].GetUInt64()+1;
7344
7345 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
7346 if (result)
7347 sGuildMgr->SetNextGuildId((*result)[0].GetUInt64()+1);
7348
7349 result = CharacterDatabase.Query("SELECT MAX(guid) FROM `groups`");
7350 if (result)
7351 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+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 return _guidGenerators.try_emplace(high, high).first->second;
7365}
7366
7368{
7369 if (_auctionId >= 0xFFFFFFFE)
7370 {
7371 TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7373 }
7374 return _auctionId++;
7375}
7376
7378{
7379 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
7380 {
7381 TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7383 }
7384 return _equipmentSetGuid++;
7385}
7386
7388{
7389 if (_mailId >= UI64LIT(0xFFFFFFFFFFFFFFFE))
7390 {
7391 TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7393 }
7394 return _mailId++;
7395}
7396
7398{
7399 if (_hiPetNumber >= 0xFFFFFFFE)
7400 {
7401 TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7403 }
7404 return _hiPetNumber++;
7405}
7406
7408{
7409 if (_creatureSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7410 {
7411 TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7413 }
7414 return _creatureSpawnId++;
7415}
7416
7418{
7419 if (_gameObjectSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7420 {
7421 TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7423 }
7424 return _gameObjectSpawnId++;
7425}
7426
7428{
7429 uint32 oldMSTime = getMSTime();
7430
7431 _gameObjectLocaleStore.clear(); // need for reload case
7432
7433 // 0 1 2 3 4
7434 QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption, unk1 FROM gameobject_template_locale");
7435 if (!result)
7436 return;
7437
7438 do
7439 {
7440 Field* fields = result->Fetch();
7441
7442 uint32 id = fields[0].GetUInt32();
7443 std::string_view localeName = fields[1].GetStringView();
7444
7445 LocaleConstant locale = GetLocaleByName(localeName);
7446 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
7447 continue;
7448
7450 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
7451 AddLocaleString(fields[3].GetStringView(), locale, data.CastBarCaption);
7452 AddLocaleString(fields[4].GetStringView(), locale, data.Unk1);
7453
7454 } while (result->NextRow());
7455
7456 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject_template_locale strings in {} ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
7457}
7458
7460{
7461 uint32 oldMSTime = getMSTime();
7462
7463 _destructibleHitpointStore.clear(); // need for reload case
7464
7465 // 0 1 2
7466 QueryResult result = WorldDatabase.Query("SELECT Id, IntactNumHits, DamagedNumHits FROM destructible_hitpoint");
7467 if (!result)
7468 return;
7469
7470 do
7471 {
7472 Field* fields = result->Fetch();
7473
7474 uint32 id = fields[0].GetUInt32();
7475
7477 data.Id = id;
7478 data.IntactNumHits = fields[1].GetUInt32();
7479 data.DamagedNumHits = fields[2].GetUInt32();
7480
7481 } while (result->NextRow());
7482
7483 TC_LOG_INFO("server.loading", ">> Loaded {} destructible_hitpoint records in {} ms", _destructibleHitpointStore.size(), GetMSTimeDiffToNow(oldMSTime));
7484}
7485
7486inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7487{
7488 if (sLockStore.LookupEntry(dataN))
7489 return;
7490
7491 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but lock (Id: {}) not found.",
7492 goInfo->entry, goInfo->type, N, dataN, dataN);
7493}
7494
7495inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7496{
7497 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
7498 {
7499 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
7500 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but GO (Entry {}) have not GAMEOBJECT_TYPE_TRAP ({}) type.",
7501 goInfo->entry, goInfo->type, N, dataN, dataN, uint32(GAMEOBJECT_TYPE_TRAP));
7502 }
7503}
7504
7505inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7506{
7507 if (sSpellMgr->GetSpellInfo(dataN, DIFFICULTY_NONE))
7508 return;
7509
7510 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but Spell (Entry {}) not exist.",
7511 goInfo->entry, goInfo->type, N, dataN, dataN);
7512}
7513
7514inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
7515{
7517 return;
7518
7519 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but correct chair height in range 0..{}.",
7521
7522 // prevent client and server unexpected work
7523 dataN = 0;
7524}
7525
7527{
7528 // 0/1 correct values
7529 if (dataN <= 1)
7530 return;
7531
7532 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
7533}
7534
7535inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7536{
7537 // 0/1 correct values
7538 if (dataN <= 1)
7539 return;
7540
7541 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) consumable field value.",
7542 goInfo->entry, goInfo->type, N, dataN);
7543}
7544
7546{
7547 uint32 oldMSTime = getMSTime();
7548
7549 for (GameObjectsEntry const* db2go : sGameObjectsStore)
7550 {
7552 go.entry = db2go->ID;
7553 go.type = db2go->TypeID;
7554 go.displayId = db2go->DisplayID;
7555 go.name = db2go->Name[sWorld->GetDefaultDbcLocale()];
7556 go.size = db2go->Scale;
7557 memset(go.raw.data, 0, sizeof(go.raw.data));
7558 std::ranges::copy(db2go->PropValue, std::begin(go.raw.data));
7559 go.ContentTuningId = 0;
7560 go.RequiredLevel = 0;
7561 go.ScriptId = 0;
7562 }
7563
7564 // 0 1 2 3 4 5 6 7
7565 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
7566 // 8 9 10 11 12 13 14 15 16 17 18 19 20
7567 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
7568 // 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
7569 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, "
7570 // 37 38 39 40 41 42 43 44 45 46 47
7571 "Data29, Data30, Data31, Data32, Data33, Data34, ContentTuningId, RequiredLevel, AIName, ScriptName, StringId "
7572 "FROM gameobject_template");
7573
7574 if (!result)
7575 {
7576 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
7577 return;
7578 }
7579
7580 _gameObjectTemplateStore.reserve(result->GetRowCount());
7581 do
7582 {
7583 Field* fields = result->Fetch();
7584
7585 uint32 entry = fields[0].GetUInt32();
7586
7588 got.entry = entry;
7589 got.type = uint32(fields[1].GetUInt8());
7590 got.displayId = fields[2].GetUInt32();
7591 got.name = fields[3].GetString();
7592 got.IconName = fields[4].GetString();
7593 got.castBarCaption = fields[5].GetString();
7594 got.unk1 = fields[6].GetString();
7595 got.size = fields[7].GetFloat();
7596
7597 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
7598 got.raw.data[i] = fields[8 + i].GetUInt32();
7599
7600 got.ContentTuningId = fields[43].GetInt32();
7601 got.RequiredLevel = fields[44].GetInt32();
7602 got.AIName = fields[45].GetString();
7603 got.ScriptId = GetScriptId(fields[46].GetStringView());
7604 got.StringId = fields[47].GetString();
7605
7606 // Checks
7607 if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
7608 {
7609 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
7610 got.AIName.clear();
7611 }
7612
7613 switch (got.type)
7614 {
7615 case GAMEOBJECT_TYPE_DOOR: //0
7616 {
7617 if (got.door.open)
7618 CheckGOLockId(&got, got.door.open, 1);
7620 break;
7621 }
7622 case GAMEOBJECT_TYPE_BUTTON: //1
7623 {
7624 if (got.button.open)
7625 CheckGOLockId(&got, got.button.open, 1);
7627 break;
7628 }
7630 {
7631 if (got.questgiver.open)
7632 CheckGOLockId(&got, got.questgiver.open, 0);
7634 break;
7635 }
7636 case GAMEOBJECT_TYPE_CHEST: //3
7637 {
7638 if (got.chest.open)
7639 CheckGOLockId(&got, got.chest.open, 0);
7640
7641 if (got.chest.linkedTrap) // linked trap
7642 CheckGOLinkedTrapId(&got, got.chest.linkedTrap, 7);
7643 break;
7644 }
7645 case GAMEOBJECT_TYPE_TRAP: //6
7646 {
7647 if (got.trap.open)
7648 CheckGOLockId(&got, got.trap.open, 0);
7649 break;
7650 }
7651 case GAMEOBJECT_TYPE_CHAIR: //7
7653 break;
7655 {
7656 if (got.spellFocus.spellFocusType)
7657 {
7658 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.spellFocusType))
7659 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but SpellFocus (Id: {}) not exist.",
7661 }
7662
7663 if (got.spellFocus.linkedTrap) // linked trap
7665 break;
7666 }
7667 case GAMEOBJECT_TYPE_GOOBER: //10
7668 {
7669 if (got.goober.open)
7670 CheckGOLockId(&got, got.goober.open, 0);
7671
7672 CheckGOConsumable(&got, got.goober.consumable, 3);
7673
7674 if (got.goober.pageID) // pageId
7675 {
7676 if (!GetPageText(got.goober.pageID))
7677 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data7={} but PageText (Entry {}) not exist.",
7678 entry, got.type, got.goober.pageID, got.goober.pageID);
7679 }
7681 if (got.goober.linkedTrap) // linked trap
7682 CheckGOLinkedTrapId(&got, got.goober.linkedTrap, 12);
7683 break;
7684 }
7686 {
7687 if (got.areaDamage.open)
7688 CheckGOLockId(&got, got.areaDamage.open, 0);
7689 break;
7690 }
7691 case GAMEOBJECT_TYPE_CAMERA: //13
7692 {
7693 if (got.camera.open)
7694 CheckGOLockId(&got, got.camera.open, 0);
7695 break;
7696 }
7698 {
7699 if (got.moTransport.taxiPathID)
7700 {
7702 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but TaxiPath (Id: {}) not exist.",
7703 entry, got.type, got.moTransport.taxiPathID, got.moTransport.taxiPathID);
7704 }
7705 if (uint32 transportMap = got.moTransport.SpawnMap)
7706 _transportMaps.insert(transportMap);
7707 break;
7708 }
7709 case GAMEOBJECT_TYPE_RITUAL: //18
7710 break;
7712 {
7713 // always must have spell
7714 CheckGOSpellId(&got, got.spellCaster.spell, 0);
7715 break;
7716 }
7717 case GAMEOBJECT_TYPE_FLAGSTAND: //24
7718 {
7719 if (got.flagStand.open)
7720 CheckGOLockId(&got, got.flagStand.open, 0);
7722 break;
7723 }
7725 {
7726 if (got.fishingHole.open)
7727 CheckGOLockId(&got, got.fishingHole.open, 4);
7728 break;
7729 }
7730 case GAMEOBJECT_TYPE_FLAGDROP: //26
7731 {
7732 if (got.flagDrop.open)
7733 CheckGOLockId(&got, got.flagDrop.open, 0);
7735 break;
7736 }
7739
7741 {
7742 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data2 = {} but AnimKit.dbc (Id: {}) not exist, set to 0.",
7745 }
7746 break;
7749 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) Has non existing Destructible Hitpoint Record {}.", entry, got.destructibleBuilding.HealthRec);
7750 break;
7752 if (uint32 transportMap = got.garrisonBuilding.SpawnMap)
7753 _transportMaps.insert(transportMap);
7754 break;
7756 if (got.gatheringNode.open)
7757 CheckGOLockId(&got, got.gatheringNode.open, 0);
7758 if (got.gatheringNode.linkedTrap)
7760 break;
7761 }
7762 } while (result->NextRow());
7763
7764 TC_LOG_INFO("server.loading", ">> Loaded {} game object templates in {} ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
7765}
7766
7768{
7769 uint32 oldMSTime = getMSTime();
7770
7771 // 0 1 2 3 4 5 6 7 8 9 10 11
7772 QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3, artkit4, WorldEffectID, AIAnimKitID FROM gameobject_template_addon");
7773
7774 if (!result)
7775 {
7776 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
7777 return;
7778 }
7779
7780 uint32 count = 0;
7781 do
7782 {
7783 Field* fields = result->Fetch();
7784
7785 uint32 entry = fields[0].GetUInt32();
7786
7787 GameObjectTemplate const* got = GetGameObjectTemplate(entry);
7788 if (!got)
7789 {
7790 TC_LOG_ERROR("sql.sql", "GameObject template (Entry: {}) does not exist but has a record in `gameobject_template_addon`", entry);
7791 continue;
7792 }
7793
7795 gameObjectAddon.Faction = uint32(fields[1].GetUInt16());
7796 gameObjectAddon.Flags = fields[2].GetUInt32();
7797 gameObjectAddon.Mingold = fields[3].GetUInt32();
7798 gameObjectAddon.Maxgold = fields[4].GetUInt32();
7799 gameObjectAddon.WorldEffectID = fields[10].GetUInt32();
7800 gameObjectAddon.AIAnimKitID = fields[11].GetUInt32();
7801
7802 for (uint32 i = 0; i < gameObjectAddon.ArtKits.size(); ++i)
7803 {
7804 uint32 artKitID = fields[5 + i].GetUInt32();
7805 if (!artKitID)
7806 continue;
7807
7808 if (!sGameObjectArtKitStore.LookupEntry(artKitID))
7809 {
7810 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` ({}) defined, set to zero instead.", entry, i, artKitID);
7811 continue;
7812 }
7813
7814 gameObjectAddon.ArtKits[i] = artKitID;
7815 }
7816
7817 // checks
7818 if (gameObjectAddon.Faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.Faction))
7819 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid faction ({}) defined in `gameobject_template_addon`.", entry, gameObjectAddon.Faction);
7820
7821 if (gameObjectAddon.Maxgold > 0)
7822 {
7823 switch (got->type)
7824 {
7827 break;
7828 default:
7829 TC_LOG_ERROR("sql.sql", "GameObject (Entry {} GoType: {}) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
7830 break;
7831 }
7832 }
7833
7834 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
7835 {
7836 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid WorldEffectID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.WorldEffectID);
7837 gameObjectAddon.WorldEffectID = 0;
7838 }
7839
7840 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
7841 {
7842 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid AIAnimKitID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.AIAnimKitID);
7843 gameObjectAddon.AIAnimKitID = 0;
7844 }
7845
7846 ++count;
7847 }
7848 while (result->NextRow());
7849
7850 TC_LOG_INFO("server.loading", ">> Loaded {} game object template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7851}
7852
7854{
7855 uint32 oldMSTime = getMSTime();
7856
7857 // 0 1 2
7858 QueryResult result = WorldDatabase.Query("SELECT spawnId, faction, flags FROM gameobject_overrides");
7859 if (!result)
7860 {
7861 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject faction and flags overrides. DB table `gameobject_overrides` is empty.");
7862 return;
7863 }
7864
7865 uint32 count = 0;
7866 do
7867 {
7868 Field* fields = result->Fetch();
7869
7870 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
7871 GameObjectData const* goData = GetGameObjectData(spawnId);
7872 if (!goData)
7873 {
7874 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) does not exist but has a record in `gameobject_overrides`", spawnId);
7875 continue;
7876 }
7877
7878 GameObjectOverride& gameObjectOverride = _gameObjectOverrideStore[spawnId];
7879 gameObjectOverride.Faction = fields[1].GetUInt16();
7880 gameObjectOverride.Flags = fields[2].GetUInt32();
7881
7882 if (gameObjectOverride.Faction && !sFactionTemplateStore.LookupEntry(gameObjectOverride.Faction))
7883 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) has invalid faction ({}) defined in `gameobject_overrides`.", spawnId, gameObjectOverride.Faction);
7884
7885 ++count;
7886 } while (result->NextRow());
7887
7888 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject faction and flags overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7889}
7890
7892{
7893 uint32 oldMSTime = getMSTime();
7894
7895 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
7896
7897 if (!result)
7898 {
7899 TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
7900 return;
7901 }
7902
7903 uint32 count = 0;
7904
7905 do
7906 {
7907 Field* fields = result->Fetch();
7908 uint8 level = fields[0].GetUInt8();
7909 uint32 basexp = fields[1].GetInt32();
7910 _baseXPTable[level] = basexp;
7911 ++count;
7912 }
7913 while (result->NextRow());
7914
7915 TC_LOG_INFO("server.loading", ">> Loaded {} BaseXP definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7916}
7917
7919{
7920 return _baseXPTable[level] ? _baseXPTable[level] : 0;
7921}
7922
7924{
7925 if (level < _playerXPperLevel.size())
7926 return _playerXPperLevel[level];
7927 return 0;
7928}
7929
7931{
7932 if (!areaEntry)
7933 return 0;
7934
7935 // Get level for the area
7936 FishingBaseSkillContainer::const_iterator itr = _fishingBaseForAreaStore.find(areaEntry->ID);
7937 if (itr != _fishingBaseForAreaStore.end())
7938 return itr->second;
7939
7940 // If there is no data for the current area and it has a parent area, get data from the last (recursive)
7941 if (AreaTableEntry const* parentAreaEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID))
7942 return GetFishingBaseSkillLevel(parentAreaEntry);
7943
7944 TC_LOG_ERROR("sql.sql", "Fishable areaId {} is not properly defined in `skill_fishing_base_level`.", areaEntry->ID);
7945
7946 return 0;
7947}
7948
7950{
7951 auto itr = _skillTiers.find(skillTierId);
7952 return itr != _skillTiers.end() ? &itr->second : nullptr;
7953}
7954
7956{
7957 if (tierIndex >= MAX_SKILL_STEP)
7958 tierIndex = MAX_SKILL_STEP - 1;
7959
7960 while (Value[tierIndex] == 0 && tierIndex > 0)
7961 --tierIndex;
7962
7963 return Value[tierIndex];
7964}
7965
7967{
7968 uint32 oldMSTime = getMSTime();
7969 // 0 1 2
7970 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
7971
7972 if (!result)
7973 {
7974 TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
7975 return;
7976 }
7977
7978 uint32 count = 0;
7979
7980 do
7981 {
7982 Field* fields = result->Fetch();
7983 std::string word = fields[0].GetString();
7984 uint32 entry = fields[1].GetUInt32();
7985 bool half = fields[2].GetBool();
7986 if (half)
7987 _petHalfName1[entry].push_back(word);
7988 else
7989 _petHalfName0[entry].push_back(word);
7990 ++count;
7991 }
7992 while (result->NextRow());
7993
7994 TC_LOG_INFO("server.loading", ">> Loaded {} pet name parts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7995}
7996
7998{
7999 uint32 oldMSTime = getMSTime();
8000
8001 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
8002 if (result)
8003 {
8004 Field* fields = result->Fetch();
8005 _hiPetNumber = fields[0].GetUInt32()+1;
8006 }
8007
8008 TC_LOG_INFO("server.loading", ">> Loaded the max pet number: {} in {} ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
8009}
8010
8012{
8013 std::vector<std::string>& list0 = _petHalfName0[entry];
8014 std::vector<std::string>& list1 = _petHalfName1[entry];
8015
8016 if (list0.empty() || list1.empty())
8017 {
8018 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
8019 if (!cinfo)
8020 return std::string();
8021
8022 char const* petname = DB2Manager::GetCreatureFamilyPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
8023 if (petname)
8024 return std::string(petname);
8025 else
8026 return cinfo->Name;
8027 }
8028
8029 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
8030}
8031
8033{
8034 uint32 oldMSTime = getMSTime();
8035
8036 _repRewardRateStore.clear(); // for reload case
8037
8038 uint32 count = 0; // 0 1 2 3 4 5 6 7
8039 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");
8040 if (!result)
8041 {
8042 TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
8043 return;
8044 }
8045
8046 do
8047 {
8048 Field* fields = result->Fetch();
8049
8050 uint32 factionId = fields[0].GetUInt32();
8051
8052 RepRewardRate repRate;
8053
8054 repRate.questRate = fields[1].GetFloat();
8055 repRate.questDailyRate = fields[2].GetFloat();
8056 repRate.questWeeklyRate = fields[3].GetFloat();
8057 repRate.questMonthlyRate = fields[4].GetFloat();
8058 repRate.questRepeatableRate = fields[5].GetFloat();
8059 repRate.creatureRate = fields[6].GetFloat();
8060 repRate.spellRate = fields[7].GetFloat();
8061
8062 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8063 if (!factionEntry)
8064 {
8065 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_reward_rate`", factionId);
8066 continue;
8067 }
8068
8069 if (repRate.questRate < 0.0f)
8070 {
8071 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate {}, skipping data for faction {}", repRate.questRate, factionId);
8072 continue;
8073 }
8074
8075 if (repRate.questDailyRate < 0.0f)
8076 {
8077 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate {}, skipping data for faction {}", repRate.questDailyRate, factionId);
8078 continue;
8079 }
8080
8081 if (repRate.questWeeklyRate < 0.0f)
8082 {
8083 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate {}, skipping data for faction {}", repRate.questWeeklyRate, factionId);
8084 continue;
8085 }
8086
8087 if (repRate.questMonthlyRate < 0.0f)
8088 {
8089 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate {}, skipping data for faction {}", repRate.questMonthlyRate, factionId);
8090 continue;
8091 }
8092
8093 if (repRate.questRepeatableRate < 0.0f)
8094 {
8095 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate {}, skipping data for faction {}", repRate.questRepeatableRate, factionId);
8096 continue;
8097 }
8098
8099 if (repRate.creatureRate < 0.0f)
8100 {
8101 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate {}, skipping data for faction {}", repRate.creatureRate, factionId);
8102 continue;
8103 }
8104
8105 if (repRate.spellRate < 0.0f)
8106 {
8107 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate {}, skipping data for faction {}", repRate.spellRate, factionId);
8108 continue;
8109 }
8110
8111 _repRewardRateStore[factionId] = repRate;
8112
8113 ++count;
8114 }
8115 while (result->NextRow());
8116
8117 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_reward_rate in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8118}
8119
8121{
8122 uint32 oldMSTime = getMSTime();
8123
8124 // For reload case
8125 _repOnKillStore.clear();
8126
8127 uint32 count = 0;
8128
8129 // 0 1 2
8130 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
8131 // 3 4 5 6 7 8 9
8132 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
8133 "FROM creature_onkill_reputation");
8134
8135 if (!result)
8136 {
8137 TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
8138 return;
8139 }
8140
8141 do
8142 {
8143 Field* fields = result->Fetch();
8144
8145 uint32 creature_id = fields[0].GetUInt32();
8146
8147 ReputationOnKillEntry repOnKill;
8148 repOnKill.RepFaction1 = fields[1].GetInt16();
8149 repOnKill.RepFaction2 = fields[2].GetInt16();
8150 repOnKill.IsTeamAward1 = fields[3].GetBool();
8151 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
8152 repOnKill.RepValue1 = fields[5].GetInt32();
8153 repOnKill.IsTeamAward2 = fields[6].GetBool();
8154 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
8155 repOnKill.RepValue2 = fields[8].GetInt32();
8156 repOnKill.TeamDependent = fields[9].GetBool();
8157
8158 if (!GetCreatureTemplate(creature_id))
8159 {
8160 TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry ({}), skipped", creature_id);
8161 continue;
8162 }
8163
8164 if (repOnKill.RepFaction1)
8165 {
8166 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
8167 if (!factionEntry1)
8168 {
8169 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
8170 continue;
8171 }
8172 }
8173
8174 if (repOnKill.RepFaction2)
8175 {
8176 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
8177 if (!factionEntry2)
8178 {
8179 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
8180 continue;
8181 }
8182 }
8183
8184 _repOnKillStore[creature_id] = repOnKill;
8185
8186 ++count;
8187 } while (result->NextRow());
8188
8189 TC_LOG_INFO("server.loading", ">> Loaded {} creature award reputation definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8190}
8191
8193{
8194 uint32 oldMSTime = getMSTime();
8195
8196 _repSpilloverTemplateStore.clear(); // for reload case
8197
8198 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8199 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");
8200
8201 if (!result)
8202 {
8203 TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
8204 return;
8205 }
8206
8207 do
8208 {
8209 Field* fields = result->Fetch();
8210
8211 uint32 factionId = fields[0].GetUInt16();
8212
8213 RepSpilloverTemplate repTemplate;
8214
8215 repTemplate.faction[0] = fields[1].GetUInt16();
8216 repTemplate.faction_rate[0] = fields[2].GetFloat();
8217 repTemplate.faction_rank[0] = fields[3].GetUInt8();
8218 repTemplate.faction[1] = fields[4].GetUInt16();
8219 repTemplate.faction_rate[1] = fields[5].GetFloat();
8220 repTemplate.faction_rank[1] = fields[6].GetUInt8();
8221 repTemplate.faction[2] = fields[7].GetUInt16();
8222 repTemplate.faction_rate[2] = fields[8].GetFloat();
8223 repTemplate.faction_rank[2] = fields[9].GetUInt8();
8224 repTemplate.faction[3] = fields[10].GetUInt16();
8225 repTemplate.faction_rate[3] = fields[11].GetFloat();
8226 repTemplate.faction_rank[3] = fields[12].GetUInt8();
8227 repTemplate.faction[4] = fields[13].GetUInt16();
8228 repTemplate.faction_rate[4] = fields[14].GetFloat();
8229 repTemplate.faction_rank[4] = fields[15].GetUInt8();
8230
8231 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8232
8233 if (!factionEntry)
8234 {
8235 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template`", factionId);
8236 continue;
8237 }
8238
8239 if (factionEntry->ParentFactionID == 0)
8240 {
8241 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} in `reputation_spillover_template` does not belong to any team, skipping", factionId);
8242 continue;
8243 }
8244
8245 bool invalidSpilloverFaction = false;
8246 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
8247 {
8248 if (repTemplate.faction[i])
8249 {
8250 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
8251
8252 if (!factionSpillover)
8253 {
8254 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);
8255 invalidSpilloverFaction = true;
8256 break;
8257 }
8258
8259 if (!factionSpillover->CanHaveReputation())
8260 {
8261 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);
8262 invalidSpilloverFaction = true;
8263 break;
8264 }
8265
8266 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
8267 {
8268 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]);
8269 invalidSpilloverFaction = true;
8270 break;
8271 }
8272 }
8273 }
8274
8275 if (invalidSpilloverFaction)
8276 continue;
8277
8278 _repSpilloverTemplateStore[factionId] = repTemplate;
8279
8280 ++count;
8281 }
8282 while (result->NextRow());
8283
8284 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_spillover_template in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8285}
8286
8288{
8289 uint32 oldMSTime = getMSTime();
8290
8291 _pointsOfInterestStore.clear(); // need for reload case
8292
8293 uint32 count = 0;
8294
8295 // 0 1 2 3 4 5 6 7 8
8296 QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, PositionZ, Icon, Flags, Importance, Name, WMOGroupID FROM points_of_interest");
8297
8298 if (!result)
8299 {
8300 TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
8301 return;
8302 }
8303
8304 do
8305 {
8306 Field* fields = result->Fetch();
8307
8308 uint32 id = fields[0].GetUInt32();
8309
8310 PointOfInterest pointOfInterest;
8311 pointOfInterest.ID = id;
8312 pointOfInterest.Pos.Relocate(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat());
8313 pointOfInterest.Icon = fields[4].GetUInt32();
8314 pointOfInterest.Flags = fields[5].GetUInt32();
8315 pointOfInterest.Importance = fields[6].GetUInt32();
8316 pointOfInterest.Name = fields[7].GetString();
8317 pointOfInterest.WMOGroupID = fields[8].GetInt32();
8318
8319 if (!Trinity::IsValidMapCoord(pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ()))
8320 {
8321 TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: {}) have invalid coordinates (PositionX: {} PositionY: {}, PositionZ: {}), ignored.",
8322 id, pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ());
8323 continue;
8324 }
8325
8326 _pointsOfInterestStore[id] = pointOfInterest;
8327
8328 ++count;
8329 } while (result->NextRow());
8330
8331 TC_LOG_INFO("server.loading", ">> Loaded {} Points of Interest definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8332}
8333
8335{
8336 uint32 oldMSTime = getMSTime();
8337
8338 _questPOIStore.clear(); // need for reload case
8339
8340 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
8341 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");
8342 if (!result)
8343 {
8344 TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
8345 return;
8346 }
8347
8348 // 0 1 2 3 4
8349 QueryResult pointsResult = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y, Z FROM quest_poi_points ORDER BY QuestID DESC, Idx1, Idx2");
8350
8351 std::unordered_map<int32, std::map<int32, std::vector<QuestPOIBlobPoint>>> allPoints;
8352
8353 if (pointsResult)
8354 {
8355 do
8356 {
8357 Field* fields = pointsResult->Fetch();
8358
8359 int32 QuestID = fields[0].GetInt32();
8360 int32 Idx1 = fields[1].GetInt32();
8361 int32 x = fields[2].GetInt32();
8362 int32 y = fields[3].GetInt32();
8363 int32 z = fields[4].GetInt32();
8364
8365 allPoints[QuestID][Idx1].emplace_back(x, y, z);
8366 } while (pointsResult->NextRow());
8367 }
8368
8369 do
8370 {
8371 Field* fields = result->Fetch();
8372
8373 int32 questID = fields[0].GetInt32();
8374 int32 blobIndex = fields[1].GetInt32();
8375 int32 idx1 = fields[2].GetInt32();
8376 int32 objectiveIndex = fields[3].GetInt32();
8377 int32 questObjectiveID = fields[4].GetInt32();
8378 int32 questObjectID = fields[5].GetInt32();
8379 int32 mapID = fields[6].GetInt32();
8380 int32 uiMapID = fields[7].GetInt32();
8381 int32 priority = fields[8].GetInt32();
8382 int32 flags = fields[9].GetInt32();
8383 int32 worldEffectID = fields[10].GetInt32();
8384 int32 playerConditionID = fields[11].GetInt32();
8385 int32 navigationPlayerConditionID = fields[12].GetInt32();
8386 int32 spawnTrackingID = fields[13].GetInt32();
8387 bool alwaysAllowMergingBlobs = fields[14].GetBool();
8388
8389 if (!GetQuestTemplate(questID))
8390 TC_LOG_ERROR("sql.sql", "`quest_poi` quest id ({}) Idx1 ({}) does not exist in `quest_template`", questID, idx1);
8391
8392 if (std::map<int32, std::vector<QuestPOIBlobPoint>>* blobs = Trinity::Containers::MapGetValuePtr(allPoints, questID))
8393 {
8394 if (std::vector<QuestPOIBlobPoint>* points = Trinity::Containers::MapGetValuePtr(*blobs, idx1))
8395 {
8396 QuestPOIData& poiData = _questPOIStore[questID];
8397 poiData.QuestID = questID;
8398 poiData.Blobs.emplace_back(blobIndex, objectiveIndex, questObjectiveID, questObjectID, mapID, uiMapID, priority, flags,
8399 worldEffectID, playerConditionID, navigationPlayerConditionID, spawnTrackingID, std::move(*points), alwaysAllowMergingBlobs);
8400 continue;
8401 }
8402 }
8403
8404 TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest {} POI id {}", questID, blobIndex);
8405
8406 } while (result->NextRow());
8407
8408 TC_LOG_INFO("server.loading", ">> Loaded {} quest POI definitions in {} ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
8409}
8410
8412{
8413 uint32 oldMSTime = getMSTime();
8414
8415 _spellClickInfoStore.clear();
8416 // 0 1 2 3
8417 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
8418
8419 if (!result)
8420 {
8421 TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
8422 return;
8423 }
8424
8425 uint32 count = 0;
8426
8427 do
8428 {
8429 Field* fields = result->Fetch();
8430
8431 uint32 npc_entry = fields[0].GetUInt32();
8432 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
8433 if (!cInfo)
8434 {
8435 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template {}. Skipping entry.", npc_entry);
8436 continue;
8437 }
8438
8439 uint32 spellid = fields[1].GetUInt32();
8440 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid, DIFFICULTY_NONE);
8441 if (!spellinfo)
8442 {
8443 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown spellid {}. Skipping entry.", npc_entry, spellid);
8444 continue;
8445 }
8446
8447 uint8 userType = fields[3].GetUInt16();
8448 if (userType >= SPELL_CLICK_USER_MAX)
8449 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown user type {}. Skipping entry.", npc_entry, uint32(userType));
8450
8451 uint8 castFlags = fields[2].GetUInt8();
8452 SpellClickInfo info;
8453 info.spellId = spellid;
8454 info.castFlags = castFlags;
8455 info.userType = SpellClickUserTypes(userType);
8456 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
8457
8458 ++count;
8459 }
8460 while (result->NextRow());
8461
8462 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
8463 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
8464 for (auto& creatureTemplatePair : _creatureTemplateStore)
8465 {
8466 if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
8467 {
8468 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);
8469 creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
8470 }
8471 }
8472
8473 TC_LOG_INFO("server.loading", ">> Loaded {} spellclick definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8474}
8475
8480
8482{
8483 // remove mapid*cellid -> guid_set map
8484 auto itr = _creatureDataStore.find(spawnId);
8485 if (itr == _creatureDataStore.end())
8486 return;
8487
8488 RemoveCreatureFromGrid(&itr->second);
8489 OnDeleteSpawnData(&itr->second);
8490
8491 _creatureDataStore.erase(itr);
8492}
8493
8498
8500{
8501 // remove mapid*cellid -> guid_set map
8502 auto itr = _gameObjectDataStore.find(spawnId);
8503 if (itr == _gameObjectDataStore.end())
8504 return;
8505
8506 RemoveGameobjectFromGrid(&itr->second);
8507 OnDeleteSpawnData(&itr->second);
8508
8509 _gameObjectDataStore.erase(itr);
8510}
8511
8512void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table)
8513{
8514 uint32 oldMSTime = getMSTime();
8515
8516 map.clear(); // need for reload case
8517
8518 uint32 count = 0;
8519
8520 QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM {}", table);
8521
8522 if (!result)
8523 {
8524 TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `{}`, table is empty.", table);
8525 return;
8526 }
8527
8528 do
8529 {
8530 uint32 id = result->Fetch()[0].GetUInt32();
8531 uint32 quest = result->Fetch()[1].GetUInt32();
8532
8533 if (!_questTemplates.count(quest))
8534 {
8535 TC_LOG_ERROR("sql.sql", "Table `{}`: Quest {} listed for entry {} does not exist.", table, quest, id);
8536 continue;
8537 }
8538
8539 map.insert(QuestRelations::value_type(id, quest));
8540 if (reverseMap)
8541 reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
8542 ++count;
8543 } while (result->NextRow());
8544
8545 TC_LOG_INFO("server.loading", ">> Loaded {} quest relations from {} in {} ms", count, table, GetMSTimeDiffToNow(oldMSTime));
8546}
8547
8549{
8550 LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter");
8551
8552 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
8553 {
8554 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8555 if (!goInfo)
8556 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8557 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8558 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);
8559 }
8560}
8561
8563{
8565
8566 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
8567 {
8568 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8569 if (!goInfo)
8570 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8571 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8572 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);
8573 }
8574}
8575
8577{
8578 LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter");
8579
8580 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
8581 {
8582 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8583 if (!cInfo)
8584 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8585 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8586 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);
8587 }
8588}
8589
8591{
8593
8594 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
8595 {
8596 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8597 if (!cInfo)
8598 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8599 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8600 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);
8601 }
8602}
8603
8605{
8606 while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second))
8607 ++_it;
8608}
8609
8611{
8612 return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId));
8613}
8614
8616{
8617 uint32 oldMSTime = getMSTime();
8618
8619 _reservedNamesStore.clear(); // need for reload case
8620
8621 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
8622
8623 if (!result)
8624 {
8625 TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
8626 return;
8627 }
8628
8629 uint32 count = 0;
8630
8631 Field* fields;
8632 do
8633 {
8634 fields = result->Fetch();
8635 std::string name= fields[0].GetString();
8636
8637 std::wstring wstr;
8638 if (!Utf8toWStr (name, wstr))
8639 {
8640 TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: {}", name);
8641 continue;
8642 }
8643
8644 wstrToLower(wstr);
8645
8646 _reservedNamesStore.insert(wstr);
8647 ++count;
8648 }
8649 while (result->NextRow());
8650
8651 TC_LOG_INFO("server.loading", ">> Loaded {} reserved player names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8652}
8653
8654bool ObjectMgr::IsReservedName(std::string_view name) const
8655{
8656 std::wstring wstr;
8657 if (!Utf8toWStr (name, wstr))
8658 return false;
8659
8660 wstrToLower(wstr);
8661
8662 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
8663}
8664
8666{
8667 if (std::shared_ptr<Realm const> currentRealm = sRealmList->GetCurrentRealm())
8668 if (Cfg_CategoriesEntry const* category = sCfgCategoriesStore.LookupEntry(currentRealm->Timezone))
8669 return create ? category->GetCreateCharsetMask() : category->GetExistingCharsetMask();
8670
8671 return create ? CfgCategoriesCharsets::English : CfgCategoriesCharsets::Any; // basic-Latin at create, any at login
8672}
8673
8674bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
8675{
8676 if (strictMask == 0) // any language, ignore realm
8677 {
8678 if (isExtendedLatinString(wstr, numericOrSpace))
8679 return true;
8680 if (isCyrillicString(wstr, numericOrSpace))
8681 return true;
8682 if (isKoreanString(wstr, numericOrSpace))
8683 return true;
8684 if (isChineseString(wstr, numericOrSpace))
8685 return true;
8686 return false;
8687 }
8688
8689 if (strictMask & 0x2) // realm zone specific
8690 {
8693 return true;
8694 if (lt.HasFlag(CfgCategoriesCharsets::Latin1) && isExtendedLatinString(wstr, numericOrSpace))
8695 return true;
8696 if (lt.HasFlag(CfgCategoriesCharsets::English) && isBasicLatinString(wstr, numericOrSpace))
8697 return true;
8698 if (lt.HasFlag(CfgCategoriesCharsets::Russian) && isCyrillicString(wstr, numericOrSpace))
8699 return true;
8700 if (lt.HasFlag(CfgCategoriesCharsets::Korean) && isKoreanString(wstr, numericOrSpace))
8701 return true;
8702 if (lt.HasFlag(CfgCategoriesCharsets::Chinese) && isChineseString(wstr, numericOrSpace))
8703 return true;
8704 }
8705
8706 if (strictMask & 0x1) // basic Latin
8707 {
8708 if (isBasicLatinString(wstr, numericOrSpace))
8709 return true;
8710 }
8711
8712 return false;
8713}
8714
8715ResponseCodes ObjectMgr::CheckPlayerName(std::string_view name, LocaleConstant locale, bool create /*= false*/)
8716{
8717 std::wstring wname;
8718 if (!Utf8toWStr(name, wname))
8720
8721 if (wname.size() > MAX_PLAYER_NAME)
8722 return CHAR_NAME_TOO_LONG;
8723
8724 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
8725 if (wname.size() < minName)
8726 return CHAR_NAME_TOO_SHORT;
8727
8728 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
8729 if (!isValidString(wname, strictMask, false, create))
8731
8732 wstrToLower(wname);
8733 for (size_t i = 2; i < wname.size(); ++i)
8734 if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
8736
8737 return sDB2Manager.ValidateName(wname, locale);
8738}
8739
8740bool ObjectMgr::IsValidCharterName(std::string_view name)
8741{
8742 std::wstring wname;
8743 if (!Utf8toWStr(name, wname))
8744 return false;
8745
8746 if (wname.size() > MAX_CHARTER_NAME)
8747 return false;
8748
8749 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
8750 if (wname.size() < minName)
8751 return false;
8752
8753 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
8754
8755 return isValidString(wname, strictMask, true);
8756}
8757
8759{
8760 std::wstring wname;
8761 if (!Utf8toWStr(name, wname))
8762 return PET_NAME_INVALID;
8763
8764 if (wname.size() > MAX_PET_NAME)
8765 return PET_NAME_TOO_LONG;
8766
8767 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
8768 if (wname.size() < minName)
8769 return PET_NAME_TOO_SHORT;
8770
8771 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
8772 if (!isValidString(wname, strictMask, false))
8774
8775 return PET_NAME_SUCCESS;
8776}
8777
8779{
8780 uint32 oldMSTime = getMSTime();
8781
8782 _gameObjectForQuestStore.clear(); // need for reload case
8783
8784 if (_gameObjectTemplateStore.empty())
8785 {
8786 TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
8787 return;
8788 }
8789
8790 uint32 count = 0;
8791
8792 // collect GO entries for GO that must activated
8793 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
8794 {
8795 switch (gameObjectTemplatePair.second.type)
8796 {
8798 break;
8800 {
8801 // scan GO chest with loot including quest items
8802 // find quest loot for GO
8803 if (gameObjectTemplatePair.second.chest.questID
8804 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestLoot)
8805 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPersonalLoot)
8806 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPushLoot))
8807 break;
8808 continue;
8809 }
8811 {
8812 if (gameObjectTemplatePair.second.generic.questID > 0) //quests objects
8813 break;
8814 continue;
8815 }
8817 {
8818 if (gameObjectTemplatePair.second.spellFocus.questID > 0) //quests objects
8819 break;
8820 continue;
8821 }
8823 {
8824 if (gameObjectTemplatePair.second.goober.questID > 0) //quests objects
8825 break;
8826 continue;
8827 }
8829 {
8830 // scan GO chest with loot including quest items
8831 // find quest loot for GO
8832 if (LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.gatheringNode.chestLoot))
8833 break;
8834 continue;
8835 }
8836 default:
8837 continue;
8838 }
8839
8840 _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
8841 ++count;
8842 }
8843
8844 for (auto [questObjectiveId, objective] : _questObjectives)
8845 {
8846 if (objective->Type != QUEST_OBJECTIVE_GAMEOBJECT)
8847 continue;
8848
8849 _gameObjectForQuestStore.insert(objective->ObjectID);
8850 ++count;
8851 }
8852
8853 TC_LOG_INFO("server.loading", ">> Loaded {} GameObjects for quests in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8854}
8855
8857{
8858 uint32 oldMSTime = getMSTime();
8859
8860 _trinityStringStore.clear(); // for reload case
8861
8862 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");
8863 if (!result)
8864 {
8865 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.");
8866 return false;
8867 }
8868
8869 do
8870 {
8871 Field* fields = result->Fetch();
8872
8873 uint32 entry = fields[0].GetUInt32();
8874
8875 TrinityString& data = _trinityStringStore[entry];
8876
8877 data.Content.resize(DEFAULT_LOCALE + 1);
8878
8879 for (int8 i = OLD_TOTAL_LOCALES - 1; i >= 0; --i)
8880 AddLocaleString(fields[i + 1].GetStringView(), LocaleConstant(i), data.Content);
8881 }
8882 while (result->NextRow());
8883
8884 TC_LOG_INFO("server.loading", ">> Loaded {} trinity strings in {} ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
8885 return true;
8886}
8887
8888char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
8889{
8890 if (TrinityString const* ts = GetTrinityString(entry))
8891 {
8892 if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
8893 return ts->Content[locale].c_str();
8894 return ts->Content[DEFAULT_LOCALE].c_str();
8895 }
8896
8897 TC_LOG_ERROR("sql.sql", "Trinity string entry {} not found in DB.", entry);
8898 return "<error>";
8899}
8900
8902{
8903 uint32 oldMSTime = getMSTime();
8904
8905 _fishingBaseForAreaStore.clear(); // for reload case
8906
8907 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
8908
8909 if (!result)
8910 {
8911 TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
8912 return;
8913 }
8914
8915 uint32 count = 0;
8916
8917 do
8918 {
8919 Field* fields = result->Fetch();
8920 uint32 entry = fields[0].GetUInt32();
8921 int32 skill = fields[1].GetInt16();
8922
8923 AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
8924 if (!fArea)
8925 {
8926 TC_LOG_ERROR("sql.sql", "AreaId {} defined in `skill_fishing_base_level` does not exist", entry);
8927 continue;
8928 }
8929
8930 _fishingBaseForAreaStore[entry] = skill;
8931 ++count;
8932 }
8933 while (result->NextRow());
8934
8935 TC_LOG_INFO("server.loading", ">> Loaded {} areas for fishing base skill level in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8936}
8937
8939{
8940 uint32 oldMSTime = getMSTime();
8941
8942 QueryResult result = WorldDatabase.Query("SELECT ID, Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9, Value10, "
8943 " Value11, Value12, Value13, Value14, Value15, Value16 FROM skill_tiers");
8944
8945 if (!result)
8946 {
8947 TC_LOG_ERROR("server.loading", ">> Loaded 0 skill max values. DB table `skill_tiers` is empty.");
8948 return;
8949 }
8950
8951 do
8952 {
8953 Field* fields = result->Fetch();
8954 uint32 id = fields[0].GetUInt32();
8955 SkillTiersEntry& tier = _skillTiers[id];
8956 for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
8957 tier.Value[i] = fields[1 + i].GetUInt32();
8958
8959 } while (result->NextRow());
8960
8961 TC_LOG_INFO("server.loading", ">> Loaded {} skill max values in {} ms", uint32(_skillTiers.size()), GetMSTimeDiffToNow(oldMSTime));
8962}
8963
8964bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
8965{
8966 // get main part of the name
8967 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
8968 // prepare flags
8969 bool x = true;
8970 bool y = true;
8971
8972 // check declined names
8973 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
8974 {
8975 std::wstring wname;
8976 if (!Utf8toWStr(names.name[i], wname))
8977 return false;
8978
8979 if (mainpart != GetMainPartOfName(wname, i+1))
8980 x = false;
8981
8982 if (w_ownname != wname)
8983 y = false;
8984 }
8985 return (x || y);
8986}
8987
8989{
8990 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
8991 if (i!= _areaTriggerScriptStore.end())
8992 return i->second;
8993 return 0;
8994}
8995
8997{
8998 return SpellScriptsBounds(_spellScriptsStore.equal_range(spellId));
8999}
9000
9002{
9003 EventScriptContainer::const_iterator i = _eventScriptStore.find(eventId);
9004 if (i != _eventScriptStore.end())
9005 return i->second;
9006 return 0;
9007}
9008
9010{
9011 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
9012 if (!skill)
9013 return SKILL_RANGE_NONE;
9014
9015 if (sObjectMgr->GetSkillTier(rcEntry->SkillTierID))
9016 return SKILL_RANGE_RANK;
9017
9018 if (rcEntry->SkillID == SKILL_RUNEFORGING)
9019 return SKILL_RANGE_MONO;
9020
9021 switch (skill->CategoryID)
9022 {
9024 return SKILL_RANGE_MONO;
9026 return SKILL_RANGE_LANGUAGE;
9027 }
9028
9029 return SKILL_RANGE_LEVEL;
9030}
9031
9033{
9034 uint32 oldMSTime = getMSTime();
9035
9036 _gameTeleStore.clear(); // for reload case
9037
9038 // 0 1 2 3 4 5 6
9039 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
9040
9041 if (!result)
9042 {
9043 TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
9044 return;
9045 }
9046
9047 uint32 count = 0;
9048
9049 do
9050 {
9051 Field* fields = result->Fetch();
9052
9053 uint32 id = fields[0].GetUInt32();
9054
9055 GameTele gt;
9056
9057 gt.position_x = fields[1].GetFloat();
9058 gt.position_y = fields[2].GetFloat();
9059 gt.position_z = fields[3].GetFloat();
9060 gt.orientation = fields[4].GetFloat();
9061 gt.mapId = fields[5].GetUInt16();
9062 gt.name = fields[6].GetString();
9063
9065 {
9066 TC_LOG_ERROR("sql.sql", "Wrong position for id {} (name: {}) in `game_tele` table, ignoring.", id, gt.name);
9067 continue;
9068 }
9069
9070 if (!Utf8toWStr(gt.name, gt.wnameLow))
9071 {
9072 TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_tele` table, ignoring.", id);
9073 continue;
9074 }
9075
9077
9078 _gameTeleStore[id] = gt;
9079
9080 ++count;
9081 }
9082 while (result->NextRow());
9083
9084 TC_LOG_INFO("server.loading", ">> Loaded {} GameTeleports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9085}
9086
9087GameTele const* ObjectMgr::GetGameTele(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 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
9098 GameTele const* alt = nullptr;
9099 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9100 {
9101 if (itr->second.wnameLow == wname)
9102 return &itr->second;
9103 else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
9104 alt = &itr->second;
9105 }
9106
9107 return alt;
9108}
9109
9110GameTele const* ObjectMgr::GetGameTeleExactName(std::string_view name) const
9111{
9112 // explicit name case
9113 std::wstring wname;
9114 if (!Utf8toWStr(name, wname))
9115 return nullptr;
9116
9117 // converting string that we try to find to lower case
9118 wstrToLower(wname);
9119
9120 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9121 {
9122 if (itr->second.wnameLow == wname)
9123 return &itr->second;
9124 }
9125
9126 return nullptr;
9127}
9128
9130{
9131 // find max id
9132 uint32 new_id = 0;
9133 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9134 if (itr->first > new_id)
9135 new_id = itr->first;
9136
9137 // use next
9138 ++new_id;
9139
9140 if (!Utf8toWStr(tele.name, tele.wnameLow))
9141 return false;
9142
9143 wstrToLower(tele.wnameLow);
9144
9145 _gameTeleStore[new_id] = tele;
9146
9148
9149 stmt->setUInt32(0, new_id);
9150 stmt->setFloat(1, tele.position_x);
9151 stmt->setFloat(2, tele.position_y);
9152 stmt->setFloat(3, tele.position_z);
9153 stmt->setFloat(4, tele.orientation);
9154 stmt->setUInt16(5, uint16(tele.mapId));
9155 stmt->setString(6, tele.name);
9156
9157 WorldDatabase.Execute(stmt);
9158
9159 return true;
9160}
9161
9162bool ObjectMgr::DeleteGameTele(std::string_view name)
9163{
9164 // explicit name case
9165 std::wstring wname;
9166 if (!Utf8toWStr(name, wname))
9167 return false;
9168
9169 // converting string that we try to find to lower case
9170 wstrToLower(wname);
9171
9172 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9173 {
9174 if (itr->second.wnameLow == wname)
9175 {
9177
9178 stmt->setString(0, itr->second.name);
9179
9180 WorldDatabase.Execute(stmt);
9181
9182 _gameTeleStore.erase(itr);
9183 return true;
9184 }
9185 }
9186
9187 return false;
9188}
9189
9191{
9192 uint32 oldMSTime = getMSTime();
9193
9194 _mailLevelRewardStore.clear(); // for reload case
9195
9196 // 0 1 2 3
9197 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
9198
9199 if (!result)
9200 {
9201 TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
9202 return;
9203 }
9204
9205 uint32 count = 0;
9206
9207 do
9208 {
9209 Field* fields = result->Fetch();
9210
9211 uint8 level = fields[0].GetUInt8();
9212 Trinity::RaceMask<uint64> raceMask = { fields[1].GetUInt64() };
9213 uint32 mailTemplateId = fields[2].GetUInt32();
9214 uint32 senderEntry = fields[3].GetUInt32();
9215
9216 if (level > MAX_LEVEL)
9217 {
9218 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level {} that more supported by client ({}), ignoring.", level, MAX_LEVEL);
9219 continue;
9220 }
9221
9222 if ((raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
9223 {
9224 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask ({}) for level {} that not include any player races, ignoring.", raceMask.RawValue[0], level);
9225 continue;
9226 }
9227
9228 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
9229 {
9230 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId ({}) for level {} that invalid not include any player races, ignoring.", mailTemplateId, level);
9231 continue;
9232 }
9233
9234 if (!GetCreatureTemplate(senderEntry))
9235 {
9236 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);
9237 continue;
9238 }
9239
9240 _mailLevelRewardStore[level].emplace_back(raceMask, mailTemplateId, senderEntry);
9241
9242 ++count;
9243 }
9244 while (result->NextRow());
9245
9246 TC_LOG_INFO("server.loading", ">> Loaded {} level dependent mail rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9247}
9248
9250{
9251 uint32 oldMSTime = getMSTime();
9252
9253 // For reload case
9254 _trainers.clear();
9255
9256 std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
9257 if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
9258 {
9259 do
9260 {
9261 Field* fields = trainerSpellsResult->Fetch();
9262
9263 Trainer::Spell spell;
9264 uint32 trainerId = fields[0].GetUInt32();
9265 spell.SpellId = fields[1].GetUInt32();
9266 spell.MoneyCost = fields[2].GetUInt32();
9267 spell.ReqSkillLine = fields[3].GetUInt32();
9268 spell.ReqSkillRank = fields[4].GetUInt32();
9269 spell.ReqAbility[0] = fields[5].GetUInt32();
9270 spell.ReqAbility[1] = fields[6].GetUInt32();
9271 spell.ReqAbility[2] = fields[7].GetUInt32();
9272 spell.ReqLevel = fields[8].GetUInt8();
9273
9274 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId, DIFFICULTY_NONE);
9275 if (!spellInfo)
9276 {
9277 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) for TrainerId {}, ignoring", spell.SpellId, trainerId);
9278 continue;
9279 }
9280
9281 if (spell.ReqSkillLine && !sSkillLineStore.LookupEntry(spell.ReqSkillLine))
9282 {
9283 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing skill (ReqSkillLine: {}) for TrainerId {} and SpellId {}, ignoring",
9284 spell.ReqSkillLine, trainerId, spell.SpellId);
9285 continue;
9286 }
9287
9288 bool allReqValid = true;
9289 for (std::size_t i = 0; i < spell.ReqAbility.size(); ++i)
9290 {
9291 uint32 requiredSpell = spell.ReqAbility[i];
9292 if (requiredSpell && !sSpellMgr->GetSpellInfo(requiredSpell, DIFFICULTY_NONE))
9293 {
9294 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (ReqAbility{}: {}) for TrainerId {} and SpellId {}, ignoring",
9295 i + 1, requiredSpell, trainerId, spell.SpellId);
9296 allReqValid = false;
9297 }
9298 }
9299
9300 if (!allReqValid)
9301 continue;
9302
9303 spellsByTrainer[trainerId].push_back(spell);
9304
9305 } while (trainerSpellsResult->NextRow());
9306 }
9307
9308 if (QueryResult trainersResult = WorldDatabase.Query("SELECT Id, Type, Greeting FROM trainer"))
9309 {
9310 do
9311 {
9312 Field* fields = trainersResult->Fetch();
9313 uint32 trainerId = fields[0].GetUInt32();
9314 Trainer::Type trainerType = Trainer::Type(fields[1].GetUInt8());
9315 std::string_view greeting = fields[2].GetStringView();
9316 std::vector<Trainer::Spell> spells;
9317 auto spellsItr = spellsByTrainer.find(trainerId);
9318 if (spellsItr != spellsByTrainer.end())
9319 {
9320 spells = std::move(spellsItr->second);
9321 spellsByTrainer.erase(spellsItr);
9322 }
9323
9324 _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, greeting, std::move(spells)));
9325
9326 } while (trainersResult->NextRow());
9327 }
9328
9329 for (auto const& unusedSpells : spellsByTrainer)
9330 {
9331 for (Trainer::Spell const& unusedSpell : unusedSpells.second)
9332 {
9333 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing trainer (TrainerId: {}) for SpellId {}, ignoring", unusedSpells.first, unusedSpell.SpellId);
9334 }
9335 }
9336
9337 if (QueryResult trainerLocalesResult = WorldDatabase.Query("SELECT Id, locale, Greeting_lang FROM trainer_locale"))
9338 {
9339 do
9340 {
9341 Field* fields = trainerLocalesResult->Fetch();
9342 uint32 trainerId = fields[0].GetUInt32();
9343 std::string localeName = fields[1].GetString();
9344
9345 LocaleConstant locale = GetLocaleByName(localeName);
9346 if (!IsValidLocale(locale) || !sWorld->getBoolConfig(CONFIG_LOAD_LOCALES) || locale == LOCALE_enUS)
9347 continue;
9348
9349 if (Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId))
9350 trainer->AddGreetingLocale(locale, fields[2].GetString());
9351 else
9352 TC_LOG_ERROR("sql.sql", "Table `trainer_locale` references non-existing trainer (TrainerId: {}) for locale {}, ignoring",
9353 trainerId, localeName);
9354
9355 } while (trainerLocalesResult->NextRow());
9356 }
9357
9358 TC_LOG_INFO("server.loading", ">> Loaded {} Trainers in {} ms", _trainers.size(), GetMSTimeDiffToNow(oldMSTime));
9359}
9360
9362{
9363 uint32 oldMSTime = getMSTime();
9364
9365 _creatureDefaultTrainers.clear();
9366
9367 if (QueryResult result = WorldDatabase.Query("SELECT CreatureID, TrainerID, MenuID, OptionID FROM creature_trainer"))
9368 {
9369 do
9370 {
9371 Field* fields = result->Fetch();
9372 uint32 creatureId = fields[0].GetUInt32();
9373 uint32 trainerId = fields[1].GetUInt32();
9374 uint32 gossipMenuId = fields[2].GetUInt32();
9375 uint32 gossipOptionId = fields[3].GetUInt32();
9376
9377 if (!GetCreatureTemplate(creatureId))
9378 {
9379 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing creature template (CreatureID: {}), ignoring", creatureId);
9380 continue;
9381 }
9382
9383 if (!GetTrainer(trainerId))
9384 {
9385 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing trainer (TrainerID: {}) for CreatureID {} MenuID {} OptionID {}, ignoring",
9386 trainerId, creatureId, gossipMenuId, gossipOptionId);
9387 continue;
9388 }
9389
9390 if (gossipMenuId || gossipOptionId)
9391 {
9392 Trinity::IteratorPair<GossipMenuItemsContainer::const_iterator> gossipMenuItems = GetGossipMenuItemsMapBounds(gossipMenuId);
9393 auto gossipOptionItr = std::find_if(gossipMenuItems.begin(), gossipMenuItems.end(), [gossipOptionId](std::pair<uint32 const, GossipMenuItems> const& entry)
9394 {
9395 return entry.second.OrderIndex == gossipOptionId;
9396 });
9397 if (gossipOptionItr == gossipMenuItems.end())
9398 {
9399 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing gossip menu option (MenuID {} OptionID {}) for CreatureID {} and TrainerID {}, ignoring",
9400 gossipMenuId, gossipOptionId, creatureId, trainerId);
9401 continue;
9402 }
9403 }
9404
9405 _creatureDefaultTrainers[std::make_tuple(creatureId, gossipMenuId, gossipOptionId)] = trainerId;
9406 } while (result->NextRow());
9407 }
9408
9409 TC_LOG_INFO("server.loading", ">> Loaded {} default trainers in {} ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
9410}
9411
9412uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
9413{
9414 // find all items from the reference vendor
9416 stmt->setUInt32(0, uint32(item));
9417 PreparedQueryResult result = WorldDatabase.Query(stmt);
9418
9419 if (!result)
9420 return 0;
9421
9422 uint32 count = 0;
9423 do
9424 {
9425 Field* fields = result->Fetch();
9426
9427 int32 item_id = fields[0].GetInt32();
9428
9429 // if item is a negative, its a reference
9430 if (item_id < 0)
9431 count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
9432 else
9433 {
9434 VendorItem vItem;
9435 vItem.item = item_id;
9436 vItem.maxcount = fields[1].GetUInt32();
9437 vItem.incrtime = fields[2].GetUInt32();
9438 vItem.ExtendedCost = fields[3].GetUInt32();
9439 vItem.Type = fields[4].GetUInt8();
9440 vItem.PlayerConditionId = fields[6].GetUInt32();
9441 vItem.IgnoreFiltering = fields[7].GetBool();
9442
9443 for (std::string_view token : Trinity::Tokenize(fields[5].GetStringView(), ' ', false))
9444 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9445 vItem.BonusListIDs.push_back(*bonusListID);
9446
9447 if (!IsVendorItemValid(vendor, vItem, nullptr, skip_vendors))
9448 continue;
9449
9450 VendorItemData& vList = _cacheVendorItemStore[vendor];
9451 vList.AddItem(std::move(vItem));
9452 ++count;
9453 }
9454 } while (result->NextRow());
9455
9456 return count;
9457}
9458
9460{
9461 uint32 oldMSTime = getMSTime();
9462
9463 // For reload case
9464 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
9465 itr->second.Clear();
9466 _cacheVendorItemStore.clear();
9467
9468 std::set<uint32> skip_vendors;
9469
9470 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionID, IgnoreFiltering FROM npc_vendor ORDER BY entry, slot ASC");
9471 if (!result)
9472 {
9473 TC_LOG_ERROR("server.loading", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
9474 return;
9475 }
9476
9477 uint32 count = 0;
9478
9479 do
9480 {
9481 Field* fields = result->Fetch();
9482
9483 uint32 entry = fields[0].GetUInt32();
9484 int32 item_id = fields[1].GetInt32();
9485
9486 // if item is a negative, its a reference
9487 if (item_id < 0)
9488 count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
9489 else
9490 {
9491 VendorItem vItem;
9492 vItem.item = item_id;
9493 vItem.maxcount = fields[2].GetUInt32();
9494 vItem.incrtime = fields[3].GetUInt32();
9495 vItem.ExtendedCost = fields[4].GetUInt32();
9496 vItem.Type = fields[5].GetUInt8();
9497 vItem.PlayerConditionId = fields[7].GetUInt32();
9498 vItem.IgnoreFiltering = fields[8].GetBool();
9499
9500 for (std::string_view token : Trinity::Tokenize(fields[6].GetStringView(), ' ', false))
9501 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9502 vItem.BonusListIDs.push_back(*bonusListID);
9503
9504 if (!IsVendorItemValid(entry, vItem, nullptr, &skip_vendors))
9505 continue;
9506
9507 VendorItemData& vList = _cacheVendorItemStore[entry];
9508 vList.AddItem(std::move(vItem));
9509 ++count;
9510 }
9511 }
9512 while (result->NextRow());
9513
9514 TC_LOG_INFO("server.loading", ">> Loaded {} Vendors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9515}
9516
9518{
9519 uint32 oldMSTime = getMSTime();
9520
9521 _gossipMenusStore.clear();
9522
9523 // 0 1
9524 QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
9525
9526 if (!result)
9527 {
9528 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
9529 return;
9530 }
9531
9532 do
9533 {
9534 Field* fields = result->Fetch();
9535
9536 GossipMenus gMenu;
9537
9538 gMenu.MenuID = fields[0].GetUInt32();
9539 gMenu.TextID = fields[1].GetUInt32();
9540
9541 if (!GetNpcText(gMenu.TextID))
9542 {
9543 TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID {} is using non-existing TextID {}", gMenu.MenuID, gMenu.TextID);
9544 continue;
9545 }
9546
9547 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
9548 } while (result->NextRow());
9549
9550 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu IDs in {} ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
9551}
9552
9554{
9555 uint32 oldMSTime = getMSTime();
9556
9557 _gossipMenuItemsStore.clear();
9558
9559 QueryResult result = WorldDatabase.Query(
9560 // 0 1 2 3 4 5 6 7 8 9 10
9561 "SELECT MenuID, GossipOptionID, OptionID, OptionNpc, OptionText, OptionBroadcastTextID, Language, Flags, ActionMenuID, ActionPoiID, GossipNpcOptionID, "
9562 //11 12 13 14 15 16
9563 "BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID, SpellID, OverrideIconID "
9564 "FROM gossip_menu_option ORDER BY MenuID, OptionID");
9565
9566 if (!result)
9567 {
9568 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
9569 return;
9570 }
9571
9572 std::unordered_map<int32, int32> optionToNpcOption;
9573 for (GossipNPCOptionEntry const* npcOption : sGossipNPCOptionStore)
9574 optionToNpcOption[npcOption->GossipOptionID] = npcOption->ID;
9575
9576 do
9577 {
9578 Field* fields = result->Fetch();
9579
9580 GossipMenuItems gMenuItem;
9581
9582 gMenuItem.MenuID = fields[0].GetUInt32();
9583 gMenuItem.GossipOptionID = fields[1].GetInt32();
9584 gMenuItem.OrderIndex = fields[2].GetUInt32();
9585 gMenuItem.OptionNpc = GossipOptionNpc(fields[3].GetUInt8());
9586 gMenuItem.OptionText = fields[4].GetString();
9587 gMenuItem.OptionBroadcastTextID = fields[5].GetUInt32();
9588 gMenuItem.Language = fields[6].GetUInt32();
9589 gMenuItem.Flags = GossipOptionFlags(fields[7].GetInt32());
9590 gMenuItem.ActionMenuID = fields[8].GetUInt32();
9591 gMenuItem.ActionPoiID = fields[9].GetUInt32();
9592 gMenuItem.GossipNpcOptionID = fields[10].GetInt32OrNull();
9593 gMenuItem.BoxCoded = fields[11].GetBool();
9594 gMenuItem.BoxMoney = fields[12].GetUInt64();
9595 gMenuItem.BoxText = fields[13].GetString();
9596 gMenuItem.BoxBroadcastTextID = fields[14].GetUInt32();
9597 gMenuItem.SpellID = fields[15].GetInt32OrNull();
9598 gMenuItem.OverrideIconID = fields[16].GetInt32OrNull();
9599
9600 if (gMenuItem.OptionNpc >= GossipOptionNpc::Count)
9601 {
9602 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));
9603 gMenuItem.OptionNpc = GossipOptionNpc::None;
9604 }
9605
9606 if (gMenuItem.OptionBroadcastTextID)
9607 {
9608 if (!sBroadcastTextStore.LookupEntry(gMenuItem.OptionBroadcastTextID))
9609 {
9610 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);
9611 gMenuItem.OptionBroadcastTextID = 0;
9612 }
9613 }
9614
9615 if (gMenuItem.Language && !sLanguagesStore.LookupEntry(gMenuItem.Language))
9616 {
9617 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Language {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.Language);
9618 gMenuItem.Language = 0;
9619 }
9620
9621 if (gMenuItem.ActionMenuID && gMenuItem.OptionNpc != GossipOptionNpc::None)
9622 {
9623 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);
9624 gMenuItem.ActionMenuID = 0;
9625 }
9626
9627 if (gMenuItem.ActionPoiID)
9628 {
9629 if (gMenuItem.OptionNpc != GossipOptionNpc::None)
9630 {
9631 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);
9632 gMenuItem.ActionPoiID = 0;
9633 }
9634 else if (!GetPointOfInterest(gMenuItem.ActionPoiID))
9635 {
9636 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing ActionPoiID {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.ActionPoiID);
9637 gMenuItem.ActionPoiID = 0;
9638 }
9639 }
9640
9641 if (gMenuItem.GossipNpcOptionID)
9642 {
9643 if (!sGossipNPCOptionStore.LookupEntry(*gMenuItem.GossipNpcOptionID))
9644 {
9645 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing GossipNPCOption {}, ignoring",
9646 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.GossipNpcOptionID);
9647 gMenuItem.GossipNpcOptionID.reset();
9648 }
9649 }
9650 else if (int32 const* npcOptionId = Trinity::Containers::MapGetValuePtr(optionToNpcOption, gMenuItem.GossipOptionID))
9651 gMenuItem.GossipNpcOptionID = *npcOptionId;
9652
9653 if (gMenuItem.BoxBroadcastTextID)
9654 {
9655 if (!sBroadcastTextStore.LookupEntry(gMenuItem.BoxBroadcastTextID))
9656 {
9657 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);
9658 gMenuItem.BoxBroadcastTextID = 0;
9659 }
9660 }
9661
9662 if (gMenuItem.SpellID)
9663 {
9664 if (!sSpellMgr->GetSpellInfo(*gMenuItem.SpellID, DIFFICULTY_NONE))
9665 {
9666 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Spell {}, ignoring",
9667 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.SpellID);
9668 gMenuItem.SpellID.reset();
9669 }
9670 }
9671
9672 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
9673 } while (result->NextRow());
9674
9675 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option entries in {} ms", _gossipMenuItemsStore.size(), GetMSTimeDiffToNow(oldMSTime));
9676}
9677
9679{
9680 uint32 oldMSTime = getMSTime();
9681
9682 _gossipMenuAddonStore.clear();
9683
9684 // 0 1 2
9685 QueryResult result = WorldDatabase.Query("SELECT MenuID, FriendshipFactionID, LfgDungeonsID FROM gossip_menu_addon");
9686
9687 if (!result)
9688 {
9689 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_addon IDs. DB table `gossip_menu_addon` is empty!");
9690 return;
9691 }
9692
9693 do
9694 {
9695 Field* fields = result->Fetch();
9696
9697 uint32 menuID = fields[0].GetUInt32();
9698 GossipMenuAddon& addon = _gossipMenuAddonStore[menuID];
9699 addon.FriendshipFactionID = fields[1].GetInt32();
9700 addon.LfgDungeonsID = fields[2].GetInt32();
9701
9702 if (addon.FriendshipFactionID)
9703 {
9704 if (FactionEntry const* faction = sFactionStore.LookupEntry(addon.FriendshipFactionID))
9705 {
9706 if (!sFriendshipReputationStore.LookupEntry(faction->FriendshipRepID))
9707 {
9708 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using FriendshipFactionID {} referencing non-existing FriendshipRepID {}",
9709 menuID, addon.FriendshipFactionID, faction->FriendshipRepID);
9710 addon.FriendshipFactionID = 0;
9711 }
9712 }
9713 else
9714 {
9715 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using non-existing FriendshipFactionID {}", menuID, addon.FriendshipFactionID);
9716 addon.FriendshipFactionID = 0;
9717 }
9718 }
9719
9720 if (addon.LfgDungeonsID && sLFGDungeonsStore.LookupEntry(addon.LfgDungeonsID))
9721 {
9722 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using non-existing LfgDungeonsID {}", menuID, addon.LfgDungeonsID);
9723 addon.LfgDungeonsID = 0;
9724 }
9725
9726 } while (result->NextRow());
9727
9728 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_addon IDs in {} ms", uint32(_gossipMenuAddonStore.size()), GetMSTimeDiffToNow(oldMSTime));
9729}
9730
9732{
9733 return Trinity::Containers::MapGetValuePtr(_trainers, trainerId);
9734}
9735
9736uint32 ObjectMgr::GetCreatureTrainerForGossipOption(uint32 creatureId, uint32 gossipMenuId, uint32 gossipOptionId) const
9737{
9738 auto itr = _creatureDefaultTrainers.find(std::make_tuple(creatureId, gossipMenuId, gossipOptionId));
9739 if (itr != _creatureDefaultTrainers.end())
9740 return itr->second;
9741
9742 return 0;
9743}
9744
9745void ObjectMgr::AddVendorItem(uint32 entry, VendorItem const& vItem, bool persist /*= true*/)
9746{
9747 VendorItemData& vList = _cacheVendorItemStore[entry];
9748 vList.AddItem(vItem);
9749
9750 if (persist)
9751 {
9753
9754 stmt->setUInt32(0, entry);
9755 stmt->setUInt32(1, vItem.item);
9756 stmt->setUInt8(2, vItem.maxcount);
9757 stmt->setUInt32(3, vItem.incrtime);
9758 stmt->setUInt32(4, vItem.ExtendedCost);
9759 stmt->setUInt8(5, vItem.Type);
9760
9761 WorldDatabase.Execute(stmt);
9762 }
9763}
9764
9765bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/)
9766{
9767 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
9768 if (iter == _cacheVendorItemStore.end())
9769 return false;
9770
9771 if (!iter->second.RemoveItem(item, type))
9772 return false;
9773
9774 if (persist)
9775 {
9777
9778 stmt->setUInt32(0, entry);
9779 stmt->setUInt32(1, item);
9780 stmt->setUInt8(2, type);
9781
9782 WorldDatabase.Execute(stmt);
9783 }
9784
9785 return true;
9786}
9787
9788bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
9789{
9790 CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry);
9791 if (!cInfo)
9792 {
9793 if (player)
9795 else
9796 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: {}), ignore", vendor_entry);
9797 return false;
9798 }
9799
9800 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
9801 {
9802 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9803 {
9804 if (player)
9806 else
9807 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: {}) without vendor flag, ignore", vendor_entry);
9808
9809 if (skip_vendors)
9810 skip_vendors->insert(vendor_entry);
9811 }
9812 return false;
9813 }
9814
9815 if ((vItem.Type == ITEM_VENDOR_TYPE_ITEM && !GetItemTemplate(vItem.item)) ||
9816 (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(vItem.item)))
9817 {
9818 if (player)
9820 else
9821 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);
9822 return false;
9823 }
9824
9825 if (vItem.PlayerConditionId && !sPlayerConditionStore.LookupEntry(vItem.PlayerConditionId))
9826 {
9827 if (!sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_PLAYER_CONDITION, vItem.PlayerConditionId))
9828 {
9829 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: {}) with serverside PlayerConditionId ({}) for vendor ({}) without conditions, ignore", vItem.item, vItem.PlayerConditionId, vendor_entry);
9830 return false;
9831 }
9832 }
9833
9834 if (vItem.ExtendedCost && !sItemExtendedCostStore.LookupEntry(vItem.ExtendedCost))
9835 {
9836 if (player)
9838 else
9839 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);
9840 return false;
9841 }
9842
9843 if (vItem.Type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
9844 {
9845 if (vItem.maxcount > 0 && vItem.incrtime == 0)
9846 {
9847 if (player)
9848 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", vItem.maxcount);
9849 else
9850 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);
9851 return false;
9852 }
9853 else if (vItem.maxcount == 0 && vItem.incrtime > 0)
9854 {
9855 if (player)
9856 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
9857 else
9858 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);
9859 return false;
9860 }
9861
9862 for (int32 bonusListId : vItem.BonusListIDs)
9863 {
9864 if (ItemBonusMgr::GetItemBonuses(bonusListId).empty())
9865 {
9866 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}) with invalid bonus {} for vendor ({}), ignore", vItem.item, bonusListId, vendor_entry);
9867 return false;
9868 }
9869 }
9870 }
9871
9872 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
9873 if (!vItems)
9874 return true; // later checks for non-empty lists
9875
9876 if (vItems->FindItemCostPair(vItem.item, vItem.ExtendedCost, vItem.Type))
9877 {
9878 if (player)
9880 else
9881 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);
9882 return false;
9883 }
9884
9885 if (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && vItem.maxcount == 0)
9886 {
9887 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);
9888 return false;
9889 }
9890
9891 return true;
9892}
9893
9895{
9896 // We insert an empty placeholder here so we can use the
9897 // script id 0 as dummy for "no script found".
9898 [[maybe_unused]] uint32 const id = insert("", false);
9899
9900 ASSERT(id == 0);
9901}
9902
9904{
9905 IndexToName.reserve(capacity);
9906}
9907
9908uint32 ObjectMgr::ScriptNameContainer::insert(std::string_view scriptName, bool isScriptNameBound)
9909{
9910 auto result = NameToIndex.lower_bound(scriptName);
9911 if (result == NameToIndex.end() || NameToIndex.key_comp()(scriptName, result->first))
9912 {
9913 ASSERT(NameToIndex.size() <= std::numeric_limits<uint32>::max());
9914 result = NameToIndex.emplace_hint(result, scriptName, Entry(static_cast<uint32>(NameToIndex.size()), isScriptNameBound));
9915 IndexToName.emplace_back(result);
9916 }
9917
9918 return result->second.Id;
9919}
9920
9922{
9923 return IndexToName.size();
9924}
9925
9926ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(size_t index) const
9927{
9928 return index < IndexToName.size() ? IndexToName[index] : end();
9929}
9930
9931ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(std::string_view name) const
9932{
9933 // assume "" is the first element
9934 if (name.empty())
9935 return end();
9936
9937 return NameToIndex.find(name);
9938}
9939
9940ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::end() const
9941{
9942 return NameToIndex.end();
9943}
9944
9945std::unordered_set<std::string> ObjectMgr::ScriptNameContainer::GetAllDBScriptNames() const
9946{
9947 std::unordered_set<std::string> scriptNames;
9948
9949 for (std::pair<std::string const, Entry> const& entry : NameToIndex)
9950 {
9951 if (entry.second.IsScriptDatabaseBound)
9952 {
9953 scriptNames.insert(entry.first);
9954 }
9955 }
9956
9957 return scriptNames;
9958}
9959
9960std::unordered_set<std::string> ObjectMgr::GetAllDBScriptNames() const
9961{
9962 return _scriptNamesStore.GetAllDBScriptNames();
9963}
9964
9965std::string const& ObjectMgr::GetScriptName(uint32 id) const
9966{
9967 auto const itr = _scriptNamesStore.find(id);
9968 if (itr != _scriptNamesStore.end())
9969 {
9970 return itr->first;
9971 }
9972 else
9973 {
9974 static std::string const empty;
9975 return empty;
9976 }
9977}
9978
9980{
9981 auto const itr = _scriptNamesStore.find(id);
9982 if (itr != _scriptNamesStore.end())
9983 {
9984 return itr->second.IsScriptDatabaseBound;
9985 }
9986 else
9987 {
9988 return false;
9989 }
9990}
9991
9992uint32 ObjectMgr::GetScriptId(std::string_view name, bool isDatabaseBound)
9993{
9994 return _scriptNamesStore.insert(name, isDatabaseBound);
9995}
9996
9998{
9999 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
10000
10001 if (it != _creatureBaseStatsStore.end())
10002 return &(it->second);
10003
10004 static constexpr CreatureBaseStats defStats
10005 {
10006 .BaseMana = 0,
10007 .AttackPower = 0,
10008 .RangedAttackPower = 0
10009 };
10010 return &defStats;
10011}
10012
10014{
10015 uint32 oldMSTime = getMSTime();
10016 // 0 1 2 3 4
10017 QueryResult result = WorldDatabase.Query("SELECT level, class, basemana, attackpower, rangedattackpower FROM creature_classlevelstats");
10018
10019 if (!result)
10020 {
10021 TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
10022 return;
10023 }
10024
10025 uint32 count = 0;
10026 do
10027 {
10028 Field* fields = result->Fetch();
10029
10030 uint8 Level = fields[0].GetUInt8();
10031 uint8 Class = fields[1].GetUInt8();
10032
10033 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
10034 TC_LOG_ERROR("sql.sql", "Creature base stats for level {} has invalid class {}", Level, Class);
10035
10036 CreatureBaseStats stats;
10037
10038 stats.BaseMana = fields[2].GetUInt32();
10039
10040 stats.AttackPower = fields[3].GetUInt16();
10041 stats.RangedAttackPower = fields[4].GetUInt16();
10042
10043 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
10044
10045 ++count;
10046 }
10047 while (result->NextRow());
10048
10050 for (uint8 unitLevel = 1; unitLevel <= maxLevel + 3; ++unitLevel)
10051 {
10052 for (uint8 unitClass = 1; unitClass <= MAX_UNIT_CLASSES; ++unitClass)
10053 {
10054 uint8 unitClassMask = 1 << (unitClass - 1);
10055 if (!_creatureBaseStatsStore.count(MAKE_PAIR16(unitLevel, unitClassMask)))
10056 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class {} level {}", unitClassMask, unitLevel);
10057 }
10058 }
10059
10060 TC_LOG_INFO("server.loading", ">> Loaded {} creature base stats in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10061}
10062
10064{
10065 uint32 oldMSTime = getMSTime();
10066
10067 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
10068
10069 if (!result)
10070 {
10071 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
10072 return;
10073 }
10074
10075 uint32 count = 0;
10076
10077 do
10078 {
10079 Field* fields = result->Fetch();
10080
10081 uint32 alliance = fields[0].GetUInt32();
10082 uint32 horde = fields[1].GetUInt32();
10083
10084 if (!sAchievementStore.LookupEntry(alliance))
10085 TC_LOG_ERROR("sql.sql", "Achievement {} (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
10086 else if (!sAchievementStore.LookupEntry(horde))
10087 TC_LOG_ERROR("sql.sql", "Achievement {} (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
10088 else
10089 FactionChangeAchievements[alliance] = horde;
10090
10091 ++count;
10092 }
10093 while (result->NextRow());
10094
10095 TC_LOG_INFO("server.loading", ">> Loaded {} faction change achievement pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10096}
10097
10099{
10100 uint32 oldMSTime = getMSTime();
10101 uint32 count = 0;
10102
10103 for (std::pair<uint32 const, ItemTemplate> const& itemPair : _itemTemplateStore)
10104 {
10105 if (!itemPair.second.GetOtherFactionItemId())
10106 continue;
10107
10108 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_HORDE))
10109 FactionChangeItemsHordeToAlliance[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10110
10111 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
10112 FactionChangeItemsAllianceToHorde[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10113
10114 ++count;
10115 }
10116
10117 TC_LOG_INFO("server.loading", ">> Loaded {} faction change item pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10118}
10119
10121{
10122 uint32 oldMSTime = getMSTime();
10123
10124 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
10125
10126 if (!result)
10127 {
10128 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
10129 return;
10130 }
10131
10132 uint32 count = 0;
10133
10134 do
10135 {
10136 Field* fields = result->Fetch();
10137
10138 uint32 alliance = fields[0].GetUInt32();
10139 uint32 horde = fields[1].GetUInt32();
10140
10141 if (!GetQuestTemplate(alliance))
10142 TC_LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
10143 else if (!GetQuestTemplate(horde))
10144 TC_LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
10145 else
10146 FactionChangeQuests[alliance] = horde;
10147
10148 ++count;
10149 }
10150 while (result->NextRow());
10151
10152 TC_LOG_INFO("server.loading", ">> Loaded {} faction change quest pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10153}
10154
10156{
10157 uint32 oldMSTime = getMSTime();
10158
10159 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
10160
10161 if (!result)
10162 {
10163 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
10164 return;
10165 }
10166
10167 uint32 count = 0;
10168
10169 do
10170 {
10171 Field* fields = result->Fetch();
10172
10173 uint32 alliance = fields[0].GetUInt32();
10174 uint32 horde = fields[1].GetUInt32();
10175
10176 if (!sFactionStore.LookupEntry(alliance))
10177 TC_LOG_ERROR("sql.sql", "Reputation {} (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
10178 else if (!sFactionStore.LookupEntry(horde))
10179 TC_LOG_ERROR("sql.sql", "Reputation {} (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
10180 else
10181 FactionChangeReputation[alliance] = horde;
10182
10183 ++count;
10184 }
10185 while (result->NextRow());
10186
10187 TC_LOG_INFO("server.loading", ">> Loaded {} faction change reputation pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10188}
10189
10191{
10192 uint32 oldMSTime = getMSTime();
10193
10194 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
10195
10196 if (!result)
10197 {
10198 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
10199 return;
10200 }
10201
10202 uint32 count = 0;
10203
10204 do
10205 {
10206 Field* fields = result->Fetch();
10207
10208 uint32 alliance = fields[0].GetUInt32();
10209 uint32 horde = fields[1].GetUInt32();
10210
10211 if (!sSpellMgr->GetSpellInfo(alliance, DIFFICULTY_NONE))
10212 TC_LOG_ERROR("sql.sql", "Spell {} (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
10213 else if (!sSpellMgr->GetSpellInfo(horde, DIFFICULTY_NONE))
10214 TC_LOG_ERROR("sql.sql", "Spell {} (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
10215 else
10216 FactionChangeSpells[alliance] = horde;
10217
10218 ++count;
10219 }
10220 while (result->NextRow());
10221
10222 TC_LOG_INFO("server.loading", ">> Loaded {} faction change spell pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10223}
10224
10226{
10227 uint32 oldMSTime = getMSTime();
10228
10229 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
10230
10231 if (!result)
10232 {
10233 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
10234 return;
10235 }
10236
10237 uint32 count = 0;
10238
10239 do
10240 {
10241 Field* fields = result->Fetch();
10242
10243 uint32 alliance = fields[0].GetUInt32();
10244 uint32 horde = fields[1].GetUInt32();
10245
10246 if (!sCharTitlesStore.LookupEntry(alliance))
10247 TC_LOG_ERROR("sql.sql", "Title {} (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
10248 else if (!sCharTitlesStore.LookupEntry(horde))
10249 TC_LOG_ERROR("sql.sql", "Title {} (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
10250 else
10251 FactionChangeTitles[alliance] = horde;
10252
10253 ++count;
10254 }
10255 while (result->NextRow());
10256
10257 TC_LOG_INFO("server.loading", ">> Loaded {} faction change title pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10258}
10259
10261{
10262 for (PhaseEntry const* phase : sPhaseStore)
10263 _phaseInfoById.emplace(std::make_pair(phase->ID, PhaseInfoStruct{ phase->ID, std::unordered_set<uint32>{} }));
10264
10265 for (MapEntry const* map : sMapStore)
10266 if (map->ParentMapID != -1)
10267 _terrainSwapInfoById.emplace(std::make_pair(map->ID, TerrainSwapInfo{ map->ID, std::vector<uint32>{} }));
10268
10269 TC_LOG_INFO("server.loading", "Loading Terrain World Map definitions...");
10270 LoadTerrainWorldMaps();
10271
10272 TC_LOG_INFO("server.loading", "Loading Terrain Swap Default definitions...");
10273 LoadTerrainSwapDefaults();
10274
10275 TC_LOG_INFO("server.loading", "Loading Phase Area definitions...");
10276 LoadAreaPhases();
10277}
10278
10280{
10281 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10282 for (PhaseAreaInfo& phase : itr->second)
10283 phase.Conditions.clear();
10284}
10285
10287{
10288 uint32 oldMSTime = getMSTime();
10289
10290 // 0 1
10291 QueryResult result = WorldDatabase.Query("SELECT TerrainSwapMap, UiMapPhaseId FROM `terrain_worldmap`");
10292
10293 if (!result)
10294 {
10295 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain world maps. DB table `terrain_worldmap` is empty.");
10296 return;
10297 }
10298
10299 uint32 count = 0;
10300 do
10301 {
10302 Field* fields = result->Fetch();
10303
10304 uint32 mapId = fields[0].GetUInt32();
10305 uint32 uiMapPhaseId = fields[1].GetUInt32();
10306
10307 if (!sMapStore.LookupEntry(mapId))
10308 {
10309 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_worldmap` does not exist, skipped.", mapId);
10310 continue;
10311 }
10312
10313 if (!sDB2Manager.IsUiMapPhase(uiMapPhaseId))
10314 {
10315 TC_LOG_ERROR("sql.sql", "Phase {} defined in `terrain_worldmap` is not a valid terrain swap phase, skipped.", uiMapPhaseId);
10316 continue;
10317 }
10318
10319 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[mapId];
10320 terrainSwapInfo->Id = mapId;
10321 terrainSwapInfo->UiMapPhaseIDs.push_back(uiMapPhaseId);
10322
10323 ++count;
10324 } while (result->NextRow());
10325
10326 TC_LOG_INFO("server.loading", ">> Loaded {} terrain world maps in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10327}
10328
10330{
10331 uint32 oldMSTime = getMSTime();
10332
10333 // 0 1
10334 QueryResult result = WorldDatabase.Query("SELECT MapId, TerrainSwapMap FROM `terrain_swap_defaults`");
10335
10336 if (!result)
10337 {
10338 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain swap defaults. DB table `terrain_swap_defaults` is empty.");
10339 return;
10340 }
10341
10342 uint32 count = 0;
10343 do
10344 {
10345 Field* fields = result->Fetch();
10346
10347 uint32 mapId = fields[0].GetUInt32();
10348
10349 if (!sMapStore.LookupEntry(mapId))
10350 {
10351 TC_LOG_ERROR("sql.sql", "Map {} defined in `terrain_swap_defaults` does not exist, skipped.", mapId);
10352 continue;
10353 }
10354
10355 uint32 terrainSwap = fields[1].GetUInt32();
10356
10357 if (!sMapStore.LookupEntry(terrainSwap))
10358 {
10359 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_swap_defaults` does not exist, skipped.", terrainSwap);
10360 continue;
10361 }
10362
10363 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[terrainSwap];
10364 terrainSwapInfo->Id = terrainSwap;
10365 _terrainSwapInfoByMap[mapId].push_back(terrainSwapInfo);
10366
10367 ++count;
10368 } while (result->NextRow());
10369
10370 TC_LOG_INFO("server.loading", ">> Loaded {} terrain swap defaults in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10371}
10372
10374{
10375 uint32 oldMSTime = getMSTime();
10376
10377 // 0 1
10378 QueryResult result = WorldDatabase.Query("SELECT AreaId, PhaseId FROM `phase_area`");
10379
10380 if (!result)
10381 {
10382 TC_LOG_INFO("server.loading", ">> Loaded 0 phase areas. DB table `phase_area` is empty.");
10383 return;
10384 }
10385
10386 auto getOrCreatePhaseIfMissing = [this](uint32 phaseId)
10387 {
10388 PhaseInfoStruct* phaseInfo = &_phaseInfoById[phaseId];
10389 phaseInfo->Id = phaseId;
10390 return phaseInfo;
10391 };
10392
10393 uint32 count = 0;
10394 do
10395 {
10396 Field* fields = result->Fetch();
10397 uint32 area = fields[0].GetUInt32();
10398 uint32 phaseId = fields[1].GetUInt32();
10399 if (!sAreaTableStore.LookupEntry(area))
10400 {
10401 TC_LOG_ERROR("sql.sql", "Area {} defined in `phase_area` does not exist, skipped.", area);
10402 continue;
10403 }
10404
10405 if (!sPhaseStore.LookupEntry(phaseId))
10406 {
10407 TC_LOG_ERROR("sql.sql", "Phase {} defined in `phase_area` does not exist, skipped.", phaseId);
10408 continue;
10409 }
10410
10411 PhaseInfoStruct* phase = getOrCreatePhaseIfMissing(phaseId);
10412 phase->Areas.insert(area);
10413 _phaseInfoByArea[area].emplace_back(phase);
10414
10415 ++count;
10416 } while (result->NextRow());
10417
10418 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10419 {
10420 for (PhaseAreaInfo& phase : itr->second)
10421 {
10422 uint32 parentAreaId = itr->first;
10423 do
10424 {
10425 AreaTableEntry const* area = sAreaTableStore.LookupEntry(parentAreaId);
10426 if (!area)
10427 break;
10428
10429 parentAreaId = area->ParentAreaID;
10430 if (!parentAreaId)
10431 break;
10432
10433 if (std::vector<PhaseAreaInfo>* parentAreaPhases = Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, parentAreaId))
10434 for (PhaseAreaInfo& parentAreaPhase : *parentAreaPhases)
10435 if (parentAreaPhase.PhaseInfo->Id == phase.PhaseInfo->Id)
10436 parentAreaPhase.SubAreaExclusions.insert(itr->first);
10437
10438 } while (true);
10439 }
10440 }
10441
10442 TC_LOG_INFO("server.loading", ">> Loaded {} phase areas in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10443}
10444
10446{
10447 return std::any_of(Areas.begin(), Areas.end(), [areaId](uint32 areaToCheck)
10448 {
10449 return DB2Manager::IsInArea(areaId, areaToCheck);
10450 });
10451}
10452
10454{
10455 return Trinity::Containers::MapGetValuePtr(_phaseInfoById, phaseId);
10456}
10457
10458std::vector<PhaseAreaInfo> const* ObjectMgr::GetPhasesForArea(uint32 areaId) const
10459{
10460 return Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, areaId);
10461}
10462
10464{
10465 return Trinity::Containers::MapGetValuePtr(_terrainSwapInfoById, terrainSwapId);
10466}
10467
10469{
10470 return Trinity::Containers::MapGetValuePtr(_destructibleHitpointStore, entry);
10471}
10472
10474{
10475 return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
10476}
10477
10479{
10480 auto itr = _gameObjectTemplateAddonStore.find(entry);
10481 if (itr != _gameObjectTemplateAddonStore.end())
10482 return &itr->second;
10483
10484 return nullptr;
10485}
10486
10488{
10489 return Trinity::Containers::MapGetValuePtr(_gameObjectOverrideStore, spawnId);
10490}
10491
10493{
10494 return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
10495}
10496
10498{
10499 return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
10500}
10501
10503{
10504 return Trinity::Containers::MapGetValuePtr(_vehicleTemplateStore, veh->GetCreatureEntry());
10505}
10506
10508{
10509 if (Creature* cre = veh->GetBase()->ToCreature())
10510 {
10511 // Give preference to GUID-based accessories
10512 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
10513 if (itr != _vehicleAccessoryStore.end())
10514 return &itr->second;
10515 }
10516
10517 // Otherwise return entry-based
10518 VehicleAccessoryTemplateContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
10519 if (itr != _vehicleTemplateAccessoryStore.end())
10520 return &itr->second;
10521 return nullptr;
10522}
10523
10525{
10526 return Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
10527}
10528
10530{
10531 uint32 oldMSTime = getMSTime();
10532 _raceClassRequirementStore.clear();
10533
10534 // 0 1 2 3
10535 QueryResult result = WorldDatabase.Query("SELECT ClassID, RaceID, ActiveExpansionLevel, AccountExpansionLevel FROM `class_expansion_requirement`");
10536
10537 if (result)
10538 {
10539 std::map<uint8, std::map<uint8, std::pair<uint8, uint8>>> temp;
10540 std::array<uint8, MAX_CLASSES> minRequirementForClass = { };
10541 minRequirementForClass.fill(MAX_ACCOUNT_EXPANSIONS);
10542 uint32 count = 0;
10543 do
10544 {
10545 Field* fields = result->Fetch();
10546
10547 uint8 classID = fields[0].GetUInt8();
10548 uint8 raceID = fields[1].GetUInt8();
10549 uint8 activeExpansionLevel = fields[2].GetUInt8();
10550 uint8 accountExpansionLevel = fields[3].GetUInt8();
10551
10552 ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classID);
10553 if (!classEntry)
10554 {
10555 TC_LOG_ERROR("sql.sql", "Class {} (race {}) defined in `class_expansion_requirement` does not exists, skipped.",
10556 uint32(classID), uint32(raceID));
10557 continue;
10558 }
10559
10560 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10561 if (!raceEntry)
10562 {
10563 TC_LOG_ERROR("sql.sql", "Race {} (class {}) defined in `class_expansion_requirement` does not exists, skipped.",
10564 uint32(raceID), uint32(classID));
10565 continue;
10566 }
10567
10568 if (activeExpansionLevel >= MAX_EXPANSIONS)
10569 {
10570 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect ActiveExpansionLevel {}, skipped.",
10571 uint32(classID), uint32(raceID), activeExpansionLevel);
10572 continue;
10573 }
10574
10575 if (accountExpansionLevel >= MAX_ACCOUNT_EXPANSIONS)
10576 {
10577 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect AccountExpansionLevel {}, skipped.",
10578 uint32(classID), uint32(raceID), accountExpansionLevel);
10579 continue;
10580 }
10581
10582 temp[raceID][classID] = { activeExpansionLevel, accountExpansionLevel };
10583 minRequirementForClass[classID] = std::min(minRequirementForClass[classID], activeExpansionLevel);
10584
10585 ++count;
10586 }
10587 while (result->NextRow());
10588
10589 for (auto&& race : temp)
10590 {
10591 RaceClassAvailability& raceClassAvailability = _raceClassRequirementStore.emplace_back();
10592
10593 raceClassAvailability.RaceID = race.first;
10594
10595 for (auto&& class_ : race.second)
10596 {
10597 ClassAvailability& classAvailability = raceClassAvailability.Classes.emplace_back();
10598
10599 classAvailability.ClassID = class_.first;
10600 classAvailability.ActiveExpansionLevel = class_.second.first;
10601 classAvailability.AccountExpansionLevel = class_.second.second;
10602 classAvailability.MinActiveExpansionLevel = minRequirementForClass[class_.first];
10603 }
10604 }
10605
10606 TC_LOG_INFO("server.loading", ">> Loaded {} class expansion requirements in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10607 }
10608 else
10609 TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
10610
10611 oldMSTime = getMSTime();
10612
10613 // 0 1 2
10614 result = WorldDatabase.Query("SELECT raceID, expansion, achievementId FROM `race_unlock_requirement`");
10615
10616 if (result)
10617 {
10618 uint32 loadedRows = 0;
10619 do
10620 {
10621 Field* fields = result->Fetch();
10622
10623 uint8 raceID = fields[0].GetUInt8();
10624 uint8 expansion = fields[1].GetUInt8();
10625 uint32 achievementId = fields[2].GetUInt32();
10626
10627 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10628 if (!raceEntry)
10629 {
10630 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` does not exists, skipped.", raceID);
10631 continue;
10632 }
10633
10634 if (expansion >= MAX_ACCOUNT_EXPANSIONS)
10635 {
10636 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect expansion {}, skipped.", raceID, expansion);
10637 continue;
10638 }
10639
10640 if (achievementId && !sAchievementStore.LookupEntry(achievementId))
10641 {
10642 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect achievement {}, skipped.", raceID, achievementId);
10643 continue;
10644 }
10645
10646 auto itr = std::ranges::find(_raceClassRequirementStore, raceID, &RaceClassAvailability::RaceID);
10647 if (itr == _raceClassRequirementStore.end())
10648 {
10649 itr = _raceClassRequirementStore.emplace(_raceClassRequirementStore.end());
10650 itr->RaceID = raceID;
10651 }
10652
10653 RaceUnlockRequirement& raceUnlockRequirement = itr->UnlockRequirement;
10654 raceUnlockRequirement.Expansion = expansion;
10655 raceUnlockRequirement.AchievementId = achievementId;
10656 ++loadedRows;
10657 }
10658 while (result->NextRow());
10659 TC_LOG_INFO("server.loading", ">> Loaded {} race expansion requirements in {} ms.", loadedRows, GetMSTimeDiffToNow(oldMSTime));
10660 }
10661 else
10662 TC_LOG_INFO("server.loading", ">> Loaded 0 race expansion requirements. DB table `race_expansion_requirement` is empty.");
10663}
10664
10666{
10667 auto raceItr = std::ranges::find(_raceClassRequirementStore, raceId, &RaceClassAvailability::RaceID);
10668 if (raceItr == _raceClassRequirementStore.end())
10669 return nullptr;
10670
10671 return &raceItr->UnlockRequirement;
10672}
10673
10675{
10676 auto raceItr = std::ranges::find(_raceClassRequirementStore, raceId, &RaceClassAvailability::RaceID);
10677 if (raceItr == _raceClassRequirementStore.end())
10678 return nullptr;
10679
10680 auto classItr = std::ranges::find(raceItr->Classes, classId, &ClassAvailability::ClassID);
10681 if (classItr == raceItr->Classes.end())
10682 return nullptr;
10683
10684 return &(*classItr);
10685}
10686
10688{
10689 for (RaceClassAvailability const& raceClassAvailability : _raceClassRequirementStore)
10690 for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
10691 if (classAvailability.ClassID == classId)
10692 return &classAvailability;
10693
10694 return nullptr;
10695}
10696
10698{
10699 return Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
10700}
10701
10703{
10704 return Trinity::Containers::MapGetValuePtr(_jumpChargeParams, id);
10705}
10706
10708{
10709 CreatureStaticFlagsOverride const* staticFlagsOverride = Trinity::Containers::MapGetValuePtr(_creatureStaticFlagsOverrideStore, std::make_pair(spawnId, difficultyId));
10710 if (staticFlagsOverride)
10711 return staticFlagsOverride;
10712
10713 // If there is no data for the difficulty, try to get data for the fallback difficulty
10714 DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficultyId);
10715 if (difficultyEntry)
10716 return GetCreatureStaticFlagsOverride(spawnId, Difficulty(difficultyEntry->FallbackDifficultyID));
10717
10718 return nullptr;
10719}
10720
10722{
10723 uint32 oldMSTime = getMSTime();
10724
10725 // 0 1 2
10726 QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
10727
10728 if (!result)
10729 {
10730 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
10731 return;
10732 }
10733
10734 uint32 count = 0;
10735 do
10736 {
10737 Field* fields = result->Fetch();
10738
10739 uint32 entry = fields[0].GetUInt32();
10740 uint32 item = fields[1].GetUInt32();
10741 uint32 idx = fields[2].GetUInt32();
10742
10743 GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
10744 if (!goInfo)
10745 {
10746 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
10747 continue;
10748 };
10749
10750 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10751 if (!db2Data)
10752 {
10753 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
10754 continue;
10755 };
10756
10757 _gameObjectQuestItemStore[entry].push_back(item);
10758
10759 ++count;
10760 }
10761 while (result->NextRow());
10762
10763 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10764}
10765
10767{
10768 uint32 oldMSTime = getMSTime();
10769
10770 // 0 1 2 3
10771 QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
10772
10773 if (!result)
10774 {
10775 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
10776 return;
10777 }
10778
10779 uint32 count = 0;
10780 do
10781 {
10782 Field* fields = result->Fetch();
10783
10784 uint32 entry = fields[0].GetUInt32();
10785 Difficulty difficulty = Difficulty(fields[1].GetInt32());
10786 uint32 item = fields[2].GetUInt32();
10787 uint32 idx = fields[3].GetUInt32();
10788
10789 CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
10790 if (!creatureInfo)
10791 {
10792 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, difficulty: {}, idx: {}), skipped", entry, difficulty, idx);
10793 continue;
10794 }
10795
10796 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10797 if (!db2Data)
10798 {
10799 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, difficulty: {}, idx: {}), skipped", item, entry, difficulty, idx);
10800 continue;
10801 }
10802
10803 _creatureQuestItemStore[std::make_pair(entry, difficulty)].push_back(item);
10804
10805 ++count;
10806 }
10807 while (result->NextRow());
10808
10809 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10810}
10811
10813{
10814 uint32 oldMSTime = getMSTime();
10815
10816 // 0 1
10817 QueryResult result = WorldDatabase.Query("SELECT CreatureId, CurrencyId FROM creature_quest_currency ORDER BY CreatureId, CurrencyId ASC");
10818
10819 if (!result)
10820 {
10821 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest currencies. DB table `creature_quest_currency` is empty.");
10822 return;
10823 }
10824
10825 uint32 count = 0;
10826 do
10827 {
10828 Field* fields = result->Fetch();
10829
10830 uint32 entry = fields[0].GetUInt32();
10831 int32 currency = fields[1].GetInt32();
10832
10833 if (!GetCreatureTemplate(entry))
10834 {
10835 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has data for nonexistent creature (entry: {}, currency: {}), skipped", entry, currency);
10836 continue;
10837 }
10838
10839 if (!sCurrencyTypesStore.HasRecord(currency))
10840 {
10841 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has nonexistent currency (ID: {}) in creature (entry: {}, currency: {}), skipped", currency, entry, currency);
10842 continue;
10843 }
10844
10845 _creatureQuestCurrenciesStore[entry].push_back(currency);
10846
10847 ++count;
10848 }
10849 while (result->NextRow());
10850
10851 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest currencies in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10852}
10853
10855{
10856 // reload case
10857 _creatureStaticFlagsOverrideStore.clear();
10858
10859 uint32 oldMSTime = getMSTime();
10860
10861 // 0 1 2 3 4 5 6 7 8 9
10862 QueryResult result = WorldDatabase.Query("SELECT SpawnId, DifficultyId, StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 FROM creature_static_flags_override");
10863
10864 if (!result)
10865 {
10866 TC_LOG_INFO("server.loading", ">> Loaded 0 creature static flag overrides. DB table `creature_static_flags_override` is empty.");
10867 return;
10868 }
10869
10870 uint32 count = 0;
10871 do
10872 {
10873 Field* fields = result->Fetch();
10874
10875 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
10876 Difficulty difficultyId = static_cast<Difficulty>(fields[1].GetInt32());
10877
10878 CreatureData const* creatureData = GetCreatureData(spawnId);
10879 if (!creatureData)
10880 {
10881 TC_LOG_ERROR("sql.sql", "Table `creature_static_flags_override` has data for nonexistent creature (SpawnId: {}), skipped", spawnId);
10882 continue;
10883 }
10884
10885 // DIFFICULTY_NONE is always a valid fallback
10886 if (difficultyId != DIFFICULTY_NONE)
10887 {
10888 if (!advstd::ranges::contains(creatureData->spawnDifficulties, difficultyId))
10889 {
10890 TC_LOG_ERROR("sql.sql", "Table `creature_static_flags_override` has data for a creature that is not available for the specified DifficultyId (SpawnId: {}, DifficultyId: {}), skipped", spawnId, difficultyId);
10891 continue;
10892 }
10893 }
10894
10895 CreatureStaticFlagsOverride& staticFlagsOverride = _creatureStaticFlagsOverrideStore[std::make_pair(spawnId, difficultyId)];
10896 if (!fields[2].IsNull())
10897 staticFlagsOverride.StaticFlags1 = static_cast<CreatureStaticFlags>(fields[2].GetUInt32());
10898 if (!fields[3].IsNull())
10899 staticFlagsOverride.StaticFlags2 = static_cast<CreatureStaticFlags2>(fields[3].GetUInt32());
10900 if (!fields[4].IsNull())
10901 staticFlagsOverride.StaticFlags3 = static_cast<CreatureStaticFlags3>(fields[4].GetUInt32());
10902 if (!fields[5].IsNull())
10903 staticFlagsOverride.StaticFlags4 = static_cast<CreatureStaticFlags4>(fields[5].GetUInt32());
10904 if (!fields[6].IsNull())
10905 staticFlagsOverride.StaticFlags5 = static_cast<CreatureStaticFlags5>(fields[6].GetUInt32());
10906 if (!fields[7].IsNull())
10907 staticFlagsOverride.StaticFlags6 = static_cast<CreatureStaticFlags6>(fields[7].GetUInt32());
10908 if (!fields[8].IsNull())
10909 staticFlagsOverride.StaticFlags7 = static_cast<CreatureStaticFlags7>(fields[8].GetUInt32());
10910 if (!fields[9].IsNull())
10911 staticFlagsOverride.StaticFlags8 = static_cast<CreatureStaticFlags8>(fields[9].GetUInt32());
10912
10913 ++count;
10914 } while (result->NextRow());
10915
10916 TC_LOG_INFO("server.loading", ">> Loaded {} creature static flag overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10917}
10918
10920{
10921 uint32 oldMSTime = getMSTime();
10922
10923 // cache disabled
10924 if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
10925 {
10926 TC_LOG_INFO("server.loading", ">> Query data caching is disabled. Skipped initialization.");
10927 return;
10928 }
10929
10931
10932 // Initialize Query data for creatures
10933 if (mask & QUERY_DATA_CREATURES)
10934 for (auto& creatureTemplatePair : _creatureTemplateStore)
10935 pool.PostWork([creature = &creatureTemplatePair.second]() { creature->InitializeQueryData(); });
10936
10937 // Initialize Query Data for gameobjects
10938 if (mask & QUERY_DATA_GAMEOBJECTS)
10939 for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
10940 pool.PostWork([gobj = &gameObjectTemplatePair.second]() { gobj->InitializeQueryData(); });
10941
10942 // Initialize Query Data for quests
10943 if (mask & QUERY_DATA_QUESTS)
10944 for (auto& questTemplatePair : _questTemplates)
10945 pool.PostWork([quest = questTemplatePair.second.get()]() { quest->InitializeQueryData(); });
10946
10947 // Initialize Quest POI data
10948 if (mask & QUERY_DATA_POIS)
10949 for (auto& poiWrapperPair : _questPOIStore)
10950 pool.PostWork([poi = &poiWrapperPair.second]() { poi->InitializeQueryData(); });
10951
10952 pool.Join();
10953
10954 TC_LOG_INFO("server.loading", ">> Initialized query cache data in {} ms", GetMSTimeDiffToNow(oldMSTime));
10955}
10956
10958{
10959 ByteBuffer tempBuffer;
10960 tempBuffer << *this;
10961 tempBuffer.shrink_to_fit();
10962
10963 QueryDataBuffer = std::move(tempBuffer).Release();
10964}
10965
10967{
10968 uint32 oldMSTime = getMSTime();
10969 _sceneTemplateStore.clear();
10970
10971 QueryResult templates = WorldDatabase.Query("SELECT SceneId, Flags, ScriptPackageID, Encrypted, ScriptName FROM scene_template");
10972
10973 if (!templates)
10974 {
10975 TC_LOG_INFO("server.loading", ">> Loaded 0 scene templates. DB table `scene_template` is empty.");
10976 return;
10977 }
10978
10979 do
10980 {
10981 Field* fields = templates->Fetch();
10982
10983 uint32 sceneId = fields[0].GetUInt32();
10984 SceneTemplate& sceneTemplate = _sceneTemplateStore[sceneId];
10985 sceneTemplate.SceneId = sceneId;
10986 sceneTemplate.PlaybackFlags = static_cast<SceneFlag>(fields[1].GetUInt32());
10987 sceneTemplate.ScenePackageId = fields[2].GetUInt32();
10988 sceneTemplate.Encrypted = fields[3].GetUInt8() != 0;
10989 sceneTemplate.ScriptId = GetScriptId(fields[4].GetStringView());
10990
10991 } while (templates->NextRow());
10992
10993 TC_LOG_INFO("server.loading", ">> Loaded {} scene templates in {} ms.", _sceneTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
10994}
10995
10997{
10998 uint32 oldMSTime = getMSTime();
10999 _playerChoices.clear();
11000
11001 QueryResult choices = WorldDatabase.Query("SELECT ChoiceId, UiTextureKitId, SoundKitId, CloseSoundKitId, Duration, Question, PendingChoiceText, "
11002 "InfiniteRange, HideWarboardHeader, KeepOpenAfterChoice, ShowChoicesAsList, RequiresSelection, ShowChoicesAsGrid, HideAnswerArt, "
11003 "ShowChoicesAsColumns, MaxResponses, ScriptName FROM playerchoice");
11004 if (!choices)
11005 {
11006 TC_LOG_INFO("server.loading", ">> Loaded 0 player choices. DB table `playerchoice` is empty.");
11007 return;
11008 }
11009
11010 uint32 responseCount = 0;
11011 uint32 rewardCount = 0;
11012 uint32 itemRewardCount = 0;
11013 uint32 currencyRewardCount = 0;
11014 uint32 factionRewardCount = 0;
11015 uint32 itemChoiceRewardCount = 0;
11016 uint32 mawPowersCount = 0;
11017
11018 {
11019 do
11020 {
11021 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(UiTextureKitId)(SoundKitId)(CloseSoundKitId)(Duration)(Question)(PendingChoiceText)
11022 (InfiniteRange)(HideWarboardHeader)(KeepOpenAfterChoice)(ShowChoicesAsList)(RequiresSelection)(ShowChoicesAsGrid)(HideAnswerArt)
11023 (ShowChoicesAsColumns)(MaxResponses)(ScriptName)) fields { *choices };
11024
11025 int32 choiceId = fields.ChoiceId().GetInt32();
11026
11027 PlayerChoice& choice = _playerChoices[choiceId];
11028 choice.ChoiceId = choiceId;
11029 choice.UiTextureKitId = fields.UiTextureKitId().GetInt32();
11030 choice.SoundKitId = fields.SoundKitId().GetUInt32();
11031 choice.CloseSoundKitId = fields.CloseSoundKitId().GetUInt32();
11032 if (!fields.Duration().IsNull())
11033 choice.Duration = Seconds(fields.Duration().GetInt64());
11034 choice.Question = fields.Question().GetStringView();
11035 choice.PendingChoiceText = fields.PendingChoiceText().GetStringView();
11036 choice.InfiniteRange = fields.InfiniteRange().GetBool();
11037 choice.HideWarboardHeader = fields.HideWarboardHeader().GetBool();
11038 choice.KeepOpenAfterChoice = fields.KeepOpenAfterChoice().GetBool();
11039 choice.ShowChoicesAsList = fields.ShowChoicesAsList().GetBool();
11040 choice.RequiresSelection = fields.RequiresSelection().GetBool();
11041 choice.ShowChoicesAsGrid = fields.ShowChoicesAsGrid().GetBool();
11042 choice.HideAnswerArt = fields.HideAnswerArt().GetBool();
11043 choice.ShowChoicesAsColumns = fields.ShowChoicesAsColumns().GetBool();
11044 choice.MaxResponses = fields.MaxResponses().GetUInt32OrNull();
11045 choice.ScriptId = GetScriptId(fields.ScriptName().GetStringView());
11046
11047 } while (choices->NextRow());
11048 }
11049
11050 if (QueryResult responses = WorldDatabase.Query("SELECT ChoiceId, ResponseId, NULL, ChoiceArtFileId, Flags, WidgetSetID, "
11051 "UiTextureAtlasElementID, SoundKitID, GroupID, UiTextureKitID, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation, RewardQuestID "
11052 "FROM playerchoice_response ORDER BY `Index` ASC"))
11053 {
11054 do
11055 {
11056 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ChoiceArtFileId)(Flags)(WidgetSetID)(UiTextureAtlasElementID)(SoundKitID)
11057 (GroupID)(UiTextureKitID)(Answer)(Header)(SubHeader)(ButtonTooltip)(Description)(Confirmation)(RewardQuestID)) fields{ *responses };
11058
11059 int32 choiceId = fields.ChoiceId().GetInt32();
11060 int32 responseId = fields.ResponseId().GetInt32();
11061
11062 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11063 if (!choice)
11064 {
11065 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11066 continue;
11067 }
11068
11069 choice->Responses.emplace_back();
11070
11071 PlayerChoiceResponse& response = choice->Responses.back();
11072 response.ResponseId = responseId;
11073 response.ChoiceArtFileId = fields.ChoiceArtFileId().GetInt32();
11074 response.Flags = static_cast<PlayerChoiceResponseFlags>(fields.Flags().GetInt32());
11075 response.WidgetSetID = fields.WidgetSetID().GetUInt32();
11076 response.UiTextureAtlasElementID = fields.UiTextureAtlasElementID().GetUInt32();
11077 response.SoundKitID = fields.SoundKitID().GetUInt32();
11078 response.GroupID = fields.GroupID().GetUInt8();
11079 response.UiTextureKitID = fields.UiTextureKitID().GetInt32();
11080 response.Answer = fields.Answer().GetStringView();
11081 response.Header = fields.Header().GetStringView();
11082 response.SubHeader = fields.SubHeader().GetStringView();
11083 response.ButtonTooltip = fields.ButtonTooltip().GetStringView();
11084 response.Description = fields.Description().GetStringView();
11085 response.Confirmation = fields.Confirmation().GetStringView();
11086 response.RewardQuestID = fields.RewardQuestID().GetUInt32OrNull();
11087
11088 ++responseCount;
11089
11090 } while (responses->NextRow());
11091 }
11092
11093 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TitleId, PackageId, SkillLineId, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"))
11094 {
11095 do
11096 {
11097 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(TitleId)(PackageId)(SkillLineId)
11098 (SkillPointCount)(ArenaPointCount)(HonorPointCount)(Money)(Xp)) fields{ *rewards };
11099
11100 int32 choiceId = fields.ChoiceId().GetInt32();
11101 int32 responseId = fields.ResponseId().GetInt32();
11102
11103 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11104 if (!choice)
11105 {
11106 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11107 continue;
11108 }
11109
11110 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11111 if (responseItr == choice->Responses.end())
11112 {
11113 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11114 continue;
11115 }
11116
11117 PlayerChoiceResponseReward* reward = &responseItr->Reward.emplace();
11118 reward->TitleId = fields.TitleId().GetInt32();
11119 reward->PackageId = fields.PackageId().GetInt32();
11120 reward->SkillLineId = fields.SkillLineId().GetInt32();
11121 reward->SkillPointCount = fields.SkillPointCount().GetUInt32();
11122 reward->ArenaPointCount = fields.ArenaPointCount().GetUInt32();
11123 reward->HonorPointCount = fields.HonorPointCount().GetUInt32();
11124 reward->Money = fields.Money().GetUInt64();
11125 reward->Xp = fields.Xp().GetUInt32();
11126 ++rewardCount;
11127
11128 if (reward->TitleId && !sCharTitlesStore.LookupEntry(reward->TitleId))
11129 {
11130 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing Title {} for ChoiceId {}, ResponseId: {}, set to 0",
11131 reward->TitleId, choiceId, responseId);
11132 reward->TitleId = 0;
11133 }
11134
11135 if (reward->PackageId && !sDB2Manager.GetQuestPackageItems(reward->PackageId))
11136 {
11137 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing QuestPackage {} for ChoiceId {}, ResponseId: {}, set to 0",
11138 reward->TitleId, choiceId, responseId);
11139 reward->PackageId = 0;
11140 }
11141
11142 if (reward->SkillLineId && !sSkillLineStore.LookupEntry(reward->SkillLineId))
11143 {
11144 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing SkillLine {} for ChoiceId {}, ResponseId: {}, set to 0",
11145 reward->TitleId, choiceId, responseId);
11146 reward->SkillLineId = 0;
11147 reward->SkillPointCount = 0;
11148 }
11149
11150 } while (rewards->NextRow());
11151 }
11152
11153 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item ORDER BY `Index` ASC"))
11154 {
11155 do
11156 {
11157 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ItemId)(BonusListIDs)(Quantity)) fields { *rewards };
11158
11159 int32 choiceId = fields.ChoiceId().GetInt32();
11160 int32 responseId = fields.ResponseId().GetInt32();
11161 uint32 itemId = fields.ItemId().GetUInt32();
11162 std::vector<int32> bonusListIds;
11163 for (std::string_view token : Trinity::Tokenize(fields.BonusListIDs().GetStringView(), ' ', false))
11164 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11165 bonusListIds.push_back(*bonusListID);
11166 int32 quantity = fields.Quantity().GetInt32();
11167
11168 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11169 if (!choice)
11170 {
11171 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11172 continue;
11173 }
11174
11175 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11176 if (responseItr == choice->Responses.end())
11177 {
11178 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11179 continue;
11180 }
11181
11182 if (!responseItr->Reward)
11183 {
11184 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11185 choiceId, responseId);
11186 continue;
11187 }
11188
11189 if (!GetItemTemplate(itemId))
11190 {
11191 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11192 itemId, choiceId, responseId);
11193 continue;
11194 }
11195
11196 responseItr->Reward->Items.emplace_back(itemId, std::move(bonusListIds), quantity);
11197 ++itemRewardCount;
11198
11199 } while (rewards->NextRow());
11200 }
11201
11202 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, CurrencyId, Quantity FROM playerchoice_response_reward_currency ORDER BY `Index` ASC"))
11203 {
11204 do
11205 {
11206 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(CurrencyId)(Quantity)) fields { *rewards };
11207
11208 int32 choiceId = fields.ChoiceId().GetInt32();
11209 int32 responseId = fields.ResponseId().GetInt32();
11210 uint32 currencyId = fields.CurrencyId().GetUInt32();
11211 int32 quantity = fields.Quantity().GetInt32();
11212
11213 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11214 if (!choice)
11215 {
11216 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11217 continue;
11218 }
11219
11220 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11221 if (responseItr == choice->Responses.end())
11222 {
11223 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11224 continue;
11225 }
11226
11227 if (!responseItr->Reward)
11228 {
11229 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11230 choiceId, responseId);
11231 continue;
11232 }
11233
11234 if (!sCurrencyTypesStore.LookupEntry(currencyId))
11235 {
11236 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing currency {} for ChoiceId {}, ResponseId: {}, skipped",
11237 currencyId, choiceId, responseId);
11238 continue;
11239 }
11240
11241 responseItr->Reward->Currency.emplace_back(currencyId, quantity);
11242 ++currencyRewardCount;
11243
11244 } while (rewards->NextRow());
11245 }
11246
11247 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, FactionId, Quantity FROM playerchoice_response_reward_faction ORDER BY `Index` ASC"))
11248 {
11249 do
11250 {
11251 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(FactionId)(Quantity)) fields { *rewards };
11252
11253 int32 choiceId = fields.ChoiceId().GetInt32();
11254 int32 responseId = fields.ResponseId().GetInt32();
11255 uint32 factionId = fields.FactionId().GetUInt32();
11256 int32 quantity = fields.Quantity().GetInt32();
11257
11258 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11259 if (!choice)
11260 {
11261 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11262 continue;
11263 }
11264
11265 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11266 if (responseItr == choice->Responses.end())
11267 {
11268 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11269 continue;
11270 }
11271
11272 if (!responseItr->Reward)
11273 {
11274 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11275 choiceId, responseId);
11276 continue;
11277 }
11278
11279 if (!sFactionStore.LookupEntry(factionId))
11280 {
11281 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing faction {} for ChoiceId {}, ResponseId: {}, skipped",
11282 factionId, choiceId, responseId);
11283 continue;
11284 }
11285
11286 responseItr->Reward->Faction.emplace_back(factionId, quantity);
11287 ++factionRewardCount;
11288
11289 } while (rewards->NextRow());
11290 }
11291
11292 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item_choice ORDER BY `Index` ASC"))
11293 {
11294 do
11295 {
11296 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ItemId)(BonusListIDs)(Quantity)) fields { *rewards };
11297
11298 int32 choiceId = fields.ChoiceId().GetInt32();
11299 int32 responseId = fields.ResponseId().GetInt32();
11300 uint32 itemId = fields.ItemId().GetUInt32();
11301 std::vector<int32> bonusListIds;
11302 for (std::string_view token : Trinity::Tokenize(fields.BonusListIDs().GetStringView(), ' ', false))
11303 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11304 bonusListIds.push_back(*bonusListID);
11305 int32 quantity = fields.Quantity().GetInt32();
11306
11307 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11308 if (!choice)
11309 {
11310 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11311 continue;
11312 }
11313
11314 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11315 if (responseItr == choice->Responses.end())
11316 {
11317 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11318 continue;
11319 }
11320
11321 if (!responseItr->Reward)
11322 {
11323 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11324 choiceId, responseId);
11325 continue;
11326 }
11327
11328 if (!GetItemTemplate(itemId))
11329 {
11330 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11331 itemId, choiceId, responseId);
11332 continue;
11333 }
11334
11335 responseItr->Reward->ItemChoices.emplace_back(itemId, std::move(bonusListIds), quantity);
11336 ++itemChoiceRewardCount;
11337
11338 } while (rewards->NextRow());
11339 }
11340
11341 if (QueryResult mawPowersResult = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TypeArtFileID, Rarity, SpellID, MaxStacks FROM playerchoice_response_maw_power"))
11342 {
11343 do
11344 {
11345 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(TypeArtFileID)(Rarity)(SpellID)(MaxStacks)) fields { *mawPowersResult };
11346
11347 int32 choiceId = fields.ChoiceId().GetInt32();
11348 int32 responseId = fields.ResponseId().GetInt32();
11349
11350 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11351 if (!choice)
11352 {
11353 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11354 continue;
11355 }
11356
11357 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11358 if (responseItr == choice->Responses.end())
11359 {
11360 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11361 continue;
11362 }
11363
11364 PlayerChoiceResponseMawPower& mawPower = responseItr->MawPower.emplace();
11365 mawPower.TypeArtFileID = fields.TypeArtFileID().GetInt32();
11366 mawPower.Rarity = fields.Rarity().GetInt32OrNull();
11367 mawPower.SpellID = fields.SpellID().GetInt32();
11368 mawPower.MaxStacks = fields.MaxStacks().GetInt32();
11369
11370 ++mawPowersCount;
11371
11372 } while (mawPowersResult->NextRow());
11373 }
11374
11375 TC_LOG_INFO("server.loading", ">> Loaded {} player choices, {} responses, {} rewards, {} item rewards, {} currency rewards, {} faction rewards, {} item choice rewards and {} maw powers in {} ms.",
11376 _playerChoices.size(), responseCount, rewardCount, itemRewardCount, currencyRewardCount, factionRewardCount, itemChoiceRewardCount, mawPowersCount, GetMSTimeDiffToNow(oldMSTime));
11377}
11378
11380{
11381 uint32 oldMSTime = getMSTime();
11382
11383 // need for reload case
11384 _playerChoiceLocales.clear();
11385
11386 // 0 1 2
11387 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceId, locale, Question FROM playerchoice_locale"))
11388 {
11389 do
11390 {
11391 Field* fields = result->Fetch();
11392
11393 uint32 choiceId = fields[0].GetUInt32();
11394 std::string_view localeName = fields[1].GetStringView();
11395
11396 if (!GetPlayerChoice(choiceId))
11397 {
11398 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for locale {}, skipped", choiceId, localeName);
11399 continue;
11400 }
11401
11402 LocaleConstant locale = GetLocaleByName(localeName);
11403 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11404 continue;
11405
11406 PlayerChoiceLocale& data = _playerChoiceLocales[choiceId];
11407 AddLocaleString(fields[2].GetStringView(), locale, data.Question);
11408 } while (result->NextRow());
11409
11410 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _playerChoiceLocales.size(), GetMSTimeDiffToNow(oldMSTime));
11411 }
11412
11413 oldMSTime = getMSTime();
11414
11415 // 0 1 2 3 4 5 6 7 8
11416 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceID, ResponseID, locale, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation FROM playerchoice_response_locale"))
11417 {
11418 std::size_t count = 0;
11419 do
11420 {
11421 Field* fields = result->Fetch();
11422
11423 int32 choiceId = fields[0].GetInt32();
11424 int32 responseId = fields[1].GetInt32();
11425 std::string_view localeName = fields[2].GetStringView();
11426
11427 auto itr = _playerChoiceLocales.find(choiceId);
11428 if (itr == _playerChoiceLocales.end())
11429 {
11430 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for ResponseId {} locale {}, skipped",
11431 choiceId, responseId, localeName);
11432 continue;
11433 }
11434
11435 PlayerChoice const* playerChoice = ASSERT_NOTNULL(GetPlayerChoice(choiceId));
11436 if (!playerChoice->GetResponse(responseId))
11437 {
11438 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ResponseId: {} for ChoiceId {} locale {}, skipped",
11439 responseId, choiceId, localeName);
11440 continue;
11441 }
11442
11443 LocaleConstant locale = GetLocaleByName(localeName);
11444 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11445 continue;
11446
11447 PlayerChoiceResponseLocale& data = itr->second.Responses[responseId];
11448 AddLocaleString(fields[3].GetStringView(), locale, data.Answer);
11449 AddLocaleString(fields[4].GetStringView(), locale, data.Header);
11450 AddLocaleString(fields[5].GetStringView(), locale, data.SubHeader);
11451 AddLocaleString(fields[6].GetStringView(), locale, data.ButtonTooltip);
11452 AddLocaleString(fields[7].GetStringView(), locale, data.Description);
11453 AddLocaleString(fields[8].GetStringView(), locale, data.Confirmation);
11454 ++count;
11455 } while (result->NextRow());
11456
11457 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice Response locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11458 }
11459}
11460
11462{
11463 uint32 oldMSTime = getMSTime();
11464
11465 // need for reload case
11466 _uiMapQuestLinesStore.clear();
11467
11468 // 0 1
11469 QueryResult result = WorldDatabase.Query("SELECT UiMapId, QuestLineId FROM ui_map_quest_line");
11470
11471 if (!result)
11472 {
11473 TC_LOG_INFO("server.loading", ">> Loaded 0 questlines for UIMaps. DB table `ui_map_quest_line` is empty!");
11474 return;
11475 }
11476
11477 uint32 count = 0;
11478
11479 do
11480 {
11481 Field* fields = result->Fetch();
11482
11483 uint32 uiMapId = fields[0].GetUInt32();
11484 uint32 questLineId = fields[1].GetUInt32();
11485
11486 if (!sUiMapStore.HasRecord(uiMapId))
11487 {
11488 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest_line` references non-existing UIMap {}, skipped", uiMapId);
11489 continue;
11490 }
11491
11492 if (QuestMgr::GetQuestsForQuestLine(questLineId).empty())
11493 {
11494 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest_line` references empty or non-existing questline {}, skipped", questLineId);
11495 continue;
11496 }
11497
11498 _uiMapQuestLinesStore[uiMapId].push_back(questLineId);
11499 ++count;
11500
11501 } while (result->NextRow());
11502
11503 TC_LOG_INFO("server.loading", ">> Loaded {} UiMap questlines definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11504}
11505
11506std::vector<uint32> const* ObjectMgr::GetUiMapQuestLinesList(uint32 uiMapId) const
11507{
11508 return Trinity::Containers::MapGetValuePtr(_uiMapQuestLinesStore, uiMapId);
11509}
11510
11512{
11513 uint32 oldMSTime = getMSTime();
11514
11515 // need for reload case
11516 _uiMapQuestsStore.clear();
11517
11518 // 0 1
11519 QueryResult result = WorldDatabase.Query("SELECT UiMapId, QuestId FROM ui_map_quest");
11520
11521 if (!result)
11522 {
11523 TC_LOG_INFO("server.loading", ">> Loaded 0 quests for UIMaps. DB table `ui_map_quest` is empty!");
11524 return;
11525 }
11526
11527 uint32 count = 0;
11528
11529 do
11530 {
11531 Field* fields = result->Fetch();
11532
11533 uint32 uiMapId = fields[0].GetUInt32();
11534 uint32 questId = fields[1].GetUInt32();
11535
11536 if (!sUiMapStore.HasRecord(uiMapId))
11537 {
11538 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest` references non-existing UIMap {}, skipped", uiMapId);
11539 continue;
11540 }
11541
11542 if (!GetQuestTemplate(questId))
11543 {
11544 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest` references non-existing quest {}, skipped", questId);
11545 continue;
11546 }
11547
11548 _uiMapQuestsStore[uiMapId].push_back(questId);
11549 ++count;
11550
11551 } while (result->NextRow());
11552
11553 TC_LOG_INFO("server.loading", ">> Loaded {} UiMap quests definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11554}
11555
11556std::vector<uint32> const* ObjectMgr::GetUiMapQuestsList(uint32 uiMapId) const
11557{
11558 return Trinity::Containers::MapGetValuePtr(_uiMapQuestsStore, uiMapId);
11559}
11560
11562{
11563 uint32 oldMSTime = getMSTime();
11564
11565 // need for reload case
11566 _spawnTrackingDataStore.clear();
11567
11568 // 0 1 2 3 4
11569 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, MapId, PhaseId, PhaseGroup, PhaseUseFlags FROM spawn_tracking_template");
11570
11571 if (!result)
11572 {
11573 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking templates. DB table `spawn_tracking_template` is empty!");
11574 return;
11575 }
11576
11577 do
11578 {
11579 Field* fields = result->Fetch();
11580
11581 uint32 spawnTrackingId = fields[0].GetUInt32();
11582 uint32 mapId = fields[1].GetUInt32();
11583
11584 if (!sMapStore.HasRecord(mapId))
11585 {
11586 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` references non-existing map {}, skipped", mapId);
11587 continue;
11588 }
11589
11590 SpawnTrackingTemplateData& data = _spawnTrackingDataStore[spawnTrackingId];
11591 data.SpawnTrackingId = spawnTrackingId;
11592 data.MapId = mapId;
11593 data.PhaseId = fields[2].GetUInt32();
11594 data.PhaseGroup = fields[3].GetUInt32();
11595 data.PhaseUseFlags = fields[4].GetUInt8();
11596
11597 if (data.PhaseGroup && data.PhaseId)
11598 {
11599 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) with `PhaseId` and `PhaseGroup` set, `PhaseGroup` set to 0", data.SpawnTrackingId);
11600 data.PhaseGroup = 0;
11601 }
11602
11603 if (data.PhaseId)
11604 {
11605 if (!sPhaseStore.HasRecord(data.PhaseId))
11606 {
11607 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing non-existing `PhaseId` {}, set to 0", data.SpawnTrackingId, data.PhaseId);
11608 data.PhaseId = 0;
11609 }
11610 }
11611
11612 if (data.PhaseGroup)
11613 {
11614 if (!sDB2Manager.GetPhasesForGroup(data.PhaseGroup))
11615 {
11616 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing non-existing `PhaseGroup` {}, set to 0", data.SpawnTrackingId, data.PhaseGroup);
11617 data.PhaseGroup = 0;
11618 }
11619 }
11620
11622 {
11623 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing unknown `PhaseUseFlags`, removed unknown value.", data.SpawnTrackingId);
11625 }
11626
11628 {
11629 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) with `PhaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
11630 " removing PHASE_USE_FLAGS_INVERSE.", data.SpawnTrackingId);
11631 data.PhaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
11632 }
11633
11634 } while (result->NextRow());
11635
11636 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking templates in {} ms", _spawnTrackingDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
11637}
11638
11640{
11641 return Trinity::Containers::MapGetValuePtr(_spawnTrackingDataStore, spawnTrackingId);
11642}
11643
11644bool ObjectMgr::IsQuestObjectiveForSpawnTracking(uint32 spawnTrackingId, uint32 questObjectiveId) const
11645{
11646 if (std::vector<QuestObjective const*> const* questObjectiveList = Trinity::Containers::MapGetValuePtr(_spawnTrackingQuestObjectiveStore, spawnTrackingId))
11647 return advstd::ranges::contains(*questObjectiveList, questObjectiveId, &QuestObjective::ID);
11648
11649 return false;
11650}
11651
11653{
11654 uint32 oldMSTime = getMSTime();
11655
11656 // need for reload case
11657 _spawnTrackingQuestObjectiveStore.clear();
11658
11659 // 0 1
11660 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, QuestObjectiveId FROM spawn_tracking_quest_objective");
11661
11662 if (!result)
11663 {
11664 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking quest objectives. DB table `spawn_tracking_quest_objective` is empty!");
11665 return;
11666 }
11667
11668 uint32 count = 0;
11669
11670 do
11671 {
11672 Field* fields = result->Fetch();
11673
11674 uint32 spawnTrackingId = fields[0].GetUInt32();
11675 uint32 objectiveId = fields[1].GetUInt32();
11676
11677 if (!GetSpawnTrackingData(spawnTrackingId))
11678 {
11679 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_quest_objective` has quest objective {} assigned to spawn tracking {}, but spawn tracking does not exist!", objectiveId, spawnTrackingId);
11680 continue;
11681 }
11682
11683 QuestObjective const* questObjective = GetQuestObjective(objectiveId);
11684 if (!questObjective)
11685 {
11686 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_quest_objective` has quest objective {} assigned to spawn tracking {}, but quest objective does not exist!", objectiveId, spawnTrackingId);
11687 continue;
11688 }
11689
11690 _spawnTrackingQuestObjectiveStore[spawnTrackingId].push_back(questObjective);
11691
11692 ++count;
11693 } while (result->NextRow());
11694
11695 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking quest objectives in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11696}
11697
11699{
11700 uint32 oldMSTime = getMSTime();
11701
11702 // need for reload case
11703 _spawnTrackingMapStore.clear();
11704
11705 // 0 1 2 3
11706 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, SpawnType, SpawnId, QuestObjectiveIds FROM spawn_tracking");
11707
11708 if (!result)
11709 {
11710 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking members. DB table `spawn_tracking` is empty!");
11711 return;
11712 }
11713
11714 uint32 count = 0;
11715
11716 do
11717 {
11718 Field* fields = result->Fetch();
11719 uint32 spawnTrackingId = fields[0].GetUInt32();
11720 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
11721 ObjectGuid::LowType spawnId = fields[2].GetUInt64();
11722
11723 if (!SpawnData::TypeIsValid(spawnType))
11724 {
11725 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn data with invalid type {} listed for spawn tracking {}. Skipped.", uint32(spawnType), spawnTrackingId);
11726 continue;
11727 }
11728 else if (spawnType == SPAWN_TYPE_AREATRIGGER)
11729 {
11730 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has areatrigger spawn ({}) listed for spawn tracking {}. Skipped.", uint32(spawnType), spawnTrackingId);
11731 continue;
11732 }
11733
11734 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
11735 if (!data)
11736 {
11737 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn ({},{}) not found, but is listed as a member of spawn tracking {}!", uint32(spawnType), spawnId, spawnTrackingId);
11738 continue;
11739 }
11740 else if (data->spawnTrackingData)
11741 {
11742 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn ({},{}) is listed as a member of spawn tracking {}, but is already a member of spawn tracking {}. Skipped.",
11743 uint32(spawnType), spawnId, spawnTrackingId, data->spawnTrackingData->SpawnTrackingId);
11744 continue;
11745 }
11746
11747 SpawnTrackingTemplateData const* spawnTrackingTemplateData = GetSpawnTrackingData(spawnTrackingId);
11748 if (!spawnTrackingTemplateData)
11749 {
11750 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} assigned to spawn ({},{}), but spawn tracking does not exist!", spawnTrackingId, uint32(spawnType), spawnId);
11751 continue;
11752 }
11753
11754 if (spawnTrackingTemplateData->MapId != data->mapId)
11755 {
11756 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} (map {}) assigned to spawn ({},{}), but spawn has map {} - spawn NOT added to spawn tracking!",
11757 spawnTrackingId, spawnTrackingTemplateData->MapId, uint32(spawnType), spawnId, data->mapId);
11758 continue;
11759 }
11760
11761 SpawnData const* spawnData = data->ToSpawnData();
11762 if (spawnTrackingTemplateData->PhaseId != spawnData->phaseId || spawnTrackingTemplateData->PhaseGroup != spawnData->phaseGroup || spawnTrackingTemplateData->PhaseUseFlags != spawnData->phaseUseFlags)
11763 {
11764 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} with phase info (PhaseId: {}, PhaseGroup: {}, PhaseUseFlags: {}), ",
11765 "but spawn ({},{}) has different phase info (PhaseId: {}, PhaseGroup: {}, PhaseUseFlags: {}) - spawn NOT added to spawn tracking!",
11766 spawnTrackingId, spawnTrackingTemplateData->PhaseId, spawnTrackingTemplateData->PhaseGroup, spawnTrackingTemplateData->PhaseUseFlags,
11767 uint32(spawnType), spawnId, spawnData->phaseId, spawnData->phaseGroup, spawnData->phaseUseFlags);
11768 continue;
11769 }
11770
11771 std::vector<uint32> objectiveList;
11772 if (Optional<std::string_view> objectivesStr = fields[3].GetStringViewOrNull())
11773 {
11774 for (std::string_view objectiveStr : Trinity::Tokenize(*objectivesStr, ',', false))
11775 {
11776 Optional<uint32> objectiveId = Trinity::StringTo<uint32>(objectiveStr);
11777 if (!objectiveId)
11778 continue;
11779
11780 if (!IsQuestObjectiveForSpawnTracking(spawnTrackingId, *objectiveId))
11781 {
11782 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} assigned to spawn ({},{}), but spawn tracking is not linked to quest objective {}. Skipped.", spawnTrackingId, uint32(spawnType), spawnId, objectiveId);
11783 continue;
11784 }
11785
11786 objectiveList.push_back(*objectiveId);
11787 }
11788
11789 if (objectiveList.empty())
11790 {
11791 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} assigned to spawn ({},{}), but spawn tracking is not linked to any quest objective - spawn NOT added to spawn tracking!", spawnTrackingId, uint32(spawnType), spawnId);
11792 continue;
11793 }
11794 }
11795
11796 const_cast<SpawnMetadata*>(data)->spawnTrackingData = spawnTrackingTemplateData;
11797 const_cast<SpawnMetadata*>(data)->spawnTrackingQuestObjectives = std::move(objectiveList);
11798 _spawnTrackingMapStore.emplace(spawnTrackingId, data);
11799
11800 ++count;
11801 } while (result->NextRow());
11802
11803 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking members in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11804}
11805
11807{
11808 uint32 oldMSTime = getMSTime();
11809
11810 // 0 1 2 3 4 5 6 7
11811 QueryResult result = WorldDatabase.Query("SELECT SpawnType, SpawnId, State, Visible, StateSpellVisualId, StateAnimId, StateAnimKitId, StateWorldEffects FROM spawn_tracking_state");
11812
11813 if (!result)
11814 {
11815 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking states. DB table `spawn_tracking_state` is empty!");
11816 return;
11817 }
11818
11819 uint32 count = 0;
11820
11821 do
11822 {
11823 Field* fields = result->Fetch();
11824 SpawnObjectType spawnType = SpawnObjectType(fields[0].GetUInt8());
11825 ObjectGuid::LowType spawnId = fields[1].GetUInt64();
11826 SpawnTrackingState state = SpawnTrackingState(fields[2].GetUInt8());
11827
11828 if (!SpawnData::TypeIsValid(spawnType))
11829 {
11830 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn data with invalid type {}. Skipped.", uint32(spawnType));
11831 continue;
11832 }
11833 else if (spawnType == SPAWN_TYPE_AREATRIGGER)
11834 {
11835 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has areatrigger spawn ({}). Skipped.", uint32(spawnType));
11836 continue;
11837 }
11838
11839 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
11840 if (!data)
11841 {
11842 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn ({},{}) not found!", uint32(spawnType), spawnId);
11843 continue;
11844 }
11845 else if (!data->spawnTrackingData)
11846 {
11847 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn ({},{}) with spawn tracking states, but is not part of a spawn tracking. Skipped.", uint32(spawnType), spawnId);
11848 continue;
11849 }
11850
11851 if (state >= SpawnTrackingState::Max)
11852 {
11853 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn ({},{}) with invalid state type {}. Skipped.", uint32(spawnType), spawnId, uint8(state));
11854 continue;
11855 }
11856
11857 SpawnTrackingStateData& spawnTrackingStateData = const_cast<SpawnMetadata*>(data)->spawnTrackingStates[AsUnderlyingType(state)];
11858 spawnTrackingStateData.Visible = fields[3].GetBool();
11859 spawnTrackingStateData.StateSpellVisualId = fields[4].GetUInt32OrNull();
11860 spawnTrackingStateData.StateAnimId = fields[5].GetUInt16OrNull();
11861 spawnTrackingStateData.StateAnimKitId = fields[6].GetUInt16OrNull();
11862
11863 if (spawnTrackingStateData.StateSpellVisualId && !sSpellVisualStore.HasRecord(*spawnTrackingStateData.StateSpellVisualId))
11864 {
11865 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateSpellVisualId {} for spawn ({},{}), set to none.",
11866 *spawnTrackingStateData.StateSpellVisualId, uint32(spawnType), spawnId);
11867 spawnTrackingStateData.StateSpellVisualId.reset();
11868 }
11869
11870 if (spawnTrackingStateData.StateAnimId && *spawnTrackingStateData.StateAnimId != sDB2Manager.GetEmptyAnimStateID() && !sAnimationDataStore.HasRecord(*spawnTrackingStateData.StateAnimId))
11871 {
11872 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateAnimId {} for spawn ({},{}), set to none.",
11873 *spawnTrackingStateData.StateAnimId, uint32(spawnType), spawnId);
11874 spawnTrackingStateData.StateAnimId.reset();
11875 }
11876
11877 if (spawnTrackingStateData.StateAnimKitId && !sAnimKitStore.HasRecord(*spawnTrackingStateData.StateAnimKitId))
11878 {
11879 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateAnimKitId {} for spawn ({},{}), set to none.",
11880 *spawnTrackingStateData.StateAnimKitId, uint32(spawnType), spawnId);
11881 spawnTrackingStateData.StateAnimKitId.reset();
11882 }
11883
11884 if (Optional<std::string_view> worldEffectsStr = fields[7].GetStringViewOrNull())
11885 {
11886 for (std::string_view worldEffectStr : Trinity::Tokenize(*worldEffectsStr, ',', false))
11887 {
11888 Optional<uint32> worldEffectId = Trinity::StringTo<uint32>(worldEffectStr);
11889 if (!worldEffectId)
11890 continue;
11891
11892 if (!sWorldEffectStore.HasRecord(*worldEffectId))
11893 {
11894 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid WorldEffectId {} for spawn ({},{}). Skipped.",
11895 *worldEffectId, uint32(spawnType), spawnId);
11896 continue;
11897 }
11898
11899 spawnTrackingStateData.StateWorldEffects.push_back(*worldEffectId);
11900 }
11901 }
11902
11903 ++count;
11904 } while (result->NextRow());
11905
11906 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking states in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11907}
11908
11910{
11911 uint32 oldMSTime = getMSTime();
11912
11913 // need for reload case
11914 _jumpChargeParams.clear();
11915
11916 QueryResult result = WorldDatabase.Query("SELECT id, speed, treatSpeedAsMoveTimeSeconds, minHeight, maxHeight, unlimitedSpeed, spellVisualId, progressCurveId, parabolicCurveId, triggerSpellId FROM jump_charge_params");
11917 if (!result)
11918 {
11919 return;
11920 }
11921
11922 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (id)(speed)(treatSpeedAsMoveTimeSeconds)(minHeight)(maxHeight)(unlimitedSpeed)
11923 (spellVisualId)(progressCurveId)(parabolicCurveId)(triggerSpellId)) fields { *result };
11924
11925 do
11926 {
11927 int32 id = fields.id().GetInt32();
11928 JumpChargeParams& params = _jumpChargeParams[id];
11929 params.Speed = fields.speed().GetFloat();
11930 params.TreatSpeedAsMoveTimeSeconds = fields.treatSpeedAsMoveTimeSeconds().GetBool();
11931 params.UnlimitedSpeed = fields.unlimitedSpeed().GetBool();
11932 params.MinHeight = fields.minHeight().GetFloatOrNull();
11933 params.MaxHeight = fields.maxHeight().GetFloatOrNull();
11934 params.SpellVisualId = fields.spellVisualId().GetInt32OrNull();
11935 params.ProgressCurveId = fields.progressCurveId().GetInt32OrNull();
11936 params.ParabolicCurveId = fields.parabolicCurveId().GetInt32OrNull();
11937 params.TriggerSpellId = fields.triggerSpellId().GetInt32OrNull();
11938
11939 if (params.Speed <= 0.0f)
11940 {
11941 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid speed {} for id {}, set to default charge speed {}.",
11942 params.Speed, id, SPEED_CHARGE);
11943 params.Speed = SPEED_CHARGE;
11944 }
11945
11946 if (params.MinHeight && *params.MinHeight <= 0.0f)
11947 {
11948 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid min height {} for id {}, set to none.",
11949 params.MinHeight, id);
11950 params.MinHeight.reset();
11951 }
11952
11953 if (params.MaxHeight && *params.MaxHeight <= 0.0f)
11954 {
11955 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid max height {} for id {}, set to none.",
11956 params.MaxHeight, id);
11957 params.MaxHeight.reset();
11958 }
11959
11960 if (params.MinHeight && params.MaxHeight && *params.MinHeight >= *params.MaxHeight)
11961 {
11962 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid max height {} (must be greated than min height {}) for id {}, set to none.",
11963 params.MaxHeight, params.MinHeight, id);
11964 params.MaxHeight.reset();
11965 }
11966
11967 if (params.SpellVisualId && !sSpellVisualStore.LookupEntry(*params.SpellVisualId))
11968 {
11969 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing SpellVisual: {} for id {}, ignored.",
11970 *params.SpellVisualId, id);
11971 params.SpellVisualId.reset();
11972 }
11973
11974 if (params.ProgressCurveId && !sCurveStore.LookupEntry(*params.ProgressCurveId))
11975 {
11976 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing progress Curve: {} for id {}, ignored.",
11977 *params.ProgressCurveId, id);
11978 params.ProgressCurveId.reset();
11979 }
11980
11981 if (params.ParabolicCurveId && !sCurveStore.LookupEntry(*params.ParabolicCurveId))
11982 {
11983 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing parabolic Curve: {} for id {}, ignored.",
11984 *params.ParabolicCurveId, id);
11985 params.ParabolicCurveId.reset();
11986 }
11987
11988 if (params.TriggerSpellId && !sSpellMgr->GetSpellInfo(*params.TriggerSpellId, DIFFICULTY_NONE))
11989 {
11990 TC_LOG_DEBUG("sql.sql", "Table `jump_charge_params` references non-existing trigger spell id: {} for id {}, ignored.",
11991 *params.TriggerSpellId, id);
11992 params.TriggerSpellId.reset();
11993 }
11994
11995 } while (result->NextRow());
11996
11997 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _jumpChargeParams.size(), GetMSTimeDiffToNow(oldMSTime));
11998}
11999
12001{
12002 uint32 oldMSTime = getMSTime();
12003 _phaseNameStore.clear();
12004
12005 // 0 1
12006 QueryResult result = WorldDatabase.Query("SELECT `ID`, `Name` FROM `phase_name`");
12007
12008 if (!result)
12009 {
12010 TC_LOG_INFO("server.loading", ">> Loaded 0 phase names. DB table `phase_name` is empty.");
12011 return;
12012 }
12013
12014 uint32 count = 0;
12015 do
12016 {
12017 Field* fields = result->Fetch();
12018
12019 uint32 phaseId = fields[0].GetUInt32();
12020 std::string name = fields[1].GetString();
12021
12022 _phaseNameStore[phaseId] = name;
12023
12024 ++count;
12025 } while (result->NextRow());
12026 TC_LOG_INFO("server.loading", ">> Loaded {} phase names in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
12027}
12028
12029std::string ObjectMgr::GetPhaseName(uint32 phaseId) const
12030{
12031 PhaseNameContainer::const_iterator iter = _phaseNameStore.find(phaseId);
12032 return iter != _phaseNameStore.end() ? iter->second : "Unknown Name";
12033}
#define sAreaTriggerDataStore
#define sArenaTeamMgr
@ 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:51
@ TOTAL_LOCALES
Definition Common.h:65
@ LOCALE_enUS
Definition Common.h:52
constexpr bool IsValidLocale(LocaleConstant locale)
Definition Common.h:98
#define DEFAULT_LOCALE
Definition Common.h:69
const uint8 OLD_TOTAL_LOCALES
Definition Common.h:68
@ DAY
Definition Common.h:34
#define M_PI
Definition Common.h:118
#define sConditionMgr
@ CONDITION_SOURCE_TYPE_PLAYER_CONDITION
@ CONDITION_SOURCE_TYPE_GRAVEYARD
@ CONDITION_TEAM
#define sCreatureAIRegistry
@ CREATURE_FLAG_EXTRA_DB_ALLOWED
@ CREATURE_FLAG_EXTRA_TRIGGER
@ CREATURE_FLAG_EXTRA_INSTANCE_BIND
CreatureStaticFlags7
CreatureStaticFlags6
CreatureChaseMovementType
const uint8 MAX_KILL_CREDIT
CreatureStaticFlags8
CreatureStaticFlags5
@ CREATURE_STATIC_FLAG_5_INTERACT_WHILE_HOSTILE
CreatureStaticFlags
@ CREATURE_STATIC_FLAG_CAN_SWIM
CreatureStaticFlags3
@ CREATURE_STATIC_FLAG_3_CANT_SWIM
@ CREATURE_STATIC_FLAG_3_ALLOW_INTERACTION_WHILE_IN_COMBAT
@ CREATURE_STATIC_FLAG_3_CANNOT_TURN
CreatureStaticFlags4
@ CREATURE_STATIC_FLAG_4_AI_WILL_ONLY_SWIM_IF_TARGET_SWIMS
CreatureStaticFlags2
CreatureRandomMovementType
const uint32 MAX_CREATURE_SPELLS
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< AnimationDataEntry > sAnimationDataStore("AnimationData.db2", &AnimationDataLoadInfo::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
DB2Storage< CreatureTypeEntry > sCreatureTypeStore("CreatureType.db2", &CreatureTypeLoadInfo::Instance)
DB2Storage< CriteriaEntry > sCriteriaStore("Criteria.db2", &CriteriaLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
DB2Storage< ItemXItemEffectEntry > sItemXItemEffectStore("ItemXItemEffect.db2", &ItemXItemEffectLoadInfo::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< UiMapEntry > sUiMapStore("UiMap.db2", &UiMapLoadInfo::Instance)
DB2Storage< LFGDungeonsEntry > sLFGDungeonsStore("LFGDungeons.db2", &LfgDungeonsLoadInfo::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:579
@ MAX_LEVEL
Definition DBCEnums.h:45
@ STRONG_MAX_LEVEL
Definition DBCEnums.h:49
ItemContext
Definition DBCEnums.h:1324
Difficulty
Definition DBCEnums.h:935
@ DIFFICULTY_NONE
Definition DBCEnums.h:936
ItemSpecStat
Definition DBCEnums.h:1552
@ ITEM_SPEC_STAT_TWO_HANDED_MACE
Definition DBCEnums.h:1565
@ ITEM_SPEC_STAT_GUN
Definition DBCEnums.h:1568
@ ITEM_SPEC_STAT_HASTE
Definition DBCEnums.h:1578
@ ITEM_SPEC_STAT_CRIT
Definition DBCEnums.h:1577
@ ITEM_SPEC_STAT_CLOAK
Definition DBCEnums.h:1580
@ ITEM_SPEC_STAT_DODGE
Definition DBCEnums.h:1558
@ ITEM_SPEC_STAT_INTELLECT
Definition DBCEnums.h:1553
@ ITEM_SPEC_STAT_RELIC_FROST
Definition DBCEnums.h:1587
@ ITEM_SPEC_STAT_RELIC_BLOOD
Definition DBCEnums.h:1583
@ ITEM_SPEC_STAT_STRENGTH
Definition DBCEnums.h:1555
@ ITEM_SPEC_STAT_SHIELD
Definition DBCEnums.h:1575
@ ITEM_SPEC_STAT_NONE
Definition DBCEnums.h:1594
@ ITEM_SPEC_STAT_ONE_HANDED_AXE
Definition DBCEnums.h:1560
@ ITEM_SPEC_STAT_BONUS_ARMOR
Definition DBCEnums.h:1579
@ ITEM_SPEC_STAT_RELIC
Definition DBCEnums.h:1576
@ ITEM_SPEC_STAT_STAFF
Definition DBCEnums.h:1571
@ ITEM_SPEC_STAT_DAGGER
Definition DBCEnums.h:1566
@ ITEM_SPEC_STAT_AGILITY
Definition DBCEnums.h:1554
@ ITEM_SPEC_STAT_RELIC_HOLY
Definition DBCEnums.h:1592
@ ITEM_SPEC_STAT_RELIC_WIND
Definition DBCEnums.h:1591
@ ITEM_SPEC_STAT_THROWN
Definition DBCEnums.h:1573
@ ITEM_SPEC_STAT_WAND
Definition DBCEnums.h:1574
@ ITEM_SPEC_STAT_BOW
Definition DBCEnums.h:1569
@ ITEM_SPEC_STAT_RELIC_FIRE
Definition DBCEnums.h:1588
@ ITEM_SPEC_STAT_FIST_WEAPON
Definition DBCEnums.h:1567
@ ITEM_SPEC_STAT_TWO_HANDED_SWORD
Definition DBCEnums.h:1563
@ ITEM_SPEC_STAT_PARRY
Definition DBCEnums.h:1559
@ ITEM_SPEC_STAT_POLEARM
Definition DBCEnums.h:1572
@ ITEM_SPEC_STAT_RELIC_FEL
Definition DBCEnums.h:1585
@ ITEM_SPEC_STAT_RELIC_WATER
Definition DBCEnums.h:1589
@ ITEM_SPEC_STAT_ONE_HANDED_MACE
Definition DBCEnums.h:1564
@ ITEM_SPEC_STAT_RELIC_SHADOW
Definition DBCEnums.h:1584
@ ITEM_SPEC_STAT_WARGLAIVES
Definition DBCEnums.h:1581
@ ITEM_SPEC_STAT_RELIC_LIFE
Definition DBCEnums.h:1590
@ ITEM_SPEC_STAT_RELIC_ARCANE
Definition DBCEnums.h:1586
@ ITEM_SPEC_STAT_CROSSBOW
Definition DBCEnums.h:1570
@ ITEM_SPEC_STAT_TWO_HANDED_AXE
Definition DBCEnums.h:1561
@ ITEM_SPEC_STAT_RELIC_IRON
Definition DBCEnums.h:1582
@ ITEM_SPEC_STAT_ONE_HANDED_SWORD
Definition DBCEnums.h:1562
@ ITEM_SPEC_STAT_HIT
Definition DBCEnums.h:1557
CriteriaStartEvent
Definition DBCEnums.h:515
CriteriaFailEvent
Definition DBCEnums.h:494
@ PHASE_USE_FLAGS_ALWAYS_VISIBLE
Definition DBCEnums.h:2182
@ PHASE_USE_FLAGS_ALL
Definition DBCEnums.h:2185
@ PHASE_USE_FLAGS_INVERSE
Definition DBCEnums.h:2183
#define MAX_ITEM_PROTO_STATS
Definition DBCEnums.h:1214
CriteriaType
Definition DBCEnums.h:548
@ PlayerTriggerGameEvent
@ AnyoneTriggerGameEventScenario
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
#define UI64FMTD
Definition Define.h:138
uint8_t uint8
Definition Define.h:156
int16_t int16
Definition Define.h:151
int8_t int8
Definition Define.h:152
int32_t int32
Definition Define.h:150
uint64_t uint64
Definition Define.h:153
#define UI64LIT(N)
Definition Define.h:139
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
std::unordered_set< uint32 > params[2]
uint16 flags
@ DISABLE_TYPE_QUEST
Definition DisableMgr.h:28
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:28
#define ABORT_MSG
Definition Errors.h:80
#define ABORT
Definition Errors.h:79
#define ASSERT_NOTNULL(pointer)
Definition Errors.h:74
#define ASSERT
Definition Errors.h:72
#define sGameObjectAIRegistry
GameTable< GtXpEntry > sXpGameTable
GameTable< GtBaseMPEntry > sBaseMPGameTable
float GetGameTableColumnForClass(T const *row, int32 class_)
Definition GameTables.h:215
GossipOptionNpc
Definition GossipDef.h:38
GossipOptionFlags
Definition GossipDef.h:125
#define MAX_NUMBER_OF_GRIDS
Definition GridDefines.h:38
#define sGroupMgr
Definition GroupMgr.h:66
#define sGuildMgr
Definition GuildMgr.h:76
@ TO_BE_DECIDED
@ ITEM_VENDOR_TYPE_ITEM
@ ITEM_VENDOR_TYPE_CURRENCY
@ ITEM_CLASS_GEM
@ ITEM_CLASS_ARMOR
@ ITEM_CLASS_WEAPON
@ ITEM_CLASS_CONSUMABLE
@ ITEM_FLAG2_FACTION_HORDE
@ ITEM_FLAG2_FACTION_ALLIANCE
@ ITEM_SUBCLASS_WEAPON_CROSSBOW
@ ITEM_SUBCLASS_WEAPON_GUN
@ ITEM_SUBCLASS_WEAPON_AXE2
@ ITEM_SUBCLASS_WEAPON_STAFF
@ ITEM_SUBCLASS_WEAPON_MACE
@ ITEM_SUBCLASS_WEAPON_WARGLAIVES
@ ITEM_SUBCLASS_WEAPON_MACE2
@ ITEM_SUBCLASS_WEAPON_DAGGER
@ ITEM_SUBCLASS_WEAPON_BOW
@ ITEM_SUBCLASS_WEAPON_SWORD
@ ITEM_SUBCLASS_WEAPON_AXE
@ ITEM_SUBCLASS_WEAPON_FIST_WEAPON
@ ITEM_SUBCLASS_WEAPON_WAND
@ ITEM_SUBCLASS_WEAPON_THROWN
@ ITEM_SUBCLASS_WEAPON_SWORD2
@ ITEM_SUBCLASS_WEAPON_POLEARM
@ ITEM_MOD_AGI_STR_INT
@ ITEM_MOD_AGI_INT
@ ITEM_MOD_STR_INT
@ ITEM_MOD_PARRY_RATING
@ ITEM_MOD_EXTRA_ARMOR
@ ITEM_MOD_HASTE_RATING
@ ITEM_MOD_CRIT_SPELL_RATING
@ ITEM_MOD_CRIT_RANGED_RATING
@ ITEM_MOD_CRIT_MELEE_RATING
@ ITEM_MOD_STRENGTH
@ ITEM_MOD_HIT_RATING
@ ITEM_MOD_INTELLECT
@ ITEM_MOD_AGILITY
@ ITEM_MOD_DODGE_RATING
@ ITEM_MOD_CRIT_RATING
@ ITEM_MOD_AGI_STR
@ ITEM_SUBCLASS_ARMOR_MAIL
@ ITEM_SUBCLASS_ARMOR_CLOTH
@ ITEM_SUBCLASS_ARMOR_RELIC
@ ITEM_SUBCLASS_ARMOR_LEATHER
@ ITEM_SUBCLASS_ARMOR_SHIELD
@ ITEM_SUBCLASS_ARMOR_PLATE
#define MAX_INVTYPE
@ SOCKET_COLOR_RELIC_FEL
@ SOCKET_COLOR_RELIC_WIND
@ SOCKET_COLOR_RELIC_FIRE
@ SOCKET_COLOR_RELIC_LIFE
@ SOCKET_COLOR_RELIC_ARCANE
@ SOCKET_COLOR_RELIC_SHADOW
@ SOCKET_COLOR_RELIC_FROST
@ SOCKET_COLOR_RELIC_IRON
@ SOCKET_COLOR_RELIC_WATER
@ SOCKET_COLOR_RELIC_HOLY
@ SOCKET_COLOR_RELIC_BLOOD
constexpr std::array< InventoryType, 10 > InventoryTypesEquipable
#define MAX_ITEM_SUBCLASS_WEAPON
@ ITEM_SUBCLASS_FOOD_DRINK
@ INVTYPE_CLOAK
@ INVTYPE_ROBE
@ LANG_ITEM_ALREADY_IN_LIST
Definition Language.h:254
@ LANG_COMMAND_VENDORSELECTION
Definition Language.h:329
@ LANG_ITEM_NOT_FOUND
Definition Language.h:251
@ LANG_EXTENDED_COST_NOT_EXIST
Definition Language.h:386
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:176
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
@ MAIL_CHECK_MASK_COD_PAYMENT
This mail is payable on delivery.
Definition Mail.h:57
@ MAIL_CHECK_MASK_RETURNED
This mail was returned. Do not allow returning mail back again.
Definition Mail.h:55
@ MAIL_CHECK_MASK_NOT_RETURNABLE
This mail cannot be returned even if mail was not returned before.
Definition Mail.h:63
std::vector< MailItemInfo > MailItemInfoVec
Definition Mail.h:178
@ MAIL_NORMAL
Definition Mail.h:40
#define sMapMgr
Definition MapManager.h:186
@ 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
#define DEFAULT_VISIBILITY_DISTANCE
TempSummonType
@ TEMPSUMMON_MANUAL_DESPAWN
#define INTERACTION_DISTANCE
#define DEFAULT_PLAYER_DISPLAY_SCALE
uint64 MAKE_PAIR64(uint32 l, uint32 h)
uint16 MAKE_PAIR16(uint8 l, uint8 h)
VisibilityDistanceType
TypeID
Definition ObjectGuid.h:37
@ TYPEID_GAMEOBJECT
Definition ObjectGuid.h:46
@ TYPEID_UNIT
Definition ObjectGuid.h:43
HighGuid
Definition ObjectGuid.h:109
void CheckAndFixGOChairHeightId(GameObjectTemplate const *goInfo, uint32 &dataN, uint32 N)
static EnumFlag< CfgCategoriesCharsets > GetRealmLanguageType(bool create)
#define ChooseCreatureFlagSource(field)
void CheckGONoDamageImmuneId(GameObjectTemplate *goTemplate, uint32 dataN, uint32 N)
void CheckGOSpellId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
ScriptMapMap * GetScriptsMapByType(ScriptsType type)
Definition ObjectMgr.cpp:97
std::string GetScriptCommandName(ScriptCommands command)
bool isValidString(const std::wstring &wstr, uint32 strictMask, bool numericOrSpace, bool create=false)
uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
ScriptMapMap sEventScripts
Definition ObjectMgr.cpp:84
void CheckGOConsumable(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
void CheckGOLinkedTrapId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
void CheckGOLockId(GameObjectTemplate const *goInfo, uint32 dataN, uint32 N)
std::string GetScriptsTableNameByType(ScriptsType type)
Definition ObjectMgr.cpp:86
ExtendedPlayerName ExtractExtendedPlayerName(std::string const &name)
bool normalizePlayerName(std::string &name)
std::vector< PlayerCreateInfoItem > PlayerCreateInfoItems
Definition ObjectMgr.h:627
#define MAX_PLAYER_NAME
Definition ObjectMgr.h:887
std::pair< GraveyardContainer::const_iterator, GraveyardContainer::const_iterator > GraveyardMapBounds
Definition ObjectMgr.h:846
std::multimap< uint32, ScriptInfo > ScriptMap
Definition ObjectMgr.h:412
#define MAX_CHARTER_NAME
Definition ObjectMgr.h:890
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const *rcEntry)
std::pair< SpellScriptsContainer::iterator, SpellScriptsContainer::iterator > SpellScriptsBounds
Definition ObjectMgr.h:415
ScriptMapMap * GetScriptsMapByType(ScriptsType type)
Definition ObjectMgr.cpp:97
ScriptCommands
Definition ObjectMgr.h:99
@ SCRIPT_COMMAND_EMOTE
Definition ObjectMgr.h:101
@ SCRIPT_COMMAND_CREATE_ITEM
Definition ObjectMgr.h:117
@ SCRIPT_COMMAND_DESPAWN_SELF
Definition ObjectMgr.h:118
@ SCRIPT_COMMAND_CLOSE_DOOR
Definition ObjectMgr.h:112
@ SCRIPT_COMMAND_CAST_SPELL
Definition ObjectMgr.h:115
@ SCRIPT_COMMAND_RESPAWN_GAMEOBJECT
Definition ObjectMgr.h:109
@ SCRIPT_COMMAND_QUEST_EXPLORED
Definition ObjectMgr.h:107
@ SCRIPT_COMMAND_ACTIVATE_OBJECT
Definition ObjectMgr.h:113
@ SCRIPT_COMMAND_TALK
Definition ObjectMgr.h:100
@ SCRIPT_COMMAND_OPEN_DOOR
Definition ObjectMgr.h:111
@ SCRIPT_COMMAND_EQUIP
Definition ObjectMgr.h:126
@ SCRIPT_COMMAND_PLAYMOVIE
Definition ObjectMgr.h:129
@ SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT
Definition ObjectMgr.h:121
@ SCRIPT_COMMAND_PLAY_ANIMKIT
Definition ObjectMgr.h:131
@ SCRIPT_COMMAND_TELEPORT_TO
Definition ObjectMgr.h:106
@ SCRIPT_COMMAND_FIELD_SET_DEPRECATED
Definition ObjectMgr.h:102
@ SCRIPT_COMMAND_MOVE_TO
Definition ObjectMgr.h:103
@ SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED
Definition ObjectMgr.h:105
@ SCRIPT_COMMAND_TEMP_SUMMON_CREATURE
Definition ObjectMgr.h:110
@ SCRIPT_COMMAND_MOVEMENT
Definition ObjectMgr.h:130
@ SCRIPT_COMMAND_KILL_CREDIT
Definition ObjectMgr.h:108
@ SCRIPT_COMMAND_KILL
Definition ObjectMgr.h:122
@ SCRIPT_COMMAND_LOAD_PATH
Definition ObjectMgr.h:120
@ SCRIPT_COMMAND_ORIENTATION
Definition ObjectMgr.h:125
@ SCRIPT_COMMAND_PLAY_SOUND
Definition ObjectMgr.h:116
@ SCRIPT_COMMAND_MODEL
Definition ObjectMgr.h:127
@ SCRIPT_COMMAND_CLOSE_GOSSIP
Definition ObjectMgr.h:128
@ SCRIPT_COMMAND_REMOVE_AURA
Definition ObjectMgr.h:114
@ SCRIPT_COMMAND_FLAG_SET_DEPRECATED
Definition ObjectMgr.h:104
std::string GetScriptCommandName(ScriptCommands command)
QueryDataGroup
Definition ObjectMgr.h:949
@ QUERY_DATA_CREATURES
Definition ObjectMgr.h:950
@ QUERY_DATA_QUESTS
Definition ObjectMgr.h:953
@ QUERY_DATA_GAMEOBJECTS
Definition ObjectMgr.h:951
@ QUERY_DATA_POIS
Definition ObjectMgr.h:954
std::unordered_map< uint32, GridObjectGuids > GridObjectGuidsMap
Definition ObjectMgr.h:477
#define SPAWNGROUP_MAP_UNSET
Definition ObjectMgr.h:893
TC_GAME_API ScriptMapMap sEventScripts
Definition ObjectMgr.cpp:84
#define sObjectMgr
Definition ObjectMgr.h:1873
std::pair< GossipMenusContainer::const_iterator, GossipMenusContainer::const_iterator > GossipMenusMapBounds
Definition ObjectMgr.h:776
@ CHAT_TYPE_WHISPER
Definition ObjectMgr.h:140
std::map< uint32, ScriptMap > ScriptMapMap
Definition ObjectMgr.h:413
SummonerType
Definition ObjectMgr.h:72
@ SUMMONER_TYPE_MAP
Definition ObjectMgr.h:75
@ SUMMONER_TYPE_CREATURE
Definition ObjectMgr.h:73
@ SUMMONER_TYPE_GAMEOBJECT
Definition ObjectMgr.h:74
std::string GetScriptsTableNameByType(ScriptsType type)
Definition ObjectMgr.cpp:86
ScriptsType
Definition ObjectMgr.h:170
@ SCRIPTS_EVENT
Definition ObjectMgr.h:173
#define MAX_SKILL_STEP
Definition ObjectMgr.h:875
std::multimap< uint32, uint32 > QuestRelationsReverse
Definition ObjectMgr.h:559
std::multimap< uint32, uint32 > QuestRelations
Definition ObjectMgr.h:558
#define MAX_PET_NAME
Definition ObjectMgr.h:889
SkillRangeType
Definition ObjectMgr.h:867
@ SKILL_RANGE_MONO
Definition ObjectMgr.h:870
@ SKILL_RANGE_NONE
Definition ObjectMgr.h:872
@ SKILL_RANGE_LANGUAGE
Definition ObjectMgr.h:868
@ SKILL_RANGE_RANK
Definition ObjectMgr.h:871
@ SKILL_RANGE_LEVEL
Definition ObjectMgr.h:869
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
PlayerChoiceResponseFlags
#define DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(result_type, fields_list)
#define QUEST_ITEM_DROP_COUNT
Definition QuestDef.h:45
#define QUEST_REWARD_ITEM_COUNT
Definition QuestDef.h:47
#define QUEST_REWARD_REPUTATIONS_COUNT
Definition QuestDef.h:48
@ QUEST_OBJECTIVE_DEFEATBATTLEPET
Definition QuestDef.h:366
@ QUEST_OBJECTIVE_WINPVPPETBATTLES
Definition QuestDef.h:367
@ QUEST_OBJECTIVE_INCREASE_REPUTATION
Definition QuestDef.h:372
@ QUEST_OBJECTIVE_HAVE_CURRENCY
Definition QuestDef.h:370
@ QUEST_OBJECTIVE_WINPETBATTLEAGAINSTNPC
Definition QuestDef.h:365
@ QUEST_OBJECTIVE_MONSTER
Definition QuestDef.h:354
@ QUEST_OBJECTIVE_TALKTO
Definition QuestDef.h:357
@ QUEST_OBJECTIVE_KILL_WITH_LABEL
Definition QuestDef.h:375
@ QUEST_OBJECTIVE_CRITERIA_TREE
Definition QuestDef.h:368
@ QUEST_OBJECTIVE_OBTAIN_CURRENCY
Definition QuestDef.h:371
@ QUEST_OBJECTIVE_PROGRESS_BAR
Definition QuestDef.h:369
@ QUEST_OBJECTIVE_PLAYERKILLS
Definition QuestDef.h:363
@ QUEST_OBJECTIVE_ITEM
Definition QuestDef.h:355
@ QUEST_OBJECTIVE_MONEY
Definition QuestDef.h:362
@ QUEST_OBJECTIVE_MAX_REPUTATION
Definition QuestDef.h:361
@ QUEST_OBJECTIVE_AREA_TRIGGER_EXIT
Definition QuestDef.h:374
@ QUEST_OBJECTIVE_AREATRIGGER
Definition QuestDef.h:364
@ QUEST_OBJECTIVE_CURRENCY
Definition QuestDef.h:358
@ QUEST_OBJECTIVE_LEARNSPELL
Definition QuestDef.h:359
@ QUEST_OBJECTIVE_AREA_TRIGGER_ENTER
Definition QuestDef.h:373
@ QUEST_OBJECTIVE_GAMEOBJECT
Definition QuestDef.h:356
@ QUEST_OBJECTIVE_MIN_REPUTATION
Definition QuestDef.h:360
#define QUEST_REWARD_CURRENCY_COUNT
Definition QuestDef.h:50
@ QUEST_OBJECTIVE_FLAG_SEQUENCED
Definition QuestDef.h:384
#define QUEST_REWARD_CHOICES_COUNT
Definition QuestDef.h:46
@ QUEST_FLAGS_DAILY
Definition QuestDef.h:226
@ QUEST_FLAGS_WEEKLY
Definition QuestDef.h:229
@ QUEST_FLAGS_COMPLETION_AREA_TRIGGER
Definition QuestDef.h:216
@ QUEST_FLAGS_COMPLETION_EVENT
Definition QuestDef.h:215
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:224
@ QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES
Definition QuestDef.h:329
@ QUEST_SPECIAL_FLAGS_REPEATABLE
Definition QuestDef.h:320
@ QUEST_SPECIAL_FLAGS_DB_ALLOWED
Definition QuestDef.h:327
@ QUEST_SPECIAL_FLAGS_MONTHLY
Definition QuestDef.h:324
constexpr Trinity::RaceMask< uint64 > RACEMASK_ALL_PLAYABLE
Definition RaceMask.h:293
Races
Definition RaceMask.h:28
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:43
#define sRealmList
Definition RealmList.h:95
SceneFlag
#define sScriptMgr
Definition ScriptMgr.h:1449
#define MAX_STATS
#define MAX_REPUTATION_RANK
#define CURRENT_EXPANSION
@ SPELL_EFFECT_SEND_EVENT
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_PALADIN
@ CLASS_ROGUE
#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
@ GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
constexpr uint32 GetMaxLevelForExpansion(uint32 expansion)
@ GENDER_UNKNOWN
@ GENDER_MALE
@ GENDER_FEMALE
@ MAX_ITEM_QUALITY
CreatureFamily
@ 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_DB_ALLOWED_QUEST_TYPES
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_LEVEL_CURRENT
@ MAX_ACCOUNT_EXPANSIONS
@ MAX_EXPANSIONS
@ CHAT_MSG_RAID_BOSS_WHISPER
GOState
@ GO_STATE_TRANSPORT_ACTIVE
@ SKILL_RUNEFORGING
#define MAX_SPILLOVER_FACTIONS
SpawnGroupFlags
Definition SpawnData.h:54
@ SPAWNGROUP_FLAGS_ALL
Definition SpawnData.h:63
@ SPAWNGROUP_FLAG_MANUAL_SPAWN
Definition SpawnData.h:58
@ SPAWNGROUP_FLAG_COMPATIBILITY_MODE
Definition SpawnData.h:57
@ SPAWNGROUP_FLAG_SYSTEM
Definition SpawnData.h:56
@ LINKED_RESPAWN_CREATURE_TO_GO
Definition SpawnData.h:154
@ LINKED_RESPAWN_GO_TO_GO
Definition SpawnData.h:155
@ LINKED_RESPAWN_CREATURE_TO_CREATURE
Definition SpawnData.h:153
@ LINKED_RESPAWN_GO_TO_CREATURE
Definition SpawnData.h:156
SpawnObjectType
Definition SpawnData.h:35
@ SPAWN_TYPE_GAMEOBJECT
Definition SpawnData.h:37
@ SPAWN_TYPE_AREATRIGGER
Definition SpawnData.h:38
@ SPAWN_TYPE_CREATURE
Definition SpawnData.h:36
SpawnTrackingState
Definition SpawnData.h:94
@ SPELL_AURA_CONTROL_VEHICLE
#define sSpellMgr
Definition SpellMgr.h:812
#define sTerrainMgr
Definition TerrainMgr.h:167
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
#define sTransportMgr
@ UNIT_FLAG2_ALLOWED
@ UNIT_FLAG2_AI_WILL_ONLY_SWIM_IF_TARGET_SWIMS
@ UNIT_FLAG2_INTERACT_WHILE_HOSTILE
@ UNIT_FLAG2_CANNOT_TURN
@ UNIT_FLAG2_FEIGN_DEATH
@ 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
@ UNIT_NPC_FLAG_VENDOR
@ UNIT_NPC_FLAG_GOSSIP
@ UNIT_NPC_FLAG_QUESTGIVER
@ UNIT_NPC_FLAG_SPELLCLICK
@ UNIT_FLAG3_ALLOWED
@ UNIT_FLAG3_FAKE_DEAD
@ UNIT_FLAG3_ALLOW_INTERACTION_WHILE_IN_COMBAT
@ MAX_SHEATH_STATE
Definition UnitDefines.h:86
#define MAX_EQUIPMENT_ITEMS
Definition UnitDefines.h:37
AnimTier
Definition UnitDefines.h:69
@ UNIT_FLAG_IMMUNE_TO_NPC
@ UNIT_FLAG_ALLOWED
@ UNIT_FLAG_CAN_SWIM
@ UNIT_FLAG_CANT_SWIM
@ UNIT_FLAG_IMMUNE_TO_PC
float DegToRad(float degrees)
Definition Util.cpp:880
std::wstring GetMainPartOfName(std::wstring const &wname, uint32 declension)
Definition Util.cpp:622
bool WStrToUtf8(wchar_t const *wstr, size_t size, std::string &utf8str)
Definition Util.cpp:386
void wstrToLower(std::wstring &str)
Definition Util.cpp:433
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition Util.cpp:336
bool isKoreanString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:255
bool isCyrillicString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:247
bool isChineseString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:263
bool isBasicLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:231
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
bool isExtendedLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:239
struct WcharToUpper wcharToUpper
VehicleCustomFlags
std::vector< VehicleAccessory > VehicleAccessoryList
@ WORLD_INS_CONDITION
@ WORLD_DEL_GAME_TELE
@ WORLD_INS_GRAVEYARD_ZONE
@ WORLD_SEL_NPC_VENDOR_REF
@ WORLD_REP_LINKED_RESPAWN
@ WORLD_INS_GAME_TELE
@ WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA
@ WORLD_UPD_CREATURE_ZONE_AREA_DATA
@ WORLD_DEL_LINKED_RESPAWN
@ WORLD_SEL_CREATURE_TEMPLATE
@ WORLD_DEL_NPC_VENDOR
@ WORLD_INS_NPC_VENDOR
std::strong_ordering operator<=>(WowTime const &left, WowTime const &right)
Definition WowTime.cpp:142
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
void shrink_to_fit()
Definition ByteBuffer.h:596
void PSendSysMessage(char const *fmt, Args &&... args)
Definition Chat.h:62
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:111
bool HasFlag(CreatureStaticFlags flag) const
static float GetDamageMod(CreatureClassifications classification)
static char const * GetCreatureFamilyPetName(uint32 petfamily, LocaleConstant locale)
constexpr bool HasFlag(T flag) const
Definition EnumFlag.h:106
Class used to access individual fields of database query result.
Definition Field.h:94
float GetFloat() const noexcept
Definition Field.cpp:85
Optional< uint16 > GetUInt16OrNull() const noexcept
Definition Field.cpp:150
Optional< uint64 > GetUInt64OrNull() const noexcept
Definition Field.cpp:178
Optional< float > GetFloatOrNull() const noexcept
Definition Field.cpp:192
uint64 GetUInt64() const noexcept
Definition Field.cpp:71
int16 GetInt16() const noexcept
Definition Field.cpp:50
bool GetBool() const noexcept
Definition Field.h:102
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
Optional< int32 > GetInt32OrNull() const noexcept
Definition Field.cpp:171
uint16 GetUInt16() const noexcept
Definition Field.cpp:43
std::string_view GetStringView() const noexcept
Definition Field.cpp:118
bool IsNull() const noexcept
Definition Field.h:131
uint8 GetUInt8() const noexcept
Definition Field.cpp:29
int32 GetInt32() const noexcept
Definition Field.cpp:64
Optional< uint32 > GetUInt32OrNull() const noexcept
Definition Field.cpp:164
std::string GetString() const noexcept
Definition Field.cpp:113
int8 GetInt8() const noexcept
Definition Field.cpp:36
int64 GetInt64() const noexcept
Definition Field.cpp:78
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
Definition Item.cpp:1180
bool HaveQuestLootFor(uint32 loot_id) const
Definition LootMgr.cpp:189
static bool IsValidMapCoord(uint32 mapid, float x, float y)
Definition MapManager.h:83
static bool IsValidMAP(uint32 mapId)
Definition Map.h:227
void Set(ObjectGuid::LowType val)
uint64 LowType
Definition ObjectGuid.h:321
std::unordered_set< std::string > GetAllDBScriptNames() const
void reserve(size_t capacity)
uint32 insert(std::string_view scriptName, bool isScriptNameBound=true)
NameMap::const_iterator find(size_t index) const
NameMap::const_iterator end() const
static void ChooseCreatureFlags(CreatureTemplate const *cInfo, uint64 *npcFlags, uint32 *unitFlags, uint32 *unitFlags2, uint32 *unitFlags3, CreatureStaticFlagsHolder const &staticFlags, CreatureData const *data=nullptr)
std::unordered_map< uint32, AreaTriggerPolygon > _areaTriggerPolygons
Definition ObjectMgr.h:1707
std::set< uint32 > EventContainer
Definition ObjectMgr.h:988
CreatureMovementData const * GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
QuestOfferRewardLocaleContainer _questOfferRewardLocaleStore
Definition ObjectMgr.h:1843
uint32 LoadReferenceVendor(int32 vendor, int32 item_id, std::set< uint32 > *skip_vendors)
Trainer::Trainer const * GetTrainer(uint32 trainerId) const
std::unordered_set< std::string > GetAllDBScriptNames() const
AccessRequirement const * GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
void LoadQuestRelationsHelper(QuestRelations &map, QuestRelationsReverse *reverseMap, std::string const &table)
EquipmentInfo const * GetEquipmentInfo(uint32 entry, int8 &id) const
TempSummonDataContainer _tempSummonDataStore
Stores temp summon data grouped by summoner's entry, summoner's type and group id.
Definition ObjectMgr.h:1837
QuestRelations _creatureQuestInvolvedRelations
Definition ObjectMgr.h:1729
PetLevelInfo const * GetPetLevelInfo(uint32 creature_id, uint8 level) const
void LoadCreatureTemplateResistances()
VehicleAccessoryList const * GetVehicleAccessoryList(Vehicle *veh) const
VehicleAccessoryContainer _vehicleAccessoryStore
Definition ObjectMgr.h:1748
int32 GetFishingBaseSkillLevel(AreaTableEntry const *areaEntry) const
bool HasPersonalSpawns(uint32 mapid, Difficulty spawnMode, uint32 phaseId) const
uint32 GetXPForLevel(uint8 level) const
void LoadCreatureTemplate(Field *fields)
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore
Definition ObjectMgr.h:1827
CreatureBaseStats const * GetCreatureBaseStats(uint8 level, uint8 unitClass)
void LoadCreatureTemplateSpells()
ObjectGuid::LowType GenerateGameObjectSpawnId()
InstanceTemplate const * GetInstanceTemplate(uint32 mapId) const
uint64 GenerateMailID()
TrinityString const * GetTrinityString(uint32 entry) const
Definition ObjectMgr.h:1507
QuestObjectivesByIdContainer _questObjectives
Definition ObjectMgr.h:1692
MapPersonalObjectGuids _mapPersonalObjectGuidsStore
Definition ObjectMgr.h:1806
void LoadQuestTemplateLocale()
void LoadReputationOnKill()
std::vector< PhaseAreaInfo > const * GetPhasesForArea(uint32 areaId) const
void LoadEventScripts()
void LoadGameObjectTemplate()
CreatureAddon const * GetCreatureTemplateAddon(uint32 entry) const
void LoadFactionChangeSpells()
void LoadFactionChangeItems()
void LoadAreaTriggerTeleports()
void LoadNPCText()
WorldSafeLocsEntry const * GetClosestGraveyardInZone(WorldLocation const &location, uint32 team, WorldObject *conditionObject, uint32 zoneId) const
QuestGreetingLocale const * GetQuestGreetingLocale(TypeID type, uint32 id) const
std::unordered_map< std::pair< Races, Classes >, std::unique_ptr< PlayerInfo > > _playerInfo
Definition ObjectMgr.h:1789
CreatureQuestCurrenciesMap _creatureQuestCurrenciesStore
Definition ObjectMgr.h:1818
void LoadPageTextLocales()
AreaTriggerTeleport const * GetGoBackTrigger(uint32 Map) const
HalfNameContainer _petHalfName1
Definition ObjectMgr.h:1803
void LoadVehicleTemplate()
void LoadInstanceTemplate()
CreatureTemplateSparringContainer _creatureTemplateSparringStore
Definition ObjectMgr.h:1813
void LoadItemScriptNames()
uint32 GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team=false)
SpellScriptsContainer _spellScriptsStore
Definition ObjectMgr.h:1744
SpawnGroupDataContainer _spawnGroupDataStore
Definition ObjectMgr.h:1829
QuestPOIData const * GetQuestPOIData(int32 questId)
WorldSafeLocsEntry const * GetDefaultGraveyard(uint32 team) const
void RemoveSpawnDataFromGrid(SpawnData const *data)
void LoadQuestGreetingLocales()
CreatureDataContainer _creatureDataStore
Definition ObjectMgr.h:1807
void LoadPageTexts()
void ValidateSpellScripts()
void LoadCreatureQuestEnders()
QuestRelations _goQuestRelations
Definition ObjectMgr.h:1725
void LoadSceneTemplates()
void LoadGameobjectQuestEnders()
void LoadCreatureMovementOverrides()
void LoadCreatureLocales()
void LoadDestructibleHitpoints()
TavernAreaTriggerContainer _tavernAreaTriggerStore
Definition ObjectMgr.h:1700
void LoadCreatureClassLevelStats()
PlayerInfo const * GetPlayerInfo(uint32 race, uint32 class_) const
void LoadCreatureTemplates()
GameObjectTemplateAddon const * GetGameObjectTemplateAddon(uint32 entry) const
void LoadEventSet()
CreatureStaticFlagsOverride const * GetCreatureStaticFlagsOverride(ObjectGuid::LowType spawnId, Difficulty difficultyId) const
Trinity::IteratorPair< std::unordered_map< uint32, WorldSafeLocsEntry >::const_iterator > GetWorldSafeLocs() const
PageTextLocaleContainer _pageTextLocaleStore
Definition ObjectMgr.h:1845
EquipmentInfoContainer _equipmentInfoStore
Definition ObjectMgr.h:1820
ObjectGuid::LowType _gameObjectSpawnId
Definition ObjectMgr.h:1684
void GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32 &baseMana) const
void LoadTrainers()
AccessRequirementContainer _accessRequirementStore
Definition ObjectMgr.h:1708
JumpChargeParams const * GetJumpChargeParams(int32 id) const
void LoadQuestAreaTriggers()
QuestGreetingLocaleContainer _questGreetingLocaleStore
Definition ObjectMgr.h:1704
SpawnData const * GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const
std::unordered_map< uint32, SkillTiersEntry > _skillTiers
Definition ObjectMgr.h:1799
CreatureTemplateContainer _creatureTemplateStore
Definition ObjectMgr.h:1808
bool AddGameTele(GameTele &data)
std::vector< uint32 > const * GetUiMapQuestLinesList(uint32 uiMapId) const
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
void LoadQuestOfferRewardLocale()
void LoadFactionChangeAchievements()
SpawnTrackingLinkContainer _spawnTrackingMapStore
Definition ObjectMgr.h:1834
static void AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector< std::string > &data)
void LoadGameObjectOverrides()
SpellScriptsBounds GetSpellScriptsBounds(uint32 spellId)
void LoadGossipMenuAddon()
QuestRequestItemsLocaleContainer _questRequestItemsLocaleStore
Definition ObjectMgr.h:1844
std::map< HighGuid, ObjectGuidGenerator > _guidGenerators
Definition ObjectMgr.h:1689
PlayerChoice const * GetPlayerChoice(int32 choiceId) const
void LoadPhases()
QuestGreetingContainer _questGreetingStore
Definition ObjectMgr.h:1703
SpawnGroupTemplateData const * GetLegacySpawnGroup() const
Definition ObjectMgr.h:1365
void LoadFactionChangeReputations()
void RemoveCreatureFromGrid(CreatureData const *data)
std::set< uint32 > _transportMaps
Definition ObjectMgr.h:1869
void LoadPetNumber()
std::unordered_map< ObjectGuid::LowType, CreatureMovementData > _creatureMovementOverrides
Definition ObjectMgr.h:1814
GraveyardContainer GraveyardStore
Definition ObjectMgr.h:1605
std::string const & GetScriptName(uint32 id) const
GameObjectTemplateContainer _gameObjectTemplateStore
Definition ObjectMgr.h:1826
void LoadReputationSpilloverTemplate()
void LoadExplorationBaseXP()
void LoadFishingBaseSkillLevel()
InstanceSpawnGroupContainer _instanceSpawnGroupStore
Definition ObjectMgr.h:1832
uint32 GetScriptId(std::string_view name, bool isDatabaseBound=true)
void LoadQuestRequestItemsLocale()
EventScriptContainer _eventScriptStore
Definition ObjectMgr.h:1712
void LoadPetLevelInfo()
void LoadQuestObjectivesLocale()
void UnloadPhaseConditions()
void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo *info) const
std::vector< int32 > const * GetCreatureQuestCurrencyList(uint32 creatureId) const
PhaseInfoStruct const * GetPhaseInfo(uint32 phaseId) const
void LoadAreaTriggerPolygons()
CreatureAddon const * GetCreatureAddon(ObjectGuid::LowType lowguid) const
void LoadLinkedRespawn()
void LoadEquipmentTemplates()
GameObjectData const * GetGameObjectData(ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1445
bool IsValidEvent(uint32 eventId) const
Definition ObjectMgr.h:1117
RaceUnlockRequirement const * GetRaceUnlockRequirement(uint8 raceId) const
void LoadSpawnGroups()
NpcText const * GetNpcText(uint32 textID) const
void LoadGossipMenu()
void LoadCreatureStaticFlagsOverride()
QuestRelationsReverse _creatureQuestInvolvedRelationsReverse
Definition ObjectMgr.h:1730
ItemTemplateContainer _itemTemplateStore
Definition ObjectMgr.h:1840
void LoadQuestStartersAndEnders()
GameObjectOverride const * GetGameObjectOverride(ObjectGuid::LowType spawnId) const
static PetNameInvalidReason CheckPetName(std::string_view name)
GameObjectDataContainer _gameObjectDataStore
Definition ObjectMgr.h:1823
void LoadSpawnGroupTemplates()
AreaTriggerTeleport const * GetAreaTrigger(uint32 trigger) const
void LoadUiMapQuestLines()
void LoadGraveyardZones()
uint32 GeneratePetNumber()
CreatureSummonedData const * GetCreatureSummonedData(uint32 entryId) const
uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
void LoadTerrainWorldMaps()
void LoadPlayerInfo()
void LoadGossipMenuItemsLocales()
void LoadQuests()
CreatureLocaleContainer _creatureLocaleStore
Definition ObjectMgr.h:1822
void CheckCreatureTemplate(CreatureTemplate const *cInfo)
std::atomic< uint32 > _hiPetNumber
Definition ObjectMgr.h:1682
bool IsReservedName(std::string_view name) const
void AddCreatureToGrid(CreatureData const *data)
std::unordered_map< uint32, std::vector< uint32 > > _spawnGroupsByMap
Definition ObjectMgr.h:1830
std::atomic< uint64 > _mailId
Definition ObjectMgr.h:1681
void RemoveGameobjectFromGrid(GameObjectData const *data)
GameTele const * GetGameTeleExactName(std::string_view name) const
void LoadGameTele()
void LoadCreatureSummonedData()
PointOfInterestLocaleContainer _pointOfInterestLocaleStore
Definition ObjectMgr.h:1847
InstanceTemplateContainer _instanceTemplateStore
Definition ObjectMgr.h:1753
void LoadTerrainSwapDefaults()
bool DeleteGameTele(std::string_view name)
ObjectGuid::LowType GenerateCreatureSpawnId()
QuestRelations _goQuestInvolvedRelations
Definition ObjectMgr.h:1726
void LoadCreatureQuestItems()
void LoadNPCSpellClickSpells()
QuestAreaTriggerContainer _questAreaTriggerStore
Definition ObjectMgr.h:1699
FishingBaseSkillContainer _fishingBaseForAreaStore
Definition ObjectMgr.h:1798
bool IsTransportMap(uint32 mapId) const
Definition ObjectMgr.h:1642
ObjectGuid::LowType _creatureSpawnId
Definition ObjectMgr.h:1683
AreaTriggerContainer _areaTriggerStore
Definition ObjectMgr.h:1705
GridObjectGuids const * GetCellPersonalObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 phaseId, uint32 gridId) const
void LoadUiMapQuests()
void LoadAccessRequirements()
MapObjectGuids _mapObjectGuidsStore
Definition ObjectMgr.h:1805
CreatureAddonContainer _creatureAddonStore
Definition ObjectMgr.h:1811
void LoadSpawnTrackingStates()
PetLevelInfoContainer _petInfoStore
Definition ObjectMgr.h:1785
static CreatureModel const * ChooseDisplayId(CreatureTemplate const *cinfo, CreatureData const *data=nullptr)
VehicleAccessoryTemplateContainer _vehicleTemplateAccessoryStore
Definition ObjectMgr.h:1747
static ObjectMgr * instance()
void LoadVehicleTemplateAccessories()
void DeleteGameObjectData(ObjectGuid::LowType spawnId)
void LoadJumpChargeParams()
WorldSafeLocsEntry const * GetWorldSafeLoc(uint32 id) const
TerrainSwapInfo const * GetTerrainSwapInfo(uint32 terrainSwapId) const
std::vector< uint32 > const * GetUiMapQuestsList(uint32 uiMapId) const
HalfNameContainer _petHalfName0
Definition ObjectMgr.h:1802
void LoadSpawnTrackings()
void LoadGameObjectLocales()
bool IsQuestObjectiveForSpawnTracking(uint32 spawnTrackingId, uint32 questObjectiveId) const
void LoadAreaPhases()
void LoadTempSummons()
void LoadQuestGreetings()
NpcTextContainer _npcTextStore
Definition ObjectMgr.h:1702
QuestContainer _questTemplates
Definition ObjectMgr.h:1690
void PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
CreatureModelInfo const * GetCreatureModelRandomGender(CreatureModel *model, CreatureTemplate const *creatureTemplate) const
PageText const * GetPageText(uint32 pageEntry)
void LoadCreatureTemplateAddons()
SpawnTrackingTemplateData const * GetSpawnTrackingData(uint32 spawnTrackingId) const
static ResponseCodes CheckPlayerName(std::string_view name, LocaleConstant locale, bool create=false)
void LoadItemTemplates()
uint32 GetCreatureTrainerForGossipOption(uint32 creatureId, uint32 gossipMenuId, uint32 gossipOptionId) const
bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist=true)
std::vector< Quest const * > _questTemplatesAutoPush
Definition ObjectMgr.h:1691
ObjectGuidGenerator & GetGuidSequenceGenerator(HighGuid high)
uint32 GetBaseXP(uint8 level)
CreatureTemplateAddonContainer _creatureTemplateAddonStore
Definition ObjectMgr.h:1812
static bool IsValidCharterName(std::string_view name)
std::unordered_map< uint32, VehicleTemplate > _vehicleTemplateStore
Definition ObjectMgr.h:1746
void LoadPetNames()
GameObjectTemplate const * GetGameObjectTemplate(uint32 entry) const
GridObjectGuids const * GetGridObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 gridId)
GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore
Definition ObjectMgr.h:1846
BaseXPContainer _baseXPTable
Definition ObjectMgr.h:1795
void LoadQuestPOI()
void LoadGameObjectTemplateAddons()
DestructibleHitpoint const * GetDestructibleHitpoint(uint32 entry) const
void LoadTavernAreaTriggers()
void LoadReputationRewardRate()
AreaTriggerPolygon const * GetAreaTriggerPolygon(uint32 areaTriggerId) const
AreaTriggerScriptContainer _areaTriggerScriptStore
Definition ObjectMgr.h:1706
void CheckCreatureMovement(char const *table, uint64 id, CreatureMovementData &creatureMovement)
QuestRelations _creatureQuestRelations
Definition ObjectMgr.h:1728
ClassAvailability const * GetClassExpansionRequirementFallback(uint8 classId) const
void AddSpawnDataToGrid(SpawnData const *data)
SceneTemplate const * GetSceneTemplate(uint32 sceneId) const
Definition ObjectMgr.h:1662
Quest const * GetQuestTemplate(uint32 quest_id) const
void LoadCreatureQuestCurrencies()
VehicleTemplate const * GetVehicleTemplate(Vehicle *veh) const
GameObjectAddon const * GetGameObjectAddon(ObjectGuid::LowType lowguid) const
void LoadGameobjectQuestStarters()
void LoadScripts(ScriptsType type)
RepOnKillContainer _repOnKillStore
Definition ObjectMgr.h:1715
void LoadGameObjectForQuests()
GridObjectGuidsMap const * GetMapObjectGuids(uint32 mapid, Difficulty spawnMode)
void LoadFactionChangeTitles()
uint32 GetAreaTriggerScriptId(uint32 trigger_id) const
void LoadPlayerChoices()
SpawnGroupLinkContainer _spawnGroupMapStore
Definition ObjectMgr.h:1831
std::string GetPhaseName(uint32 phaseId) const
uint64 _equipmentSetGuid
Definition ObjectMgr.h:1680
RepSpilloverTemplateContainer _repSpilloverTemplateStore
Definition ObjectMgr.h:1716
void LoadSpawnTrackingTemplates()
void LoadAreaTriggerScripts()
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid)
SpawnMetadata const * GetSpawnMetadata(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1413
void AddVendorItem(uint32 entry, VendorItem const &vItem, bool persist=true)
void ReturnOrDeleteOldMails(bool serverUp)
void LoadGameObjectAddons()
SpawnGroupTemplateData const * GetDefaultSpawnGroup() const
Definition ObjectMgr.h:1364
GameObjectData & NewOrExistGameObjectData(ObjectGuid::LowType spawnId)
CreatureModelInfo const * GetCreatureModelInfo(uint32 modelId) const
CreatureModelContainer _creatureModelStore
Definition ObjectMgr.h:1809
LinkedRespawnContainer _linkedRespawnStore
Definition ObjectMgr.h:1821
uint64 GenerateEquipmentSetGuid()
void LoadSkillTiers()
uint32 _auctionId
Definition ObjectMgr.h:1679
void GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
void DeleteCreatureData(ObjectGuid::LowType spawnId)
void LoadCreatureTemplateSparring()
GameObjectLocaleContainer _gameObjectLocaleStore
Definition ObjectMgr.h:1824
GameObjectAddonContainer _gameObjectAddonStore
Definition ObjectMgr.h:1815
QuestObjectivesLocaleContainer _questObjectivesLocaleStore
Definition ObjectMgr.h:1842
void LoadCreatureQuestStarters()
bool IsScriptDatabaseBound(uint32 id) const
GameTele const * GetGameTele(uint32 id) const
Definition ObjectMgr.h:1536
void LoadInstanceSpawnGroups()
void LoadItemTemplateAddon()
PlayerXPperLevel _playerXPperLevel
Definition ObjectMgr.h:1792
QuestRelationsReverse _goQuestInvolvedRelationsReverse
Definition ObjectMgr.h:1727
void LoadSpellScriptNames()
CreatureTemplate const * GetCreatureTemplate(uint32 entry) const
void LoadMailLevelRewards()
void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo *plinfo) const
CreatureData const * GetCreatureData(ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1424
void AddGameobjectToGrid(GameObjectData const *data)
PointOfInterestContainer _pointsOfInterestStore
Definition ObjectMgr.h:1721
void LoadGameObjectQuestItems()
std::vector< Difficulty > ParseSpawnDifficulties(std::string_view difficultyString, std::string_view table, ObjectGuid::LowType spawnId, uint32 mapId, std::set< Difficulty > const &mapDifficulties)
void LoadPointsOfInterest()
void LoadFactionChangeQuests()
RepRewardRateContainer _repRewardRateStore
Definition ObjectMgr.h:1714
EventContainer _eventStore
Definition ObjectMgr.h:1711
uint32 GenerateAuctionID()
std::string GeneratePetName(uint32 entry)
void LoadCreatureTemplateGossip()
void LoadPointOfInterestLocales()
VehicleSeatAddonContainer _vehicleSeatAddonStore
Definition ObjectMgr.h:1870
QuestTemplateLocaleContainer _questTemplateLocaleStore
Definition ObjectMgr.h:1841
bool LoadTrinityStrings()
std::vector< float > const * GetCreatureTemplateSparringValues(uint32 entry) const
void LoadCreatureTemplateModels()
CreatureData & NewOrExistCreatureData(ObjectGuid::LowType spawnId)
std::vector< uint32 > const * GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
PageTextContainer _pageTextStore
Definition ObjectMgr.h:1752
void LoadGossipMenuItems()
void LoadCreatureTrainers()
ExclusiveQuestGroups _exclusiveQuestGroups
Definition ObjectMgr.h:1732
void LoadVehicleAccessories()
void LoadVendors()
void LoadCreatureModelInfo()
ClassAvailability const * GetClassExpansionRequirement(uint8 raceId, uint8 classId) const
void LoadVehicleSeatAddon()
GraveyardData const * FindGraveyardData(uint32 id, uint32 zone) const
uint32 GetEventScriptId(uint32 eventId) const
void SetHighestGuids()
void LoadCreatureTemplateDifficulty()
void InitializeQueriesData(QueryDataGroup mask)
void LoadReservedPlayersNames()
CreatureQuestItemMap _creatureQuestItemStore
Definition ObjectMgr.h:1817
void LoadRaceAndClassExpansionRequirements()
AreaTriggerTeleport const * GetMapEntranceTrigger(uint32 Map) const
QuestPOIContainer _questPOIStore
Definition ObjectMgr.h:1723
void OnDeleteSpawnData(SpawnData const *data)
DestructibleHitpointContainer _destructibleHitpointStore
Definition ObjectMgr.h:1825
void LoadGameObjects()
void LoadSpawnTrackingQuestObjectives()
SpellClickInfoContainer _spellClickInfoStore
Definition ObjectMgr.h:1742
void LoadPlayerChoicesLocale()
QuestGreeting const * GetQuestGreeting(TypeID type, uint32 id) const
void LoadPhaseNames()
void LoadWorldSafeLocs()
std::unordered_map< uint32, WorldSafeLocsEntry > _worldSafeLocs
Definition ObjectMgr.h:1709
bool IsVendorItemValid(uint32 vendor_entry, VendorItem const &vItem, Player *player=nullptr, std::set< uint32 > *skip_vendors=nullptr, uint32 ORnpcflag=0) const
void LoadCreatureAddons()
bool AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist=true)
GameObjectOverrideContainer _gameObjectOverrideStore
Definition ObjectMgr.h:1828
void LoadCreatures()
std::unordered_map< uint32, CreatureSummonedData > _creatureSummonedDataStore
Definition ObjectMgr.h:1810
SkillTiersEntry const * GetSkillTier(uint32 skillTierId) const
ItemTemplate const * GetItemTemplate(uint32 entry) const
WorldSafeLocsEntry const * GetClosestGraveyard(WorldLocation const &location, uint32 team, WorldObject *conditionObject) const
Player * ToPlayer()
Definition Object.h:126
Creature * ToCreature()
Definition Object.h:121
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:2256
void setUInt16(uint8 index, uint16 value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setInt64(uint8 index, int64 value)
void setFloat(uint8 index, float value)
void setUInt64(uint8 index, uint64 value)
void setUInt8(uint8 index, uint8 value)
uint32 _soundAccept
Definition QuestDef.h:823
int32 _prevQuestID
Definition QuestDef.h:871
int32 _questSortID
Definition QuestDef.h:789
uint32 _contentTuningID
Definition QuestDef.h:788
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionValue
Definition QuestDef.h:748
uint32 _requiredSkillPoints
Definition QuestDef.h:878
std::array< LootItemType, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemType
Definition QuestDef.h:741
std::vector< uint32 > DependentBreadcrumbQuests
Definition QuestDef.h:776
Trinity::RaceMask< int32, 2 > _allowableRaces
Definition QuestDef.h:827
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyId
Definition QuestDef.h:751
int32 _breadcrumbForQuestId
Definition QuestDef.h:874
void LoadQuestDetails(Field *fields)
Definition QuestDef.cpp:190
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemCount
Definition QuestDef.h:743
bool HasQuestObjectiveType(QuestObjectiveType type) const
Definition QuestDef.h:621
void LoadRewardDisplaySpell(Field *fields)
Definition QuestDef.cpp:154
uint32 _sourceItemIdCount
Definition QuestDef.h:883
static bool IsTakingQuestEnabled(uint32 questId)
Definition QuestDef.cpp:470
void LoadConditionalConditionalRequestItemsText(Field *fields)
Definition QuestDef.cpp:344
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemId
Definition QuestDef.h:742
uint32 _rewardSkillPoints
Definition QuestDef.h:817
void LoadQuestRequestItems(Field *fields)
Definition QuestDef.cpp:207
uint32 _requiredMinRepFaction
Definition QuestDef.h:879
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemId
Definition QuestDef.h:745
uint32 _requiredSkillId
Definition QuestDef.h:877
int32 _requiredMinRepValue
Definition QuestDef.h:880
uint32 _flags
Definition QuestDef.h:806
void LoadConditionalConditionalQuestDescription(Field *fields)
Definition QuestDef.cpp:321
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemCount
Definition QuestDef.h:746
void LoadQuestMailSender(Field *fields)
Definition QuestDef.cpp:267
void LoadQuestObjective(Field *fields)
Definition QuestDef.cpp:272
int32 _exclusiveGroup
Definition QuestDef.h:873
uint32 _sourceItemId
Definition QuestDef.h:805
int32 GetPrevQuestId() const
Definition QuestDef.h:650
void LoadConditionalConditionalOfferRewardText(Field *fields)
Definition QuestDef.cpp:367
int32 _requiredMaxRepValue
Definition QuestDef.h:882
uint32 GetQuestType() const
Definition QuestDef.h:634
uint32 _requiredMaxRepFaction
Definition QuestDef.h:881
std::vector< uint32 > DependentPreviousQuests
Definition QuestDef.h:775
uint32 GetQuestId() const
Definition QuestDef.h:633
uint32 _rewardNextQuest
Definition QuestDef.h:792
void LoadQuestOfferReward(Field *fields)
Definition QuestDef.cpp:223
uint32 _rewardMailDelay
Definition QuestDef.h:876
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDropQuantity
Definition QuestDef.h:740
uint32 _sourceSpellID
Definition QuestDef.h:870
uint32 _rewardSkillId
Definition QuestDef.h:816
uint32 _soundTurnIn
Definition QuestDef.h:824
int32 GetBreadcrumbForQuestId() const
Definition QuestDef.h:653
void LoadRewardHouseDecor(Field *fields)
Definition QuestDef.cpp:423
uint32 _specialFlags
Definition QuestDef.h:885
void SetSpecialFlag(QuestSpecialFlags flag)
Definition QuestDef.h:620
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionOverride
Definition QuestDef.h:749
std::array< uint32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionId
Definition QuestDef.h:747
uint32 _rewardSpell
Definition QuestDef.h:798
uint32 _allowableClasses
Definition QuestDef.h:869
QuestObjectives const & GetObjectives() const
Definition QuestDef.h:677
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyCount
Definition QuestDef.h:752
uint32 _id
Definition QuestDef.h:785
uint32 _rewardMailSenderEntry
Definition QuestDef.h:884
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDrop
Definition QuestDef.h:739
void LoadRewardHouseRoom(Field *fields)
Definition QuestDef.cpp:418
void LoadConditionalConditionalQuestCompletionLog(Field *fields)
Definition QuestDef.cpp:390
uint32 _rewardTitleId
Definition QuestDef.h:814
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:615
uint32 _rewardMailTemplateId
Definition QuestDef.h:875
void LoadRewardChoiceItems(Field *fields)
Definition QuestDef.cpp:184
void LoadTreasurePickers(Field *fields)
Definition QuestDef.cpp:413
uint32 _nextQuestID
Definition QuestDef.h:872
void LoadQuestTemplateAddon(Field *fields)
Definition QuestDef.cpp:242
static const int32 Reputation_Cap
SpellInfo const * GetFirstRankSpell() const
uint32 const Id
Definition SpellInfo.h:328
bool IsRanked() const
int32 GetDuration() const
SpellInfo const * GetNextRankSpell() const
bool HasAura(AuraType aura) const
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:143
uint8 value_type
Definition DBCEnums.h:2526
Unit * GetSummonerUnit() const
std::pair< iterator, bool > emplace(Args &&... args)
Definition FlatSet.h:70
Utility class to enable range for loop syntax for multimap.equal_range uses.
constexpr end_iterator end() const
constexpr iterator begin() const
decltype(auto) PostWork(T &&work)
Definition ThreadPool.h:35
Definition Unit.h:647
TempSummon * ToTempSummon()
Definition Unit.h:1844
bool IsSummon() const
Definition Unit.h:761
bool IsInRaidWith(Unit const *unit) const
Definition Unit.cpp:12272
bool IsInPartyWith(Unit const *unit) const
Definition Unit.cpp:12253
static VMapManager * createOrGetVMapManager()
Unit * GetBase() const
Definition Vehicle.h:49
uint32 GetCreatureEntry() const
Definition Vehicle.h:51
constexpr void WorldRelocate(WorldLocation const &loc)
Definition Position.h:202
constexpr uint32 GetMapId() const
Definition Position.h:216
PhaseShift & GetPhaseShift()
Definition Object.h:310
std::string const & GetName() const
Definition Object.h:342
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2191
static void StopNow(uint8 exitcode)
Definition World.h:670
#define sWorld
Definition World.h:919
@ CONFIG_STRICT_PET_NAMES
Definition World.h:251
@ CONFIG_MIN_PET_NAME
Definition World.h:254
@ CONFIG_MAX_PLAYER_LEVEL
Definition World.h:263
@ CONFIG_MIN_CHARTER_NAME
Definition World.h:253
@ CONFIG_STRICT_CHARTER_NAMES
Definition World.h:250
@ CONFIG_STRICT_PLAYER_NAMES
Definition World.h:249
@ CONFIG_MIN_PLAYER_NAME
Definition World.h:252
@ CONFIG_CREATURE_CHECK_INVALID_POSITION
Definition World.h:191
@ 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:190
@ CONFIG_GAME_OBJECT_CHECK_INVALID_POSITION
Definition World.h:192
@ CONFIG_LOAD_LOCALES
Definition World.h:201
@ ERROR_EXIT_CODE
Definition World.h:76
static constexpr ObjectData creatureData[]
static constexpr uint32 Entry[5]
bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const *ref, uint8 flags)
time_t GetGameTime()
Definition GameTime.cpp:52
std::span< ItemBonusEntry const * > GetItemBonuses(uint32 bonusListId)
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
TC_GAME_API std::span< QuestLineXQuestEntry const *const > GetQuestsForQuestLine(uint32 questLineId)
Definition QuestMgr.cpp:91
ItemModifiedAppearanceEntry const * GetDefaultItemModifiedAppearance(uint32 itemId)
ItemModifiedAppearanceEntry const * GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
constexpr bool Intersects(Iterator1 first1, Sentinel1 last1, Iterator2 first2, Sentinel2 last2)
Definition Containers.h:203
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition Util.cpp:57
bool IsValidMapCoord(float c)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
Optional< Result > StringTo(std::string_view str, Params &&... params)
constexpr GridCoord ComputeGridCoord(float x, float y)
struct advstd::ranges::Contains contains
STL namespace.
std::string questFailedText
Definition ObjectMgr.h:468
std::vector< Position > Vertices
Definition ObjectMgr.h:455
Optional< float > Height
Definition ObjectMgr.h:456
uint8 MinActiveExpansionLevel
Definition ObjectMgr.h:932
uint8 AccountExpansionLevel
Definition ObjectMgr.h:931
uint8 ActiveExpansionLevel
Definition ObjectMgr.h:930
std::weak_ptr< ConditionContainer > Conditions
bool Meets(WorldObject const *object) const
uint32 x_coord
constexpr uint32 GetId() const
uint32 y_coord
std::vector< uint32 > auras
uint16 meleeAnimKit
uint16 movementAnimKit
VisibilityDistanceType visibilityDistanceType
float wander_distance
Optional< uint32 > curHealthPct
Optional< uint64 > npcflag
Optional< CreatureModel > display
Optional< uint32 > unit_flags2
uint32 currentwaypoint
Optional< uint32 > unit_flags3
Optional< uint32 > unit_flags
CreatureStaticFlagsHolder StaticFlags
std::vector< std::string > Title
std::vector< std::string > Name
std::vector< std::string > TitleAlt
std::vector< std::string > NameAlt
uint32 displayId_other_gender
uint32 CreatureDisplayID
CreatureRandomMovementType Random
CreatureChaseMovementType Chase
Optional< CreatureStaticFlags > StaticFlags1
Optional< CreatureStaticFlags5 > StaticFlags5
Optional< CreatureStaticFlags7 > StaticFlags7
Optional< CreatureStaticFlags3 > StaticFlags3
Optional< CreatureStaticFlags2 > StaticFlags2
Optional< CreatureStaticFlags6 > StaticFlags6
Optional< CreatureStaticFlags8 > StaticFlags8
Optional< CreatureStaticFlags4 > StaticFlags4
Optional< uint32 > CreatureIDVisibleToSummoner
Optional< uint32 > GroundMountDisplayID
Optional< std::vector< uint32 > > DespawnOnQuestsRemoved
Optional< uint32 > FlyingMountDisplayID
CreatureModel const * GetRandomValidModel() const
Definition Creature.cpp:113
std::string TitleAlt
CreatureClassifications Classification
CreatureModel const * GetFirstInvisibleModel() const
Definition Creature.cpp:148
std::string SubName
CreatureFamily family
std::string Name
uint32 spells[MAX_CREATURE_SPELLS]
int32 resistance[MAX_SPELL_SCHOOL]
std::string StringId
std::vector< uint32 > GossipMenuIds
CreatureMovementData Movement
uint32 KillCredit[MAX_KILL_CREDIT]
int32 WidgetSetUnitConditionID
std::vector< CreatureModel > Models
std::string FemaleName
std::string AIName
std::string IconName
std::string name[MAX_DECLINED_NAME_CASES]
EquipmentItem Items[MAX_EQUIPMENT_ITEMS]
uint16 AppearanceModId
uint16 ParentFactionID
bool CanHaveReputation() const
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::@199::@216 moTransport
uint32 data[MAX_GAMEOBJECT_DATA]
struct GameObjectTemplate::@199::@209 spellFocus
struct GameObjectTemplate::@199::@266 raw
struct GameObjectTemplate::@199::@239 garrisonBuilding
struct GameObjectTemplate::@199::@226 fishingHole
struct GameObjectTemplate::@199::@211 goober
struct GameObjectTemplate::@199::@207 trap
struct GameObjectTemplate::@199::@208 chair
bool IsDespawnAtAction() const
struct GameObjectTemplate::@199::@234 destructibleBuilding
struct GameObjectTemplate::@199::@203 questgiver
struct GameObjectTemplate::@199::@213 areaDamage
struct GameObjectTemplate::@199::@233 barberChair
struct GameObjectTemplate::@199::@223 spellCaster
struct GameObjectTemplate::@199::@227 flagDrop
std::string castBarCaption
struct GameObjectTemplate::@199::@202 button
struct GameObjectTemplate::@199::@251 gatheringNode
struct GameObjectTemplate::@199::@204 chest
struct GameObjectTemplate::@199::@225 flagStand
struct GameObjectTemplate::@199::@201 door
float position_y
Definition ObjectMgr.h:159
float orientation
Definition ObjectMgr.h:161
float position_x
Definition ObjectMgr.h:158
uint32 mapId
Definition ObjectMgr.h:162
std::string name
Definition ObjectMgr.h:163
float position_z
Definition ObjectMgr.h:160
std::wstring wnameLow
Definition ObjectMgr.h:164
int32 FriendshipFactionID
Definition ObjectMgr.h:771
int32 LfgDungeonsID
Definition ObjectMgr.h:772
std::vector< std::string > BoxText
Definition ObjectMgr.h:528
std::vector< std::string > OptionText
Definition ObjectMgr.h:527
Optional< int32 > SpellID
Definition ObjectMgr.h:757
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:756
std::string OptionText
Definition ObjectMgr.h:746
int32 GossipOptionID
Definition ObjectMgr.h:743
uint32 ActionMenuID
Definition ObjectMgr.h:750
uint32 OrderIndex
Definition ObjectMgr.h:744
Optional< int32 > GossipNpcOptionID
Definition ObjectMgr.h:752
Optional< int32 > OverrideIconID
Definition ObjectMgr.h:758
uint32 ActionPoiID
Definition ObjectMgr.h:751
GossipOptionNpc OptionNpc
Definition ObjectMgr.h:745
std::string BoxText
Definition ObjectMgr.h:755
GossipOptionFlags Flags
Definition ObjectMgr.h:749
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:747
uint32 TextID
Definition ObjectMgr.h:765
uint32 MenuID
Definition ObjectMgr.h:764
uint32 safeLocId
Definition ObjectMgr.h:841
ConditionsReference Conditions
Definition ObjectMgr.h:842
uint8 SubclassID
int8 InventoryType
std::array< int32, MAX_ITEM_PROTO_STATS > StatModifierBonusStat
ItemSpecStats(ItemEntry const *item, ItemSparseEntry const *sparse)
void AddModStat(int32 itemStatType)
uint32 ItemSpecStatTypes[MAX_ITEM_PROTO_STATS]
uint32 ItemSpecStatCount
void AddStat(ItemSpecStat statType)
std::bitset< MAX_CLASSES *MAX_SPECIALIZATIONS > Specializations[3]
uint32 MinMoneyLoot
uint32 MaxMoneyLoot
ItemEntry const * BasicData
static std::size_t CalculateItemSpecBit(ChrSpecializationEntry const *spec)
ItemSparseEntry const * ExtendedData
int32 QuestLogItemId
uint32 RandomBonusListTemplateId
uint32 MaxDurability
uint32 ItemSpecClassMask
ObjectGuid::LowType item_guid
Definition Mail.h:175
uint32 item_template
Definition Mail.h:176
Definition Mail.h:181
uint64 messageID
Definition Mail.h:182
ObjectGuid::LowType receiver
Definition Mail.h:187
uint8 messageType
Definition Mail.h:183
uint64 COD
Definition Mail.h:195
time_t expire_time
Definition Mail.h:192
ObjectGuid::LowType sender
Definition Mail.h:186
std::vector< MailItemInfo > items
Definition Mail.h:190
time_t deliver_time
Definition Mail.h:193
uint32 checked
Definition Mail.h:196
uint16 mailTemplateId
Definition Mail.h:185
DBCPosition2D Corpse
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:68
std::string Text
Definition ObjectMgr.h:65
uint32 NextPageID
Definition ObjectMgr.h:66
int32 PlayerConditionID
Definition ObjectMgr.h:67
std::vector< PathPropertyEntry const * > Properties
Definition DB2Stores.h:355
std::vector< DBCPosition3D > Locations
Definition DB2Stores.h:354
uint16 health
Definition ObjectMgr.h:679
uint16 armor
Definition ObjectMgr.h:681
uint16 stats[MAX_STATS]
Definition ObjectMgr.h:678
uint16 mana
Definition ObjectMgr.h:680
std::unordered_set< uint32 > Areas
Definition ObjectMgr.h:913
bool IsAllowedInArea(uint32 areaId) const
std::vector< std::string > Question
Definition ObjectMgr.h:552
std::vector< std::string > Confirmation
Definition ObjectMgr.h:547
std::vector< std::string > SubHeader
Definition ObjectMgr.h:544
std::vector< std::string > Header
Definition ObjectMgr.h:543
std::vector< std::string > Description
Definition ObjectMgr.h:546
std::vector< std::string > Answer
Definition ObjectMgr.h:542
std::vector< std::string > ButtonTooltip
Definition ObjectMgr.h:545
std::string Confirmation
std::string ButtonTooltip
Optional< uint32 > RewardQuestID
std::string Description
std::string SubHeader
EnumFlag< PlayerChoiceResponseFlags > Flags
bool ShowChoicesAsColumns
uint32 SoundKitId
bool HideWarboardHeader
uint32 CloseSoundKitId
bool KeepOpenAfterChoice
std::vector< PlayerChoiceResponse > Responses
std::string Question
bool ShowChoicesAsGrid
bool RequiresSelection
int32 UiTextureKitId
Optional< uint32 > MaxResponses
Seconds Duration
std::string PendingChoiceText
bool ShowChoicesAsList
PlayerChoiceResponse const * GetResponse(int32 responseId) const
std::unique_ptr< PlayerLevelInfo[]> levelInfo
Definition ObjectMgr.h:673
PlayerCreateInfoItems item
Definition ObjectMgr.h:662
int32 stats[MAX_STATS]
Definition ObjectMgr.h:631
std::vector< std::string > Name
Definition ObjectMgr.h:535
std::string Name
Definition ObjectMgr.h:737
uint32 Importance
Definition ObjectMgr.h:735
constexpr float GetPositionX() const
Definition Position.h:87
constexpr float GetPositionY() const
Definition Position.h:88
constexpr float GetExactDist2dSq(const float x, const float y) const
Definition Position.h:108
constexpr void GetPosition(float &x, float &y) const
Definition Position.h:92
constexpr void Relocate(float x, float y)
Definition Position.h:74
constexpr float GetExactDistSq(float x, float y, float z) const
Definition Position.h:121
constexpr float GetOrientation() const
Definition Position.h:90
constexpr float GetPositionZ() const
Definition Position.h:89
static QuaternionData fromOrientation(float orientation)
bool isUnit() const
std::vector< std::string > Greeting
Definition QuestDef.h:436
uint32 QuestID
Definition QuestDef.h:479
std::vector< std::string > Description
Definition QuestDef.h:459
std::vector< std::string > RewardText
Definition QuestDef.h:464
int32 QuestID
Definition ObjectMgr.h:820
void InitializeQueryData()
std::vector< QuestPOIBlobData > Blobs
Definition ObjectMgr.h:821
QuestRelations::const_iterator _it
Definition ObjectMgr.h:590
QuestRelations::const_iterator _end
Definition ObjectMgr.h:590
bool HasQuest(uint32 questId) const
QuestRelations::const_iterator _end
Definition ObjectMgr.h:604
QuestRelations::const_iterator _begin
Definition ObjectMgr.h:604
Iterator end() const
Definition ObjectMgr.h:599
std::vector< std::string > CompletionText
Definition QuestDef.h:454
std::vector< std::string > PortraitTurnInText
Definition QuestDef.h:447
std::vector< std::string > QuestCompletionLog
Definition QuestDef.h:449
std::vector< std::string > LogTitle
Definition QuestDef.h:441
std::vector< std::string > PortraitGiverName
Definition QuestDef.h:446
std::vector< std::string > PortraitTurnInName
Definition QuestDef.h:448
std::vector< std::string > QuestDescription
Definition QuestDef.h:443
std::vector< std::string > LogDescription
Definition QuestDef.h:442
std::vector< std::string > PortraitGiverText
Definition QuestDef.h:445
std::vector< std::string > AreaDescription
Definition QuestDef.h:444
std::vector< ClassAvailability > Classes
Definition ObjectMgr.h:945
float questMonthlyRate
Definition ObjectMgr.h:703
float questWeeklyRate
Definition ObjectMgr.h:702
float questDailyRate
Definition ObjectMgr.h:701
float questRepeatableRate
Definition ObjectMgr.h:704
float spellRate
Definition ObjectMgr.h:706
float questRate
Definition ObjectMgr.h:700
float creatureRate
Definition ObjectMgr.h:705
uint32 faction_rank[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:726
uint32 faction[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:724
float faction_rate[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:725
uint32 SceneId
Definition ObjectMgr.h:855
EnumFlag< SceneFlag > PlaybackFlags
Definition ObjectMgr.h:856
uint32 ScriptId
Definition ObjectMgr.h:859
uint32 ScenePackageId
Definition ObjectMgr.h:857
float Orientation
Definition ObjectMgr.h:273
struct ScriptInfo::@272::@275 Talk
uint32 Flags
Definition ObjectMgr.h:230
struct ScriptInfo::@272::@289 CreateItem
struct ScriptInfo::@272::@284 TempSummonCreature
ScriptsType type
Definition ObjectMgr.h:214
uint32 QuestID
Definition ObjectMgr.h:278
struct ScriptInfo::@272::@286 RemoveAura
struct ScriptInfo::@272::@282 KillCredit
int32 TextID
Definition ObjectMgr.h:231
float DestX
Definition ObjectMgr.h:252
struct ScriptInfo::@272::@280 TeleportTo
uint32 ItemEntry
Definition ObjectMgr.h:338
uint32 ChatType
Definition ObjectMgr.h:229
uint32 id
Definition ObjectMgr.h:215
uint32 delay
Definition ObjectMgr.h:216
float fData[4]
Definition ObjectMgr.h:224
struct ScriptInfo::@272::@287 CastSpell
float PosY
Definition ObjectMgr.h:301
ScriptCommands command
Definition ObjectMgr.h:217
float DestY
Definition ObjectMgr.h:253
uint32 AnimKitID
Definition ObjectMgr.h:405
uint32 MapID
Definition ObjectMgr.h:266
uint32 nData[3]
Definition ObjectMgr.h:223
struct ScriptInfo::@272::@299 PlayAnimKit
float PosZ
Definition ObjectMgr.h:302
struct ScriptInfo::@272::@276 Emote
struct ScriptInfo::@272::@274 Raw
float PosX
Definition ObjectMgr.h:300
struct ScriptInfo::@272::@285 ToggleDoor
uint32 Distance
Definition ObjectMgr.h:279
struct ScriptInfo::@272::@283 RespawnGameobject
struct ScriptInfo::@272::@281 QuestExplored
uint32 SpellID
Definition ObjectMgr.h:317
std::string GetDebugInfo() const
float DestZ
Definition ObjectMgr.h:254
uint32 GOGuid
Definition ObjectMgr.h:290
uint32 CreatureEntry
Definition ObjectMgr.h:284
uint32 Amount
Definition ObjectMgr.h:339
uint32 EmoteID
Definition ObjectMgr.h:236
uint32 GetValueForTierIndex(uint32 tierIndex) const
uint32 Value[MAX_SKILL_STEP]
Definition ObjectMgr.h:880
uint32 scriptId
Definition SpawnData.h:144
uint8 phaseUseFlags
Definition SpawnData.h:137
uint32 id
Definition SpawnData.h:135
uint32 phaseId
Definition SpawnData.h:138
Position spawnPoint
Definition SpawnData.h:136
int32 spawntimesecs
Definition SpawnData.h:142
int32 terrainSwapMap
Definition SpawnData.h:140
uint32 phaseGroup
Definition SpawnData.h:139
uint32 poolId
Definition SpawnData.h:141
std::string StringId
Definition SpawnData.h:145
std::vector< Difficulty > spawnDifficulties
Definition SpawnData.h:143
SpawnGroupFlags flags
Definition SpawnData.h:72
SpawnObjectType const type
Definition SpawnData.h:120
static constexpr bool TypeHasData(SpawnObjectType type)
Definition SpawnData.h:113
SpawnGroupTemplateData const * spawnGroupData
Definition SpawnData.h:124
static constexpr bool TypeIsValid(SpawnObjectType type)
Definition SpawnData.h:114
uint64 spawnId
Definition SpawnData.h:121
SpawnData const * ToSpawnData() const
Definition SpawnData.h:118
SpawnTrackingTemplateData const * spawnTrackingData
Definition SpawnData.h:125
Optional< uint16 > StateAnimKitId
Definition SpawnData.h:89
Optional< uint32 > StateSpellVisualId
Definition SpawnData.h:87
std::vector< uint32 > StateWorldEffects
Definition SpawnData.h:90
Optional< uint16 > StateAnimId
Definition SpawnData.h:88
bool IsFitToRequirements(Unit const *clicker, Unit const *clickee) const
SpellClickUserTypes userType
Definition ObjectMgr.h:443
std::array< int32, 2 > MountCreatureID
Stores data for temp summons.
Definition ObjectMgr.h:90
Milliseconds time
Despawn time, usable only with certain temp summon types.
Definition ObjectMgr.h:94
TempSummonType type
Summon type, see TempSummonType for available types.
Definition ObjectMgr.h:93
uint32 entry
Entry of summoned creature.
Definition ObjectMgr.h:91
Position pos
Position, where should be creature spawned.
Definition ObjectMgr.h:92
Key for storing temp summon data in TempSummonDataContainer.
Definition ObjectMgr.h:80
uint32 SummonerEntry
Summoner's entry.
Definition ObjectMgr.h:83
std::vector< uint32 > UiMapPhaseIDs
Definition ObjectMgr.h:907
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:483
std::array< T, N > RawValue
Definition RaceMask.h:95
constexpr bool HasRace(uint32 raceId) const
Definition RaceMask.h:149
constexpr bool IsEmpty() const
Definition RaceMask.h:167
EnumFlag< VehicleCustomFlags > CustomFlags
Milliseconds DespawnDelay
Optional< float > Pitch
VendorItem const * FindItemCostPair(uint32 item_id, uint32 extendedCost, uint8 type) const
Definition Creature.cpp:92
void AddItem(VendorItem vItem)
uint32 ExtendedCost
bool IgnoreFiltering
uint32 PlayerConditionId
uint32 maxcount
std::vector< int32 > BonusListIDs
uint32 incrtime
Optional< ObjectGuid::LowType > TransportSpawnId
Definition ObjectMgr.h:836
WorldLocation Loc
Definition ObjectMgr.h:835