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
86
88{
89 std::string res = "";
90 switch (type)
91 {
92 case SCRIPTS_SPELL: res = "spell_scripts"; break;
93 case SCRIPTS_EVENT: res = "event_scripts"; break;
94 default: break;
95 }
96 return res;
97}
98
100{
101 ScriptMapMap* res = nullptr;
102 switch (type)
103 {
104 case SCRIPTS_SPELL: res = &sSpellScripts; break;
105 case SCRIPTS_EVENT: res = &sEventScripts; break;
106 default: break;
107 }
108 return res;
109}
110
112{
113 std::string res = "";
114 switch (command)
115 {
116 case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
117 case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
118 case SCRIPT_COMMAND_FIELD_SET_DEPRECATED: res = "SCRIPT_COMMAND_FIELD_SET_DEPRECATED"; break;
119 case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
120 case SCRIPT_COMMAND_FLAG_SET_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_SET_DEPRECATED"; break;
121 case SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED: res = "SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED"; break;
122 case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
123 case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
124 case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
125 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
126 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
127 case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
128 case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
129 case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
130 case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
131 case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
132 case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
133 case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
134 case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
135 case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
136 case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
137 case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
138 // TrinityCore only
139 case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
140 case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
141 case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
142 case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
143 case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
144 case SCRIPT_COMMAND_MOVEMENT: res = "SCRIPT_COMMAND_MOVEMENT"; break;
145 case SCRIPT_COMMAND_PLAY_ANIMKIT: res = "SCRIPT_COMMAND_PLAY_ANIMKIT"; break;
146 default:
147 {
148 res = Trinity::StringFormat("Unknown command: {}", command);
149 break;
150 }
151 }
152 return res;
153}
154
155std::string ScriptInfo::GetDebugInfo() const
156{
157 return Trinity::StringFormat("{} ('{}' script id: {})", GetScriptCommandName(command), GetScriptsTableNameByType(type), id);
158}
159
160bool normalizePlayerName(std::string& name)
161{
162 if (name.empty())
163 return false;
164
165 std::wstring tmp;
166 if (!Utf8toWStr(name, tmp))
167 return false;
168
169 wstrToLower(tmp);
170 if (!tmp.empty())
171 tmp[0] = wcharToUpper(tmp[0]);
172
173 if (!WStrToUtf8(tmp, name))
174 return false;
175
176 return true;
177}
178
179// Extracts player and realm names delimited by -
181{
182 size_t pos = name.find('-');
183 if (pos != std::string::npos)
184 return ExtendedPlayerName(name.substr(0, pos), name.substr(pos + 1));
185 else
186 return ExtendedPlayerName(name, "");
187}
188
189bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
190{
191 Player const* playerClicker = clicker->ToPlayer();
192 if (!playerClicker)
193 return true;
194
195 Unit const* summoner = nullptr;
196 // Check summoners for party
197 if (clickee->IsSummon())
198 summoner = clickee->ToTempSummon()->GetSummonerUnit();
199 if (!summoner)
200 summoner = clickee;
201
202 // This only applies to players
203 switch (userType)
204 {
206 if (!playerClicker->IsFriendlyTo(summoner))
207 return false;
208 break;
210 if (!playerClicker->IsInRaidWith(summoner))
211 return false;
212 break;
214 if (!playerClicker->IsInPartyWith(summoner))
215 return false;
216 break;
217 default:
218 break;
219 }
220
221 return true;
222}
223
225 _auctionId(1),
226 _equipmentSetGuid(1),
227 _mailId(1),
228 _hiPetNumber(1),
229 _creatureSpawnId(1),
230 _gameObjectSpawnId(1),
231 DBCLocaleIndex(LOCALE_enUS)
232{
233}
234
236{
237 static ObjectMgr instance;
238 return &instance;
239}
240
244
245void ObjectMgr::AddLocaleString(std::string_view value, LocaleConstant localeConstant, std::vector<std::string>& data)
246{
247 if (data.size() <= size_t(localeConstant))
248 {
249 data.reserve(TOTAL_LOCALES);
250 data.resize(localeConstant + 1);
251 }
252
253 data[localeConstant] = value.empty() ? "" : value;
254}
255
257{
258 uint32 oldMSTime = getMSTime();
259
260 _creatureLocaleStore.clear(); // need for reload case
261
262 // 0 1 2 3 4 5
263 QueryResult result = WorldDatabase.Query("SELECT entry, locale, Name, NameAlt, Title, TitleAlt FROM creature_template_locale");
264 if (!result)
265 return;
266
267 do
268 {
269 Field* fields = result->Fetch();
270
271 uint32 id = fields[0].GetUInt32();
272 std::string_view localeName = fields[1].GetStringView();
273
274 LocaleConstant locale = GetLocaleByName(localeName);
275 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
276 continue;
277
279 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
280 AddLocaleString(fields[3].GetStringView(), locale, data.NameAlt);
281 AddLocaleString(fields[4].GetStringView(), locale, data.Title);
282 AddLocaleString(fields[5].GetStringView(), locale, data.TitleAlt);
283
284 } while (result->NextRow());
285
286 TC_LOG_INFO("server.loading", ">> Loaded {} creature locale strings in {} ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
287}
288
290{
291 uint32 oldMSTime = getMSTime();
292
293 _gossipMenuItemsLocaleStore.clear(); // need for reload case
294
295 // 0 1 2 3 4
296 QueryResult result = WorldDatabase.Query("SELECT MenuID, OptionID, Locale, OptionText, BoxText FROM gossip_menu_option_locale");
297
298 if (!result)
299 return;
300
301 do
302 {
303 Field* fields = result->Fetch();
304
305 uint32 menuId = fields[0].GetUInt32();
306 uint32 optionId = fields[1].GetUInt32();
307 std::string_view localeName = fields[2].GetStringView();
308
309 LocaleConstant locale = GetLocaleByName(localeName);
310 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
311 continue;
312
313 GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[std::make_pair(menuId, optionId)];
314 AddLocaleString(fields[3].GetStringView(), locale, data.OptionText);
315 AddLocaleString(fields[4].GetStringView(), locale, data.BoxText);
316 } while (result->NextRow());
317
318 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option locale strings in {} ms", _gossipMenuItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
319}
320
322{
323 uint32 oldMSTime = getMSTime();
324
325 _pointOfInterestLocaleStore.clear(); // need for reload case
326
327 // 0 1 2
328 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM points_of_interest_locale");
329 if (!result)
330 return;
331
332 do
333 {
334 Field* fields = result->Fetch();
335
336 uint32 id = fields[0].GetUInt32();
337 std::string_view localeName = fields[1].GetStringView();
338
339 LocaleConstant locale = GetLocaleByName(localeName);
340 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
341 continue;
342
344 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
345 } while (result->NextRow());
346
347 TC_LOG_INFO("server.loading", ">> Loaded {} points_of_interest locale strings in {} ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
348}
349
351{
352 uint32 oldMSTime = getMSTime();
353
354 // 0 1 2 3 4 5
355 // "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, "
356 // 6 7 8 9
357 // "TitleAlt, IconName, RequiredExpansion, VignetteID, "
358 // 10 11 12 13 14 15 16 17 18 19 20
359 // "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, "
360 // 21 22 23 24
361 // "unit_class, unit_flags, unit_flags2, unit_flags3, "
362 // 25 26 27 28 29 30
363 // "family, trainer_class, type, VehicleId, AIName, MovementType, "
364 // 31 32 33 34 35
365 // "ctm.HoverInitiallyEnabled, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, "
366 // 36 37 38 39 40
367 // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
368 // 41 42
369 // "CreatureImmunitiesId, flags_extra, "
370 // 43 44
371 // "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
372
374 stmt->setUInt32(0, 0);
375 stmt->setUInt32(1, 1);
376
377 PreparedQueryResult result = WorldDatabase.Query(stmt);
378 if (!result)
379 {
380 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
381 return;
382 }
383
384 _creatureTemplateStore.reserve(result->GetRowCount());
385 do
386 {
387 Field* fields = result->Fetch();
388 LoadCreatureTemplate(fields);
389 } while (result->NextRow());
390
393
394 // We load the creature models after loading but before checking
396
397 // Checking needs to be done after loading because of the difficulty self referencing
398 for (auto const& ctPair : _creatureTemplateStore)
399 CheckCreatureTemplate(&ctPair.second);
400
401 TC_LOG_INFO("server.loading", ">> Loaded {} creature definitions in {} ms", _creatureTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
402}
403
405{
406 uint32 entry = fields[0].GetUInt32();
407 CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
408
409 creatureTemplate.Entry = entry;
410
411 for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
412 creatureTemplate.KillCredit[i] = fields[1 + i].GetUInt32();
413
414 creatureTemplate.Name = fields[3].GetString();
415 creatureTemplate.FemaleName = fields[4].GetString();
416 creatureTemplate.SubName = fields[5].GetString();
417 creatureTemplate.TitleAlt = fields[6].GetString();
418 creatureTemplate.IconName = fields[7].GetString();
419 creatureTemplate.RequiredExpansion = fields[8].GetUInt32();
420 creatureTemplate.VignetteID = fields[9].GetUInt32();
421 creatureTemplate.faction = fields[10].GetUInt16();
422 creatureTemplate.npcflag = fields[11].GetUInt64();
423 creatureTemplate.speed_walk = fields[12].GetFloat();
424 creatureTemplate.speed_run = fields[13].GetFloat();
425 creatureTemplate.scale = fields[14].GetFloat();
426 creatureTemplate.Classification = CreatureClassifications(fields[15].GetUInt8());
427 creatureTemplate.dmgschool = uint32(fields[16].GetInt8());
428 creatureTemplate.BaseAttackTime = fields[17].GetUInt32();
429 creatureTemplate.RangeAttackTime = fields[18].GetUInt32();
430 creatureTemplate.BaseVariance = fields[19].GetFloat();
431 creatureTemplate.RangeVariance = fields[20].GetFloat();
432 creatureTemplate.unit_class = uint32(fields[21].GetUInt8());
433 creatureTemplate.unit_flags = fields[22].GetUInt32();
434 creatureTemplate.unit_flags2 = fields[23].GetUInt32();
435 creatureTemplate.unit_flags3 = fields[24].GetUInt32();
436 creatureTemplate.family = CreatureFamily(fields[25].GetInt32());
437 creatureTemplate.trainer_class = uint32(fields[26].GetUInt8());
438 creatureTemplate.type = uint32(fields[27].GetUInt8());
439
440 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
441 creatureTemplate.resistance[i] = 0;
442
443 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
444 creatureTemplate.spells[i] = 0;
445
446 creatureTemplate.VehicleId = fields[28].GetUInt32();
447 creatureTemplate.AIName = fields[29].GetString();
448 creatureTemplate.MovementType = uint32(fields[30].GetUInt8());
449
450 if (!fields[31].IsNull())
451 creatureTemplate.Movement.HoverInitiallyEnabled = fields[31].GetBool();
452
453 if (Optional<uint8> chaseMovementType = fields[32].GetUInt8OrNull())
454 creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(*chaseMovementType);
455
456 if (Optional<uint8> randomMovementType = fields[33].GetUInt8OrNull())
457 creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(*randomMovementType);
458
459 if (Optional<uint32> interactionPauseTimer = fields[34].GetUInt32OrNull())
460 creatureTemplate.Movement.InteractionPauseTimer = *interactionPauseTimer;
461
462 creatureTemplate.ModExperience = fields[35].GetFloat();
463 creatureTemplate.RacialLeader = fields[36].GetBool();
464 creatureTemplate.movementId = fields[37].GetUInt32();
465 creatureTemplate.WidgetSetID = fields[38].GetInt32();
466 creatureTemplate.WidgetSetUnitConditionID = fields[39].GetInt32();
467 creatureTemplate.RegenHealth = fields[40].GetBool();
468 creatureTemplate.CreatureImmunitiesId = fields[41].GetInt32();
469 creatureTemplate.flags_extra = fields[42].GetUInt32();
470 creatureTemplate.ScriptID = GetScriptId(fields[43].GetStringView());
471 creatureTemplate.StringId = fields[44].GetString();
472}
473
475{
476 uint32 oldMSTime = getMSTime();
477
478 // 0 1
479 QueryResult result = WorldDatabase.Query("SELECT CreatureID, MenuID FROM creature_template_gossip");
480
481 if (!result)
482 {
483 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template gossip definitions. DB table `creature_template_gossip` is empty.");
484 return;
485 }
486
487 uint32 count = 0;
488
489 do
490 {
491 Field* fields = result->Fetch();
492
493 uint32 creatureID = fields[0].GetUInt32();
494 uint32 menuID = fields[1].GetUInt32();
495
496 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
497 if (itr == _creatureTemplateStore.end())
498 {
499 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for creature {} but this creature doesn't exist", creatureID);
500 continue;
501 }
502
503 GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuID);
504 if (menuBounds.first == menuBounds.second)
505 {
506 TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for menu id {} but this menu doesn't exist", menuID);
507 continue;
508 }
509
510 CreatureTemplate& creatureTemplate = itr->second;
511 creatureTemplate.GossipMenuIds.push_back(menuID);
512
513 ++count;
514
515 } while (result->NextRow());
516
517 TC_LOG_INFO("server.loading", ">> Loaded {} creature template gossip menus in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
518}
519
521{
522 uint32 oldMSTime = getMSTime();
523
524 // 0 1 2
525 QueryResult result = WorldDatabase.Query("SELECT CreatureID, School, Resistance FROM creature_template_resistance");
526
527 if (!result)
528 {
529 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template resistance definitions. DB table `creature_template_resistance` is empty.");
530 return;
531 }
532
533 uint32 count = 0;
534
535 do
536 {
537 Field* fields = result->Fetch();
538
539 uint32 creatureID = fields[0].GetUInt32();
540 uint8 school = fields[1].GetUInt8();
541
542 if (school == SPELL_SCHOOL_NORMAL || school >= MAX_SPELL_SCHOOL)
543 {
544 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this school {} doesn't exist", creatureID, school);
545 continue;
546 }
547
548 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
549 if (itr == _creatureTemplateStore.end())
550 {
551 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this creature doesn't exist", creatureID);
552 continue;
553 }
554
555 CreatureTemplate& creatureTemplate = itr->second;
556 creatureTemplate.resistance[school] = fields[2].GetInt16();
557
558 ++count;
559
560 } while (result->NextRow());
561
562 TC_LOG_INFO("server.loading", ">> Loaded {} creature template resistances in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
563}
564
566{
567 uint32 oldMSTime = getMSTime();
568
569 // 0 1 2
570 QueryResult result = WorldDatabase.Query("SELECT CreatureID, `Index`, Spell FROM creature_template_spell");
571
572 if (!result)
573 {
574 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template spell definitions. DB table `creature_template_spell` is empty.");
575 return;
576 }
577
578 uint32 count = 0;
579
580 do
581 {
582 Field* fields = result->Fetch();
583
584 uint32 creatureID = fields[0].GetUInt32();
585 uint8 index = fields[1].GetUInt8();
586
587 if (index >= MAX_CREATURE_SPELLS)
588 {
589 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} with a incorrect index {}", creatureID, index);
590 continue;
591 }
592
593 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
594 if (itr == _creatureTemplateStore.end())
595 {
596 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} but this creature doesn't exist", creatureID);
597 continue;
598 }
599
600 CreatureTemplate& creatureTemplate = itr->second;
601 creatureTemplate.spells[index] = fields[2].GetUInt32();;
602
603 ++count;
604
605 } while (result->NextRow());
606
607 TC_LOG_INFO("server.loading", ">> Loaded {} creature template spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
608}
609
611{
612 uint32 oldMSTime = getMSTime();
613
614 // 0 1 2 3
615 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureDisplayID, DisplayScale, Probability FROM creature_template_model ORDER BY Idx ASC");
616
617 if (!result)
618 {
619 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template model definitions. DB table `creature_template_model` is empty.");
620 return;
621 }
622
623 uint32 count = 0;
624 do
625 {
626 Field* fields = result->Fetch();
627
628 uint32 creatureId = fields[0].GetUInt32();
629 uint32 creatureDisplayId = fields[1].GetUInt32();
630 float displayScale = fields[2].GetFloat();
631 float probability = fields[3].GetFloat();
632
633 CreatureTemplate const* cInfo = GetCreatureTemplate(creatureId);
634 if (!cInfo)
635 {
636 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_model`", creatureId);
637 continue;
638 }
639
640 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(creatureDisplayId);
641 if (!displayEntry)
642 {
643 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing CreatureDisplayID id ({}), this can crash the client.", creatureId, creatureDisplayId);
644 continue;
645 }
646
647 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(creatureDisplayId);
648 if (!modelInfo)
649 TC_LOG_ERROR("sql.sql", "No model data exist for `CreatureDisplayID` = {} listed by creature (Entry: {}).", creatureDisplayId, creatureId);
650
651 if (displayScale <= 0.0f)
652 displayScale = 1.0f;
653
654 const_cast<CreatureTemplate*>(cInfo)->Models.emplace_back(creatureDisplayId, displayScale, probability);
655
656 ++count;
657 }
658 while (result->NextRow());
659
660 TC_LOG_INFO("server.loading", ">> Loaded {} creature template models in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
661}
662
664{
665 uint32 oldMSTime = getMSTime();
666
667 // 0 1 2 3 4
668 QueryResult result = WorldDatabase.Query("SELECT CreatureID, CreatureIDVisibleToSummoner, GroundMountDisplayID, FlyingMountDisplayID, DespawnOnQuestsRemoved FROM creature_summoned_data");
669
670 if (!result)
671 {
672 TC_LOG_INFO("server.loading", ">> Loaded 0 creature summoned data definitions. DB table `creature_summoned_data` is empty.");
673 return;
674 }
675
676 do
677 {
678 Field* fields = result->Fetch();
679
680 uint32 creatureId = fields[0].GetUInt32();
681 if (!GetCreatureTemplate(creatureId))
682 {
683 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {}, skipped", creatureId);
684 continue;
685 }
686
687 CreatureSummonedData& summonedData = _creatureSummonedDataStore[creatureId];
688 summonedData.CreatureIDVisibleToSummoner = fields[1].GetUInt32OrNull();
689 summonedData.GroundMountDisplayID = fields[2].GetUInt32OrNull();
690 summonedData.FlyingMountDisplayID = fields[3].GetUInt32OrNull();
691
693 {
694 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing creature {} in CreatureIDVisibleToSummoner for creature {}, set to 0",
695 *summonedData.CreatureIDVisibleToSummoner, creatureId);
696 summonedData.CreatureIDVisibleToSummoner.reset();
697 }
698
699 if (summonedData.GroundMountDisplayID && !sCreatureDisplayInfoStore.LookupEntry(*summonedData.GroundMountDisplayID))
700 {
701 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in GroundMountDisplayID for creature {}, set to 0",
702 *summonedData.GroundMountDisplayID, creatureId);
703 summonedData.CreatureIDVisibleToSummoner.reset();
704 }
705
706 if (summonedData.FlyingMountDisplayID && !sCreatureDisplayInfoStore.LookupEntry(*summonedData.FlyingMountDisplayID))
707 {
708 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing display id {} in FlyingMountDisplayID for creature {}, set to 0",
709 *summonedData.FlyingMountDisplayID, creatureId);
710 summonedData.GroundMountDisplayID.reset();
711 }
712
713 if (Optional<std::string_view> despawnOnQuestsRemoved = fields[4].GetStringViewOrNull())
714 {
715 summonedData.DespawnOnQuestsRemoved.emplace();
716 for (std::string_view questStr : Trinity::Tokenize(*despawnOnQuestsRemoved, ',', false))
717 {
718 Optional<uint32> questId = Trinity::StringTo<uint32>(questStr);
719 if (!questId)
720 continue;
721
722 Quest const* quest = GetQuestTemplate(*questId);
723 if (!quest)
724 {
725 TC_LOG_ERROR("sql.sql", "Table `creature_summoned_data` references non-existing quest {} in DespawnOnQuestsRemoved for creature {}, skipping",
726 *questId, creatureId);
727 continue;
728 }
729
730 summonedData.DespawnOnQuestsRemoved->push_back(*questId);
731 }
732
733 if (summonedData.DespawnOnQuestsRemoved->empty())
734 summonedData.DespawnOnQuestsRemoved.reset();
735 }
736
737 } while (result->NextRow());
738
739 TC_LOG_INFO("server.loading", ">> Loaded {} creature summoned data definitions in {} ms", _creatureSummonedDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
740}
741
743{
744 uint32 oldMSTime = getMSTime();
745
746 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
747 QueryResult result = WorldDatabase.Query("SELECT entry, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon");
748
749 if (!result)
750 {
751 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
752 return;
753 }
754
755 uint32 count = 0;
756 do
757 {
758 Field* fields = result->Fetch();
759
760 uint32 entry = fields[0].GetUInt32();
761
762 if (!GetCreatureTemplate(entry))
763 {
764 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
765 continue;
766 }
767
768 CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
769
770 creatureAddon.PathId = fields[1].GetUInt32();
771 creatureAddon.mount = fields[2].GetUInt32();
772 creatureAddon.standState = fields[3].GetUInt8();
773 creatureAddon.animTier = fields[4].GetUInt8();
774 creatureAddon.visFlags = fields[5].GetUInt8();
775 creatureAddon.sheathState = fields[6].GetUInt8();
776 creatureAddon.pvpFlags = fields[7].GetUInt8();
777 creatureAddon.emote = fields[8].GetUInt32();
778 creatureAddon.aiAnimKit = fields[9].GetUInt16();
779 creatureAddon.movementAnimKit = fields[10].GetUInt16();
780 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
781 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
782
783 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
784 {
785 SpellInfo const* spellInfo = nullptr;
786 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
787 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
788
789 if (!spellInfo)
790 {
791 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong spell '{}' defined in `auras` field in `creature_template_addon`.", entry, std::string(aura));
792 continue;
793 }
794
795 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
796 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
797
798 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
799 {
800 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has duplicate aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
801 continue;
802 }
803
804 if (spellInfo->GetDuration() > 0)
805 {
806 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has temporary aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
807 continue;
808 }
809
810 creatureAddon.auras.push_back(spellInfo->Id);
811 }
812
813 if (creatureAddon.mount)
814 {
815 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
816 {
817 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid displayInfoId ({}) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
818 creatureAddon.mount = 0;
819 }
820 }
821
822 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
823 {
824 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit stand state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState);
825 creatureAddon.standState = 0;
826 }
827
828 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
829 {
830 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid animation tier ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier);
831 creatureAddon.animTier = 0;
832 }
833
834 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
835 {
836 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid sheath state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState);
837 creatureAddon.sheathState = 0;
838 }
839
840 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
841
842 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
843 {
844 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid emote ({}) defined in `creature_template_addon`.", entry, creatureAddon.emote);
845 creatureAddon.emote = 0;
846 }
847
848 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
849 {
850 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid aiAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.aiAnimKit);
851 creatureAddon.aiAnimKit = 0;
852 }
853
854 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
855 {
856 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid movementAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.movementAnimKit);
857 creatureAddon.movementAnimKit = 0;
858 }
859
860 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
861 {
862 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid meleeAnimKit ({}) defined in `creature_template_addon`.", entry, creatureAddon.meleeAnimKit);
863 creatureAddon.meleeAnimKit = 0;
864 }
865
867 {
868 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid visibilityDistanceType ({}) defined in `creature_template_addon`.",
869 entry, AsUnderlyingType(creatureAddon.visibilityDistanceType));
871 }
872
873 ++count;
874 }
875 while (result->NextRow());
876
877 TC_LOG_INFO("server.loading", ">> Loaded {} creature template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
878}
879
881{
882 uint32 oldMSTime = getMSTime();
883
884 // 0 1
885 QueryResult result = WorldDatabase.Query("SELECT Entry, NoNPCDamageBelowHealthPct FROM creature_template_sparring");
886
887 if (!result)
888 {
889 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template sparring definitions. DB table `creature_template_sparring` is empty.");
890 return;
891 }
892
893 uint32 count = 0;
894 do
895 {
896 Field* fields = result->Fetch();
897
898 uint32 entry = fields[0].GetUInt32();
899 float noNPCDamageBelowHealthPct = fields[1].GetFloat();
900
901 if (!sObjectMgr->GetCreatureTemplate(entry))
902 {
903 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_sparring`", entry);
904 continue;
905 }
906
907 if (noNPCDamageBelowHealthPct <= 0 || noNPCDamageBelowHealthPct > 100)
908 {
909 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid NoNPCDamageBelowHealthPct ({}) defined in `creature_template_sparring`. Skipping",
910 entry, noNPCDamageBelowHealthPct);
911 continue;
912 }
913 _creatureTemplateSparringStore[entry].push_back(noNPCDamageBelowHealthPct);
914
915 ++count;
916 } while (result->NextRow());
917
918 TC_LOG_INFO("server.loading", ">> Loaded {} creature template sparring rows in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
919}
920
922{
923 uint32 oldMSTime = getMSTime();
924
925 // 0 1 2 3 4 5
926 QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, "
927 // 6 7 8 9 10 11 12 13
928 "HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, TypeFlags3, "
929 // 14 15 16 17 18
930 "LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax,"
931 // 19 20 21 22 23 24 25 26
932 "StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 "
933 "FROM creature_template_difficulty ORDER BY Entry");
934
935 if (!result)
936 {
937 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
938 return;
939 }
940
941 uint32 count = 0;
942 do
943 {
944 Field* fields = result->Fetch();
945
946 uint32 entry = fields[0].GetUInt32();
947 Difficulty difficulty = Difficulty(fields[1].GetInt32());
948
949 auto itr = _creatureTemplateStore.find(entry);
950 if (itr == _creatureTemplateStore.end())
951 {
952 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_difficulty`", entry);
953 continue;
954 }
955
956 CreatureDifficulty& creatureDifficulty = itr->second.difficultyStore.try_emplace(difficulty).first->second;
957 creatureDifficulty.DeltaLevelMin = fields[2].GetInt16();
958 creatureDifficulty.DeltaLevelMax = fields[3].GetInt16();
959 creatureDifficulty.ContentTuningID = fields[4].GetInt32();
960 creatureDifficulty.HealthScalingExpansion = fields[5].GetInt32();
961 creatureDifficulty.HealthModifier = fields[6].GetFloat();
962 creatureDifficulty.ManaModifier = fields[7].GetFloat();
963 creatureDifficulty.ArmorModifier = fields[8].GetFloat();
964 creatureDifficulty.DamageModifier = fields[9].GetFloat();
965 creatureDifficulty.CreatureDifficultyID = fields[10].GetInt32();
966 creatureDifficulty.TypeFlags = fields[11].GetUInt32();
967 creatureDifficulty.TypeFlags2 = fields[12].GetUInt32();
968 creatureDifficulty.TypeFlags3 = fields[13].GetUInt32();
969 creatureDifficulty.LootID = fields[14].GetUInt32();
970 creatureDifficulty.PickPocketLootID = fields[15].GetUInt32();
971 creatureDifficulty.SkinLootID = fields[16].GetUInt32();
972 creatureDifficulty.GoldMin = fields[17].GetUInt32();
973 creatureDifficulty.GoldMax = fields[18].GetUInt32();
974 creatureDifficulty.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[19].GetUInt32()), CreatureStaticFlags2(fields[20].GetUInt32()),
975 CreatureStaticFlags3(fields[21].GetUInt32()), CreatureStaticFlags4(fields[22].GetUInt32()), CreatureStaticFlags5(fields[23].GetUInt32()),
976 CreatureStaticFlags6(fields[24].GetUInt32()), CreatureStaticFlags7(fields[25].GetUInt32()), CreatureStaticFlags8(fields[26].GetUInt32()));
977
978 // TODO: Check if this still applies
979 creatureDifficulty.DamageModifier *= Creature::GetDamageMod(itr->second.Classification);
980
981 if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS)
982 {
983 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.",
984 entry, creatureDifficulty.HealthScalingExpansion);
985 creatureDifficulty.HealthScalingExpansion = 0;
986 }
987
988 if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
989 {
990 TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with `GoldMin` {} greater than `GoldMax` {}, setting `GoldMax` to {}.",
991 entry, creatureDifficulty.GoldMin, creatureDifficulty.GoldMax, creatureDifficulty.GoldMin);
992 creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
993 }
994
995 ++count;
996 } while (result->NextRow());
997
998 TC_LOG_INFO("server.loading", ">> Loaded {} creature template difficulty data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
999}
1000
1002{
1003 if (!cInfo)
1004 return;
1005
1006 if (!cInfo->AIName.empty())
1007 {
1008 auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
1009 if (!registryItem)
1010 {
1011 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-registered `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1012 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1013 }
1014 else
1015 {
1016 DBPermit const* permit = dynamic_cast<DBPermit const*>(registryItem);
1017 if (!ASSERT_NOTNULL(permit)->IsScriptNameAllowedInDB())
1018 {
1019 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has not-allowed `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1020 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1021 }
1022 }
1023 }
1024
1025 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
1026 if (!factionTemplate)
1027 {
1028 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);
1029 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
1030 }
1031
1032 for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
1033 {
1034 if (cInfo->KillCredit[k])
1035 {
1036 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
1037 {
1038 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing creature entry {} in `KillCredit{}`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
1039 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
1040 }
1041 }
1042 }
1043
1044 if (cInfo->Models.empty())
1045 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in creature_template_model.", cInfo->Entry);
1046
1047 if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
1048 {
1049 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);
1050 const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
1051 }
1052
1053 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
1054 {
1055 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid spell school value ({}) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
1056 const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
1057 }
1058
1059 if (cInfo->BaseAttackTime == 0)
1060 const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime = BASE_ATTACK_TIME;
1061
1062 if (cInfo->RangeAttackTime == 0)
1063 const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
1064
1065 if (cInfo->speed_walk == 0.0f)
1066 {
1067 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
1068 const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
1069 }
1070
1071 if (cInfo->speed_run == 0.0f)
1072 {
1073 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
1074 const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
1075 }
1076
1077 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
1078 {
1079 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature type ({}) in `type`.", cInfo->Entry, cInfo->type);
1080 const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
1081 }
1082
1083 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family))
1084 {
1085 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature family ({}) in `family`.", cInfo->Entry, cInfo->family);
1086 const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
1087 }
1088
1089 CheckCreatureMovement("creature_template_movement", cInfo->Entry, const_cast<CreatureTemplate*>(cInfo)->Movement);
1090
1091 if (cInfo->VehicleId)
1092 {
1093 VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
1094 if (!vehId)
1095 {
1096 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has a non-existing VehicleId ({}). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
1097 const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
1098 }
1099 }
1100
1101 for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
1102 {
1103 if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j], DIFFICULTY_NONE))
1104 {
1105 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing Spell{} ({}), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
1106 const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
1107 }
1108 }
1109
1110 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
1111 {
1112 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong movement generator type ({}), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
1113 const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
1114 }
1115
1116 if (cInfo->RequiredExpansion >= MAX_EXPANSIONS)
1117 {
1118 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `RequiredExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->RequiredExpansion);
1119 const_cast<CreatureTemplate*>(cInfo)->RequiredExpansion = 0;
1120 }
1121
1122 if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
1123 {
1124 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `flags_extra` {}, removing incorrect flag.", cInfo->Entry, badFlags);
1125 const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
1126 }
1127
1128 if (uint32 disallowedUnitFlags = (cInfo->unit_flags & ~UNIT_FLAG_ALLOWED))
1129 {
1130 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags);
1131 const_cast<CreatureTemplate*>(cInfo)->unit_flags &= UNIT_FLAG_ALLOWED;
1132 }
1133
1134 if (uint32 disallowedUnitFlags2 = (cInfo->unit_flags2 & ~UNIT_FLAG2_ALLOWED))
1135 {
1136 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags2);
1137 const_cast<CreatureTemplate*>(cInfo)->unit_flags2 &= UNIT_FLAG2_ALLOWED;
1138 }
1139
1140 if (uint32 disallowedUnitFlags3 = (cInfo->unit_flags3 & ~UNIT_FLAG3_ALLOWED))
1141 {
1142 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags3);
1143 const_cast<CreatureTemplate*>(cInfo)->unit_flags3 &= UNIT_FLAG3_ALLOWED;
1144 }
1145
1146 if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP))
1147 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry);
1148 else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)
1149 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry);
1150
1151 if (cInfo->VignetteID && !sVignetteStore.HasRecord(cInfo->VignetteID))
1152 {
1153 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has non-existing Vignette {}, set to 0.", cInfo->Entry, cInfo->VignetteID);
1154 const_cast<CreatureTemplate*>(cInfo)->VignetteID = 0;
1155 }
1156}
1157
1158void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement)
1159{
1160 if (creatureMovement.Chase >= CreatureChaseMovementType::Max)
1161 {
1162 TC_LOG_ERROR("sql.sql", "`{}`.`Chase` wrong value ({}) for Id {}, setting to Run.",
1163 table, uint32(creatureMovement.Chase), id);
1164 creatureMovement.Chase = CreatureChaseMovementType::Run;
1165 }
1166
1167 if (creatureMovement.Random >= CreatureRandomMovementType::Max)
1168 {
1169 TC_LOG_ERROR("sql.sql", "`{}`.`Random` wrong value ({}) for Id {}, setting to Walk.",
1170 table, uint32(creatureMovement.Random), id);
1171 creatureMovement.Random = CreatureRandomMovementType::Walk;
1172 }
1173}
1174
1176{
1177 uint32 oldMSTime = getMSTime();
1178
1179 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
1180 QueryResult result = WorldDatabase.Query("SELECT guid, PathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon");
1181
1182 if (!result)
1183 {
1184 TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
1185 return;
1186 }
1187
1188 uint32 count = 0;
1189 do
1190 {
1191 Field* fields = result->Fetch();
1192
1193 ObjectGuid::LowType guid = fields[0].GetUInt64();
1194
1195 CreatureData const* creData = GetCreatureData(guid);
1196 if (!creData)
1197 {
1198 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_addon`", guid);
1199 continue;
1200 }
1201
1202 CreatureAddon& creatureAddon = _creatureAddonStore[guid];
1203
1204 creatureAddon.PathId = fields[1].GetUInt32();
1205 if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.PathId)
1206 {
1207 const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
1208 TC_LOG_ERROR("sql.sql", "Creature (GUID {}) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
1209 }
1210
1211 creatureAddon.mount = fields[2].GetUInt32();
1212 creatureAddon.standState = fields[3].GetUInt8();
1213 creatureAddon.animTier = fields[4].GetUInt8();
1214 creatureAddon.visFlags = fields[5].GetUInt8();
1215 creatureAddon.sheathState = fields[6].GetUInt8();
1216 creatureAddon.pvpFlags = fields[7].GetUInt8();
1217 creatureAddon.emote = fields[8].GetUInt32();
1218 creatureAddon.aiAnimKit = fields[9].GetUInt16();
1219 creatureAddon.movementAnimKit = fields[10].GetUInt16();
1220 creatureAddon.meleeAnimKit = fields[11].GetUInt16();
1221 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[12].GetUInt8());
1222
1223 for (std::string_view aura : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
1224 {
1225 SpellInfo const* spellInfo = nullptr;
1226 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
1227 spellInfo = sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE);
1228
1229 if (!spellInfo)
1230 {
1231 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has wrong spell '{}' defined in `auras` field in `creature_addon`.", guid, std::string(aura));
1232 continue;
1233 }
1234
1235 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
1236 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1237
1238 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
1239 {
1240 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has duplicate aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1241 continue;
1242 }
1243
1244 if (spellInfo->GetDuration() > 0)
1245 {
1246 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has temporary aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1247 continue;
1248 }
1249
1250 creatureAddon.auras.push_back(spellInfo->Id);
1251 }
1252
1253 if (creatureAddon.mount)
1254 {
1255 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
1256 {
1257 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid displayInfoId ({}) for mount defined in `creature_addon`", guid, creatureAddon.mount);
1258 creatureAddon.mount = 0;
1259 }
1260 }
1261
1262 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
1263 {
1264 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid unit stand state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState);
1265 creatureAddon.standState = 0;
1266 }
1267
1268 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
1269 {
1270 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid animation tier ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier);
1271 creatureAddon.animTier = 0;
1272 }
1273
1274 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
1275 {
1276 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid sheath state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState);
1277 creatureAddon.sheathState = 0;
1278 }
1279
1280 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
1281
1282 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
1283 {
1284 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid emote ({}) defined in `creature_addon`.", guid, creatureAddon.emote);
1285 creatureAddon.emote = 0;
1286 }
1287
1288 if (creatureAddon.aiAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.aiAnimKit))
1289 {
1290 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid aiAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.aiAnimKit);
1291 creatureAddon.aiAnimKit = 0;
1292 }
1293
1294 if (creatureAddon.movementAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.movementAnimKit))
1295 {
1296 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid movementAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.movementAnimKit);
1297 creatureAddon.movementAnimKit = 0;
1298 }
1299
1300 if (creatureAddon.meleeAnimKit && !sAnimKitStore.LookupEntry(creatureAddon.meleeAnimKit))
1301 {
1302 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid meleeAnimKit ({}) defined in `creature_addon`.", guid, creatureAddon.meleeAnimKit);
1303 creatureAddon.meleeAnimKit = 0;
1304 }
1305
1307 {
1308 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid visibilityDistanceType ({}) defined in `creature_addon`.",
1309 guid, AsUnderlyingType(creatureAddon.visibilityDistanceType));
1311 }
1312
1313 ++count;
1314 }
1315 while (result->NextRow());
1316
1317 TC_LOG_INFO("server.loading", ">> Loaded {} creature addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1318}
1319
1321{
1322 uint32 oldMSTime = getMSTime();
1323
1324 // 0 1 2 3 4 5 6 7 8
1325 QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue, WorldEffectID, AIAnimKitID FROM gameobject_addon");
1326
1327 if (!result)
1328 {
1329 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
1330 return;
1331 }
1332
1333 uint32 count = 0;
1334 do
1335 {
1336 Field* fields = result->Fetch();
1337
1338 ObjectGuid::LowType guid = fields[0].GetUInt64();
1339
1340 GameObjectData const* goData = GetGameObjectData(guid);
1341 if (!goData)
1342 {
1343 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) does not exist but has a record in `gameobject_addon`", guid);
1344 continue;
1345 }
1346
1347 GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
1348 gameObjectAddon.ParentRotation = QuaternionData(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
1349 gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
1350 gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
1351 gameObjectAddon.WorldEffectID = fields[7].GetUInt32();
1352 gameObjectAddon.AIAnimKitID = fields[8].GetUInt32();
1353
1354 if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
1355 {
1356 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
1357 gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
1358 gameObjectAddon.InvisibilityValue = 0;
1359 }
1360
1361 if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue)
1362 {
1363 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid);
1364 gameObjectAddon.InvisibilityValue = 1;
1365 }
1366
1367 if (!gameObjectAddon.ParentRotation.isUnit())
1368 {
1369 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid parent rotation in `gameobject_addon`, set to default", guid);
1370 gameObjectAddon.ParentRotation = QuaternionData();
1371 }
1372
1373 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
1374 {
1375 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid WorldEffectID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.WorldEffectID);
1376 gameObjectAddon.WorldEffectID = 0;
1377 }
1378
1379 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
1380 {
1381 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid AIAnimKitID ({}) in `gameobject_addon`, set to 0.", guid, gameObjectAddon.AIAnimKitID);
1382 gameObjectAddon.AIAnimKitID = 0;
1383 }
1384
1385 ++count;
1386 }
1387 while (result->NextRow());
1388
1389 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1390}
1391
1393{
1394 GameObjectAddonContainer::const_iterator itr = _gameObjectAddonStore.find(lowguid);
1395 if (itr != _gameObjectAddonStore.end())
1396 return &(itr->second);
1397
1398 return nullptr;
1399}
1400
1402{
1403 CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
1404 if (itr != _creatureAddonStore.end())
1405 return &(itr->second);
1406
1407 return nullptr;
1408}
1409
1411{
1412 CreatureTemplateAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
1413 if (itr != _creatureTemplateAddonStore.end())
1414 return &(itr->second);
1415
1416 return nullptr;
1417}
1418
1423
1428
1430{
1431 EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
1432 if (itr == _equipmentInfoStore.end())
1433 return nullptr;
1434
1435 if (itr->second.empty())
1436 return nullptr;
1437
1438 if (id == -1) // select a random element
1439 {
1440 EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
1441 std::advance(ritr, urand(0u, itr->second.size() - 1));
1442 id = std::distance(itr->second.begin(), ritr) + 1;
1443 return &ritr->second;
1444 }
1445 else
1446 {
1447 EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
1448 if (itr2 != itr->second.end())
1449 return &itr2->second;
1450 }
1451
1452 return nullptr;
1453}
1454
1456{
1457 uint32 oldMSTime = getMSTime();
1458
1459 // 0 1 2 3 4
1460 QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, AppearanceModID1, ItemVisual1, "
1461 // 5 6 7
1462 "ItemID2, AppearanceModID2, ItemVisual2, "
1463 // 8 9 10
1464 "ItemID3, AppearanceModID3, ItemVisual3 "
1465 "FROM creature_equip_template");
1466
1467 if (!result)
1468 {
1469 TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
1470 return;
1471 }
1472
1473 uint32 count = 0;
1474 do
1475 {
1476 Field* fields = result->Fetch();
1477
1478 uint32 entry = fields[0].GetUInt32();
1479
1480 if (!GetCreatureTemplate(entry))
1481 {
1482 TC_LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry);
1483 continue;
1484 }
1485
1486 uint8 id = fields[1].GetUInt8();
1487 if (!id)
1488 {
1489 TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature {}, skipped.", entry);
1490 continue;
1491 }
1492
1493 EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
1494 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1495 {
1496 equipmentInfo.Items[i].ItemId = fields[2 + i * 3].GetUInt32();
1497 equipmentInfo.Items[i].AppearanceModId = fields[3 + i * 3].GetUInt16();
1498 equipmentInfo.Items[i].ItemVisual = fields[4 + i * 3].GetUInt16();
1499
1500 if (!equipmentInfo.Items[i].ItemId)
1501 continue;
1502
1503 ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.Items[i].ItemId);
1504 if (!dbcItem)
1505 {
1506 TC_LOG_ERROR("sql.sql", "Unknown item (ID={}) in creature_equip_template.ItemID{} for CreatureID = {} and ID={}, forced to 0.",
1507 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1508 equipmentInfo.Items[i].ItemId = 0;
1509 continue;
1510 }
1511
1512 // AppearanceModId 0 is always valid
1513 if (equipmentInfo.Items[i].AppearanceModId && !TransmogMgr::GetItemModifiedAppearance(equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId))
1514 {
1515 TC_LOG_ERROR("sql.sql", "Unknown item appearance for (ID={}, AppearanceModID={}) pair in creature_equip_template.ItemID{} creature_equip_template.AppearanceModID{} "
1516 "for CreatureID = {} and ID={}, forced to default.",
1517 equipmentInfo.Items[i].ItemId, equipmentInfo.Items[i].AppearanceModId, i + 1, i + 1, entry, id);
1518 if (ItemModifiedAppearanceEntry const* defaultAppearance = TransmogMgr::GetDefaultItemModifiedAppearance(equipmentInfo.Items[i].ItemId))
1519 equipmentInfo.Items[i].AppearanceModId = defaultAppearance->ItemAppearanceModifierID;
1520 else
1521 equipmentInfo.Items[i].AppearanceModId = 0;
1522 continue;
1523 }
1524
1526 {
1527 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.",
1528 equipmentInfo.Items[i].ItemId, i + 1, entry, id);
1529 equipmentInfo.Items[i].ItemId = 0;
1530 }
1531 }
1532
1533 ++count;
1534 }
1535 while (result->NextRow());
1536
1537 TC_LOG_INFO("server.loading", ">> Loaded {} equipment templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1538}
1539
1541{
1542 uint32 oldMSTime = getMSTime();
1543
1545
1546 // Load the data from creature_movement_override and if NULL fallback to creature_template_movement
1547 QueryResult result = WorldDatabase.Query(
1548 "SELECT cmo.SpawnId,"
1549 "COALESCE(cmo.HoverInitiallyEnabled, ctm.HoverInitiallyEnabled),"
1550 "COALESCE(cmo.Chase, ctm.Chase),"
1551 "COALESCE(cmo.Random, ctm.Random),"
1552 "COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
1553 "FROM creature_movement_override AS cmo "
1554 "LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
1555 "LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
1556
1557 if (!result)
1558 {
1559 TC_LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
1560 return;
1561 }
1562
1563 do
1564 {
1565 Field* fields = result->Fetch();
1566 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
1567 if (!GetCreatureData(spawnId))
1568 {
1569 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_movement_override`", spawnId);
1570 continue;
1571 }
1572
1574 if (!fields[1].IsNull())
1575 movement.HoverInitiallyEnabled = fields[1].GetBool();
1576 if (Optional<uint8> chaseMovementType = fields[2].GetUInt8OrNull())
1577 movement.Chase = static_cast<CreatureChaseMovementType>(*chaseMovementType);
1578 if (Optional<uint8> randomMovementType = fields[3].GetUInt8OrNull())
1579 movement.Random = static_cast<CreatureRandomMovementType>(*randomMovementType);
1580 if (Optional<uint32> interactionPauseTimer = fields[4].GetUInt32OrNull())
1581 movement.InteractionPauseTimer = *interactionPauseTimer;
1582
1583 CheckCreatureMovement("creature_movement_override", spawnId, movement);
1584 }
1585 while (result->NextRow());
1586
1587 TC_LOG_INFO("server.loading", ">> Loaded {} movement overrides in {} ms", _creatureMovementOverrides.size(), GetMSTimeDiffToNow(oldMSTime));
1588}
1589
1591{
1592 CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
1593 if (itr != _creatureModelStore.end())
1594 return &(itr->second);
1595
1596 return nullptr;
1597}
1598
1603
1604CreatureModel const* ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
1605{
1606 // Load creature model (display id)
1607 if (data && data->display)
1608 return &*data->display;
1609
1611 if (CreatureModel const* model = cinfo->GetRandomValidModel())
1612 return model;
1613
1614 // Triggers by default receive the invisible model
1615 return cinfo->GetFirstInvisibleModel();
1616}
1617
1618void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cInfo, uint64* npcFlags, uint32* unitFlags, uint32* unitFlags2, uint32* unitFlags3, CreatureStaticFlagsHolder const& staticFlags, CreatureData const* data /*= nullptr*/)
1619{
1620#define ChooseCreatureFlagSource(field) ((data && data->field.has_value()) ? *data->field : cInfo->field)
1621
1622 if (npcFlags)
1623 *npcFlags = ChooseCreatureFlagSource(npcflag);
1624
1625 if (unitFlags)
1626 {
1627 *unitFlags = ChooseCreatureFlagSource(unit_flags);
1628 if (staticFlags.HasFlag(CREATURE_STATIC_FLAG_CAN_SWIM))
1629 *unitFlags |= UNIT_FLAG_CAN_SWIM;
1630
1632 *unitFlags |= UNIT_FLAG_CANT_SWIM;
1633 }
1634
1635 if (unitFlags2)
1636 {
1637 *unitFlags2 = ChooseCreatureFlagSource(unit_flags2);
1639 *unitFlags2 |= UNIT_FLAG2_CANNOT_TURN;
1640
1643
1645 *unitFlags2 |= UNIT_FLAG2_INTERACT_WHILE_HOSTILE;
1646 }
1647
1648 if (unitFlags3)
1649 {
1650 *unitFlags3 = ChooseCreatureFlagSource(unit_flags3);
1653 }
1654
1655#undef ChooseCreatureFlagSource
1656}
1657
1659{
1660 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(model->CreatureDisplayID);
1661 if (!modelInfo)
1662 return nullptr;
1663
1664 // If a model for another gender exists, 50% chance to use it
1665 if (modelInfo->displayId_other_gender != 0 && urand(0, 1) == 0)
1666 {
1667 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->displayId_other_gender);
1668 if (!minfo_tmp)
1669 TC_LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", model->CreatureDisplayID, modelInfo->displayId_other_gender);
1670 else
1671 {
1672 // DisplayID changed
1673 model->CreatureDisplayID = modelInfo->displayId_other_gender;
1674 if (creatureTemplate)
1675 {
1676 auto itr = std::find_if(creatureTemplate->Models.begin(), creatureTemplate->Models.end(), [&](CreatureModel const& templateModel)
1677 {
1678 return templateModel.CreatureDisplayID == modelInfo->displayId_other_gender;
1679 });
1680 if (itr != creatureTemplate->Models.end())
1681 *model = *itr;
1682 }
1683 return minfo_tmp;
1684 }
1685 }
1686
1687 return modelInfo;
1688}
1689
1691{
1692 uint32 oldMSTime = getMSTime();
1693
1694 QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, DisplayID_Other_Gender FROM creature_model_info");
1695
1696 if (!result)
1697 {
1698 TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
1699 return;
1700 }
1701
1702 _creatureModelStore.reserve(result->GetRowCount());
1703 uint32 count = 0;
1704
1705 // List of model FileDataIDs that the client treats as invisible stalker
1706 uint32 trigggerCreatureModelFileID[5] = { 124640, 124641, 124642, 343863, 439302 };
1707
1708 do
1709 {
1710 Field* fields = result->Fetch();
1711
1712 uint32 displayId = fields[0].GetUInt32();
1713
1714 CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(displayId);
1715 if (!creatureDisplay)
1716 {
1717 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has a non-existent DisplayID (ID: {}). Skipped.", displayId);
1718 continue;
1719 }
1720
1721 CreatureModelInfo& modelInfo = _creatureModelStore[displayId];
1722
1723 modelInfo.bounding_radius = fields[1].GetFloat();
1724 modelInfo.combat_reach = fields[2].GetFloat();
1725 modelInfo.displayId_other_gender = fields[3].GetUInt32();
1726 modelInfo.gender = creatureDisplay->Gender;
1727 modelInfo.is_trigger = false;
1728
1729 // Checks
1730
1731 // to remove when the purpose of GENDER_UNKNOWN is known
1732 if (modelInfo.gender == GENDER_UNKNOWN)
1733 {
1734 // We don't need more errors
1735 //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);
1736 modelInfo.gender = GENDER_MALE;
1737 }
1738
1739 if (modelInfo.displayId_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.displayId_other_gender))
1740 {
1741 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);
1742 modelInfo.displayId_other_gender = 0;
1743 }
1744
1745 if (modelInfo.combat_reach < 0.1f)
1747
1748 if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelID))
1749 {
1750 for (uint32 i = 0; i < 5; ++i)
1751 {
1752 if (modelData->FileDataID == trigggerCreatureModelFileID[i])
1753 {
1754 modelInfo.is_trigger = true;
1755 break;
1756 }
1757 }
1758 }
1759
1760 ++count;
1761 }
1762 while (result->NextRow());
1763
1764 TC_LOG_INFO("server.loading", ">> Loaded {} creature model based info in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1765}
1766
1768{
1769 uint32 oldMSTime = getMSTime();
1770
1771 _linkedRespawnStore.clear();
1772 // 0 1 2
1773 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1774
1775 if (!result)
1776 {
1777 TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1778 return;
1779 }
1780
1781 do
1782 {
1783 Field* fields = result->Fetch();
1784
1785 ObjectGuid::LowType guidLow = fields[0].GetUInt64();
1786 ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt64();
1787 uint8 linkType = fields[2].GetUInt8();
1788
1789 ObjectGuid guid, linkedGuid;
1790 bool error = false;
1791 switch (linkType)
1792 {
1794 {
1795 CreatureData const* slave = GetCreatureData(guidLow);
1796 if (!slave)
1797 {
1798 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1799 error = true;
1800 break;
1801 }
1802
1803 CreatureData const* master = GetCreatureData(linkedGuidLow);
1804 if (!master)
1805 {
1806 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1807 error = true;
1808 break;
1809 }
1810
1811 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1812 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1813 {
1814 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1815 error = true;
1816 break;
1817 }
1818
1819 // they must have a possibility to meet (normal/heroic difficulty)
1820 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1821 {
1822 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1823 error = true;
1824 break;
1825 }
1826
1827 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1828 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1829 break;
1830 }
1832 {
1833 CreatureData const* slave = GetCreatureData(guidLow);
1834 if (!slave)
1835 {
1836 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1837 error = true;
1838 break;
1839 }
1840
1841 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1842 if (!master)
1843 {
1844 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1845 error = true;
1846 break;
1847 }
1848
1849 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1850 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1851 {
1852 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1853 error = true;
1854 break;
1855 }
1856
1857 // they must have a possibility to meet (normal/heroic difficulty)
1858 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1859 {
1860 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1861 error = true;
1862 break;
1863 }
1864
1865 guid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, guidLow);
1866 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1867 break;
1868 }
1870 {
1871 GameObjectData const* slave = GetGameObjectData(guidLow);
1872 if (!slave)
1873 {
1874 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1875 error = true;
1876 break;
1877 }
1878
1879 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1880 if (!master)
1881 {
1882 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1883 error = true;
1884 break;
1885 }
1886
1887 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1888 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1889 {
1890 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1891 error = true;
1892 break;
1893 }
1894
1895 // they must have a possibility to meet (normal/heroic difficulty)
1896 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1897 {
1898 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1899 error = true;
1900 break;
1901 }
1902
1903 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1904 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->mapId, master->id, linkedGuidLow);
1905 break;
1906 }
1908 {
1909 GameObjectData const* slave = GetGameObjectData(guidLow);
1910 if (!slave)
1911 {
1912 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1913 error = true;
1914 break;
1915 }
1916
1917 CreatureData const* master = GetCreatureData(linkedGuidLow);
1918 if (!master)
1919 {
1920 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1921 error = true;
1922 break;
1923 }
1924
1925 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1926 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1927 {
1928 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1929 error = true;
1930 break;
1931 }
1932
1933 // they must have a possibility to meet (normal/heroic difficulty)
1934 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1935 {
1936 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1937 error = true;
1938 break;
1939 }
1940
1941 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->mapId, slave->id, guidLow);
1942 linkedGuid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, linkedGuidLow);
1943 break;
1944 }
1945 }
1946
1947 if (!error)
1948 _linkedRespawnStore[guid] = linkedGuid;
1949 }
1950 while (result->NextRow());
1951
1952 TC_LOG_INFO("server.loading", ">> Loaded {} linked respawns in {} ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
1953}
1954
1956{
1957 if (!guidLow)
1958 return false;
1959
1960 CreatureData const* master = GetCreatureData(guidLow);
1961 ASSERT(master);
1962 ObjectGuid guid = ObjectGuid::Create<HighGuid::Creature>(master->mapId, master->id, guidLow);
1963
1964 if (!linkedGuidLow) // we're removing the linking
1965 {
1966 _linkedRespawnStore.erase(guid);
1968 stmt->setUInt64(0, guidLow);
1970 WorldDatabase.Execute(stmt);
1971 return true;
1972 }
1973
1974 CreatureData const* slave = GetCreatureData(linkedGuidLow);
1975 if (!slave)
1976 {
1977 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to non-existent creature '{}'.", guidLow, linkedGuidLow);
1978 return false;
1979 }
1980
1981 MapEntry const* map = sMapStore.LookupEntry(master->mapId);
1982 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1983 {
1984 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1985 return false;
1986 }
1987
1988 // they must have a possibility to meet (normal/heroic difficulty)
1989 if (!Trinity::Containers::Intersects(master->spawnDifficulties.begin(), master->spawnDifficulties.end(), slave->spawnDifficulties.begin(), slave->spawnDifficulties.end()))
1990 {
1991 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1992 return false;
1993 }
1994
1995 ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Creature>(slave->mapId, slave->id, linkedGuidLow);
1996
1997 _linkedRespawnStore[guid] = linkedGuid;
1999 stmt->setUInt64(0, guidLow);
2000 stmt->setUInt64(1, linkedGuidLow);
2002 WorldDatabase.Execute(stmt);
2003 return true;
2004}
2005
2007{
2008 uint32 oldMSTime = getMSTime();
2009
2010 _tempSummonDataStore.clear(); // needed for reload case
2011
2012 // 0 1 2 3 4 5 6 7 8 9
2013 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
2014
2015 if (!result)
2016 {
2017 TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
2018 return;
2019 }
2020
2021 uint32 count = 0;
2022 do
2023 {
2024 Field* fields = result->Fetch();
2025
2026 uint32 summonerId = fields[0].GetUInt32();
2027 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
2028 uint8 group = fields[2].GetUInt8();
2029
2030 switch (summonerType)
2031 {
2033 if (!GetCreatureTemplate(summonerId))
2034 {
2035 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for creature summoner type, skipped.", summonerId);
2036 continue;
2037 }
2038 break;
2040 if (!GetGameObjectTemplate(summonerId))
2041 {
2042 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for gameobject summoner type, skipped.", summonerId);
2043 continue;
2044 }
2045 break;
2046 case SUMMONER_TYPE_MAP:
2047 if (!sMapStore.LookupEntry(summonerId))
2048 {
2049 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for map summoner type, skipped.", summonerId);
2050 continue;
2051 }
2052 break;
2053 default:
2054 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type {} for summoner {}, skipped.", summonerType, summonerId);
2055 continue;
2056 }
2057
2058 TempSummonData data;
2059 data.entry = fields[3].GetUInt32();
2060
2061 if (!GetCreatureTemplate(data.entry))
2062 {
2063 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);
2064 continue;
2065 }
2066
2067 float posX = fields[4].GetFloat();
2068 float posY = fields[5].GetFloat();
2069 float posZ = fields[6].GetFloat();
2070 float orientation = fields[7].GetFloat();
2071
2072 data.pos.Relocate(posX, posY, posZ, orientation);
2073
2074 data.type = TempSummonType(fields[8].GetUInt8());
2075
2077 {
2078 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);
2079 continue;
2080 }
2081
2082 data.time = Milliseconds(fields[9].GetUInt32());
2083
2084 TempSummonGroupKey key{ .SummonerEntry = summonerId, .Type = summonerType, .SummonGroup = group };
2085 _tempSummonDataStore[key].push_back(data);
2086
2087 ++count;
2088
2089 } while (result->NextRow());
2090
2091 TC_LOG_INFO("server.loading", ">> Loaded {} temp summons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
2092}
2093
2094std::vector<Difficulty> ObjectMgr::ParseSpawnDifficulties(std::string_view difficultyString, std::string_view table, ObjectGuid::LowType spawnId, uint32 mapId,
2095 std::set<Difficulty> const& mapDifficulties)
2096{
2097 std::vector<Difficulty> difficulties;
2098 bool isTransportMap = sObjectMgr->IsTransportMap(mapId);
2099 for (std::string_view token : Trinity::Tokenize(difficultyString, ',', false))
2100 {
2101 Difficulty difficultyId = Difficulty(Trinity::StringTo<std::underlying_type_t<Difficulty>>(token).value_or(DIFFICULTY_NONE));
2102 if (difficultyId && !sDifficultyStore.LookupEntry(difficultyId))
2103 {
2104 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) with non invalid difficulty id {}, skipped.",
2105 table, table, spawnId, uint32(difficultyId));
2106 continue;
2107 }
2108
2109 if (!isTransportMap && mapDifficulties.find(difficultyId) == mapDifficulties.end())
2110 {
2111 TC_LOG_ERROR("sql.sql", "Table `{}` has {} (GUID: {}) has unsupported difficulty {} for map (Id: {}).",
2112 table, table, spawnId, uint32(difficultyId), mapId);
2113 continue;
2114 }
2115
2116 difficulties.push_back(difficultyId);
2117 }
2118
2119 std::sort(difficulties.begin(), difficulties.end());
2120 return difficulties;
2121}
2122
2124{
2125 uint32 oldMSTime = getMSTime();
2126
2127 // 0 1 2 3 4 5 6 7 8 9 10
2128 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
2129 // 11 12 13 14 15 16 17 18 19 20
2130 "currentwaypoint, curHealthPct, MovementType, spawnDifficulties, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, "
2131 // 21 22 23 24 25 26
2132 "creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName, creature.StringId "
2133 "FROM creature "
2134 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
2135 "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
2136
2137 if (!result)
2138 {
2139 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
2140 return;
2141 }
2142
2143 // Build single time for check spawnmask
2144 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2145 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2146 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2147
2148 PhaseShift phaseShift;
2149
2150 _creatureDataStore.reserve(result->GetRowCount());
2151
2152 do
2153 {
2154 Field* fields = result->Fetch();
2155
2156 ObjectGuid::LowType guid = fields[0].GetUInt64();
2157 uint32 entry = fields[1].GetUInt32();
2158
2159 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2160 if (!cInfo)
2161 {
2162 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, entry);
2163 continue;
2164 }
2165
2166 CreatureData& data = _creatureDataStore[guid];
2167 data.spawnId = guid;
2168 data.id = entry;
2169 data.mapId = fields[2].GetUInt16();
2170 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2171 if (uint32 displayId = fields[7].GetUInt32())
2172 data.display.emplace(displayId, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
2173 data.equipmentId = fields[8].GetInt8();
2174 data.spawntimesecs = fields[9].GetUInt32();
2175 data.wander_distance = fields[10].GetFloat();
2176 data.currentwaypoint = fields[11].GetUInt32();
2177 data.curHealthPct = fields[12].GetUInt32();
2178 data.movementType = fields[13].GetUInt8();
2179 data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "creature", guid, data.mapId, spawnMasks[data.mapId]);
2180 int16 gameEvent = fields[15].GetInt8();
2181 data.poolId = fields[16].GetUInt32();
2182 data.npcflag = fields[17].GetUInt64OrNull();
2183 data.unit_flags = fields[18].GetUInt32OrNull();
2184 data.unit_flags2 = fields[19].GetUInt32OrNull();
2185 data.unit_flags3 = fields[20].GetUInt32OrNull();
2186 data.phaseUseFlags = fields[21].GetUInt8();
2187 data.phaseId = fields[22].GetUInt32();
2188 data.phaseGroup = fields[23].GetUInt32();
2189 data.terrainSwapMap = fields[24].GetInt32();
2190 data.scriptId = GetScriptId(fields[25].GetStringView());
2191 data.StringId = fields[26].GetString();
2192 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2193
2194 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2195 if (!mapEntry)
2196 {
2197 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, data.mapId);
2198 continue;
2199 }
2200
2202 {
2204 {
2205 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2206 {
2208 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2209 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2210
2211 VMAP::LoadResult result = vmgr->existsMap(sWorld->GetDataPath() + "vmaps", data.mapId, gx, gy);
2212 if (result != VMAP::LoadResult::Success)
2213 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2214 guid, data.id, data.mapId, data.spawnPoint);
2215 }
2216 }
2217 }
2218
2219 if (data.spawnDifficulties.empty())
2220 {
2221 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2222 continue;
2223 }
2224
2225 if (data.display.has_value())
2226 {
2227 if (!GetCreatureModelInfo(data.display->CreatureDisplayID))
2228 {
2229 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with invalid `modelid` {}, ignoring.", guid, data.id, data.display->CreatureDisplayID);
2230 data.display.reset();
2231 }
2232 }
2233
2234 // -1 random, 0 no equipment
2235 if (data.equipmentId != 0)
2236 {
2237 if (!GetEquipmentInfo(data.id, data.equipmentId))
2238 {
2239 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);
2240 data.equipmentId = 0;
2241 }
2242 }
2243
2245 {
2246 if (!mapEntry->IsDungeon())
2247 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);
2248 }
2249
2250 if (data.movementType >= MAX_DB_MOTION_TYPE)
2251 {
2252 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);
2254 }
2255
2256 if (data.wander_distance < 0.0f)
2257 {
2258 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance`< 0, set to 0.", guid, data.id);
2259 data.wander_distance = 0.0f;
2260 }
2261 else if (data.wander_distance > 0.0f && data.wander_distance < 0.1f)
2262 {
2263 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);
2264 data.wander_distance = 0.0f;
2265 }
2266 else if (data.movementType == RANDOM_MOTION_TYPE)
2267 {
2268 if (G3D::fuzzyEq(data.wander_distance, 0.0f))
2269 {
2270 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);
2272 }
2273 }
2274 else if (data.movementType == IDLE_MOTION_TYPE)
2275 {
2276 if (data.wander_distance != 0.0f)
2277 {
2278 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);
2279 data.wander_distance = 0.0f;
2280 }
2281 }
2282
2284 {
2285 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2287 }
2288
2290 {
2291 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2292 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2293 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2294 }
2295
2296 if (data.phaseGroup && data.phaseId)
2297 {
2298 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2299 data.phaseGroup = 0;
2300 }
2301
2302 if (data.phaseId)
2303 {
2304 if (!sPhaseStore.LookupEntry(data.phaseId))
2305 {
2306 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2307 data.phaseId = 0;
2308 }
2309 }
2310
2311 if (data.phaseGroup)
2312 {
2313 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2314 {
2315 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `phasegroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2316 data.phaseGroup = 0;
2317 }
2318 }
2319
2320 if (data.terrainSwapMap != -1)
2321 {
2322 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2323 if (!terrainSwapEntry)
2324 {
2325 TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2326 data.terrainSwapMap = -1;
2327 }
2328 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2329 {
2330 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);
2331 data.terrainSwapMap = -1;
2332 }
2333 }
2334
2335 if (data.unit_flags.has_value())
2336 {
2337 if (uint32 disallowedUnitFlags = (*data.unit_flags & ~UNIT_FLAG_ALLOWED))
2338 {
2339 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags);
2341 }
2342 }
2343
2344 if (data.unit_flags2.has_value())
2345 {
2346 if (uint32 disallowedUnitFlags2 = (*data.unit_flags2 & ~UNIT_FLAG2_ALLOWED))
2347 {
2348 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags2);
2350 }
2351
2353 {
2354 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);
2355 *data.unit_flags2 &= ~UNIT_FLAG2_FEIGN_DEATH;
2356 }
2357 }
2358
2359 if (data.unit_flags3.has_value())
2360 {
2361 if (uint32 disallowedUnitFlags3 = (*data.unit_flags3 & ~UNIT_FLAG3_ALLOWED))
2362 {
2363 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags3` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags3);
2365 }
2366
2368 {
2369 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);
2370 *data.unit_flags3 &= ~UNIT_FLAG3_FAKE_DEAD;
2371 }
2372 }
2373
2374 uint32 healthPct = std::clamp<uint32>(data.curHealthPct, 1, 100);
2375 if (data.curHealthPct != healthPct)
2376 {
2377 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with invalid `curHealthPct` {}, set to {}.", guid, data.id, data.curHealthPct, healthPct);
2378 data.curHealthPct = healthPct;
2379 }
2380
2382 {
2383 uint32 zoneId = 0;
2384 uint32 areaId = 0;
2386 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2387
2389
2390 stmt->setUInt32(0, zoneId);
2391 stmt->setUInt32(1, areaId);
2392 stmt->setUInt64(2, guid);
2393
2394 WorldDatabase.Execute(stmt);
2395 }
2396
2397 // Add to grid if not managed by the game event
2398 if (gameEvent == 0)
2399 AddCreatureToGrid(&data);
2400 }
2401 while (result->NextRow());
2402
2403 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in {} ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2404}
2405
2407{
2408 if (CellObjectGuidsMap const* mapGuids = Trinity::Containers::MapGetValuePtr(_mapObjectGuidsStore, { mapid, spawnMode }))
2409 return Trinity::Containers::MapGetValuePtr(*mapGuids, cell_id);
2410
2411 return nullptr;
2412}
2413
2418
2419bool ObjectMgr::HasPersonalSpawns(uint32 mapid, Difficulty spawnMode, uint32 phaseId) const
2420{
2421 return Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }) != nullptr;
2422}
2423
2425{
2426 if (CellObjectGuidsMap const* guids = Trinity::Containers::MapGetValuePtr(_mapPersonalObjectGuidsStore, { mapid, spawnMode, phaseId }))
2427 return Trinity::Containers::MapGetValuePtr(*guids, cell_id);
2428
2429 return nullptr;
2430}
2431
2432template<CellGuidSet CellObjectGuids::*guids>
2434{
2436 bool isPersonalPhase = PhasingHandler::IsPersonalPhase(data->phaseId);
2437 if (!isPersonalPhase)
2438 {
2439 for (Difficulty difficulty : data->spawnDifficulties)
2440 (_mapObjectGuidsStore[{ data->mapId, difficulty }][cellId].*guids).insert(data->spawnId);
2441 }
2442 else
2443 {
2444 for (Difficulty difficulty : data->spawnDifficulties)
2445 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][cellId].*guids).insert(data->spawnId);
2446 }
2447}
2448
2449template<CellGuidSet CellObjectGuids::*guids>
2451{
2453 bool isPersonalPhase = PhasingHandler::IsPersonalPhase(data->phaseId);
2454 if (!isPersonalPhase)
2455 {
2456 for (Difficulty difficulty : data->spawnDifficulties)
2457 (_mapObjectGuidsStore[{ data->mapId, difficulty }][cellId].*guids).erase(data->spawnId);
2458 }
2459 else
2460 {
2461 for (Difficulty difficulty : data->spawnDifficulties)
2462 (_mapPersonalObjectGuidsStore[{ data->mapId, difficulty, data->phaseId }][cellId].*guids).erase(data->spawnId);
2463 }
2464}
2465
2467{
2468 AddSpawnDataToGrid<&CellObjectGuids::creatures>(data);
2469}
2470
2472{
2473 RemoveSpawnDataFromGrid<&CellObjectGuids::creatures>(data);
2474}
2475
2477{
2478 uint32 oldMSTime = getMSTime();
2479
2480 // 0 1 2 3 4 5 6
2481 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
2482 // 7 8 9 10 11 12 13 14 15 16
2483 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnDifficulties, eventEntry, poolSpawnId, "
2484 // 17 18 19 20 21 22
2485 "phaseUseFlags, phaseid, phasegroup, terrainSwapMap, ScriptName, StringId "
2486 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
2487 "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId");
2488
2489 if (!result)
2490 {
2491 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
2492 return;
2493 }
2494
2495 // build single time for check spawnmask
2496 std::unordered_map<uint32, std::set<Difficulty>> spawnMasks;
2497 for (MapDifficultyEntry const* mapDifficulty : sMapDifficultyStore)
2498 spawnMasks[mapDifficulty->MapID].insert(Difficulty(mapDifficulty->DifficultyID));
2499
2500 PhaseShift phaseShift;
2501
2502 _gameObjectDataStore.reserve(result->GetRowCount());
2503
2504 do
2505 {
2506 Field* fields = result->Fetch();
2507
2508 ObjectGuid::LowType guid = fields[0].GetUInt64();
2509 uint32 entry = fields[1].GetUInt32();
2510
2511 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
2512 if (!gInfo)
2513 {
2514 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non existing gameobject entry {}, skipped.", guid, entry);
2515 continue;
2516 }
2517
2518 if (!gInfo->displayId)
2519 {
2520 switch (gInfo->type)
2521 {
2524 break;
2525 default:
2526 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) doesn't have a displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2527 break;
2528 }
2529 }
2530
2531 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
2532 {
2533 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) has an invalid displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2534 continue;
2535 }
2536
2538
2539 data.spawnId = guid;
2540 data.id = entry;
2541 data.mapId = fields[2].GetUInt16();
2542 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2543 data.rotation.x = fields[7].GetFloat();
2544 data.rotation.y = fields[8].GetFloat();
2545 data.rotation.z = fields[9].GetFloat();
2546 data.rotation.w = fields[10].GetFloat();
2547 data.spawntimesecs = fields[11].GetInt32();
2548 data.spawnGroupData = IsTransportMap(data.mapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group
2549
2550 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2551 if (!mapEntry)
2552 {
2553 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existed map (Id: {}), skip", guid, data.id, data.mapId);
2554 continue;
2555 }
2556
2558 {
2560 {
2561 if (vmgr->isMapLoadingEnabled() && !IsTransportMap(data.mapId))
2562 {
2564 int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord;
2565 int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord;
2566
2567 VMAP::LoadResult result = vmgr->existsMap(sWorld->GetDataPath() + "vmaps", data.mapId, gx, gy);
2568 if (result != VMAP::LoadResult::Success)
2569 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {} MapID: {}) spawned on a possible invalid position ({})",
2570 guid, data.id, data.mapId, data.spawnPoint);
2571 }
2572 }
2573 }
2574
2575 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
2576 {
2577 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);
2578 }
2579
2580 data.animprogress = fields[12].GetUInt8();
2581 data.artKit = 0;
2582
2583 uint32 go_state = fields[13].GetUInt8();
2584 if (go_state >= MAX_GO_STATE)
2585 {
2587 {
2588 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skip", guid, data.id, go_state);
2589 continue;
2590 }
2591 }
2592 data.goState = GOState(go_state);
2593
2594 data.spawnDifficulties = ParseSpawnDifficulties(fields[14].GetStringView(), "gameobject", guid, data.mapId, spawnMasks[data.mapId]);
2595 if (data.spawnDifficulties.empty())
2596 {
2597 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is not spawned in any difficulty, skipped.", guid);
2598 continue;
2599 }
2600
2601 int16 gameEvent = fields[15].GetInt8();
2602 data.poolId = fields[16].GetUInt32();
2603 data.phaseUseFlags = fields[17].GetUInt8();
2604 data.phaseId = fields[18].GetUInt32();
2605 data.phaseGroup = fields[19].GetUInt32();
2606
2608 {
2609 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id);
2611 }
2612
2614 {
2615 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
2616 " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id);
2617 data.phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
2618 }
2619
2620 if (data.phaseGroup && data.phaseId)
2621 {
2622 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id);
2623 data.phaseGroup = 0;
2624 }
2625
2626 if (data.phaseId)
2627 {
2628 if (!sPhaseStore.LookupEntry(data.phaseId))
2629 {
2630 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, data.id, data.phaseId);
2631 data.phaseId = 0;
2632 }
2633 }
2634
2635 if (data.phaseGroup)
2636 {
2637 if (!sDB2Manager.GetPhasesForGroup(data.phaseGroup))
2638 {
2639 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `phaseGroup` {} does not exist, set to 0", guid, data.id, data.phaseGroup);
2640 data.phaseGroup = 0;
2641 }
2642 }
2643
2644 data.terrainSwapMap = fields[20].GetInt32();
2645 if (data.terrainSwapMap != -1)
2646 {
2647 MapEntry const* terrainSwapEntry = sMapStore.LookupEntry(data.terrainSwapMap);
2648 if (!terrainSwapEntry)
2649 {
2650 TC_LOG_ERROR("sql.sql", "Table `gameobject` have gameobject (GUID: {} Entry: {}) with `terrainSwapMap` {} does not exist, set to -1", guid, data.id, data.terrainSwapMap);
2651 data.terrainSwapMap = -1;
2652 }
2653 else if (terrainSwapEntry->ParentMapID != int16(data.mapId))
2654 {
2655 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);
2656 data.terrainSwapMap = -1;
2657 }
2658 }
2659
2660 data.scriptId = GetScriptId(fields[21].GetStringView());
2661 data.StringId = fields[22].GetString();
2662
2663 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
2664 {
2665 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skip", guid, data.id, data.rotation.x);
2666 continue;
2667 }
2668
2669 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
2670 {
2671 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skip", guid, data.id, data.rotation.y);
2672 continue;
2673 }
2674
2675 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
2676 {
2677 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skip", guid, data.id, data.rotation.z);
2678 continue;
2679 }
2680
2681 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
2682 {
2683 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skip", guid, data.id, data.rotation.w);
2684 continue;
2685 }
2686
2688 {
2689 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id);
2690 continue;
2691 }
2692
2693 if (!data.rotation.isUnit())
2694 {
2695 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);
2697 }
2698
2700 {
2701 uint32 zoneId = 0;
2702 uint32 areaId = 0;
2704 sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint);
2705
2707
2708 stmt->setUInt32(0, zoneId);
2709 stmt->setUInt32(1, areaId);
2710 stmt->setUInt64(2, guid);
2711
2712 WorldDatabase.Execute(stmt);
2713 }
2714
2715 if (gameEvent == 0) // if not this is to be managed by GameEvent System
2716 AddGameobjectToGrid(&data);
2717 }
2718 while (result->NextRow());
2719
2720 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in {} ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2721}
2722
2724{
2725 uint32 oldMSTime = getMSTime();
2726
2727 // 0 1 2
2728 QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
2729
2730 if (result)
2731 {
2732 do
2733 {
2734 Field* fields = result->Fetch();
2735 uint32 groupId = fields[0].GetUInt32();
2737 group.groupId = groupId;
2738 group.name = fields[1].GetString();
2740 uint32 flags = fields[2].GetUInt32();
2742 {
2744 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag {} on group ID {} ({}), reduced to valid flag {}.", flags, groupId, group.name, uint32(group.flags));
2745 }
2747 {
2748 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
2749 TC_LOG_ERROR("sql.sql", "System spawn group {} ({}) has invalid manual spawn flag. Ignored.", groupId, group.name);
2750 }
2751 group.flags = SpawnGroupFlags(flags);
2752 } while (result->NextRow());
2753 }
2754
2755 if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
2756 {
2757 TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
2759 data.groupId = 0;
2760 data.name = "Default Group";
2761 data.mapId = 0;
2763 }
2764 if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
2765 {
2766 TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
2768 data.groupId = 1;
2769 data.name = "Legacy Group";
2770 data.mapId = 0;
2772 }
2773
2774 if (result)
2775 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group templates in {} ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2776 else
2777 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
2778
2779 return;
2780}
2781
2783{
2784 uint32 oldMSTime = getMSTime();
2785
2786 // 0 1 2
2787 QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
2788
2789 if (!result)
2790 {
2791 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
2792 return;
2793 }
2794
2795 uint32 numMembers = 0;
2796 do
2797 {
2798 Field* fields = result->Fetch();
2799 uint32 groupId = fields[0].GetUInt32();
2800 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
2801 if (!SpawnData::TypeIsValid(spawnType))
2802 {
2803 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type {} listed for spawn group {}. Skipped.", uint32(spawnType), groupId);
2804 continue;
2805 }
2806 ObjectGuid::LowType spawnId = fields[2].GetUInt64();
2807
2808 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
2809 if (!data)
2810 {
2811 TC_LOG_ERROR("sql.sql", "Spawn data with ID ({},{}) not found, but is listed as a member of spawn group {}!", uint32(spawnType), spawnId, groupId);
2812 continue;
2813 }
2814 else if (data->spawnGroupData->groupId)
2815 {
2816 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);
2817 continue;
2818 }
2819 auto it = _spawnGroupDataStore.find(groupId);
2820 if (it == _spawnGroupDataStore.end())
2821 {
2822 TC_LOG_ERROR("sql.sql", "Spawn group {} assigned to spawn ID ({},{}), but group is found!", groupId, uint32(spawnType), spawnId);
2823 continue;
2824 }
2825 else
2826 {
2827 SpawnGroupTemplateData& groupTemplate = it->second;
2828 if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
2829 {
2830 groupTemplate.mapId = data->mapId;
2831 _spawnGroupsByMap[data->mapId].push_back(groupId);
2832 }
2833 else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2834 {
2835 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);
2836 continue;
2837 }
2838 const_cast<SpawnMetadata*>(data)->spawnGroupData = &groupTemplate;
2839 if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2840 _spawnGroupMapStore.emplace(groupId, data);
2841 ++numMembers;
2842 }
2843 } while (result->NextRow());
2844
2845 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group members in {} ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
2846}
2847
2849{
2850 uint32 oldMSTime = getMSTime();
2851
2852 // 0 1 2 3 4
2853 QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
2854
2855 if (!result)
2856 {
2857 TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
2858 return;
2859 }
2860
2861 uint32 n = 0;
2862 do
2863 {
2864 Field* fields = result->Fetch();
2865 uint32 const spawnGroupId = fields[3].GetUInt32();
2866 auto it = _spawnGroupDataStore.find(spawnGroupId);
2867 if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
2868 {
2869 TC_LOG_ERROR("sql.sql", "Invalid spawn group {} specified for instance {}. Skipped.", spawnGroupId, fields[0].GetUInt16());
2870 continue;
2871 }
2872
2873 uint16 const instanceMapId = fields[0].GetUInt16();
2874 if (it->second.mapId != instanceMapId)
2875 {
2876 TC_LOG_ERROR("sql.sql", "Instance spawn group {} specified for instance {} has spawns on a different map {}. Skipped.",
2877 spawnGroupId, instanceMapId, it->second.mapId);
2878 continue;
2879 }
2880
2881 InstanceSpawnGroupInfo& info = _instanceSpawnGroupStore[instanceMapId].emplace_back();
2882 info.SpawnGroupId = spawnGroupId;
2883 info.BossStateId = fields[1].GetUInt8();
2884
2885 uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
2886 uint8 const states = fields[2].GetUInt8();
2887 if (states & ~ALL_STATES)
2888 {
2889 info.BossStates = states & ALL_STATES;
2890 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid boss state mask {} - truncated to {}.", instanceMapId, spawnGroupId, states, info.BossStates);
2891 }
2892 else
2893 info.BossStates = states;
2894
2895 uint8 const flags = fields[4].GetUInt8();
2897 {
2899 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid flags {} - truncated to {}.", instanceMapId, spawnGroupId, flags, info.Flags);
2900 }
2901 else
2902 info.Flags = flags;
2903
2905 {
2907 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);
2908 }
2909
2910 ++n;
2911 } while (result->NextRow());
2912
2913 TC_LOG_INFO("server.loading", ">> Loaded {} instance spawn groups in {} ms", n, GetMSTimeDiffToNow(oldMSTime));
2914}
2915
2917{
2918 if (!SpawnData::TypeHasData(type))
2919 return nullptr;
2920 switch (type)
2921 {
2923 return GetCreatureData(spawnId);
2925 return GetGameObjectData(spawnId);
2927 return sAreaTriggerDataStore->GetAreaTriggerSpawn(spawnId);
2928 default:
2929 ABORT_MSG("Invalid spawn object type %u", uint32(type));
2930 return nullptr;
2931 }
2932}
2933
2935{
2936 if (data->spawnTrackingData)
2937 {
2939 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);
2940
2941 auto pair = _spawnTrackingMapStore.equal_range(data->spawnTrackingData->SpawnTrackingId);
2942 bool erased = false;
2943 for (auto it = pair.first; it != pair.second; ++it)
2944 {
2945 if (it->second != data)
2946 continue;
2947 _spawnTrackingMapStore.erase(it);
2948 erased = true;
2949 }
2950
2951 if (!erased)
2952 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);
2953 }
2954
2955 auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
2956 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);
2957 if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
2958 return;
2959
2960 auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
2961 for (auto it = pair.first; it != pair.second; ++it)
2962 {
2963 if (it->second != data)
2964 continue;
2965 _spawnGroupMapStore.erase(it);
2966 return;
2967 }
2968 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);
2969}
2970
2972{
2973 AddSpawnDataToGrid<&CellObjectGuids::gameobjects>(data);
2974}
2975
2977{
2978 RemoveSpawnDataFromGrid<&CellObjectGuids::gameobjects>(data);
2979}
2980
2981uint32 FillMaxDurability(uint32 itemClass, uint32 itemSubClass, uint32 inventoryType, uint32 quality, uint32 itemLevel)
2982{
2983 if (itemClass != ITEM_CLASS_ARMOR && itemClass != ITEM_CLASS_WEAPON)
2984 return 0;
2985
2986 static float const qualityMultipliers[MAX_ITEM_QUALITY] =
2987 {
2988 0.92f, 0.92f, 0.92f, 1.11f, 1.32f, 1.61f, 0.0f, 0.0f
2989 };
2990
2991 static float const armorMultipliers[MAX_INVTYPE] =
2992 {
2993 0.00f, // INVTYPE_NON_EQUIP
2994 0.60f, // INVTYPE_HEAD
2995 0.00f, // INVTYPE_NECK
2996 0.60f, // INVTYPE_SHOULDERS
2997 0.00f, // INVTYPE_BODY
2998 1.00f, // INVTYPE_CHEST
2999 0.33f, // INVTYPE_WAIST
3000 0.72f, // INVTYPE_LEGS
3001 0.48f, // INVTYPE_FEET
3002 0.33f, // INVTYPE_WRISTS
3003 0.33f, // INVTYPE_HANDS
3004 0.00f, // INVTYPE_FINGER
3005 0.00f, // INVTYPE_TRINKET
3006 0.00f, // INVTYPE_WEAPON
3007 0.72f, // INVTYPE_SHIELD
3008 0.00f, // INVTYPE_RANGED
3009 0.00f, // INVTYPE_CLOAK
3010 0.00f, // INVTYPE_2HWEAPON
3011 0.00f, // INVTYPE_BAG
3012 0.00f, // INVTYPE_TABARD
3013 1.00f, // INVTYPE_ROBE
3014 0.00f, // INVTYPE_WEAPONMAINHAND
3015 0.00f, // INVTYPE_WEAPONOFFHAND
3016 0.00f, // INVTYPE_HOLDABLE
3017 0.00f, // INVTYPE_AMMO
3018 0.00f, // INVTYPE_THROWN
3019 0.00f, // INVTYPE_RANGEDRIGHT
3020 0.00f, // INVTYPE_QUIVER
3021 0.00f, // INVTYPE_RELIC
3022 0.00f, // INVTYPE_PROFESSION_TOOL
3023 0.00f, // INVTYPE_PROFESSION_GEAR
3024 0.00f, // INVTYPE_EQUIPABLE_SPELL_OFFENSIVE
3025 0.00f, // INVTYPE_EQUIPABLE_SPELL_UTILITY
3026 0.00f, // INVTYPE_EQUIPABLE_SPELL_DEFENSIVE
3027 0.00f, // INVTYPE_EQUIPABLE_SPELL_MOBILITY
3028 };
3029
3030 static float const weaponMultipliers[MAX_ITEM_SUBCLASS_WEAPON] =
3031 {
3032 0.91f, // ITEM_SUBCLASS_WEAPON_AXE
3033 1.00f, // ITEM_SUBCLASS_WEAPON_AXE2
3034 1.00f, // ITEM_SUBCLASS_WEAPON_BOW
3035 1.00f, // ITEM_SUBCLASS_WEAPON_GUN
3036 0.91f, // ITEM_SUBCLASS_WEAPON_MACE
3037 1.00f, // ITEM_SUBCLASS_WEAPON_MACE2
3038 1.00f, // ITEM_SUBCLASS_WEAPON_POLEARM
3039 0.91f, // ITEM_SUBCLASS_WEAPON_SWORD
3040 1.00f, // ITEM_SUBCLASS_WEAPON_SWORD2
3041 1.00f, // ITEM_SUBCLASS_WEAPON_WARGLAIVES
3042 1.00f, // ITEM_SUBCLASS_WEAPON_STAFF
3043 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC
3044 0.00f, // ITEM_SUBCLASS_WEAPON_EXOTIC2
3045 0.66f, // ITEM_SUBCLASS_WEAPON_FIST_WEAPON
3046 0.00f, // ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
3047 0.66f, // ITEM_SUBCLASS_WEAPON_DAGGER
3048 0.00f, // ITEM_SUBCLASS_WEAPON_THROWN
3049 0.00f, // ITEM_SUBCLASS_WEAPON_SPEAR
3050 1.00f, // ITEM_SUBCLASS_WEAPON_CROSSBOW
3051 0.66f, // ITEM_SUBCLASS_WEAPON_WAND
3052 0.66f, // ITEM_SUBCLASS_WEAPON_FISHING_POLE
3053 };
3054
3055 float levelPenalty = 1.0f;
3056 if (itemLevel <= 28)
3057 levelPenalty = 0.966f - float(28u - itemLevel) / 54.0f;
3058
3059 if (itemClass == ITEM_CLASS_ARMOR)
3060 {
3061 if (inventoryType > INVTYPE_ROBE)
3062 return 0;
3063
3064 return 5 * uint32(round(25.0f * qualityMultipliers[quality] * armorMultipliers[inventoryType] * levelPenalty));
3065 }
3066
3067 return 5 * uint32(round(18.0f * qualityMultipliers[quality] * weaponMultipliers[itemSubClass] * levelPenalty));
3068}
3069
3071{
3075
3077 {
3078 memset(ItemSpecStatTypes, -1, sizeof(ItemSpecStatTypes));
3079
3080 if (item->ClassID == ITEM_CLASS_WEAPON)
3081 {
3082 ItemType = 5;
3083 switch (item->SubclassID)
3084 {
3087 break;
3090 break;
3093 break;
3096 break;
3099 break;
3102 break;
3105 break;
3108 break;
3111 break;
3114 break;
3117 break;
3120 break;
3123 break;
3126 break;
3129 break;
3132 break;
3133 default:
3134 break;
3135 }
3136 }
3137 else if (item->ClassID == ITEM_CLASS_ARMOR)
3138 {
3139 switch (item->SubclassID)
3140 {
3142 if (sparse->InventoryType != INVTYPE_CLOAK)
3143 {
3144 ItemType = 1;
3145 break;
3146 }
3147
3148 ItemType = 0;
3150 break;
3152 ItemType = 2;
3153 break;
3155 ItemType = 3;
3156 break;
3158 ItemType = 4;
3159 break;
3160 default:
3162 {
3163 ItemType = 6;
3165 }
3167 {
3168 ItemType = 6;
3170 }
3171 else
3172 ItemType = 0;
3173 break;
3174 }
3175 }
3176 else if (item->ClassID == ITEM_CLASS_GEM)
3177 {
3178 ItemType = 7;
3179 if (GemPropertiesEntry const* gem = sGemPropertiesStore.LookupEntry(sparse->GemProperties))
3180 {
3181 if (gem->Type & SOCKET_COLOR_RELIC_IRON)
3183 if (gem->Type & SOCKET_COLOR_RELIC_BLOOD)
3185 if (gem->Type & SOCKET_COLOR_RELIC_SHADOW)
3187 if (gem->Type & SOCKET_COLOR_RELIC_FEL)
3189 if (gem->Type & SOCKET_COLOR_RELIC_ARCANE)
3191 if (gem->Type & SOCKET_COLOR_RELIC_FROST)
3193 if (gem->Type & SOCKET_COLOR_RELIC_FIRE)
3195 if (gem->Type & SOCKET_COLOR_RELIC_WATER)
3197 if (gem->Type & SOCKET_COLOR_RELIC_LIFE)
3199 if (gem->Type & SOCKET_COLOR_RELIC_WIND)
3201 if (gem->Type & SOCKET_COLOR_RELIC_HOLY)
3203 }
3204 }
3205 else
3206 ItemType = 0;
3207
3208 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3209 if (sparse->StatModifierBonusStat[i] != -1)
3211 }
3212
3213 void AddStat(ItemSpecStat statType)
3214 {
3216 return;
3217
3218 for (uint32 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
3219 if (ItemSpecStatTypes[i] == uint32(statType))
3220 return;
3221
3223 }
3224
3225 void AddModStat(int32 itemStatType)
3226 {
3227 switch (itemStatType)
3228 {
3229 case ITEM_MOD_AGILITY:
3231 break;
3232 case ITEM_MOD_STRENGTH:
3234 break;
3235 case ITEM_MOD_INTELLECT:
3237 break;
3240 break;
3243 break;
3249 break;
3252 break;
3255 break;
3258 break;
3263 break;
3264 case ITEM_MOD_AGI_STR:
3267 break;
3268 case ITEM_MOD_AGI_INT:
3271 break;
3272 case ITEM_MOD_STR_INT:
3275 break;
3276 }
3277 }
3278};
3279
3281{
3282 uint32 oldMSTime = getMSTime();
3283
3284 for (ItemSparseEntry const* sparse : sItemSparseStore)
3285 {
3286 ItemEntry const* db2Data = sItemStore.LookupEntry(sparse->ID);
3287 if (!db2Data)
3288 continue;
3289
3290 ItemTemplate& itemTemplate = _itemTemplateStore[sparse->ID];
3291
3292 itemTemplate.BasicData = db2Data;
3293 itemTemplate.ExtendedData = sparse;
3294
3295 itemTemplate.MaxDurability = FillMaxDurability(db2Data->ClassID, db2Data->SubclassID, sparse->InventoryType, sparse->OverallQualityID, sparse->ItemLevel);
3296 itemTemplate.ScriptId = 0;
3297 itemTemplate.FoodType = 0;
3298 itemTemplate.MinMoneyLoot = 0;
3299 itemTemplate.MaxMoneyLoot = 0;
3300 itemTemplate.FlagsCu = 0;
3301 itemTemplate.SpellPPMRate = 0.0f;
3302 itemTemplate.RandomBonusListTemplateId = 0;
3303 itemTemplate.ItemSpecClassMask = 0;
3304 itemTemplate.QuestLogItemId = 0;
3305
3306 if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID))
3307 {
3308 for (ItemSpecOverrideEntry const* itemSpecOverride : *itemSpecOverrides)
3309 {
3310 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpecOverride->SpecID))
3311 {
3312 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3313 itemTemplate.Specializations[0].set(ItemTemplate::CalculateItemSpecBit(specialization));
3314 itemTemplate.Specializations[1] |= itemTemplate.Specializations[0];
3315 itemTemplate.Specializations[2] |= itemTemplate.Specializations[0];
3316 }
3317 }
3318 }
3319 else
3320 {
3321 ItemSpecStats itemSpecStats(db2Data, sparse);
3322
3323 for (ItemSpecEntry const* itemSpec : sItemSpecStore)
3324 {
3325 if (itemSpecStats.ItemType != itemSpec->ItemType)
3326 continue;
3327
3328 bool hasPrimary = itemSpec->PrimaryStat == ITEM_SPEC_STAT_NONE;
3329 bool hasSecondary = itemSpec->SecondaryStat == ITEM_SPEC_STAT_NONE;
3330 for (uint32 i = 0; i < itemSpecStats.ItemSpecStatCount; ++i)
3331 {
3332 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->PrimaryStat)
3333 hasPrimary = true;
3334 if (itemSpecStats.ItemSpecStatTypes[i] == itemSpec->SecondaryStat)
3335 hasSecondary = true;
3336 }
3337
3338 if (!hasPrimary || !hasSecondary)
3339 continue;
3340
3341 if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpec->SpecializationID))
3342 {
3343 if ((1 << (specialization->ClassID - 1)) & sparse->AllowableClass)
3344 {
3345 itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
3346 std::size_t specBit = ItemTemplate::CalculateItemSpecBit(specialization);
3347 itemTemplate.Specializations[0].set(specBit);
3348 if (itemSpec->MaxLevel > 40)
3349 itemTemplate.Specializations[1].set(specBit);
3350 if (itemSpec->MaxLevel >= 110)
3351 itemTemplate.Specializations[2].set(specBit);
3352 }
3353 }
3354 }
3355 }
3356
3357 // Items that have no specializations set can be used by everyone
3358 for (auto& specs : itemTemplate.Specializations)
3359 if (specs.count() == 0)
3360 specs.set();
3361 }
3362
3363 // Load item effects (spells)
3364 for (ItemXItemEffectEntry const* effectEntry : sItemXItemEffectStore)
3365 {
3367 {
3368 if (ItemEffectEntry const* effect = sItemEffectStore.LookupEntry(effectEntry->ItemEffectID))
3369 {
3370 auto itr = std::ranges::lower_bound(item->Effects, effect->LegacySlotIndex, {}, &ItemEffectEntry::LegacySlotIndex);
3371 item->Effects.insert(itr, effect);
3372 }
3373 }
3374 }
3375
3376 TC_LOG_INFO("server.loading", ">> Loaded {} item templates in {} ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3377}
3378
3380{
3381 uint32 oldMSTime = getMSTime();
3382 uint32 count = 0;
3383
3384 QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId, QuestLogItemId FROM item_template_addon");
3385 if (result)
3386 {
3387 do
3388 {
3389 Field* fields = result->Fetch();
3390 uint32 itemId = fields[0].GetUInt32();
3391 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3392 if (!itemTemplate)
3393 {
3394 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_template_addon` does not exist, skipped.", itemId);
3395 continue;
3396 }
3397
3398 uint32 minMoneyLoot = fields[3].GetUInt32();
3399 uint32 maxMoneyLoot = fields[4].GetUInt32();
3400 if (minMoneyLoot > maxMoneyLoot)
3401 {
3402 TC_LOG_ERROR("sql.sql", "Minimum money loot specified in `item_template_addon` for item {} was greater than maximum amount, swapping.", itemId);
3403 std::swap(minMoneyLoot, maxMoneyLoot);
3404 }
3405 itemTemplate->FlagsCu = fields[1].GetUInt32();
3406 itemTemplate->FoodType = fields[2].GetUInt8();
3407 itemTemplate->MinMoneyLoot = minMoneyLoot;
3408 itemTemplate->MaxMoneyLoot = maxMoneyLoot;
3409 itemTemplate->SpellPPMRate = fields[5].GetFloat();
3410 itemTemplate->RandomBonusListTemplateId = fields[6].GetUInt32();
3411 itemTemplate->QuestLogItemId = fields[7].GetInt32();
3412 ++count;
3413 } while (result->NextRow());
3414 }
3415 TC_LOG_INFO("server.loading", ">> Loaded {} item addon templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3416}
3417
3419{
3420 uint32 oldMSTime = getMSTime();
3421 uint32 count = 0;
3422
3423 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM item_script_names");
3424 if (result)
3425 {
3426 do
3427 {
3428 Field* fields = result->Fetch();
3429 uint32 itemId = fields[0].GetUInt32();
3430 ItemTemplate* itemTemplate = const_cast<ItemTemplate*>(GetItemTemplate(itemId));
3431 if (!itemTemplate)
3432 {
3433 TC_LOG_ERROR("sql.sql", "Item {} specified in `item_script_names` does not exist, skipped.", itemId);
3434 continue;
3435 }
3436
3437 itemTemplate->ScriptId = GetScriptId(fields[1].GetStringView());
3438 ++count;
3439 } while (result->NextRow());
3440 }
3441
3442 TC_LOG_INFO("server.loading", ">> Loaded {} item script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3443}
3444
3449
3451{
3452 uint32 oldMSTime = getMSTime();
3453
3454 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
3455
3456 uint32 count = 0;
3457
3458 // 0 1 2 3 4 5 6
3459 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_template_accessory`");
3460
3461 if (!result)
3462 {
3463 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
3464 return;
3465 }
3466
3467 do
3468 {
3469 Field* fields = result->Fetch();
3470
3471 uint32 entry = fields[0].GetUInt32();
3472 uint32 accessory = fields[1].GetUInt32();
3473 int8 seatId = fields[2].GetInt8();
3474 bool isMinion = fields[3].GetBool();
3475 uint8 summonType = fields[4].GetUInt8();
3476 uint32 summonTimer = fields[5].GetUInt32();
3477 Optional<uint32> rideSpellId = fields[6].GetUInt32OrNull();
3478
3479 if (rideSpellId && !sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
3480 {
3481 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: rideSpellId {} does not exist for entry {}.", *rideSpellId, entry);
3482 continue;
3483 }
3484
3485 if (!GetCreatureTemplate(entry))
3486 {
3487 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
3488 continue;
3489 }
3490
3491 if (!GetCreatureTemplate(accessory))
3492 {
3493 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", accessory);
3494 continue;
3495 }
3496
3497 if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
3498 {
3499 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} has no data in npc_spellclick_spells", entry);
3500 continue;
3501 }
3502
3503 _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer, rideSpellId));
3504
3505 ++count;
3506 }
3507 while (result->NextRow());
3508
3509 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3510}
3511
3513{
3514 uint32 oldMSTime = getMSTime();
3515
3516 _vehicleTemplateStore.clear();
3517
3518 // 0 1 2 3
3519 QueryResult result = WorldDatabase.Query("SELECT creatureId, despawnDelayMs, Pitch, CustomFlags FROM vehicle_template");
3520
3521 if (!result)
3522 {
3523 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template. DB table `vehicle_template` is empty.");
3524 return;
3525 }
3526
3527 do
3528 {
3529 Field* fields = result->Fetch();
3530
3531 uint32 creatureId = fields[0].GetUInt32();
3532
3533 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creatureId);
3534 if (!creatureInfo)
3535 {
3536 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) does not exist.", creatureId);
3537 continue;
3538 }
3539
3540 if (!creatureInfo->VehicleId)
3541 {
3542 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) is not a vehicle.", creatureId);
3543 continue;
3544 }
3545
3546 VehicleTemplate& vehicleTemplate = _vehicleTemplateStore[creatureId];
3547 vehicleTemplate.DespawnDelay = Milliseconds(fields[1].GetInt32());
3548 vehicleTemplate.Pitch = fields[2].GetFloatOrNull();
3549 vehicleTemplate.CustomFlags = VehicleCustomFlags(fields[3].GetInt32());
3550
3551 if (vehicleTemplate.DespawnDelay < 0ms)
3552 {
3553 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) has negative despawnDelayMs ({}).`. Ignoring",
3554 creatureId, vehicleTemplate.DespawnDelay.count());
3555 vehicleTemplate.DespawnDelay = 1ms;
3556 }
3557 else if (vehicleTemplate.DespawnDelay == 0ms)
3558 vehicleTemplate.DespawnDelay = 1ms;
3559
3560 if (vehicleTemplate.Pitch)
3561 {
3562 if (VehicleEntry const* vehicle = sVehicleStore.LookupEntry(creatureInfo->VehicleId))
3563 {
3564 if (*vehicleTemplate.Pitch < vehicle->PitchMin || *vehicleTemplate.Pitch > vehicle->PitchMax)
3565 {
3566 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Creature (Entry: {}) has invalid Pitch ({}).`. Ignoring",
3567 creatureId, *vehicleTemplate.Pitch);
3568 vehicleTemplate.Pitch.reset();
3569 }
3570 }
3571
3572 }
3573 } while (result->NextRow());
3574
3575 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template entries in {} ms", _vehicleTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3576}
3577
3579{
3580 uint32 oldMSTime = getMSTime();
3581
3582 _vehicleAccessoryStore.clear(); // needed for reload case
3583
3584 uint32 count = 0;
3585
3586 // 0 1 2 3 4 5 6
3587 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer`, `RideSpellID` FROM `vehicle_accessory`");
3588
3589 if (!result)
3590 {
3591 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle accessories. DB table `vehicle_accessory` is empty.");
3592 return;
3593 }
3594
3595 do
3596 {
3597 Field* fields = result->Fetch();
3598
3599 ObjectGuid::LowType uiGUID = fields[0].GetUInt64();
3600 uint32 uiAccessory = fields[1].GetUInt32();
3601 int8 uiSeat = int8(fields[2].GetInt16());
3602 bool bMinion = fields[3].GetBool();
3603 uint8 uiSummonType = fields[4].GetUInt8();
3604 uint32 uiSummonTimer= fields[5].GetUInt32();
3605 Optional<uint32> rideSpellId = fields[6].GetUInt32OrNull();
3606
3607 if (rideSpellId && !sSpellMgr->GetSpellInfo(*rideSpellId, DIFFICULTY_NONE))
3608 {
3609 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: rideSpellId {} does not exist for guid {}.", *rideSpellId, uiGUID);
3610 continue;
3611 }
3612
3613 if (!GetCreatureTemplate(uiAccessory))
3614 {
3615 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
3616 continue;
3617 }
3618
3619 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer, rideSpellId));
3620
3621 ++count;
3622 }
3623 while (result->NextRow());
3624
3625 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3626}
3627
3629{
3630 uint32 oldMSTime = getMSTime();
3631
3632 _vehicleSeatAddonStore.clear(); // needed for reload case
3633
3634 uint32 count = 0;
3635
3636 // 0 1 2 3 4 5 6
3637 QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
3638
3639 if (!result)
3640 {
3641 TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
3642 return;
3643 }
3644
3645 do
3646 {
3647 Field* fields = result->Fetch();
3648
3649 uint32 seatID = fields[0].GetUInt32();
3650 float orientation = fields[1].GetFloat();
3651 float exitX = fields[2].GetFloat();
3652 float exitY = fields[3].GetFloat();
3653 float exitZ = fields[4].GetFloat();
3654 float exitO = fields[5].GetFloat();
3655 uint8 exitParam = fields[6].GetUInt8();
3656
3657 if (!sVehicleSeatStore.LookupEntry(seatID))
3658 {
3659 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} does not exist in VehicleSeat.dbc. Skipping entry.", seatID);
3660 continue;
3661 }
3662
3663 // Sanitizing values
3664 if (orientation > float(M_PI * 2))
3665 {
3666 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid angle offset value ({}). Set Value to 0.", seatID, orientation);
3667 orientation = 0.0f;
3668 }
3669
3671 {
3672 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid exit parameter value ({}). Setting to 0 (none).", seatID, exitParam);
3673 continue;
3674 }
3675
3676 _vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
3677
3678 ++count;
3679 } while (result->NextRow());
3680
3681 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Seat Addon entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3682}
3683
3685{
3686 uint32 oldMSTime = getMSTime();
3687
3688 // 0 1 2 3 4 5 6 7 8 9
3689 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
3690
3691 if (!result)
3692 {
3693 TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
3694 return;
3695 }
3696
3697 uint32 count = 0;
3698
3699 do
3700 {
3701 Field* fields = result->Fetch();
3702
3703 uint32 creature_id = fields[0].GetUInt32();
3704 if (!GetCreatureTemplate(creature_id))
3705 {
3706 TC_LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
3707 continue;
3708 }
3709
3710 uint32 current_level = fields[1].GetUInt8();
3711 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3712 {
3713 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3714 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3715 else
3716 {
3717 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `pet_levelstats` table, ignoring.", current_level);
3718 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3719 }
3720 continue;
3721 }
3722 else if (current_level < 1)
3723 {
3724 TC_LOG_ERROR("sql.sql", "Wrong (<1) level {} in `pet_levelstats` table, ignoring.", current_level);
3725 continue;
3726 }
3727
3728 auto& pInfoMapEntry = _petInfoStore[creature_id];
3729 if (!pInfoMapEntry)
3730 pInfoMapEntry = std::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3731
3732 // data for level 1 stored in [0] array element, ...
3733 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
3734
3735 pLevelInfo->health = fields[2].GetUInt16();
3736 pLevelInfo->mana = fields[3].GetUInt16();
3737 pLevelInfo->armor = fields[9].GetUInt32();
3738
3739 for (uint8 i = 0; i < MAX_STATS; i++)
3740 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
3741
3742 ++count;
3743 }
3744 while (result->NextRow());
3745
3746 // Fill gaps and check integrity
3747 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
3748 {
3749 auto& pInfo = itr->second;
3750
3751 // fatal error if no level 1 data
3752 if (!pInfo || pInfo[0].health == 0)
3753 {
3754 TC_LOG_ERROR("sql.sql", "Creature {} does not have pet stats data for Level 1!", itr->first);
3755 ABORT();
3756 }
3757
3758 // fill level gaps
3759 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3760 {
3761 if (pInfo[level].health == 0)
3762 {
3763 TC_LOG_ERROR("sql.sql", "Creature {} has no data for Level {} pet stats data, using data of Level {}.", itr->first, level + 1, level);
3764 pInfo[level] = pInfo[level - 1];
3765 }
3766 }
3767 }
3768
3769 TC_LOG_INFO("server.loading", ">> Loaded {} level pet stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3770}
3771
3772PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
3773{
3774 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3775 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3776
3777 auto itr = _petInfoStore.find(creature_id);
3778 if (itr == _petInfoStore.end())
3779 return nullptr;
3780
3781 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
3782}
3783
3785{
3786 PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race_), Classes(class_) });
3787 if (!playerInfo)
3788 return;
3789
3790 if (count > 0)
3791 playerInfo->item.emplace_back(itemId, count);
3792 else
3793 {
3794 if (count < -1)
3795 TC_LOG_ERROR("sql.sql", "Invalid count {} specified on item {} be removed from original player create info (use -1)!", count, itemId);
3796
3797 PlayerCreateInfoItems& items = playerInfo->item;
3798
3799 auto erased = std::remove_if(items.begin(), items.end(), [itemId](PlayerCreateInfoItem const& item) { return item.item_id == itemId; });
3800 if (erased == items.end())
3801 {
3802 TC_LOG_ERROR("sql.sql", "Item {} specified to be removed from original create info not found in db2!", itemId);
3803 return;
3804 }
3805
3806 items.erase(erased, items.end());
3807 }
3808}
3809
3811{
3812 // Load playercreate
3813 {
3814 uint32 oldMSTime = getMSTime();
3815 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3816 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");
3817
3818 if (!result)
3819 {
3820 TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
3821 ABORT();
3822 }
3823 else
3824 {
3825 uint32 count = 0;
3826
3827 do
3828 {
3829 Field* fields = result->Fetch();
3830
3831 uint32 current_race = fields[0].GetUInt8();
3832 uint32 current_class = fields[1].GetUInt8();
3833 uint32 mapId = fields[2].GetUInt16();
3834 float positionX = fields[3].GetFloat();
3835 float positionY = fields[4].GetFloat();
3836 float positionZ = fields[5].GetFloat();
3837 float orientation = fields[6].GetFloat();
3838
3839 if (!sChrRacesStore.LookupEntry(current_race))
3840 {
3841 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3842 continue;
3843 }
3844
3845 if (!sChrClassesStore.LookupEntry(current_class))
3846 {
3847 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3848 continue;
3849 }
3850
3851 // accept DB data only for valid position (and non instanceable)
3852 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3853 {
3854 TC_LOG_ERROR("sql.sql", "Wrong home position for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3855 continue;
3856 }
3857
3858 if (sMapStore.LookupEntry(mapId)->Instanceable())
3859 {
3860 TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3861 continue;
3862 }
3863
3864 if (!sDB2Manager.GetChrModel(current_race, GENDER_MALE))
3865 {
3866 TC_LOG_ERROR("sql.sql", "Missing male model for race {}, ignoring.", current_race);
3867 continue;
3868 }
3869
3870 if (!sDB2Manager.GetChrModel(current_race, GENDER_FEMALE))
3871 {
3872 TC_LOG_ERROR("sql.sql", "Missing female model for race {}, ignoring.", current_race);
3873 continue;
3874 }
3875
3876 std::unique_ptr<PlayerInfo> info = std::make_unique<PlayerInfo>();
3877 info->createPosition.Loc.WorldRelocate(mapId, positionX, positionY, positionZ, orientation);
3878
3879 if (std::none_of(fields + 7, fields + 12, [](Field const& field) { return field.IsNull(); }))
3880 {
3881 info->createPositionNPE.emplace();
3882
3883 info->createPositionNPE->Loc.WorldRelocate(fields[7].GetUInt32(), fields[8].GetFloat(), fields[9].GetFloat(), fields[10].GetFloat(), fields[11].GetFloat());
3884 info->createPositionNPE->TransportGuid = fields[12].GetUInt64OrNull();
3885
3886 if (!sMapStore.LookupEntry(info->createPositionNPE->Loc.GetMapId()))
3887 {
3888 TC_LOG_ERROR("sql.sql", "Invalid NPE map id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3889 info->createPositionNPE->Loc.GetMapId(), current_class, current_race);
3890 info->createPositionNPE.reset();
3891 }
3892
3893 if (info->createPositionNPE && info->createPositionNPE->TransportGuid && !sTransportMgr->GetTransportSpawn(*info->createPositionNPE->TransportGuid))
3894 {
3895 TC_LOG_ERROR("sql.sql", "Invalid NPE transport spawn id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3896 *info->createPositionNPE->TransportGuid, current_class, current_race);
3897 info->createPositionNPE.reset(); // remove entire NPE data - assume user put transport offsets into npe_position fields
3898 }
3899 }
3900
3901 info->introMovieId = fields[13].GetUInt32OrNull();
3902 if (info->introMovieId && !sMovieStore.LookupEntry(*info->introMovieId))
3903 {
3904 TC_LOG_ERROR("sql.sql", "Invalid intro movie id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3905 *info->introMovieId, current_class, current_race);
3906 info->introMovieId.reset();
3907 }
3908
3909 info->introSceneId = fields[14].GetUInt32OrNull();
3910 if (info->introSceneId && !GetSceneTemplate(*info->introSceneId))
3911 {
3912 TC_LOG_ERROR("sql.sql", "Invalid intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3913 *info->introSceneId, current_class, current_race);
3914 info->introSceneId.reset();
3915 }
3916
3917 info->introSceneIdNPE = fields[15].GetUInt32OrNull();
3918 if (info->introSceneIdNPE && !GetSceneTemplate(*info->introSceneIdNPE))
3919 {
3920 TC_LOG_ERROR("sql.sql", "Invalid NPE intro scene id {} for class {} race {} pair in `playercreateinfo` table, ignoring.",
3921 *info->introSceneIdNPE, current_class, current_race);
3922 info->introSceneIdNPE.reset();
3923 }
3924
3925 _playerInfo[{ Races(current_race), Classes(current_class) }] = std::move(info);
3926
3927 ++count;
3928 }
3929 while (result->NextRow());
3930
3931 TC_LOG_INFO("server.loading", ">> Loaded {} player create definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3932 }
3933 }
3934
3935 // Load playercreate items
3936 TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
3937 {
3938 std::unordered_map<uint32, std::vector<ItemTemplate const*>> itemsByCharacterLoadout;
3939 for (CharacterLoadoutItemEntry const* characterLoadoutItem : sCharacterLoadoutItemStore)
3940 if (ItemTemplate const* itemTemplate = GetItemTemplate(characterLoadoutItem->ItemID))
3941 itemsByCharacterLoadout[characterLoadoutItem->CharacterLoadoutID].push_back(itemTemplate);
3942
3943 for (CharacterLoadoutEntry const* characterLoadout : sCharacterLoadoutStore)
3944 {
3945 if (!characterLoadout->IsForNewCharacter())
3946 continue;
3947
3948 std::vector<ItemTemplate const*> const* items = Trinity::Containers::MapGetValuePtr(itemsByCharacterLoadout, characterLoadout->ID);
3949 if (!items)
3950 continue;
3951
3952 for (ChrRacesEntry const* race : sChrRacesStore)
3953 {
3954 if (!characterLoadout->RaceMask.HasRace(race->ID))
3955 continue;
3956
3957 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(characterLoadout->ChrClassID) }))
3958 {
3959 playerInfo->itemContext = ItemContext(characterLoadout->ItemContext);
3960
3961 for (ItemTemplate const* itemTemplate : *items)
3962 {
3963 // BuyCount by default
3964 uint32 count = itemTemplate->GetBuyCount();
3965
3966 // special amount for food/drink
3967 if (itemTemplate->GetClass() == ITEM_CLASS_CONSUMABLE && itemTemplate->GetSubClass() == ITEM_SUBCLASS_FOOD_DRINK)
3968 {
3969 if (!itemTemplate->Effects.empty())
3970 {
3971 switch (itemTemplate->Effects[0]->SpellCategoryID)
3972 {
3973 case SPELL_CATEGORY_FOOD: // food
3974 count = characterLoadout->ChrClassID == CLASS_DEATH_KNIGHT ? 10 : 4;
3975 break;
3976 case SPELL_CATEGORY_DRINK: // drink
3977 count = 2;
3978 break;
3979 }
3980 }
3981 if (itemTemplate->GetMaxStackSize() < count)
3982 count = itemTemplate->GetMaxStackSize();
3983 }
3984
3985 playerInfo->item.emplace_back(itemTemplate->GetId(), count);
3986 }
3987 }
3988 }
3989 }
3990 }
3991
3992 TC_LOG_INFO("server.loading", "Loading Player Create Items Override Data...");
3993 {
3994 uint32 oldMSTime = getMSTime();
3995 // 0 1 2 3
3996 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
3997
3998 if (!result)
3999 {
4000 TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
4001 }
4002 else
4003 {
4004 uint32 count = 0;
4005
4006 do
4007 {
4008 Field* fields = result->Fetch();
4009
4010 uint32 current_race = fields[0].GetUInt8();
4011 if (current_race && !sChrRacesStore.HasRecord(current_race))
4012 {
4013 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
4014 continue;
4015 }
4016
4017 uint32 current_class = fields[1].GetUInt8();
4018 if (current_class && !sChrClassesStore.HasRecord(current_class))
4019 {
4020 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_item` table, ignoring.", current_class);
4021 continue;
4022 }
4023
4024 uint32 item_id = fields[2].GetUInt32();
4025
4026 if (!GetItemTemplate(item_id))
4027 {
4028 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);
4029 continue;
4030 }
4031
4032 int32 amount = fields[3].GetInt8();
4033
4034 if (!amount)
4035 {
4036 TC_LOG_ERROR("sql.sql", "Item id {} (class {} race {}) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
4037 continue;
4038 }
4039
4040 if (!current_race || !current_class)
4041 {
4042 uint32 min_race = current_race ? current_race : 1;
4043 uint32 max_race = current_race ? current_race + 1 : sChrRacesStore.GetNumRows();
4044 uint32 min_class = current_class ? current_class : 1;
4045 uint32 max_class = current_class ? current_class + 1 : sChrClassesStore.GetNumRows();
4046 for (uint32 r = min_race; r < max_race; ++r)
4047 for (uint32 c = min_class; c < max_class; ++c)
4048 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
4049 }
4050 else
4051 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
4052
4053 ++count;
4054 }
4055 while (result->NextRow());
4056
4057 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4058 }
4059 }
4060
4061 // Load playercreate skills
4062 TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
4063 {
4064 uint32 oldMSTime = getMSTime();
4065
4067 if (rcInfo->Availability == 1)
4068 for (ChrRacesEntry const* race : sChrRacesStore)
4069 if (rcInfo->RaceMask.IsEmpty() || rcInfo->RaceMask.HasRace(race->ID))
4070 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4071 if (rcInfo->ClassMask == -1 || rcInfo->ClassMask == 0 || ((1 << (classIndex - 1)) & rcInfo->ClassMask))
4072 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(classIndex) }))
4073 playerInfo->skills.push_back(rcInfo);
4074
4075 TC_LOG_INFO("server.loading", ">> Loaded player create skills in {} ms", GetMSTimeDiffToNow(oldMSTime));
4076 }
4077
4078 // Load playercreate custom spells
4079 TC_LOG_INFO("server.loading", "Loading Player Create Custom Spell Data...");
4080 {
4081 uint32 oldMSTime = getMSTime();
4082
4083 QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
4084
4085 if (!result)
4086 {
4087 TC_LOG_INFO("server.loading", ">> Loaded 0 player create custom spells. DB table `playercreateinfo_spell_custom` is empty.");
4088 }
4089 else
4090 {
4091 uint32 count = 0;
4092
4093 do
4094 {
4095 Field* fields = result->Fetch();
4096 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4097 uint32 classMask = fields[1].GetUInt32();
4098 uint32 spellId = fields[2].GetUInt32();
4099
4100 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4101 {
4102 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask.RawValue);
4103 continue;
4104 }
4105
4106 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4107 {
4108 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_spell_custom` table, ignoring.", classMask);
4109 continue;
4110 }
4111
4112 for (ChrRacesEntry const* race : sChrRacesStore)
4113 {
4114 if (raceMask.IsEmpty() || raceMask.HasRace(race->ID))
4115 {
4116 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4117 {
4118 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4119 {
4120 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, {Races(race->ID), Classes(classIndex)}))
4121 {
4122 playerInfo->customSpells.push_back(spellId);
4123 ++count;
4124 }
4125 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
4126 // Either split the masks per class, or per race, which kind of kills the point yet.
4127 // else if (raceMask != 0 && classMask != 0)
4128 // 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);
4129 }
4130 }
4131 }
4132 }
4133 }
4134 while (result->NextRow());
4135
4136 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4137 }
4138 }
4139
4140 // Load playercreate cast spell
4141 TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
4142 {
4143 uint32 oldMSTime = getMSTime();
4144
4145 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell, createMode FROM playercreateinfo_cast_spell");
4146
4147 if (!result)
4148 TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
4149 else
4150 {
4151 uint32 count = 0;
4152
4153 do
4154 {
4155 Field* fields = result->Fetch();
4156 Trinity::RaceMask<uint64> raceMask = { fields[0].GetUInt64() };
4157 uint32 classMask = fields[1].GetUInt32();
4158 uint32 spellId = fields[2].GetUInt32();
4159 int8 playerCreateMode = fields[3].GetInt8();
4160
4161 if (!raceMask.IsEmpty() && (raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
4162 {
4163 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask.RawValue);
4164 continue;
4165 }
4166
4167 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4168 {
4169 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_cast_spell` table, ignoring.", classMask);
4170 continue;
4171 }
4172
4173 if (playerCreateMode < 0 || playerCreateMode >= AsUnderlyingType(PlayerCreateMode::Max))
4174 {
4175 TC_LOG_ERROR("sql.sql", "Uses invalid createMode {} in `playercreateinfo_cast_spell` table, ignoring.", playerCreateMode);
4176 continue;
4177 }
4178
4179 for (ChrRacesEntry const* race : sChrRacesStore)
4180 {
4181 if (raceMask.IsEmpty() || raceMask.HasRace(race->ID))
4182 {
4183 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4184 {
4185 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4186 {
4187 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race->ID), Classes(classIndex) }))
4188 {
4189 playerInfo->castSpells[playerCreateMode].push_back(spellId);
4190 ++count;
4191 }
4192 }
4193 }
4194 }
4195 }
4196 } while (result->NextRow());
4197
4198 TC_LOG_INFO("server.loading", ">> Loaded {} player create cast spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4199 }
4200 }
4201
4202 // Load playercreate actions
4203 TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
4204 {
4205 uint32 oldMSTime = getMSTime();
4206
4207 // 0 1 2 3 4
4208 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
4209
4210 if (!result)
4211 {
4212 TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
4213 }
4214 else
4215 {
4216 uint32 count = 0;
4217
4218 do
4219 {
4220 Field* fields = result->Fetch();
4221
4222 uint32 current_race = fields[0].GetUInt8();
4223 if (!sChrRacesStore.HasRecord(current_race))
4224 {
4225 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
4226 continue;
4227 }
4228
4229 uint32 current_class = fields[1].GetUInt8();
4230 if (!sChrClassesStore.HasRecord(current_class))
4231 {
4232 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_action` table, ignoring.", current_class);
4233 continue;
4234 }
4235
4236 if (auto const& playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(current_race), Classes(current_class) }))
4237 playerInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
4238
4239 ++count;
4240 }
4241 while (result->NextRow());
4242
4243 TC_LOG_INFO("server.loading", ">> Loaded {} player create actions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4244 }
4245 }
4246
4247 // Loading levels data (class/race dependent)
4248 TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
4249 {
4250 struct RaceStats
4251 {
4252 Races Race;
4253 std::array<int16, MAX_STATS> StatModifier = { };
4254
4255 explicit RaceStats(Races race) : Race(race) { }
4256 std::strong_ordering operator<=>(RaceStats const& right) const { return Race <=> right.Race; }
4257 bool operator==(RaceStats const& right) const { return Race == right.Race; }
4258 };
4259
4261
4262 uint32 oldMSTime = getMSTime();
4263
4264 QueryResult raceStatsResult = WorldDatabase.Query("SELECT race, str, agi, sta, inte, spi FROM player_racestats");
4265
4266 if (!raceStatsResult)
4267 {
4268 TC_LOG_ERROR("server.loading", ">> Loaded 0 race stats definitions. DB table `player_racestats` is empty.");
4269 ABORT();
4270 }
4271
4272 do
4273 {
4274 Field* fields = raceStatsResult->Fetch();
4275
4276 uint32 current_race = fields[0].GetUInt8();
4277 if (!sChrRacesStore.HasRecord(current_race))
4278 {
4279 TC_LOG_ERROR("sql.sql", "Wrong race {} in `player_racestats` table, ignoring.", current_race);
4280 continue;
4281 }
4282
4283 RaceStats& stats = *raceStatModifiers.emplace(Races(current_race)).first;
4284 for (uint32 i = 0; i < MAX_STATS; ++i)
4285 stats.StatModifier[i] = fields[i + 1].GetInt16();
4286
4287 } while (raceStatsResult->NextRow());
4288
4289 // 0 1 2 3 4 5 6
4290 QueryResult result = WorldDatabase.Query("SELECT class, level, str, agi, sta, inte, spi FROM player_classlevelstats");
4291
4292 if (!result)
4293 {
4294 TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_classlevelstats` is empty.");
4295 ABORT();
4296 }
4297
4298 uint32 count = 0;
4299
4300 do
4301 {
4302 Field* fields = result->Fetch();
4303
4304 uint32 current_class = fields[0].GetUInt8();
4305 if (!sChrClassesStore.HasRecord(current_class))
4306 {
4307 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
4308 continue;
4309 }
4310
4311 uint32 current_level = fields[1].GetUInt8();
4312 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4313 {
4314 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4315 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_classlevelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4316 else
4317 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
4318
4319 continue;
4320 }
4321
4322 for (RaceStats const& raceStats : raceStatModifiers)
4323 {
4324 if (PlayerInfo* playerInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { raceStats.Race, Classes(current_class) }))
4325 {
4326 if (!playerInfo->levelInfo)
4327 playerInfo->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4328
4329 PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1];
4330 for (uint8 i = 0; i < MAX_STATS; ++i)
4331 levelInfo.stats[i] = fields[i + 2].GetInt32() + raceStats.StatModifier[i];
4332 }
4333 }
4334
4335 ++count;
4336 }
4337 while (result->NextRow());
4338
4339 // Fill gaps and check integrity
4340 for (auto const& [raceClass, playerInfo] : _playerInfo)
4341 {
4342 auto [race, class_] = raceClass;
4343
4344 // fatal error if no level 1 data
4345 if (!playerInfo->levelInfo || playerInfo->levelInfo[0].stats[0] == 0)
4346 {
4347 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level 1 does not have stats data!", race, class_);
4348 ABORT();
4349 }
4350
4351 // fill level gaps
4352 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4353 {
4354 if (playerInfo->levelInfo[level].stats[0] == 0)
4355 {
4356 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level {} does not have stats data. Using stats data of level {}.", race, class_, level + 1, level);
4357 playerInfo->levelInfo[level] = playerInfo->levelInfo[level - 1];
4358 }
4359 }
4360 }
4361
4362 TC_LOG_INFO("server.loading", ">> Loaded {} level stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4363 }
4364
4365 // Loading xp per level data
4366 TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
4367 {
4368 uint32 oldMSTime = getMSTime();
4369
4370 _playerXPperLevel.resize(sXpGameTable.GetTableRowCount(), 0);
4371
4372 // 0 1
4373 QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
4374
4375 // load the DBC's levels at first...
4376 for (uint32 level = 1; level < sXpGameTable.GetTableRowCount(); ++level)
4377 _playerXPperLevel[level] = sXpGameTable.GetRow(level)->Total;
4378
4379 uint32 count = 0;
4380
4381 // ...overwrite if needed (custom values)
4382 if (result)
4383 {
4384 do
4385 {
4386 Field* fields = result->Fetch();
4387
4388 uint32 current_level = fields[0].GetUInt8();
4389 uint32 current_xp = fields[1].GetUInt32();
4390
4391 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4392 {
4393 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4394 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4395 else
4396 {
4397 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_xp_for_level` table, ignoring.", current_level);
4398 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4399 }
4400 continue;
4401 }
4402 //PlayerXPperLevel
4403 _playerXPperLevel[current_level] = current_xp;
4404 ++count;
4405 } while (result->NextRow());
4406 }
4407
4408 // fill level gaps - only accounting levels > MAX_LEVEL
4409 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4410 {
4411 if (_playerXPperLevel[level] == 0)
4412 {
4413 TC_LOG_ERROR("sql.sql", "Level {} does not have XP for level data. Using data of level [{}] + 12000.", level + 1, level);
4414 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 12000;
4415 }
4416 }
4417
4418 TC_LOG_INFO("server.loading", ">> Loaded {} xp for level definition(s) from database in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4419 }
4420}
4421
4422void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32& baseMana) const
4423{
4424 if (level < 1 || class_ >= MAX_CLASSES)
4425 return;
4426
4427 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4428 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
4429
4430 GtBaseMPEntry const* mp = sBaseMPGameTable.GetRow(level);
4431 if (!mp)
4432 {
4433 TC_LOG_ERROR("misc", "Tried to get non-existant Class-Level combination data for base hp/mp. Class {} Level {}", class_, level);
4434 return;
4435 }
4436
4437 baseMana = uint32(GetGameTableColumnForClass(mp, class_));
4438}
4439
4441{
4442 if (level < 1)
4443 return;
4444
4445 PlayerInfo const* pInfo = Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
4446 if (!pInfo)
4447 return;
4448
4449 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4450 *info = pInfo->levelInfo[level - 1];
4451 else
4452 BuildPlayerLevelInfo(race, class_, level, info);
4453}
4454
4456{
4457 // base data (last known level)
4458 *info = ASSERT_NOTNULL(Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(_class) }))->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
4459
4460 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
4461 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
4462 {
4463 switch (_class)
4464 {
4465 case CLASS_WARRIOR:
4466 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4467 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4468 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4469 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4470 break;
4471 case CLASS_PALADIN:
4472 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
4473 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4474 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
4475 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
4476 break;
4477 case CLASS_HUNTER:
4478 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
4479 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4480 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4481 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
4482 break;
4483 case CLASS_ROGUE:
4484 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
4485 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4486 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
4487 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
4488 break;
4489 case CLASS_PRIEST:
4490 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4491 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4492 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
4493 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
4494 break;
4495 case CLASS_SHAMAN:
4496 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4497 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4498 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
4499 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
4500 break;
4501 case CLASS_MAGE:
4502 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4503 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4504 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4505 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
4506 break;
4507 case CLASS_WARLOCK:
4508 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4509 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4510 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4511 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4512 break;
4513 case CLASS_DRUID:
4514 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
4515 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
4516 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
4517 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
4518 break;
4519 }
4520 }
4521}
4522
4523std::vector<uint32> const* ObjectMgr::GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
4524{
4525 if (std::vector<uint32> const* items = Trinity::Containers::MapGetValuePtr(_creatureQuestItemStore, { creatureEntry, difficulty }))
4526 return items;
4527
4528 // If there is no data for the difficulty, try to get data for the fallback difficulty
4529 if (DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty))
4530 return GetCreatureQuestItemList(creatureEntry, Difficulty(difficultyEntry->FallbackDifficultyID));
4531
4532 return nullptr;
4533}
4534
4535std::vector<int32> const* ObjectMgr::GetCreatureQuestCurrencyList(uint32 creatureId) const
4536{
4538}
4539
4541{
4542 uint32 oldMSTime = getMSTime();
4543
4544 _questTemplates.clear();
4546 _questObjectives.clear();
4547
4548 _exclusiveQuestGroups.clear();
4549
4550 QueryResult result = WorldDatabase.Query("SELECT "
4551 "ID, QuestType, QuestPackageID, ContentTuningID, QuestSortID, QuestInfoID, SuggestedGroupNum, RewardNextQuest, RewardXPDifficulty, RewardXPMultiplier, "
4552 "RewardMoneyDifficulty, RewardMoneyMultiplier, RewardBonusMoney, RewardSpell, RewardHonor, RewardKillHonor, RewardFavor, StartItem, "
4553 "RewardArtifactXPDifficulty, RewardArtifactXPMultiplier, RewardArtifactCategoryID, Flags, FlagsEx, FlagsEx2, FlagsEx3, "
4554 "RewardItem1, RewardAmount1, ItemDrop1, ItemDropQuantity1, RewardItem2, RewardAmount2, ItemDrop2, ItemDropQuantity2, "
4555 "RewardItem3, RewardAmount3, ItemDrop3, ItemDropQuantity3, RewardItem4, RewardAmount4, ItemDrop4, ItemDropQuantity4, "
4556 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemDisplayID1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemDisplayID2, "
4557 "RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemDisplayID3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemDisplayID4, "
4558 "RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemDisplayID5, RewardChoiceItemID6, RewardChoiceItemQuantity6, RewardChoiceItemDisplayID6, "
4559 "POIContinent, POIx, POIy, POIPriority, RewardTitle, RewardArenaPoints, RewardSkillLineID, RewardNumSkillUps, "
4560 "PortraitGiver, PortraitGiverMount, PortraitGiverModelSceneID, PortraitTurnIn, "
4561 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionCapIn1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionCapIn2, "
4562 "RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionCapIn3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionCapIn4, "
4563 "RewardFactionID5, RewardFactionValue5, RewardFactionOverride5, RewardFactionCapIn5, RewardFactionFlags, "
4564 "RewardCurrencyID1, RewardCurrencyQty1, RewardCurrencyID2, RewardCurrencyQty2, RewardCurrencyID3, RewardCurrencyQty3, RewardCurrencyID4, RewardCurrencyQty4, "
4565 "AcceptedSoundKitID, CompleteSoundKitID, AreaGroupID, TimeAllowed, AllowableRaces, ResetByScheduler, Expansion, ManagedWorldStateID, QuestSessionBonus, "
4566 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog "
4567 "FROM quest_template");
4568 if (!result)
4569 {
4570 TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
4571 return;
4572 }
4573
4574 _questTemplates.reserve(result->GetRowCount());
4575
4576 // create multimap previous quest for each existed quest
4577 // some quests can have many previous maps set by NextQuestId in previous quest
4578 // for example set of race quests can lead to single not race specific quest
4579 do
4580 {
4581 Field* fields = result->Fetch();
4582
4583 uint32 questId = fields[0].GetUInt32();
4584 auto itr = _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(new Quest(result))).first;
4585 itr->second->_weakRef = itr->second;
4586 if (itr->second->IsAutoPush())
4587 _questTemplatesAutoPush.push_back(itr->second.get());
4588 } while (result->NextRow());
4589
4590 struct QuestLoaderHelper
4591 {
4592 typedef void(Quest::* QuestLoaderFunction)(Field* fields);
4593
4594 char const* QueryFields;
4595 char const* TableName;
4596 char const* QueryExtra;
4597 char const* TableDesc;
4598 QuestLoaderFunction LoaderFunction;
4599 };
4600
4601 // QuestID needs to be fields[0]
4602 QuestLoaderHelper const QuestLoaderHelpers[] =
4603 {
4604 // 0 1 2 3 4 5 6
4605 { "QuestID, Type1, Type2, Type3, Type4, Type5, Type6", "quest_reward_choice_items", "", "reward choice items", &Quest::LoadRewardChoiceItems },
4606
4607 // 0 1 2 3
4608 { "QuestID, SpellID, PlayerConditionID, Type", "quest_reward_display_spell", "ORDER BY QuestID ASC, Idx ASC", "reward display spells", &Quest::LoadRewardDisplaySpell },
4609
4610 // 0 1 2 3 4 5 6 7 8
4611 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4", "quest_details", "", "details", &Quest::LoadQuestDetails },
4612
4613 // 0 1 2 3 4 5
4614 { "ID, EmoteOnComplete, EmoteOnIncomplete, EmoteOnCompleteDelay, EmoteOnIncompleteDelay, CompletionText", "quest_request_items", "", "request items", &Quest::LoadQuestRequestItems },
4615
4616 // 0 1 2 3 4 5 6 7 8 9
4617 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "", "reward emotes", &Quest::LoadQuestOfferReward },
4618
4619 // 0 1 2 3 4 5 6 7 8 9
4620 { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
4621 // 10 11 12 13 14 15 16 17
4622 " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags,"
4623 // 18
4624 " ScriptName", "quest_template_addon", "", "template addons", &Quest::LoadQuestTemplateAddon },
4625
4626 // 0 1
4627 { "QuestId, RewardMailSenderEntry", "quest_mail_sender", "", "mail sender entries", &Quest::LoadQuestMailSender },
4628
4629 // 0 1 2 3 4 5 6 7 8 9 10 11 12
4630 { "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, "
4631 // 13 14 15 16 17
4632 "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 },
4633
4634 // 0 1 2 3 4
4635 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_description_conditional", "ORDER BY OrderIndex", "conditional details", &Quest::LoadConditionalConditionalQuestDescription },
4636
4637 // 0 1 2 3 4
4638 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_request_items_conditional", "ORDER BY OrderIndex", "conditional request items", &Quest::LoadConditionalConditionalRequestItemsText },
4639
4640 // 0 1 2 3 4
4641 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_offer_reward_conditional", "ORDER BY OrderIndex", "conditional reward", &Quest::LoadConditionalConditionalOfferRewardText },
4642
4643 // 0 1 2 3 4
4644 { "QuestId, PlayerConditionId, QuestgiverCreatureId, Text, locale", "quest_completion_log_conditional", "ORDER BY OrderIndex", "conditional completion log", &Quest::LoadConditionalConditionalQuestCompletionLog },
4645
4646 // 0 1
4647 { "QuestID, TreasurePickerID", "quest_treasure_pickers", "ORDER BY OrderIndex", "treasure pickers", &Quest::LoadTreasurePickers },
4648
4649 // 0 1
4650 { "QuestID, HouseRoomID", "quest_reward_house_room", "ORDER BY OrderIndex", "house room rewards", &Quest::LoadRewardHouseRoom },
4651
4652 // 0 1
4653 { "QuestID, HouseDecorID", "quest_reward_house_decor", "ORDER BY OrderIndex", "house decor rewards", &Quest::LoadRewardHouseDecor }
4654 };
4655
4656 for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
4657 {
4658 result = WorldDatabase.PQuery("SELECT {} FROM {} {}", loader.QueryFields, loader.TableName, loader.QueryExtra);
4659
4660 if (!result)
4661 TC_LOG_INFO("server.loading", ">> Loaded 0 quest {}. DB table `{}` is empty.", loader.TableDesc, loader.TableName);
4662 else
4663 {
4664 do
4665 {
4666 Field* fields = result->Fetch();
4667 uint32 questId = fields[0].GetUInt32();
4668
4669 auto itr = _questTemplates.find(questId);
4670 if (itr != _questTemplates.end())
4671 (itr->second.get()->*loader.LoaderFunction)(fields);
4672 else
4673 TC_LOG_ERROR("sql.sql", "Table `{}` has data for quest {} but such quest does not exist", loader.TableName, questId);
4674 } while (result->NextRow());
4675 }
4676 }
4677
4678 // 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)
4679 // 0 1 2 3 4
4680 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");
4681
4682 if (!result)
4683 TC_LOG_INFO("server.loading", ">> Loaded 0 quest visual effects. DB table `quest_visual_effect` is empty.");
4684 else
4685 {
4686 do
4687 {
4688 Field* fields = result->Fetch();
4689 uint32 vID = fields[0].GetUInt32();
4690 uint32 oID = fields[1].GetUInt32();
4691
4692 if (!vID)
4693 {
4694 TC_LOG_ERROR("sql.sql", "Table `quest_visual_effect` has visual effect for null objective id");
4695 continue;
4696 }
4697
4698 // objID will be null if match for table join is not found
4699 if (vID != oID)
4700 {
4701 TC_LOG_ERROR("sql.sql", "Table `quest_visual_effect` has visual effect for objective {} but such objective does not exist.", vID);
4702 continue;
4703 }
4704
4705 uint32 questId = fields[2].GetUInt32();
4706
4707 // Do not throw error here because error for non existing quest is thrown while loading quest objectives. we do not need duplication
4708 auto itr = _questTemplates.find(questId);
4709 if (itr != _questTemplates.end())
4710 itr->second->LoadQuestObjectiveVisualEffect(fields);
4711 } while (result->NextRow());
4712 }
4713
4714 std::map<uint32, uint32> usedMailTemplates;
4715
4716 // Post processing
4717 for (auto& questPair : _questTemplates)
4718 {
4719 // skip post-loading checks for disabled quests
4720 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
4721 continue;
4722
4723 Quest* qinfo = questPair.second.get();
4724
4725 // additional quest integrity checks (GO, creature_template and items must be loaded already)
4726
4728 TC_LOG_ERROR("sql.sql", "Quest {} has `Method` = {}, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestType());
4729
4731 {
4732 TC_LOG_ERROR("sql.sql", "Quest {} has `SpecialFlags` = {} > max allowed value. Correct `SpecialFlags` to value <= {}",
4735 }
4736
4737 if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
4738 {
4739 TC_LOG_ERROR("sql.sql", "Weekly Quest {} is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
4740 qinfo->_flags &= ~QUEST_FLAGS_DAILY;
4741 }
4742
4743 if (qinfo->_flags & QUEST_FLAGS_DAILY)
4744 {
4746 {
4747 TC_LOG_DEBUG("sql.sql", "Daily Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4749 }
4750 }
4751
4752 if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
4753 {
4755 {
4756 TC_LOG_DEBUG("sql.sql", "Weekly Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4758 }
4759 }
4760
4762 {
4764 {
4765 TC_LOG_DEBUG("sql.sql", "Monthly quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4767 }
4768 }
4769
4771 {
4772 // at auto-reward can be rewarded only RewardChoiceItemId[0]
4773 for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
4774 {
4775 if (uint32 id = qinfo->RewardChoiceItemId[j])
4776 {
4777 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item from `RewardChoiceItemId{}` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
4778 qinfo->GetQuestId(), j + 1, id, j + 1);
4779 // no changes, quest ignore this data
4780 }
4781 }
4782 }
4783
4784 if (qinfo->_contentTuningID && !sContentTuningStore.LookupEntry(qinfo->_contentTuningID))
4785 {
4786 TC_LOG_ERROR("sql.sql", "Quest {} has `ContentTuningID` = {} but content tuning with this id does not exist.",
4787 qinfo->GetQuestId(), qinfo->_contentTuningID);
4788 }
4789
4790 // client quest log visual (area case)
4791 if (qinfo->_questSortID > 0)
4792 {
4793 if (!sAreaTableStore.LookupEntry(qinfo->_questSortID))
4794 {
4795 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (zone case) but zone with this id does not exist.",
4796 qinfo->GetQuestId(), qinfo->_questSortID);
4797 // no changes, quest not dependent from this value but can have problems at client
4798 }
4799 }
4800 // client quest log visual (sort case)
4801 if (qinfo->_questSortID < 0)
4802 {
4803 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_questSortID));
4804 if (!qSort)
4805 {
4806 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (sort case) but quest sort with this id does not exist.",
4807 qinfo->GetQuestId(), qinfo->_questSortID);
4808 // 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)
4809 }
4810 //check for proper RequiredSkillId value (skill case)
4811 if (uint32 skill_id = SkillByQuestSort(-int32(qinfo->_questSortID)))
4812 {
4813 if (qinfo->_requiredSkillId != skill_id)
4814 {
4815 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} but `RequiredSkillId` does not have a corresponding value ({}).",
4816 qinfo->GetQuestId(), qinfo->_questSortID, skill_id);
4817 //override, and force proper value here?
4818 }
4819 }
4820 }
4821
4822 // AllowableClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
4823 if (qinfo->_allowableClasses)
4824 {
4826 {
4827 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable classes in `AllowableClasses` ({}), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_allowableClasses);
4828 qinfo->_allowableClasses = 0;
4829 }
4830 }
4831 // AllowableRaces, can be -1/RACEMASK_ALL_PLAYABLE to allow any race
4832 if (qinfo->_allowableRaces != RACEMASK_ALL_v<std::array<int32, 2>>)
4833 {
4834 if (!qinfo->_allowableRaces.IsEmpty() && (qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE_v<std::array<int32, 2>>).IsEmpty())
4835 {
4836 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` (0x{:X}{:08X}), value set to -1 (all races).",
4837 qinfo->GetQuestId(), qinfo->_allowableRaces.RawValue[1], qinfo->_allowableRaces.RawValue[0]);
4838 qinfo->_allowableRaces = RACEMASK_ALL_v<std::array<int32, 2>>;
4839 }
4840 }
4841 // RequiredSkillId, can be 0
4842 if (qinfo->_requiredSkillId)
4843 {
4844 if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
4845 {
4846 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillId` = {} but this skill does not exist",
4847 qinfo->GetQuestId(), qinfo->_requiredSkillId);
4848 }
4849 }
4850
4851 if (qinfo->_requiredSkillPoints)
4852 {
4853 if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
4854 {
4855 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillPoints` = {} but max possible skill is {}, quest can't be done.",
4856 qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
4857 // no changes, quest can't be done for this requirement
4858 }
4859 }
4860 // else Skill quests can have 0 skill level, this is ok
4861
4862 if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
4863 {
4864 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4866 // no changes, quest can't be done for this requirement
4867 }
4868
4869 if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
4870 {
4871 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4873 // no changes, quest can't be done for this requirement
4874 }
4875
4877 {
4878 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but max reputation is {}, quest can't be done.",
4880 // no changes, quest can't be done for this requirement
4881 }
4882
4884 {
4885 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} and `RequiredMinRepValue` = {}, quest can't be done.",
4886 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
4887 // no changes, quest can't be done for this requirement
4888 }
4889
4890 if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
4891 {
4892 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but `RequiredMinRepFaction` is 0, value has no effect",
4893 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
4894 // warning
4895 }
4896
4897 if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
4898 {
4899 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} but `RequiredMaxRepFaction` is 0, value has no effect",
4900 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
4901 // warning
4902 }
4903
4904 if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
4905 {
4906 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardTitleId` = {} but CharTitle Id {} does not exist, quest can't be rewarded with title.",
4907 qinfo->GetQuestId(), qinfo->_rewardTitleId, qinfo->_rewardTitleId);
4908 qinfo->_rewardTitleId = 0;
4909 // quest can't reward this title
4910 }
4911
4912 if (qinfo->_sourceItemId)
4913 {
4914 if (!GetItemTemplate(qinfo->_sourceItemId))
4915 {
4916 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = {} but item with entry {} does not exist, quest can't be done.",
4917 qinfo->GetQuestId(), qinfo->_sourceItemId, qinfo->_sourceItemId);
4918 qinfo->_sourceItemId = 0; // quest can't be done for this requirement
4919 }
4920 else if (qinfo->_sourceItemIdCount == 0)
4921 {
4922 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but `ProvidedItemCount` = 0, set to 1 but need fix in DB.",
4923 qinfo->GetQuestId(), qinfo->_sourceItemId);
4924 qinfo->_sourceItemIdCount = 1; // update to 1 for allow quest work for backward compatibility with DB
4925 }
4926 }
4927 else if (qinfo->_sourceItemIdCount > 0)
4928 {
4929 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceItemId` = 0 but `SourceItemIdCount` = {}, useless value.",
4930 qinfo->GetQuestId(), qinfo->_sourceItemIdCount);
4931 qinfo->_sourceItemIdCount = 0; // no quest work changes in fact
4932 }
4933
4934 if (qinfo->_sourceSpellID)
4935 {
4936 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellID, DIFFICULTY_NONE);
4937 if (!spellInfo)
4938 {
4939 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} doesn't exist, quest can't be done.",
4940 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4941 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4942 }
4943 else if (!SpellMgr::IsSpellValid(spellInfo))
4944 {
4945 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} is broken, quest can't be done.",
4946 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4947 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4948 }
4949 }
4950
4951 for (QuestObjective const& obj : qinfo->GetObjectives())
4952 {
4953 // Store objective for lookup by id
4954 _questObjectives[obj.ID] = &obj;
4955
4956 // Check storage index for objectives which store data
4957 if (obj.IsStoringValue() && obj.StorageIndex < 0)
4958 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid StorageIndex = {} for objective type {}", qinfo->GetQuestId(), obj.ID, obj.StorageIndex, obj.Type);
4959
4960 switch (obj.Type)
4961 {
4963 if (!GetItemTemplate(obj.ObjectID))
4964 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing item entry {}, quest can't be done.",
4965 qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4966 break;
4968 if (!GetCreatureTemplate(obj.ObjectID))
4969 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4970 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4971 break;
4973 if (!GetGameObjectTemplate(obj.ObjectID))
4974 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing gameobject entry {}, quest can't be done.",
4975 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4976 break;
4978 if (!GetCreatureTemplate(obj.ObjectID))
4979 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
4980 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
4981 break;
4985 if (!sFactionStore.LookupEntry(obj.ObjectID))
4986 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing faction id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4987 break;
4989 if (obj.Amount <= 0)
4990 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid player kills count {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
4991 break;
4995 if (!sCurrencyTypesStore.LookupEntry(obj.ObjectID))
4996 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing currency {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
4997 if (obj.Amount <= 0)
4998 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has invalid currency amount {}", qinfo->GetQuestId(), obj.ID, obj.Amount);
4999 break;
5001 if (!sSpellMgr->GetSpellInfo(obj.ObjectID, DIFFICULTY_NONE))
5002 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing spell id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5003 break;
5005 if (obj.ObjectID && !GetCreatureTemplate(obj.ObjectID))
5006 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing creature entry {}, quest can't be done.",
5007 qinfo->GetQuestId(), obj.ID, uint32(obj.ObjectID));
5008 break;
5010 if (!sBattlePetSpeciesStore.LookupEntry(obj.ObjectID))
5011 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing battlepet species id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5012 break;
5014 if (!sCriteriaTreeStore.LookupEntry(obj.ObjectID))
5015 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing criteria tree id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5016 break;
5018 if (!sAreaTriggerStore.LookupEntry(uint32(obj.ObjectID)) && obj.ObjectID != -1)
5019 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing AreaTrigger.db2 id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5020 break;
5023 if (!sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), false }) && !sAreaTriggerDataStore->GetAreaTriggerTemplate({ uint32(obj.ObjectID), true }))
5024 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has non existing areatrigger id {}", qinfo->GetQuestId(), obj.ID, obj.ObjectID);
5025 break;
5030 break;
5031 default:
5032 TC_LOG_ERROR("sql.sql", "Quest {} objective {} has unhandled type {}", qinfo->GetQuestId(), obj.ID, obj.Type);
5033 break;
5034 }
5035
5036 if (obj.Flags & QUEST_OBJECTIVE_FLAG_SEQUENCED)
5038 }
5039
5040 for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
5041 {
5042 uint32 id = qinfo->ItemDrop[j];
5043 if (id)
5044 {
5045 if (!GetItemTemplate(id))
5046 {
5047 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
5048 qinfo->GetQuestId(), j+1, id, id);
5049 // no changes, quest can't be done for this requirement
5050 }
5051 }
5052 else
5053 {
5054 if (qinfo->ItemDropQuantity[j]>0)
5055 {
5056 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = 0 but `ItemDropQuantity{}` = {}.",
5057 qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
5058 // no changes, quest ignore this data
5059 }
5060 }
5061 }
5062
5063 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
5064 {
5065 if (uint32 id = qinfo->RewardChoiceItemId[j])
5066 {
5067 switch (qinfo->RewardChoiceItemType[j])
5068 {
5069 case LootItemType::Item:
5070 if (!GetItemTemplate(id))
5071 {
5072 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5073 qinfo->GetQuestId(), j + 1, id, id);
5074 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5075 }
5076 break;
5078 if (!sCurrencyTypesStore.HasRecord(id))
5079 {
5080 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but currency with id {} does not exist, quest will not reward this currency.",
5081 qinfo->GetQuestId(), j + 1, id, id);
5082 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5083 }
5084 break;
5085 default:
5086 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemType{}` = {} but it is not a valid item type, reward removed.",
5087 qinfo->GetQuestId(), j + 1, uint32(qinfo->RewardChoiceItemType[j]));
5088 qinfo->RewardChoiceItemId[j] = 0;
5089 break;
5090 }
5091
5092 if (!qinfo->RewardChoiceItemCount[j])
5093 {
5094 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but `RewardChoiceItemCount{}` = 0.",
5095 qinfo->GetQuestId(), j + 1, id, j + 1);
5096 }
5097 }
5098 else if (qinfo->RewardChoiceItemCount[j] > 0)
5099 {
5100 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = 0 but `RewardChoiceItemCount{}` = {}.",
5101 qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewardChoiceItemCount[j]);
5102 // no changes, quest ignore this data
5103 }
5104 }
5105
5106 for (uint8 j = 0; j < QUEST_REWARD_ITEM_COUNT; ++j)
5107 {
5108 uint32 id = qinfo->RewardItemId[j];
5109 if (id)
5110 {
5111 if (!GetItemTemplate(id))
5112 {
5113 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5114 qinfo->GetQuestId(), j+1, id, id);
5115 qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
5116 }
5117
5118 if (!qinfo->RewardItemCount[j])
5119 {
5120 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but `RewardItemCount{}` = 0, quest will not reward this item.",
5121 qinfo->GetQuestId(), j+1, id, j+1);
5122 // no changes
5123 }
5124 }
5125 else if (qinfo->RewardItemCount[j]>0)
5126 {
5127 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = 0 but `RewardItemCount{}` = {}.",
5128 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemCount[j]);
5129 // no changes, quest ignore this data
5130 }
5131 }
5132
5133 for (uint8 j = 0; j < QUEST_REWARD_REPUTATIONS_COUNT; ++j)
5134 {
5135 if (qinfo->RewardFactionId[j])
5136 {
5137 if (std::abs(qinfo->RewardFactionValue[j]) > 9)
5138 {
5139 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]);
5140 }
5141 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
5142 {
5143 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]);
5144 qinfo->RewardFactionId[j] = 0; // quest will not reward this
5145 }
5146 }
5147
5148 else if (qinfo->RewardFactionOverride[j] != 0)
5149 {
5150 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = 0 but `RewardFactionValueIdOverride{}` = {}.",
5151 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionOverride[j]);
5152 // no changes, quest ignore this data
5153 }
5154 }
5155
5156 if (qinfo->_rewardSpell > 0)
5157 {
5158 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell, DIFFICULTY_NONE);
5159
5160 if (!spellInfo)
5161 {
5162 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} does not exist, quest will not have a spell reward.",
5163 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5164 qinfo->_rewardSpell = 0; // no spell will be cast on player
5165 }
5166
5167 else if (!SpellMgr::IsSpellValid(spellInfo))
5168 {
5169 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpellCast` = {} but spell {} is broken, quest will not have a spell reward.",
5170 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5171 qinfo->_rewardSpell = 0; // no spell will be cast on player
5172 }
5173 }
5174
5175 if (qinfo->_rewardMailTemplateId)
5176 {
5177 if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
5178 {
5179 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} does not exist, quest will not have a mail reward.",
5180 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
5181 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5182 qinfo->_rewardMailDelay = 0; // no mail will send to player
5183 qinfo->_rewardMailSenderEntry = 0;
5184 }
5185 else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
5186 {
5187 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
5188 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} already used for quest {}, quest will not have a mail reward.",
5189 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
5190 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5191 qinfo->_rewardMailDelay = 0; // no mail will send to player
5192 qinfo->_rewardMailSenderEntry = 0;
5193 }
5194 else
5195 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
5196 }
5197
5198 if (uint32 nextQuestInChain = qinfo->_nextQuestInChain)
5199 {
5200 if (!_questTemplates.count(nextQuestInChain))
5201 {
5202 TC_LOG_ERROR("sql.sql", "Quest {} has `NextQuestInChain` = {} but quest {} does not exist, quest chain will not work.",
5203 qinfo->GetQuestId(), qinfo->_nextQuestInChain, qinfo->_nextQuestInChain);
5204 qinfo->_nextQuestInChain = 0;
5205 }
5206 }
5207
5208 for (uint8 j = 0; j < QUEST_REWARD_CURRENCY_COUNT; ++j)
5209 {
5210 if (qinfo->RewardCurrencyId[j])
5211 {
5212 if (qinfo->RewardCurrencyCount[j] == 0)
5213 {
5214 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but `RewardCurrencyCount{}` = 0, quest can't be done.",
5215 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], j+1);
5216 // no changes, quest can't be done for this requirement
5217 }
5218
5219 if (!sCurrencyTypesStore.LookupEntry(qinfo->RewardCurrencyId[j]))
5220 {
5221 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = {} but currency with entry {} does not exist, quest can't be done.",
5222 qinfo->GetQuestId(), j+1, qinfo->RewardCurrencyId[j], qinfo->RewardCurrencyId[j]);
5223 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5224 }
5225 }
5226 else if (qinfo->RewardCurrencyCount[j] > 0)
5227 {
5228 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardCurrencyId{}` = 0 but `RewardCurrencyCount{}` = {}, quest can't be done.",
5229 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardCurrencyCount[j]);
5230 qinfo->RewardCurrencyCount[j] = 0; // prevent incorrect work of quest
5231 }
5232 }
5233
5234 if (qinfo->_soundAccept)
5235 {
5236 if (!sSoundKitStore.LookupEntry(qinfo->_soundAccept))
5237 {
5238 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundAccept` = {} but sound {} does not exist, set to 0.",
5239 qinfo->GetQuestId(), qinfo->_soundAccept, qinfo->_soundAccept);
5240 qinfo->_soundAccept = 0; // no sound will be played
5241 }
5242 }
5243
5244 if (qinfo->_soundTurnIn)
5245 {
5246 if (!sSoundKitStore.LookupEntry(qinfo->_soundTurnIn))
5247 {
5248 TC_LOG_ERROR("sql.sql", "Quest {} has `SoundTurnIn` = {} but sound {} does not exist, set to 0.",
5249 qinfo->GetQuestId(), qinfo->_soundTurnIn, qinfo->_soundTurnIn);
5250 qinfo->_soundTurnIn = 0; // no sound will be played
5251 }
5252 }
5253
5254 if (qinfo->_rewardSkillId)
5255 {
5256 if (!sSkillLineStore.LookupEntry(qinfo->_rewardSkillId))
5257 {
5258 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but this skill does not exist",
5259 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5260 }
5261 if (!qinfo->_rewardSkillPoints)
5262 {
5263 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillId` = {} but `RewardSkillPoints` is 0",
5264 qinfo->GetQuestId(), qinfo->_rewardSkillId);
5265 }
5266 }
5267
5268 if (qinfo->_rewardSkillPoints)
5269 {
5270 if (qinfo->_rewardSkillPoints > sWorld->GetConfigMaxSkillValue())
5271 {
5272 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but max possible skill is {}, quest can't be done.",
5273 qinfo->GetQuestId(), qinfo->_rewardSkillPoints, sWorld->GetConfigMaxSkillValue());
5274 // no changes, quest can't be done for this requirement
5275 }
5276 if (!qinfo->_rewardSkillId)
5277 {
5278 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSkillPoints` = {} but `RewardSkillId` is 0",
5279 qinfo->GetQuestId(), qinfo->_rewardSkillPoints);
5280 }
5281 }
5282
5283 // fill additional data stores
5284 if (uint32 prevQuestId = std::abs(qinfo->_prevQuestID))
5285 {
5286 auto prevQuestItr = _questTemplates.find(prevQuestId);
5287 if (prevQuestItr == _questTemplates.end())
5288 TC_LOG_ERROR("sql.sql", "Quest {} has PrevQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
5289 else if (prevQuestItr->second->_breadcrumbForQuestId)
5290 TC_LOG_ERROR("sql.sql", "Quest {} should not be unlocked by breadcrumb quest {}", qinfo->_id, prevQuestId);
5291 else if (qinfo->_prevQuestID > 0)
5292 qinfo->DependentPreviousQuests.push_back(prevQuestId);
5293 }
5294
5295 if (uint32 nextQuestId = qinfo->_nextQuestID)
5296 {
5297 auto nextQuestItr = _questTemplates.find(nextQuestId);
5298 if (nextQuestItr == _questTemplates.end())
5299 TC_LOG_ERROR("sql.sql", "Quest {} has NextQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestID);
5300 else
5301 nextQuestItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId());
5302 }
5303
5304 if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
5305 {
5306 if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
5307 {
5308 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb for quest {}, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
5309 qinfo->_breadcrumbForQuestId = 0;
5310 }
5311 if (qinfo->_nextQuestID)
5312 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb, should not unlock quest {}", qinfo->_id, qinfo->_nextQuestID);
5313 }
5314
5315 if (qinfo->_exclusiveGroup)
5316 _exclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->_exclusiveGroup, qinfo->GetQuestId()));
5317 }
5318
5319 // Disallow any breadcrumb loops and inform quests of their breadcrumbs
5320 for (auto& questPair : _questTemplates)
5321 {
5322 // skip post-loading checks for disabled quests
5323 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
5324 continue;
5325
5326 Quest* qinfo = questPair.second.get();
5327 uint32 qid = qinfo->GetQuestId();
5328 uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
5329 std::set<uint32> questSet;
5330
5331 while(breadcrumbForQuestId)
5332 {
5333 //a previously visited quest was found as a breadcrumb quest
5334 //breadcrumb loop found!
5335 if (!questSet.insert(qinfo->_id).second)
5336 {
5337 TC_LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} are in a loop", qid, breadcrumbForQuestId);
5338 qinfo->_breadcrumbForQuestId = 0;
5339 break;
5340 }
5341
5342 qinfo = const_cast<Quest*>(GetQuestTemplate(breadcrumbForQuestId));
5343
5344 //every quest has a list of every breadcrumb towards it
5345 qinfo->DependentBreadcrumbQuests.push_back(qid);
5346
5347 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
5348 }
5349 }
5350
5351 // don't check spells with SPELL_EFFECT_QUEST_COMPLETE, a lot of invalid db2 data
5352
5353 // Make all paragon reward quests repeatable
5354 for (ParagonReputationEntry const* paragonReputation : sParagonReputationStore)
5355 if (Quest const* quest = GetQuestTemplate(paragonReputation->QuestID))
5357
5358 TC_LOG_INFO("server.loading", ">> Loaded {} quests definitions in {} ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
5359}
5360
5362{
5363 TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
5365 TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
5367 TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
5369 TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
5371}
5372
5374{
5375 uint32 oldMSTime = getMSTime();
5376
5377 _questTemplateLocaleStore.clear(); // need for reload case
5378 // 0 1
5379 QueryResult result = WorldDatabase.Query("SELECT Id, locale, "
5380 // 2 3 4 5 6 7 8 9 10
5381 "LogTitle, LogDescription, QuestDescription, AreaDescription, PortraitGiverText, PortraitGiverName, PortraitTurnInText, PortraitTurnInName, QuestCompletionLog"
5382 " FROM quest_template_locale");
5383 if (!result)
5384 return;
5385
5386 do
5387 {
5388 Field* fields = result->Fetch();
5389
5390 uint32 id = fields[0].GetUInt32();
5391 std::string_view localeName = fields[1].GetStringView();
5392
5393 LocaleConstant locale = GetLocaleByName(localeName);
5394 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5395 continue;
5396
5398 AddLocaleString(fields[2].GetStringView(), locale, data.LogTitle);
5399 AddLocaleString(fields[3].GetStringView(), locale, data.LogDescription);
5400 AddLocaleString(fields[4].GetStringView(), locale, data.QuestDescription);
5401 AddLocaleString(fields[5].GetStringView(), locale, data.AreaDescription);
5402 AddLocaleString(fields[6].GetStringView(), locale, data.PortraitGiverText);
5403 AddLocaleString(fields[7].GetStringView(), locale, data.PortraitGiverName);
5404 AddLocaleString(fields[8].GetStringView(), locale, data.PortraitTurnInText);
5405 AddLocaleString(fields[9].GetStringView(), locale, data.PortraitTurnInName);
5406 AddLocaleString(fields[10].GetStringView(), locale, data.QuestCompletionLog);
5407 } while (result->NextRow());
5408
5409 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Template locale strings in {} ms", _questTemplateLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5410}
5411
5413{
5414 uint32 oldMSTime = getMSTime();
5415
5416 _questObjectivesLocaleStore.clear(); // need for reload case
5417 // 0 1 2
5418 QueryResult result = WorldDatabase.Query("SELECT Id, locale, Description FROM quest_objectives_locale");
5419 if (!result)
5420 return;
5421
5422 do
5423 {
5424 Field* fields = result->Fetch();
5425
5426 uint32 id = fields[0].GetUInt32();
5427 std::string_view localeName = fields[1].GetStringView();
5428
5429 LocaleConstant locale = GetLocaleByName(localeName);
5430 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5431 continue;
5432
5434 AddLocaleString(fields[2].GetStringView(), locale, data.Description);
5435 }
5436 while (result->NextRow());
5437
5438 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Objectives locale strings in {} ms", _questObjectivesLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5439}
5440
5442{
5443 uint32 oldMSTime = getMSTime();
5444
5445 for (std::size_t i = 0; i < _questGreetingLocaleStore.size(); ++i)
5446 _questGreetingLocaleStore[i].clear();
5447
5448 // 0 1 2 3
5449 QueryResult result = WorldDatabase.Query("SELECT Id, type, locale, Greeting FROM quest_greeting_locale");
5450 if (!result)
5451 return;
5452
5453 uint32 count = 0;
5454 do
5455 {
5456 Field* fields = result->Fetch();
5457
5458 uint32 id = fields[0].GetUInt32();
5459 uint8 type = fields[1].GetUInt8();
5460 switch (type)
5461 {
5462 case 0: // Creature
5463 if (!GetCreatureTemplate(id))
5464 {
5465 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id);
5466 continue;
5467 }
5468 break;
5469 case 1: // GameObject
5470 if (!GetGameObjectTemplate(id))
5471 {
5472 TC_LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id);
5473 continue;
5474 }
5475 break;
5476 default:
5477 continue;
5478 }
5479
5480 std::string_view localeName = fields[2].GetStringView();
5481
5482 LocaleConstant locale = GetLocaleByName(localeName);
5483 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5484 continue;
5485
5487 AddLocaleString(fields[3].GetStringView(), locale, data.Greeting);
5488 ++count;
5489 }
5490 while (result->NextRow());
5491
5492 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Greeting locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5493}
5494
5496{
5497 uint32 oldMSTime = getMSTime();
5498
5499 _questOfferRewardLocaleStore.clear(); // need for reload case
5500 // 0 1 2
5501 QueryResult result = WorldDatabase.Query("SELECT Id, locale, RewardText FROM quest_offer_reward_locale");
5502 if (!result)
5503 return;
5504
5505 do
5506 {
5507 Field* fields = result->Fetch();
5508
5509 uint32 id = fields[0].GetUInt32();
5510 std::string_view localeName = fields[1].GetStringView();
5511
5512 LocaleConstant locale = GetLocaleByName(localeName);
5513 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5514 continue;
5515
5517 AddLocaleString(fields[2].GetStringView(), locale, data.RewardText);
5518 } while (result->NextRow());
5519
5520 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Offer Reward locale strings in {} ms", _questOfferRewardLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5521}
5522
5524{
5525 uint32 oldMSTime = getMSTime();
5526
5527 _questRequestItemsLocaleStore.clear(); // need for reload case
5528 // 0 1 2
5529 QueryResult result = WorldDatabase.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale");
5530 if (!result)
5531 return;
5532
5533 do
5534 {
5535 Field* fields = result->Fetch();
5536
5537 uint32 id = fields[0].GetUInt32();
5538 std::string_view localeName = fields[1].GetStringView();
5539
5540 LocaleConstant locale = GetLocaleByName(localeName);
5541 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
5542 continue;
5543
5545 AddLocaleString(fields[2].GetStringView(), locale, data.CompletionText);
5546 } while (result->NextRow());
5547
5548 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Request Items locale strings in {} ms", _questRequestItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
5549}
5550
5552{
5553 uint32 oldMSTime = getMSTime();
5554
5555 ScriptMapMap* scripts = GetScriptsMapByType(type);
5556 if (!scripts)
5557 return;
5558
5559 std::string tableName = GetScriptsTableNameByType(type);
5560 if (tableName.empty())
5561 return;
5562
5563 if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
5564 return;
5565
5566 TC_LOG_INFO("server.loading", "Loading {}...", tableName);
5567
5568 scripts->clear(); // need for reload support
5569
5570 bool isSpellScriptTable = (type == SCRIPTS_SPELL);
5571 // 0 1 2 3 4 5 6 7 8 9
5572 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o{} FROM {}", isSpellScriptTable ? ", effIndex" : "", tableName);
5573
5574 if (!result)
5575 {
5576 TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `{}` is empty!", tableName);
5577 return;
5578 }
5579
5580 uint32 count = 0;
5581
5582 do
5583 {
5584 Field* fields = result->Fetch();
5585 ScriptInfo tmp;
5586 tmp.type = type;
5587 tmp.id = fields[0].GetUInt32();
5588 if (isSpellScriptTable)
5589 tmp.id |= fields[10].GetUInt8() << 24;
5590 tmp.delay = fields[1].GetUInt32();
5591 tmp.command = ScriptCommands(fields[2].GetUInt32());
5592 tmp.Raw.nData[0] = fields[3].GetUInt32();
5593 tmp.Raw.nData[1] = fields[4].GetUInt32();
5594 tmp.Raw.nData[2] = fields[5].GetInt32();
5595 tmp.Raw.fData[0] = fields[6].GetFloat();
5596 tmp.Raw.fData[1] = fields[7].GetFloat();
5597 tmp.Raw.fData[2] = fields[8].GetFloat();
5598 tmp.Raw.fData[3] = fields[9].GetFloat();
5599
5600 // generic command args check
5601 switch (tmp.command)
5602 {
5604 {
5606 {
5607 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk type (datalong = {}) in SCRIPT_COMMAND_TALK for script id {}",
5608 tableName, tmp.Talk.ChatType, tmp.id);
5609 continue;
5610 }
5611 if (!sBroadcastTextStore.LookupEntry(uint32(tmp.Talk.TextID)))
5612 {
5613 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk text id (dataint = {}) in SCRIPT_COMMAND_TALK for script id {}",
5614 tableName, tmp.Talk.TextID, tmp.id);
5615 continue;
5616 }
5617
5618 break;
5619 }
5620
5622 {
5623 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
5624 {
5625 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid emote id (datalong = {}) in SCRIPT_COMMAND_EMOTE for script id {}",
5626 tableName, tmp.Emote.EmoteID, tmp.id);
5627 continue;
5628 }
5629 break;
5630 }
5631
5633 {
5634 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
5635 {
5636 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid map (Id: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5637 tableName, tmp.TeleportTo.MapID, tmp.id);
5638 continue;
5639 }
5640
5642 {
5643 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5644 tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
5645 continue;
5646 }
5647 break;
5648 }
5649
5651 {
5652 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
5653 if (!quest)
5654 {
5655 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5656 tableName, tmp.QuestExplored.QuestID, tmp.id);
5657 continue;
5658 }
5659
5661 {
5662 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.",
5663 tableName, tmp.QuestExplored.QuestID, tmp.id);
5664 continue;
5665 }
5666
5668 {
5669 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 {}",
5670 tableName, tmp.QuestExplored.Distance, tmp.id);
5671 continue;
5672 }
5673
5675 {
5676 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",
5678 continue;
5679 }
5680
5682 {
5683 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",
5684 tableName, tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
5685 continue;
5686 }
5687
5688 break;
5689 }
5690
5692 {
5694 {
5695 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_KILL_CREDIT for script id {}",
5696 tableName, tmp.KillCredit.CreatureEntry, tmp.id);
5697 continue;
5698 }
5699 break;
5700 }
5701
5703 {
5705 if (!data)
5706 {
5707 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5708 tableName, tmp.RespawnGameobject.GOGuid, tmp.id);
5709 continue;
5710 }
5711
5712 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5713 if (!info)
5714 {
5715 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5716 tableName, tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
5717 continue;
5718 }
5719
5720 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
5722 info->type == GAMEOBJECT_TYPE_DOOR ||
5723 info->type == GAMEOBJECT_TYPE_BUTTON ||
5724 info->type == GAMEOBJECT_TYPE_TRAP)
5725 {
5726 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5727 tableName, info->entry, tmp.id);
5728 continue;
5729 }
5730 break;
5731 }
5732
5734 {
5736 {
5737 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5739 continue;
5740 }
5741
5743 {
5744 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5745 tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id);
5746 continue;
5747 }
5748 break;
5749 }
5750
5753 {
5755 if (!data)
5756 {
5757 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in {} for script id {}",
5758 tableName, tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command), tmp.id);
5759 continue;
5760 }
5761
5762 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5763 if (!info)
5764 {
5765 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in {} for script id {}",
5766 tableName, tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command), tmp.id);
5767 continue;
5768 }
5769
5770 if (info->type != GAMEOBJECT_TYPE_DOOR)
5771 {
5772 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command {} for script id {}",
5773 tableName, info->entry, GetScriptCommandName(tmp.command), tmp.id);
5774 continue;
5775 }
5776
5777 break;
5778 }
5779
5781 {
5782 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID, DIFFICULTY_NONE))
5783 {
5784 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5785 tableName, tmp.RemoveAura.SpellID, tmp.id);
5786 continue;
5787 }
5788 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
5789 {
5790 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in datalong2 ({}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5791 tableName, tmp.RemoveAura.Flags, tmp.id);
5792 continue;
5793 }
5794 break;
5795 }
5796
5798 {
5799 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID, DIFFICULTY_NONE))
5800 {
5801 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5802 tableName, tmp.CastSpell.SpellID, tmp.id);
5803 continue;
5804 }
5805 if (tmp.CastSpell.Flags > 4) // targeting type
5806 {
5807 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown target in datalong2 ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5808 tableName, tmp.CastSpell.Flags, tmp.id);
5809 continue;
5810 }
5811 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
5812 {
5813 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5814 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5815 continue;
5816 }
5817 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
5818 {
5819 TC_LOG_ERROR("sql.sql", "Table `{}` using invalid creature entry in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5820 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5821 continue;
5822 }
5823 break;
5824 }
5825
5827 {
5829 {
5830 TC_LOG_ERROR("sql.sql", "Table `{}` has nonexistent item (entry: {}) in SCRIPT_COMMAND_CREATE_ITEM for script id {}",
5831 tableName, tmp.CreateItem.ItemEntry, tmp.id);
5832 continue;
5833 }
5834 if (!tmp.CreateItem.Amount)
5835 {
5836 TC_LOG_ERROR("sql.sql", "Table `{}` SCRIPT_COMMAND_CREATE_ITEM but amount is {} for script id {}",
5837 tableName, tmp.CreateItem.Amount, tmp.id);
5838 continue;
5839 }
5840 break;
5841 }
5843 {
5844 if (!sAnimKitStore.LookupEntry(tmp.PlayAnimKit.AnimKitID))
5845 {
5846 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid AnimKid id (datalong = {}) in SCRIPT_COMMAND_PLAY_ANIMKIT for script id {}",
5847 tableName, tmp.PlayAnimKit.AnimKitID, tmp.id);
5848 continue;
5849 }
5850 break;
5851 }
5855 {
5856 TC_LOG_ERROR("sql.sql", "Table `{}` uses deprecated direct updatefield modify command {} for script id {}", tableName, GetScriptCommandName(tmp.command), tmp.id);
5857 continue;
5858 }
5859 default:
5860 break;
5861 }
5862
5863 if (scripts->find(tmp.id) == scripts->end())
5864 {
5865 ScriptMap emptyMap;
5866 (*scripts)[tmp.id] = emptyMap;
5867 }
5868 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
5869
5870 ++count;
5871 }
5872 while (result->NextRow());
5873
5874 TC_LOG_INFO("server.loading", ">> Loaded {} script definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5875}
5876
5878{
5880
5881 // check ids
5882 for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
5883 {
5884 uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
5885 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
5886
5887 if (!spellInfo)
5888 {
5889 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has not existing spell (Id: {}) as script id", spellId);
5890 continue;
5891 }
5892
5893 SpellEffIndex i = SpellEffIndex((uint32(itr->first) >> 24) & 0x000000FF);
5894 if (uint32(i) >= spellInfo->GetEffects().size())
5895 {
5896 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` has too high effect index {} for spell (Id: {}) as script id", uint32(i), spellId);
5897 continue;
5898 }
5899
5900 //check for correct spellEffect
5901 if (!spellInfo->GetEffect(i).Effect || (spellInfo->GetEffect(i).Effect != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->GetEffect(i).Effect != SPELL_EFFECT_DUMMY))
5902 TC_LOG_ERROR("sql.sql", "Table `spell_scripts` - spell {} effect {} is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, uint32(i));
5903 }
5904}
5905
5907{
5908 _eventStore.clear();
5909
5910 // Load all possible event ids from gameobjects
5911 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
5912 {
5913 EventContainer eventSet = gameObjectTemplatePair.second.GetEventScriptSet();
5914 _eventStore.insert(eventSet.begin(), eventSet.end());
5915 }
5916
5917 // Load all possible event ids from spells
5918 for (SpellEffectEntry const* spellEffect : sSpellEffectStore)
5919 if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT && spellEffect->EffectMiscValue[0])
5920 _eventStore.insert(spellEffect->EffectMiscValue[0]);
5921
5922 // Load all possible event ids from taxi path nodes
5923 for (TaxiPathNodeEntry const* node : sTaxiPathNodeStore)
5924 {
5925 if (node->ArrivalEventID)
5926 _eventStore.insert(node->ArrivalEventID);
5927
5928 if (node->DepartureEventID)
5929 _eventStore.insert(node->DepartureEventID);
5930 }
5931
5932 // Load all possible event ids from criterias
5933 auto addCriteriaEventsToStore = [&](CriteriaList const& criteriaList)
5934 {
5935 for (Criteria const* criteria : criteriaList)
5936 if (criteria->Entry->Asset.EventID)
5937 _eventStore.insert(criteria->Entry->Asset.EventID);
5938 };
5939
5940 std::array<CriteriaType, 2> eventCriteriaTypes = { CriteriaType::PlayerTriggerGameEvent, CriteriaType::AnyoneTriggerGameEventScenario };
5941 for (CriteriaType criteriaType : eventCriteriaTypes)
5942 {
5943 addCriteriaEventsToStore(sCriteriaMgr->GetPlayerCriteriaByType(criteriaType, 0));
5944 addCriteriaEventsToStore(sCriteriaMgr->GetGuildCriteriaByType(criteriaType));
5945 addCriteriaEventsToStore(sCriteriaMgr->GetQuestObjectiveCriteriaByType(criteriaType));
5946 }
5947
5948 for (ScenarioEntry const* scenario : sScenarioStore)
5949 for (CriteriaType criteriaType : eventCriteriaTypes)
5950 addCriteriaEventsToStore(sCriteriaMgr->GetScenarioCriteriaByTypeAndScenario(criteriaType, scenario->ID));
5951
5952 for (auto const& [gameEventId, _] : sCriteriaMgr->GetCriteriaByStartEvent(CriteriaStartEvent::SendEvent))
5953 if (gameEventId)
5954 _eventStore.insert(gameEventId);
5955
5956 for (auto const& [gameEventId, _] : sCriteriaMgr->GetCriteriaByFailEvent(CriteriaFailEvent::SendEvent))
5957 if (gameEventId)
5958 _eventStore.insert(gameEventId);
5959}
5960
5962{
5963 // Set of valid events referenced in several sources
5964 LoadEventSet();
5965
5966 // Deprecated
5968
5969 // Then check if all scripts are in above list of possible script entries
5970 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
5971 {
5972 if (!IsValidEvent(itr->first))
5973 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 {}",
5974 itr->first, SPELL_EFFECT_SEND_EVENT);
5975 }
5976
5977 uint32 oldMSTime = getMSTime();
5978
5979 _eventScriptStore.clear(); // Reload case
5980
5981 QueryResult result = WorldDatabase.Query("SELECT Id, ScriptName FROM event_script_names");
5982 if (!result)
5983 {
5984 TC_LOG_INFO("server.loading", ">> Loaded 0 event scripts. DB table `event_script_names` is empty.");
5985 return;
5986 }
5987
5988 do
5989 {
5990 Field* fields = result->Fetch();
5991
5992 uint32 eventId = fields[0].GetUInt32();
5993 std::string_view scriptName = fields[1].GetStringView();
5994
5995 if (!IsValidEvent(eventId))
5996 {
5997 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 {}",
5998 eventId, SPELL_EFFECT_SEND_EVENT);
5999 continue;
6000 }
6001 _eventScriptStore[eventId] = GetScriptId(scriptName);
6002 } while (result->NextRow());
6003
6004 TC_LOG_INFO("server.loading", ">> Loaded {} event scripts in {} ms", _eventScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6005}
6006
6008{
6009 uint32 oldMSTime = getMSTime();
6010
6011 _spellScriptsStore.clear(); // need for reload case
6012
6013 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
6014
6015 if (!result)
6016 {
6017 TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
6018 return;
6019 }
6020
6021 uint32 count = 0;
6022
6023 do
6024 {
6025
6026 Field* fields = result->Fetch();
6027
6028 int32 spellId = fields[0].GetInt32();
6029 std::string_view scriptName = fields[1].GetStringView();
6030
6031 bool allRanks = false;
6032 if (spellId < 0)
6033 {
6034 allRanks = true;
6035 spellId = -spellId;
6036 }
6037
6038 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
6039 if (!spellInfo)
6040 {
6041 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) does not exist.", scriptName, fields[0].GetInt32());
6042 continue;
6043 }
6044
6045 if (allRanks)
6046 {
6047 if (!spellInfo->IsRanked())
6048 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) has no ranks of spell.", scriptName, fields[0].GetInt32());
6049
6050 if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
6051 {
6052 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is not first rank of spell.", scriptName, fields[0].GetInt32());
6053 continue;
6054 }
6055
6056 while (spellInfo)
6057 {
6058 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6059 spellInfo = spellInfo->GetNextRankSpell();
6060 }
6061 }
6062 else
6063 {
6064 if (spellInfo->IsRanked())
6065 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
6066
6067 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
6068 }
6069
6070 ++count;
6071 }
6072 while (result->NextRow());
6073
6074 TC_LOG_INFO("server.loading", ">> Loaded {} spell script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6075}
6076
6078{
6079 uint32 oldMSTime = getMSTime();
6080
6081 if (_spellScriptsStore.empty())
6082 {
6083 TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
6084 return;
6085 }
6086
6087 uint32 count = 0;
6088
6089 for (auto& spell : _spellScriptsStore)
6090 {
6091 SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spell.first, DIFFICULTY_NONE);
6092
6093 if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(spell.second.first))
6094 {
6095 ++count;
6096
6097 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
6098 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
6099
6100 if (!spellScript && !auraScript)
6101 {
6102 TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `{}` do not return objects - script skipped", GetScriptName(spell.second.first));
6103
6104 spell.second.second = false;
6105 continue;
6106 }
6107
6108 if (spellScript)
6109 {
6110 spellScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6111 spellScript->_Register();
6112
6113 if (!spellScript->_Validate(spellEntry))
6114 {
6115 spell.second.second = false;
6116 continue;
6117 }
6118 }
6119
6120 if (auraScript)
6121 {
6122 auraScript->_Init(spellScriptLoader->GetName(), spellEntry->Id);
6123 auraScript->_Register();
6124
6125 if (!auraScript->_Validate(spellEntry))
6126 {
6127 spell.second.second = false;
6128 continue;
6129 }
6130 }
6131
6132 // Enable the script when all checks passed
6133 spell.second.second = true;
6134 }
6135 else
6136 spell.second.second = false;
6137 }
6138
6139 TC_LOG_INFO("server.loading", ">> Validated {} scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6140}
6141
6143{
6144 uint32 oldMSTime = getMSTime();
6145
6146 // 0 1 2 3 4
6147 QueryResult result = WorldDatabase.Query("SELECT ID, `Text`, NextPageID, PlayerConditionID, Flags FROM page_text");
6148 if (!result)
6149 {
6150 TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
6151 return;
6152 }
6153
6154 uint32 count = 0;
6155 do
6156 {
6157 Field* fields = result->Fetch();
6158
6159 uint32 id = fields[0].GetUInt32();
6160
6161 PageText& pageText = _pageTextStore[id];
6162 pageText.Text = fields[1].GetString();
6163 pageText.NextPageID = fields[2].GetUInt32();
6164 pageText.PlayerConditionID = fields[3].GetInt32();
6165 pageText.Flags = fields[4].GetUInt8();
6166
6167 ++count;
6168 }
6169 while (result->NextRow());
6170
6171 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
6172 if (itr->second.NextPageID)
6173 if (_pageTextStore.find(itr->second.NextPageID) == _pageTextStore.end())
6174 TC_LOG_ERROR("sql.sql", "Page text (ID: {}) has non-existing `NextPageID` ({})", itr->first, itr->second.NextPageID);
6175
6176 TC_LOG_INFO("server.loading", ">> Loaded {} page texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6177}
6178
6180{
6181 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
6182 if (itr != _pageTextStore.end())
6183 return &(itr->second);
6184
6185 return nullptr;
6186}
6187
6189{
6190 uint32 oldMSTime = getMSTime();
6191
6192 _pageTextLocaleStore.clear(); // needed for reload case
6193
6194 // 0 1 2
6195 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text` FROM page_text_locale");
6196 if (!result)
6197 return;
6198
6199 do
6200 {
6201 Field* fields = result->Fetch();
6202
6203 uint32 id = fields[0].GetUInt32();
6204 std::string_view localeName = fields[1].GetStringView();
6205
6206 LocaleConstant locale = GetLocaleByName(localeName);
6207 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
6208 continue;
6209
6211 AddLocaleString(fields[2].GetStringView(), locale, data.Text);
6212 } while (result->NextRow());
6213
6214 TC_LOG_INFO("server.loading", ">> Loaded {} PageText locale strings in {} ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6215}
6216
6218{
6219 uint32 oldMSTime = getMSTime();
6220
6221 // 0 1 2
6222 QueryResult result = WorldDatabase.Query("SELECT map, parent, script FROM instance_template");
6223
6224 if (!result)
6225 {
6226 TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
6227 return;
6228 }
6229
6230 uint32 count = 0;
6231 do
6232 {
6233 Field* fields = result->Fetch();
6234
6235 uint16 mapID = fields[0].GetUInt16();
6236
6237 if (!MapManager::IsValidMAP(mapID))
6238 {
6239 TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid {} for template!", mapID);
6240 continue;
6241 }
6242
6243 InstanceTemplate instanceTemplate;
6244
6245 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
6246 instanceTemplate.ScriptId = GetScriptId(fields[2].GetStringView());
6247
6248 _instanceTemplateStore[mapID] = instanceTemplate;
6249
6250 ++count;
6251 }
6252 while (result->NextRow());
6253
6254 TC_LOG_INFO("server.loading", ">> Loaded {} instance templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6255}
6256
6258{
6259 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
6260 if (itr != _instanceTemplateStore.end())
6261 return &(itr->second);
6262
6263 return nullptr;
6264}
6265
6267{
6268 NpcTextContainer::const_iterator itr = _npcTextStore.find(Text_ID);
6269 if (itr != _npcTextStore.end())
6270 return &itr->second;
6271 return nullptr;
6272}
6273
6275{
6276 uint32 oldMSTime = getMSTime();
6277
6278 QueryResult result = WorldDatabase.Query("SELECT ID, "
6279 "Probability0, Probability1, Probability2, Probability3, Probability4, Probability5, Probability6, Probability7, "
6280 "BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7"
6281 " FROM npc_text");
6282 if (!result)
6283 {
6284 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6285 return;
6286 }
6287
6288 _npcTextStore.reserve(result->GetRowCount());
6289
6290 do
6291 {
6292 Field* fields = result->Fetch();
6293
6294 uint32 textID = fields[0].GetUInt32();
6295 if (!textID)
6296 {
6297 TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
6298 continue;
6299 }
6300
6301 NpcText npcText;
6302
6303 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; ++i)
6304 {
6305 npcText.Data[i].Probability = fields[1 + i].GetFloat();
6306 npcText.Data[i].BroadcastTextID = fields[9 + i].GetUInt32();
6307 }
6308
6309 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6310 {
6311 if (npcText.Data[i].BroadcastTextID)
6312 {
6313 if (!sBroadcastTextStore.LookupEntry(npcText.Data[i].BroadcastTextID))
6314 {
6315 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a non-existing BroadcastText (ID: {}, Index: {})", textID, npcText.Data[i].BroadcastTextID, i);
6316 npcText.Data[i].Probability = 0.0f;
6317 npcText.Data[i].BroadcastTextID = 0;
6318 }
6319 }
6320 }
6321
6322 for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; i++)
6323 {
6324 if (npcText.Data[i].Probability > 0 && npcText.Data[i].BroadcastTextID == 0)
6325 {
6326 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability (Index: {}) set, but no BroadcastTextID to go with it", textID, i);
6327 npcText.Data[i].Probability = 0;
6328 }
6329 }
6330
6331 float probabilitySum = std::accumulate(std::begin(npcText.Data), std::end(npcText.Data), 0.0f, [](float sum, NpcTextData const& data) { return sum + data.Probability; });
6332 if (probabilitySum <= 0.0f)
6333 {
6334 TC_LOG_ERROR("sql.sql", "NPCText (ID: {}) has a probability sum 0, no text can be selected from it, skipped.", textID);
6335 continue;
6336 }
6337
6338 _npcTextStore[textID] = npcText;
6339 }
6340 while (result->NextRow());
6341
6342 TC_LOG_INFO("server.loading", ">> Loaded {} npc texts in {} ms", uint32(_npcTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
6343}
6344
6345//not very fast function but it is called only once a day, or on starting-up
6347{
6348 uint32 oldMSTime = getMSTime();
6349
6350 time_t curTime = GameTime::GetGameTime();
6351 tm lt;
6352 localtime_r(&curTime, &lt);
6353 TC_LOG_INFO("misc", "Returning mails current time: hour: {}, minute: {}, second: {} ", lt.tm_hour, lt.tm_min, lt.tm_sec);
6354
6355 // Delete all old mails without item and without body immediately, if starting server
6356 if (!serverUp)
6357 {
6359 stmt->setInt64(0, curTime);
6360 CharacterDatabase.Execute(stmt);
6361 }
6363 stmt->setInt64(0, curTime);
6364 PreparedQueryResult result = CharacterDatabase.Query(stmt);
6365 if (!result)
6366 {
6367 TC_LOG_INFO("server.loading", ">> No expired mails found.");
6368 return; // any mails need to be returned or deleted
6369 }
6370
6371 std::map<uint64 /*messageId*/, MailItemInfoVec> itemsCache;
6372 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
6373 stmt->setUInt32(0, curTime);
6374 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
6375 {
6376 MailItemInfo item;
6377 do
6378 {
6379 Field* fields = items->Fetch();
6380 item.item_guid = fields[0].GetUInt64();
6381 item.item_template = fields[1].GetUInt32();
6382 uint64 mailId = fields[2].GetUInt64();
6383 itemsCache[mailId].push_back(item);
6384 } while (items->NextRow());
6385 }
6386
6387 uint32 deletedCount = 0;
6388 uint32 returnedCount = 0;
6389 do
6390 {
6391 Field* fields = result->Fetch();
6392 ObjectGuid::LowType receiver = fields[3].GetUInt64();
6393 if (serverUp && ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(receiver)))
6394 continue;
6395
6396 Mail* m = new Mail;
6397 m->messageID = fields[0].GetUInt64();
6398 m->messageType = fields[1].GetUInt8();
6399 m->sender = fields[2].GetUInt64();
6400 m->receiver = receiver;
6401 bool has_items = fields[4].GetBool();
6402 m->expire_time = fields[5].GetInt64();
6403 m->deliver_time = 0;
6404 m->COD = fields[6].GetUInt64();
6405 m->checked = fields[7].GetUInt8();
6406 m->mailTemplateId = fields[8].GetInt16();
6407
6408 // Delete or return mail
6409 if (has_items)
6410 {
6411 // read items from cache
6412 m->items.swap(itemsCache[m->messageID]);
6413
6414 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
6416 {
6417 CharacterDatabaseTransaction nonTransactional(nullptr);
6418 // mail open and then not returned
6419 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6420 {
6421 Item::DeleteFromDB(nonTransactional, itr2->item_guid);
6422 AzeriteItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6423 AzeriteEmpoweredItem::DeleteFromDB(nonTransactional, itr2->item_guid);
6424 }
6425
6426 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
6427 stmt->setUInt64(0, m->messageID);
6428 CharacterDatabase.Execute(stmt);
6429 }
6430 else
6431 {
6432 // Mail will be returned
6433 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
6434 stmt->setUInt64(0, m->receiver);
6435 stmt->setUInt64(1, m->sender);
6436 stmt->setInt64 (2, curTime + 30 * DAY);
6437 stmt->setInt64 (3, curTime);
6439 stmt->setUInt64(5, m->messageID);
6440 CharacterDatabase.Execute(stmt);
6441 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6442 {
6443 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
6444 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
6445 stmt->setUInt64(0, m->sender);
6446 stmt->setUInt64(1, itr2->item_guid);
6447 CharacterDatabase.Execute(stmt);
6448
6449 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
6450 stmt->setUInt64(0, m->sender);
6451 stmt->setUInt64(1, itr2->item_guid);
6452 CharacterDatabase.Execute(stmt);
6453 }
6454 delete m;
6455 ++returnedCount;
6456 continue;
6457 }
6458 }
6459
6460 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
6461 stmt->setUInt64(0, m->messageID);
6462 CharacterDatabase.Execute(stmt);
6463 delete m;
6464 ++deletedCount;
6465 }
6466 while (result->NextRow());
6467
6468 TC_LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
6469}
6470
6472{
6473 uint32 oldMSTime = getMSTime();
6474
6475 _questAreaTriggerStore.clear(); // need for reload case
6476
6477 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
6478
6479 if (!result)
6480 {
6481 TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
6482 return;
6483 }
6484
6485 uint32 count = 0;
6486
6487 do
6488 {
6489 ++count;
6490
6491 Field* fields = result->Fetch();
6492
6493 uint32 trigger_ID = fields[0].GetUInt32();
6494 uint32 quest_ID = fields[1].GetUInt32();
6495
6496 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
6497 if (!atEntry)
6498 {
6499 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
6500 continue;
6501 }
6502
6503 Quest const* quest = GetQuestTemplate(quest_ID);
6504
6505 if (!quest)
6506 {
6507 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not existing quest {}", trigger_ID, quest_ID);
6508 continue;
6509 }
6510
6512 {
6513 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);
6514 continue;
6515 }
6516
6517 _questAreaTriggerStore[trigger_ID].insert(quest_ID);
6518
6519 } while (result->NextRow());
6520
6521 for (auto const& pair : _questObjectives)
6522 {
6523 QuestObjective const* objective = pair.second;
6524 if (objective->Type == QUEST_OBJECTIVE_AREATRIGGER)
6525 _questAreaTriggerStore[objective->ObjectID].insert(objective->QuestID);
6526 }
6527
6528 TC_LOG_INFO("server.loading", ">> Loaded {} quest trigger points in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6529}
6530
6532{
6533 uint32 typeIndex;
6534 if (type == TYPEID_UNIT)
6535 typeIndex = 0;
6536 else if (type == TYPEID_GAMEOBJECT)
6537 typeIndex = 1;
6538 else
6539 return nullptr;
6540
6542}
6543
6545{
6546 uint32 typeIndex;
6547 if (type == TYPEID_UNIT)
6548 typeIndex = 0;
6549 else if (type == TYPEID_GAMEOBJECT)
6550 typeIndex = 1;
6551 else
6552 return nullptr;
6553
6555}
6556
6558{
6559 uint32 oldMSTime = getMSTime();
6560
6561 for (std::size_t i = 0; i < _questGreetingStore.size(); ++i)
6562 _questGreetingStore[i].clear();
6563
6564 // 0 1 2 3
6565 QueryResult result = WorldDatabase.Query("SELECT ID, type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
6566 if (!result)
6567 {
6568 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6569 return;
6570 }
6571
6572 uint32 count = 0;
6573 do
6574 {
6575 Field* fields = result->Fetch();
6576
6577 uint32 id = fields[0].GetUInt32();
6578 uint8 type = fields[1].GetUInt8();
6579 switch (type)
6580 {
6581 case 0: // Creature
6582 if (!GetCreatureTemplate(id))
6583 {
6584 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id);
6585 continue;
6586 }
6587 break;
6588 case 1: // GameObject
6589 if (!GetGameObjectTemplate(id))
6590 {
6591 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id);
6592 continue;
6593 }
6594 break;
6595 default:
6596 continue;
6597 }
6598
6599 uint16 greetEmoteType = fields[2].GetUInt16();
6600 uint32 greetEmoteDelay = fields[3].GetUInt32();
6601 std::string greeting = fields[4].GetString();
6602
6603 _questGreetingStore[type].emplace(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(greetEmoteType, greetEmoteDelay, std::move(greeting)));
6604 ++count;
6605 }
6606 while (result->NextRow());
6607
6608 TC_LOG_INFO("server.loading", ">> Loaded {} quest_greeting in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6609}
6610
6612{
6613 uint32 oldMSTime = getMSTime();
6614
6615 _tavernAreaTriggerStore.clear(); // need for reload case
6616
6617 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
6618
6619 if (!result)
6620 {
6621 TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
6622 return;
6623 }
6624
6625 uint32 count = 0;
6626
6627 do
6628 {
6629 ++count;
6630
6631 Field* fields = result->Fetch();
6632
6633 uint32 Trigger_ID = fields[0].GetUInt32();
6634
6635 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6636 if (!atEntry)
6637 {
6638 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6639 continue;
6640 }
6641
6642 _tavernAreaTriggerStore.insert(Trigger_ID);
6643 } while (result->NextRow());
6644
6645 TC_LOG_INFO("server.loading", ">> Loaded {} tavern triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6646}
6647
6649{
6650 uint32 oldMSTime = getMSTime();
6651
6652 _areaTriggerScriptStore.clear(); // need for reload case
6653
6654 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
6655 if (!result)
6656 {
6657 TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
6658 return;
6659 }
6660
6661 do
6662 {
6663 Field* fields = result->Fetch();
6664
6665 uint32 triggerId = fields[0].GetUInt32();
6666 std::string_view scriptName = fields[1].GetStringView();
6667
6668 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
6669 if (!atEntry)
6670 {
6671 TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: {}) does not exist in `AreaTrigger.dbc`.", triggerId);
6672 continue;
6673 }
6674 _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
6675 }
6676 while (result->NextRow());
6677
6678 TC_LOG_INFO("server.loading", ">> Loaded {} areatrigger scripts in {} ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6679}
6680
6681uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
6682{
6683 bool found = false;
6684 float dist = 10000;
6685 uint32 id = 0;
6686
6687 auto isVisibleForFaction = [&](TaxiNodesEntry const* node)
6688 {
6689 switch (team)
6690 {
6691 case HORDE: return node->GetFlags().HasFlag(TaxiNodeFlags::ShowOnHordeMap);
6692 case ALLIANCE: return node->GetFlags().HasFlag(TaxiNodeFlags::ShowOnAllianceMap);
6693 default: break;
6694 }
6695 return false;
6696 };
6697
6698 for (TaxiNodesEntry const* node : sTaxiNodesStore)
6699 {
6700 if (!node || node->ContinentID != mapid || !isVisibleForFaction(node) || node->GetFlags().HasFlag(TaxiNodeFlags::IgnoreForFindNearest))
6701 continue;
6702
6703 uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
6704 TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
6705
6706 // skip not taxi network nodes
6707 if ((sTaxiNodesMask[field] & submask) == 0)
6708 continue;
6709
6710 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);
6711 if (found)
6712 {
6713 if (dist2 < dist)
6714 {
6715 dist = dist2;
6716 id = node->ID;
6717 }
6718 }
6719 else
6720 {
6721 found = true;
6722 dist = dist2;
6723 id = node->ID;
6724 }
6725 }
6726
6727 return id;
6728}
6729
6730void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
6731{
6732 TaxiPathEntry const* taxiPath = sDB2Manager.GetTaxiPath(source, destination);
6733 if (taxiPath)
6734 {
6735 path = taxiPath->ID;
6736 cost = taxiPath->Cost;
6737 }
6738 else
6739 {
6740 path = 0;
6741 cost = 0;
6742 }
6743}
6744
6745uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
6746{
6747 CreatureModel mountModel;
6748 CreatureTemplate const* mount_info = nullptr;
6749
6750 // select mount creature id
6751 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
6752 if (node)
6753 {
6754 uint32 mount_entry = 0;
6755 if (team == ALLIANCE)
6756 mount_entry = node->MountCreatureID[1];
6757 else
6758 mount_entry = node->MountCreatureID[0];
6759
6760 // Fix for Alliance not being able to use Acherus taxi
6761 // only one mount type for both sides
6762 if (mount_entry == 0 && allowed_alt_team)
6763 {
6764 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
6765 mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
6766 }
6767
6768 mount_info = GetCreatureTemplate(mount_entry);
6769 if (mount_info)
6770 {
6771 CreatureModel const* model = mount_info->GetRandomValidModel();
6772 if (!model)
6773 {
6774 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
6775 return 0;
6776 }
6777 mountModel = *model;
6778 }
6779 }
6780
6781 // minfo is not actually used but the mount_id was updated
6782 GetCreatureModelRandomGender(&mountModel, mount_info);
6783
6784 return mountModel.CreatureDisplayID;
6785}
6786
6788{
6789 auto itr = _questTemplates.find(quest_id);
6790 return itr != _questTemplates.end() ? itr->second.get() : nullptr;
6791}
6792
6797
6799{
6800 uint32 oldMSTime = getMSTime();
6801
6802 GraveyardStore.clear(); // needed for reload case
6803
6804 // 0 1
6805 QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone FROM graveyard_zone");
6806
6807 if (!result)
6808 {
6809 TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
6810 return;
6811 }
6812
6813 uint32 count = 0;
6814
6815 do
6816 {
6817 ++count;
6818
6819 Field* fields = result->Fetch();
6820
6821 uint32 safeLocId = fields[0].GetUInt32();
6822 uint32 zoneId = fields[1].GetUInt32();
6823
6824 WorldSafeLocsEntry const* entry = GetWorldSafeLoc(safeLocId);
6825 if (!entry)
6826 {
6827 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: {}), skipped.", safeLocId);
6828 continue;
6829 }
6830
6831 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
6832 if (!areaEntry)
6833 {
6834 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: {}), skipped.", zoneId);
6835 continue;
6836 }
6837
6838 if (!AddGraveyardLink(safeLocId, zoneId, 0, false))
6839 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
6840 } while (result->NextRow());
6841
6842 TC_LOG_INFO("server.loading", ">> Loaded {} graveyard-zone links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6843}
6844
6846{
6847 enum DefaultGraveyard
6848 {
6849 HORDE_GRAVEYARD = 10, // Crossroads
6850 ALLIANCE_GRAVEYARD = 4 // Westfall
6851 };
6852
6853 if (team == HORDE)
6854 return GetWorldSafeLoc(HORDE_GRAVEYARD);
6855 else if (team == ALLIANCE)
6856 return GetWorldSafeLoc(ALLIANCE_GRAVEYARD);
6857 else return nullptr;
6858}
6859
6860WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(WorldLocation const& location, uint32 team, WorldObject* conditionObject) const
6861{
6862 float x, y, z;
6863 location.GetPosition(x, y, z);
6864 uint32 MapId = location.GetMapId();
6865
6866 // search for zone associated closest graveyard
6867 uint32 zoneId = sTerrainMgr.GetZoneId(conditionObject ? conditionObject->GetPhaseShift() : PhasingHandler::GetEmptyPhaseShift(), MapId, x, y, z);
6868
6869 if (!zoneId)
6870 {
6871 if (z > -500)
6872 {
6873 TC_LOG_ERROR("misc", "ZoneId not found for map {} coords ({}, {}, {})", MapId, x, y, z);
6874 return GetDefaultGraveyard(team);
6875 }
6876 }
6877
6878 WorldSafeLocsEntry const* graveyard = GetClosestGraveyardInZone(location, team, conditionObject, zoneId);
6879 AreaTableEntry const* zoneEntry = sAreaTableStore.AssertEntry(zoneId);
6880 AreaTableEntry const* parentEntry = sAreaTableStore.LookupEntry(zoneEntry->ParentAreaID);
6881
6882 while (!graveyard && parentEntry)
6883 {
6884 graveyard = GetClosestGraveyardInZone(location, team, conditionObject, parentEntry->ID);
6885 if (!graveyard && parentEntry->ParentAreaID != 0)
6886 parentEntry = sAreaTableStore.LookupEntry(parentEntry->ParentAreaID);
6887 else // nothing found, cant look further, give up.
6888 parentEntry = nullptr;
6889 }
6890
6891 if (!graveyard && !sMapStore.LookupEntry(MapId)->IsBattlegroundOrArena())
6892 {
6893 if (zoneId != 0)
6894 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
6895
6896 graveyard = GetDefaultGraveyard(team);
6897 }
6898
6899 return graveyard;
6900}
6901
6902WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyardInZone(WorldLocation const& location, uint32 team, WorldObject* conditionObject, uint32 zoneId) const
6903{
6904 uint32 MapId = location.GetMapId();
6905
6906 // Simulate std. algorithm:
6907 // found some graveyard associated to (ghost_zone, ghost_map)
6908 //
6909 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
6910 // then check faction
6911 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
6912 // then check faction
6913 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6914 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
6915
6916 // at corpse map
6917 Optional<float> distNear;
6918 WorldSafeLocsEntry const* entryNear = nullptr;
6919
6920 // at entrance map for corpse map
6921 Optional<float> distEntr;
6922 WorldSafeLocsEntry const* entryEntr = nullptr;
6923
6924 // some where other
6925 WorldSafeLocsEntry const* entryFar = nullptr;
6926
6927 ConditionSourceInfo conditionSource(conditionObject);
6928
6929 for (; range.first != range.second; ++range.first)
6930 {
6931 GraveyardData const& data = range.first->second;
6932
6934
6935 if (conditionObject)
6936 {
6937 if (!data.Conditions.Meets(conditionSource))
6938 continue;
6939
6940 if (int16(entry->Loc.GetMapId()) == mapEntry->ParentMapID && !conditionObject->GetPhaseShift().HasVisibleMapId(entry->Loc.GetMapId()))
6941 continue;
6942 }
6943 else if (team != 0)
6944 {
6945 bool teamConditionMet = true;
6946 if (std::shared_ptr<std::vector<Condition>> conditions = data.Conditions.Conditions.lock())
6947 {
6948 for (Condition const& cond : *conditions)
6949 {
6950 if (cond.ConditionType != CONDITION_TEAM)
6951 continue;
6952
6953 if (cond.ConditionValue1 == team)
6954 continue;
6955
6956 teamConditionMet = false;
6957 }
6958 }
6959
6960 if (!teamConditionMet)
6961 continue;
6962 }
6963
6964 // find now nearest graveyard at other map
6965 if (MapId != entry->Loc.GetMapId() && int16(entry->Loc.GetMapId()) != mapEntry->ParentMapID)
6966 {
6967 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
6968 if (!mapEntry
6969 || mapEntry->CorpseMapID < 0
6970 || uint32(mapEntry->CorpseMapID) != entry->Loc.GetMapId()
6971 || (mapEntry->Corpse.X == 0 && mapEntry->Corpse.Y == 0)) // Check X and Y
6972 {
6973 // not have any corrdinates for check distance anyway
6974 entryFar = entry;
6975 continue;
6976 }
6977
6978 // at entrance map calculate distance (2D);
6979 float dist2 = entry->Loc.GetExactDist2dSq(mapEntry->Corpse.X, mapEntry->Corpse.Y);
6980 if (!distEntr || dist2 < *distEntr)
6981 {
6982 distEntr = dist2;
6983 entryEntr = entry;
6984 }
6985 }
6986 // find now nearest graveyard at same map
6987 else
6988 {
6989 float dist2 = entry->Loc.GetExactDistSq(location);
6990 if (!distNear || dist2 < *distNear)
6991 {
6992 distNear = dist2;
6993 entryNear = entry;
6994 }
6995 }
6996 }
6997
6998 if (entryNear)
6999 return entryNear;
7000
7001 if (entryEntr)
7002 return entryEntr;
7003
7004 return entryFar;
7005}
7006
7008{
7009 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
7010 for (; range.first != range.second; ++range.first)
7011 {
7012 GraveyardData const& data = range.first->second;
7013 if (data.safeLocId == id)
7014 return &data;
7015 }
7016 return nullptr;
7017}
7018
7020{
7021 uint32 oldMSTime = getMSTime();
7022
7023 // 0 1 2 3 4 5 6
7024 if (QueryResult result = WorldDatabase.Query("SELECT ID, MapID, LocX, LocY, LocZ, Facing, TransportSpawnId FROM world_safe_locs"))
7025 {
7026 do
7027 {
7028 Field* fields = result->Fetch();
7029 uint32 id = fields[0].GetUInt32();
7030 WorldLocation loc(fields[1].GetUInt32(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat(), DegToRad(fields[5].GetFloat()));
7032 {
7033 TC_LOG_ERROR("sql.sql", "World location (ID: {}) has a invalid position {}, skipped", id, loc);
7034 continue;
7035 }
7036
7037 Optional<ObjectGuid::LowType> transportSpawnId = fields[6].GetUInt64OrNull();
7038 if (transportSpawnId && !sTransportMgr->GetTransportSpawn(*transportSpawnId))
7039 {
7040 TC_LOG_ERROR("sql.sql", "World location (ID: {}) has a invalid transportSpawnID {}, skipped.", id, *transportSpawnId);
7041 continue;
7042 }
7043
7044 WorldSafeLocsEntry& worldSafeLocs = _worldSafeLocs[id];
7045 worldSafeLocs.ID = id;
7046 worldSafeLocs.Loc.WorldRelocate(loc);
7047 worldSafeLocs.TransportSpawnId = transportSpawnId;
7048
7049 } while (result->NextRow());
7050
7051 TC_LOG_INFO("server.loading", ">> Loaded {} world locations {} ms", _worldSafeLocs.size(), GetMSTimeDiffToNow(oldMSTime));
7052 }
7053 else
7054 TC_LOG_INFO("server.loading", ">> Loaded 0 world locations. DB table `world_safe_locs` is empty.");
7055}
7056
7061
7066
7071
7076
7077bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
7078{
7079 if (FindGraveyardData(id, zoneId))
7080 return false;
7081
7082 // add link to loaded data
7083 GraveyardData data;
7084 data.safeLocId = id;
7085
7086 GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
7087
7088 // add link to DB
7089 if (persist)
7090 {
7092
7093 stmt->setUInt32(0, id);
7094 stmt->setUInt32(1, zoneId);
7095
7096 WorldDatabase.Execute(stmt);
7097
7098 // Store graveyard condition if team is set
7099 if (team != 0)
7100 {
7101 WorldDatabasePreparedStatement* conditionStmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CONDITION);
7102 conditionStmt->setUInt32(0, CONDITION_SOURCE_TYPE_GRAVEYARD); // SourceTypeOrReferenceId
7103 conditionStmt->setUInt32(1, zoneId); // SourceGroup
7104 conditionStmt->setUInt32(2, id); // SourceEntry
7105 conditionStmt->setUInt32(3, 0); // SourceId
7106 conditionStmt->setUInt32(4, 0); // ElseGroup
7107 conditionStmt->setUInt32(5, CONDITION_TEAM); // ConditionTypeOrReference
7108 conditionStmt->setUInt8(6, 0); // ConditionTarget
7109 conditionStmt->setUInt32(7, team); // ConditionValue1
7110 conditionStmt->setUInt32(8, 0); // ConditionValue2
7111 conditionStmt->setUInt32(9, 0); // ConditionValue3
7112 conditionStmt->setUInt8(10, 0); // NegativeCondition
7113 conditionStmt->setUInt32(11, 0); // ErrorType
7114 conditionStmt->setUInt32(12, 0); // ErrorTextId
7115 conditionStmt->setString(13, ""sv); // ScriptName
7116 conditionStmt->setString(14, ""sv); // Comment
7117
7118 WorldDatabase.Execute(conditionStmt);
7119
7120 // reload conditions to make sure everything is loaded as it should be
7121 sConditionMgr->LoadConditions(true);
7122 sScriptMgr->NotifyScriptIDUpdate();
7123 }
7124 }
7125
7126 return true;
7127}
7128
7130{
7131 uint32 oldMSTime = getMSTime();
7132
7133 _areaTriggerStore.clear(); // needed for reload case
7134
7135 // 0 1
7136 QueryResult result = WorldDatabase.Query("SELECT ID, PortLocID FROM areatrigger_teleport");
7137 if (!result)
7138 {
7139 TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
7140 return;
7141 }
7142
7143 uint32 count = 0;
7144
7145 do
7146 {
7147 Field* fields = result->Fetch();
7148
7149 ++count;
7150
7151 uint32 Trigger_ID = fields[0].GetUInt32();
7152 uint32 PortLocID = fields[1].GetUInt32();
7153
7154 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
7155 if (!atEntry)
7156 {
7157 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) does not exist in AreaTrigger.dbc.", Trigger_ID);
7158 continue;
7159 }
7160
7161 WorldSafeLocsEntry const* portLoc = GetWorldSafeLoc(PortLocID);
7162 if (!portLoc)
7163 {
7164 TC_LOG_ERROR("sql.sql", "Area Trigger (ID: {}) has a non-existing Port Loc (ID: {}) in WorldSafeLocs.dbc, skipped", Trigger_ID, PortLocID);
7165 continue;
7166 }
7167
7168 _areaTriggerStore[Trigger_ID] = portLoc;
7169
7170 } while (result->NextRow());
7171
7172 TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7173}
7174
7176{
7177 for (AreaTriggerEntry const* areaTrigger : sAreaTriggerStore)
7178 {
7179 if (areaTrigger->GetShapeType() != AreaTriggerShapeType::Polygon)
7180 continue;
7181
7182 PathDb2 const* path = sDB2Manager.GetPath(areaTrigger->ShapeID);
7183 if (!path || path->Locations.size() < 4)
7184 continue;
7185
7186 AreaTriggerPolygon& polygon = _areaTriggerPolygons[areaTrigger->ID];
7187 polygon.Vertices.resize(path->Locations.size() - 1);
7188 std::ranges::transform(path->Locations.begin() + 1, path->Locations.end(), polygon.Vertices.begin(), [](DBCPosition3D const& pos)
7189 {
7190 return Position(pos.X, pos.Y, pos.Z);
7191 });
7192
7193 for (PathPropertyEntry const* pathProperty : path->Properties)
7194 if (pathProperty->GetPropertyIndex() == PathPropertyIndex::VolumeHeight)
7195 polygon.Height = pathProperty->Value * 0.001f + 0.02f;
7196 }
7197}
7198
7200{
7201 uint32 oldMSTime = getMSTime();
7202
7203 _accessRequirementStore.clear(); // need for reload case
7204
7205 // 0 1 2 3 4 5 6 7 8 9
7206 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");
7207
7208 if (!result)
7209 {
7210 TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
7211 return;
7212 }
7213
7214 uint32 count = 0;
7215
7216 do
7217 {
7218 Field* fields = result->Fetch();
7219
7220 uint32 mapid = fields[0].GetUInt32();
7221 if (!sMapStore.LookupEntry(mapid))
7222 {
7223 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not exist, skipped.", mapid);
7224 continue;
7225 }
7226
7227 Difficulty difficulty = Difficulty(fields[1].GetInt32());
7228 if (!sDB2Manager.GetMapDifficultyData(mapid, difficulty))
7229 {
7230 TC_LOG_ERROR("sql.sql", "Map {} referenced in `access_requirement` does not have difficulty {}, skipped", mapid, difficulty);
7231 continue;
7232 }
7233
7234 uint64 requirement_ID = MAKE_PAIR64(mapid, difficulty);
7235
7236 AccessRequirement* ar = &_accessRequirementStore[requirement_ID];
7237 ar->levelMin = fields[2].GetUInt8();
7238 ar->levelMax = fields[3].GetUInt8();
7239 ar->item = fields[4].GetUInt32();
7240 ar->item2 = fields[5].GetUInt32();
7241 ar->quest_A = fields[6].GetUInt32();
7242 ar->quest_H = fields[7].GetUInt32();
7243 ar->achievement = fields[8].GetUInt32();
7244 ar->questFailedText = fields[9].GetString();
7245
7246 if (ar->item)
7247 {
7248 ItemTemplate const* pProto = GetItemTemplate(ar->item);
7249 if (!pProto)
7250 {
7251 TC_LOG_ERROR("sql.sql", "Key item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item, mapid, difficulty);
7252 ar->item = 0;
7253 }
7254 }
7255
7256 if (ar->item2)
7257 {
7258 ItemTemplate const* pProto = GetItemTemplate(ar->item2);
7259 if (!pProto)
7260 {
7261 TC_LOG_ERROR("sql.sql", "Second item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item2, mapid, difficulty);
7262 ar->item2 = 0;
7263 }
7264 }
7265
7266 if (ar->quest_A)
7267 {
7268 if (!GetQuestTemplate(ar->quest_A))
7269 {
7270 TC_LOG_ERROR("sql.sql", "Required Alliance Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_A, mapid, difficulty);
7271 ar->quest_A = 0;
7272 }
7273 }
7274
7275 if (ar->quest_H)
7276 {
7277 if (!GetQuestTemplate(ar->quest_H))
7278 {
7279 TC_LOG_ERROR("sql.sql", "Required Horde Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_H, mapid, difficulty);
7280 ar->quest_H = 0;
7281 }
7282 }
7283
7284 if (ar->achievement)
7285 {
7286 if (!sAchievementStore.LookupEntry(ar->achievement))
7287 {
7288 TC_LOG_ERROR("sql.sql", "Required Achievement {} not exist for map {} difficulty {}, remove quest done requirement.", ar->achievement, mapid, difficulty);
7289 ar->achievement = 0;
7290 }
7291 }
7292 ++count;
7293
7294 } while (result->NextRow());
7295
7296 TC_LOG_INFO("server.loading", ">> Loaded {} access requirement definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7297}
7298
7299/*
7300 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
7301 */
7303{
7304 Optional<uint32> parentId;
7305 MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
7306 if (!mapEntry || mapEntry->CorpseMapID < 0)
7307 return nullptr;
7308
7309 if (mapEntry->IsDungeon())
7310 if (InstanceTemplate const* iTemplate = GetInstanceTemplate(Map))
7311 parentId = iTemplate->Parent;
7312
7313 uint32 entrance_map = parentId.value_or(mapEntry->CorpseMapID);
7314 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7315 {
7316 if (itr->second->Loc.GetMapId() == entrance_map)
7317 {
7318 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7319 if (atEntry && atEntry->ContinentID == Map)
7320 return itr->second;
7321 }
7322 }
7323 return nullptr;
7324}
7325
7330{
7331 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7332 {
7333 if (itr->second->Loc.GetMapId() == Map)
7334 {
7335 if (sAreaTriggerStore.HasRecord(itr->first))
7336 return itr->second;
7337 }
7338 }
7339 return nullptr;
7340}
7341
7343{
7344 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
7345 if (result)
7346 GetGuidSequenceGenerator(HighGuid::Player).Set((*result)[0].GetUInt64() + 1);
7347
7348 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
7349 if (result)
7350 GetGenerator<HighGuid::Item>().Set((*result)[0].GetUInt64() + 1);
7351
7352 // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
7353 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7354 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7355 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 >= '{}'",
7356 GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7357 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7358
7359 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
7360 if (result)
7361 GetGuidSequenceGenerator(HighGuid::Transport).Set((*result)[0].GetUInt64() + 1);
7362
7363 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
7364 if (result)
7365 _auctionId = (*result)[0].GetUInt32()+1;
7366
7367 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
7368 if (result)
7369 _mailId = (*result)[0].GetUInt64()+1;
7370
7371 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
7372 if (result)
7373 sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
7374
7375 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");
7376 if (result)
7377 _equipmentSetGuid = (*result)[0].GetUInt64()+1;
7378
7379 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
7380 if (result)
7381 sGuildMgr->SetNextGuildId((*result)[0].GetUInt64()+1);
7382
7383 result = CharacterDatabase.Query("SELECT MAX(guid) FROM `groups`");
7384 if (result)
7385 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
7386
7387 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
7388 if (result)
7389 _creatureSpawnId = (*result)[0].GetUInt64() + 1;
7390
7391 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
7392 if (result)
7393 _gameObjectSpawnId = (*result)[0].GetUInt64() + 1;
7394}
7395
7397{
7398 return _guidGenerators.try_emplace(high, high).first->second;
7399}
7400
7402{
7403 if (_auctionId >= 0xFFFFFFFE)
7404 {
7405 TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7407 }
7408 return _auctionId++;
7409}
7410
7412{
7413 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
7414 {
7415 TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7417 }
7418 return _equipmentSetGuid++;
7419}
7420
7422{
7423 if (_mailId >= UI64LIT(0xFFFFFFFFFFFFFFFE))
7424 {
7425 TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7427 }
7428 return _mailId++;
7429}
7430
7432{
7433 if (_hiPetNumber >= 0xFFFFFFFE)
7434 {
7435 TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7437 }
7438 return _hiPetNumber++;
7439}
7440
7442{
7443 if (_creatureSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7444 {
7445 TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7447 }
7448 return _creatureSpawnId++;
7449}
7450
7452{
7453 if (_gameObjectSpawnId >= uint64(0xFFFFFFFFFFFFFFFELL))
7454 {
7455 TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7457 }
7458 return _gameObjectSpawnId++;
7459}
7460
7462{
7463 uint32 oldMSTime = getMSTime();
7464
7465 _gameObjectLocaleStore.clear(); // need for reload case
7466
7467 // 0 1 2 3 4
7468 QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption, unk1 FROM gameobject_template_locale");
7469 if (!result)
7470 return;
7471
7472 do
7473 {
7474 Field* fields = result->Fetch();
7475
7476 uint32 id = fields[0].GetUInt32();
7477 std::string_view localeName = fields[1].GetStringView();
7478
7479 LocaleConstant locale = GetLocaleByName(localeName);
7480 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
7481 continue;
7482
7484 AddLocaleString(fields[2].GetStringView(), locale, data.Name);
7485 AddLocaleString(fields[3].GetStringView(), locale, data.CastBarCaption);
7486 AddLocaleString(fields[4].GetStringView(), locale, data.Unk1);
7487
7488 } while (result->NextRow());
7489
7490 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject_template_locale strings in {} ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
7491}
7492
7494{
7495 uint32 oldMSTime = getMSTime();
7496
7497 _destructibleHitpointStore.clear(); // need for reload case
7498
7499 // 0 1 2
7500 QueryResult result = WorldDatabase.Query("SELECT Id, IntactNumHits, DamagedNumHits FROM destructible_hitpoint");
7501 if (!result)
7502 return;
7503
7504 do
7505 {
7506 Field* fields = result->Fetch();
7507
7508 uint32 id = fields[0].GetUInt32();
7509
7511 data.Id = id;
7512 data.IntactNumHits = fields[1].GetUInt32();
7513 data.DamagedNumHits = fields[2].GetUInt32();
7514
7515 } while (result->NextRow());
7516
7517 TC_LOG_INFO("server.loading", ">> Loaded {} destructible_hitpoint records in {} ms", _destructibleHitpointStore.size(), GetMSTimeDiffToNow(oldMSTime));
7518}
7519
7520inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7521{
7522 if (sLockStore.LookupEntry(dataN))
7523 return;
7524
7525 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but lock (Id: {}) not found.",
7526 goInfo->entry, goInfo->type, N, dataN, dataN);
7527}
7528
7529inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7530{
7531 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
7532 {
7533 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
7534 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but GO (Entry {}) have not GAMEOBJECT_TYPE_TRAP ({}) type.",
7535 goInfo->entry, goInfo->type, N, dataN, dataN, uint32(GAMEOBJECT_TYPE_TRAP));
7536 }
7537}
7538
7539inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7540{
7541 if (sSpellMgr->GetSpellInfo(dataN, DIFFICULTY_NONE))
7542 return;
7543
7544 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but Spell (Entry {}) not exist.",
7545 goInfo->entry, goInfo->type, N, dataN, dataN);
7546}
7547
7548inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
7549{
7551 return;
7552
7553 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but correct chair height in range 0..{}.",
7555
7556 // prevent client and server unexpected work
7557 dataN = 0;
7558}
7559
7561{
7562 // 0/1 correct values
7563 if (dataN <= 1)
7564 return;
7565
7566 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
7567}
7568
7569inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7570{
7571 // 0/1 correct values
7572 if (dataN <= 1)
7573 return;
7574
7575 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) consumable field value.",
7576 goInfo->entry, goInfo->type, N, dataN);
7577}
7578
7580{
7581 uint32 oldMSTime = getMSTime();
7582
7583 for (GameObjectsEntry const* db2go : sGameObjectsStore)
7584 {
7586 go.entry = db2go->ID;
7587 go.type = db2go->TypeID;
7588 go.displayId = db2go->DisplayID;
7589 go.name = db2go->Name[sWorld->GetDefaultDbcLocale()];
7590 go.size = db2go->Scale;
7591 memset(go.raw.data, 0, sizeof(go.raw.data));
7592 std::ranges::copy(db2go->PropValue, std::begin(go.raw.data));
7593 go.ContentTuningId = 0;
7594 go.RequiredLevel = 0;
7595 go.ScriptId = 0;
7596 }
7597
7598 // 0 1 2 3 4 5 6 7
7599 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
7600 // 8 9 10 11 12 13 14 15 16 17 18 19 20
7601 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
7602 // 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
7603 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, "
7604 // 37 38 39 40 41 42 43 44 45 46 47
7605 "Data29, Data30, Data31, Data32, Data33, Data34, ContentTuningId, RequiredLevel, AIName, ScriptName, StringId "
7606 "FROM gameobject_template");
7607
7608 if (!result)
7609 {
7610 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
7611 return;
7612 }
7613
7614 _gameObjectTemplateStore.reserve(result->GetRowCount());
7615 do
7616 {
7617 Field* fields = result->Fetch();
7618
7619 uint32 entry = fields[0].GetUInt32();
7620
7622 got.entry = entry;
7623 got.type = uint32(fields[1].GetUInt8());
7624 got.displayId = fields[2].GetUInt32();
7625 got.name = fields[3].GetString();
7626 got.IconName = fields[4].GetString();
7627 got.castBarCaption = fields[5].GetString();
7628 got.unk1 = fields[6].GetString();
7629 got.size = fields[7].GetFloat();
7630
7631 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
7632 got.raw.data[i] = fields[8 + i].GetUInt32();
7633
7634 got.ContentTuningId = fields[43].GetInt32();
7635 got.RequiredLevel = fields[44].GetInt32();
7636 got.AIName = fields[45].GetString();
7637 got.ScriptId = GetScriptId(fields[46].GetStringView());
7638 got.StringId = fields[47].GetString();
7639
7640 // Checks
7641 if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
7642 {
7643 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
7644 got.AIName.clear();
7645 }
7646
7647 switch (got.type)
7648 {
7649 case GAMEOBJECT_TYPE_DOOR: //0
7650 {
7651 if (got.door.open)
7652 CheckGOLockId(&got, got.door.open, 1);
7654 break;
7655 }
7656 case GAMEOBJECT_TYPE_BUTTON: //1
7657 {
7658 if (got.button.open)
7659 CheckGOLockId(&got, got.button.open, 1);
7661 break;
7662 }
7664 {
7665 if (got.questgiver.open)
7666 CheckGOLockId(&got, got.questgiver.open, 0);
7668 break;
7669 }
7670 case GAMEOBJECT_TYPE_CHEST: //3
7671 {
7672 if (got.chest.open)
7673 CheckGOLockId(&got, got.chest.open, 0);
7674
7675 if (got.chest.linkedTrap) // linked trap
7676 CheckGOLinkedTrapId(&got, got.chest.linkedTrap, 7);
7677 break;
7678 }
7679 case GAMEOBJECT_TYPE_TRAP: //6
7680 {
7681 if (got.trap.open)
7682 CheckGOLockId(&got, got.trap.open, 0);
7683 break;
7684 }
7685 case GAMEOBJECT_TYPE_CHAIR: //7
7687 break;
7689 {
7690 if (got.spellFocus.spellFocusType)
7691 {
7692 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.spellFocusType))
7693 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but SpellFocus (Id: {}) not exist.",
7695 }
7696
7697 if (got.spellFocus.linkedTrap) // linked trap
7699 break;
7700 }
7701 case GAMEOBJECT_TYPE_GOOBER: //10
7702 {
7703 if (got.goober.open)
7704 CheckGOLockId(&got, got.goober.open, 0);
7705
7706 CheckGOConsumable(&got, got.goober.consumable, 3);
7707
7708 if (got.goober.pageID) // pageId
7709 {
7710 if (!GetPageText(got.goober.pageID))
7711 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data7={} but PageText (Entry {}) not exist.",
7712 entry, got.type, got.goober.pageID, got.goober.pageID);
7713 }
7715 if (got.goober.linkedTrap) // linked trap
7716 CheckGOLinkedTrapId(&got, got.goober.linkedTrap, 12);
7717 break;
7718 }
7720 {
7721 if (got.areaDamage.open)
7722 CheckGOLockId(&got, got.areaDamage.open, 0);
7723 break;
7724 }
7725 case GAMEOBJECT_TYPE_CAMERA: //13
7726 {
7727 if (got.camera.open)
7728 CheckGOLockId(&got, got.camera.open, 0);
7729 break;
7730 }
7732 {
7733 if (got.moTransport.taxiPathID)
7734 {
7736 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but TaxiPath (Id: {}) not exist.",
7737 entry, got.type, got.moTransport.taxiPathID, got.moTransport.taxiPathID);
7738 }
7739 if (uint32 transportMap = got.moTransport.SpawnMap)
7740 _transportMaps.insert(transportMap);
7741 break;
7742 }
7743 case GAMEOBJECT_TYPE_RITUAL: //18
7744 break;
7746 {
7747 // always must have spell
7748 CheckGOSpellId(&got, got.spellCaster.spell, 0);
7749 break;
7750 }
7751 case GAMEOBJECT_TYPE_FLAGSTAND: //24
7752 {
7753 if (got.flagStand.open)
7754 CheckGOLockId(&got, got.flagStand.open, 0);
7756 break;
7757 }
7759 {
7760 if (got.fishingHole.open)
7761 CheckGOLockId(&got, got.fishingHole.open, 4);
7762 break;
7763 }
7764 case GAMEOBJECT_TYPE_FLAGDROP: //26
7765 {
7766 if (got.flagDrop.open)
7767 CheckGOLockId(&got, got.flagDrop.open, 0);
7769 break;
7770 }
7773
7775 {
7776 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data2 = {} but AnimKit.dbc (Id: {}) not exist, set to 0.",
7779 }
7780 break;
7783 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) Has non existing Destructible Hitpoint Record {}.", entry, got.destructibleBuilding.HealthRec);
7784 break;
7786 if (uint32 transportMap = got.garrisonBuilding.SpawnMap)
7787 _transportMaps.insert(transportMap);
7788 break;
7790 if (got.gatheringNode.open)
7791 CheckGOLockId(&got, got.gatheringNode.open, 0);
7792 if (got.gatheringNode.linkedTrap)
7794 break;
7795 }
7796 } while (result->NextRow());
7797
7798 TC_LOG_INFO("server.loading", ">> Loaded {} game object templates in {} ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
7799}
7800
7802{
7803 uint32 oldMSTime = getMSTime();
7804
7805 // 0 1 2 3 4 5 6 7 8 9 10 11
7806 QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3, artkit4, WorldEffectID, AIAnimKitID FROM gameobject_template_addon");
7807
7808 if (!result)
7809 {
7810 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
7811 return;
7812 }
7813
7814 uint32 count = 0;
7815 do
7816 {
7817 Field* fields = result->Fetch();
7818
7819 uint32 entry = fields[0].GetUInt32();
7820
7821 GameObjectTemplate const* got = GetGameObjectTemplate(entry);
7822 if (!got)
7823 {
7824 TC_LOG_ERROR("sql.sql", "GameObject template (Entry: {}) does not exist but has a record in `gameobject_template_addon`", entry);
7825 continue;
7826 }
7827
7829 gameObjectAddon.Faction = uint32(fields[1].GetUInt16());
7830 gameObjectAddon.Flags = fields[2].GetUInt32();
7831 gameObjectAddon.Mingold = fields[3].GetUInt32();
7832 gameObjectAddon.Maxgold = fields[4].GetUInt32();
7833 gameObjectAddon.WorldEffectID = fields[10].GetUInt32();
7834 gameObjectAddon.AIAnimKitID = fields[11].GetUInt32();
7835
7836 for (uint32 i = 0; i < gameObjectAddon.ArtKits.size(); ++i)
7837 {
7838 uint32 artKitID = fields[5 + i].GetUInt32();
7839 if (!artKitID)
7840 continue;
7841
7842 if (!sGameObjectArtKitStore.LookupEntry(artKitID))
7843 {
7844 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` ({}) defined, set to zero instead.", entry, i, artKitID);
7845 continue;
7846 }
7847
7848 gameObjectAddon.ArtKits[i] = artKitID;
7849 }
7850
7851 // checks
7852 if (gameObjectAddon.Faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.Faction))
7853 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid faction ({}) defined in `gameobject_template_addon`.", entry, gameObjectAddon.Faction);
7854
7855 if (gameObjectAddon.Maxgold > 0)
7856 {
7857 switch (got->type)
7858 {
7861 break;
7862 default:
7863 TC_LOG_ERROR("sql.sql", "GameObject (Entry {} GoType: {}) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
7864 break;
7865 }
7866 }
7867
7868 if (gameObjectAddon.WorldEffectID && !sWorldEffectStore.LookupEntry(gameObjectAddon.WorldEffectID))
7869 {
7870 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid WorldEffectID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.WorldEffectID);
7871 gameObjectAddon.WorldEffectID = 0;
7872 }
7873
7874 if (gameObjectAddon.AIAnimKitID && !sAnimKitStore.LookupEntry(gameObjectAddon.AIAnimKitID))
7875 {
7876 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid AIAnimKitID ({}) defined in `gameobject_template_addon`, set to 0.", entry, gameObjectAddon.AIAnimKitID);
7877 gameObjectAddon.AIAnimKitID = 0;
7878 }
7879
7880 ++count;
7881 }
7882 while (result->NextRow());
7883
7884 TC_LOG_INFO("server.loading", ">> Loaded {} game object template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7885}
7886
7888{
7889 uint32 oldMSTime = getMSTime();
7890
7891 // 0 1 2
7892 QueryResult result = WorldDatabase.Query("SELECT spawnId, faction, flags FROM gameobject_overrides");
7893 if (!result)
7894 {
7895 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject faction and flags overrides. DB table `gameobject_overrides` is empty.");
7896 return;
7897 }
7898
7899 uint32 count = 0;
7900 do
7901 {
7902 Field* fields = result->Fetch();
7903
7904 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
7905 GameObjectData const* goData = GetGameObjectData(spawnId);
7906 if (!goData)
7907 {
7908 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) does not exist but has a record in `gameobject_overrides`", spawnId);
7909 continue;
7910 }
7911
7912 GameObjectOverride& gameObjectOverride = _gameObjectOverrideStore[spawnId];
7913 gameObjectOverride.Faction = fields[1].GetUInt16();
7914 gameObjectOverride.Flags = fields[2].GetUInt32();
7915
7916 if (gameObjectOverride.Faction && !sFactionTemplateStore.LookupEntry(gameObjectOverride.Faction))
7917 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) has invalid faction ({}) defined in `gameobject_overrides`.", spawnId, gameObjectOverride.Faction);
7918
7919 ++count;
7920 } while (result->NextRow());
7921
7922 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject faction and flags overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7923}
7924
7926{
7927 uint32 oldMSTime = getMSTime();
7928
7929 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
7930
7931 if (!result)
7932 {
7933 TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
7934 return;
7935 }
7936
7937 uint32 count = 0;
7938
7939 do
7940 {
7941 Field* fields = result->Fetch();
7942 uint8 level = fields[0].GetUInt8();
7943 uint32 basexp = fields[1].GetInt32();
7944 _baseXPTable[level] = basexp;
7945 ++count;
7946 }
7947 while (result->NextRow());
7948
7949 TC_LOG_INFO("server.loading", ">> Loaded {} BaseXP definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7950}
7951
7953{
7954 return _baseXPTable[level] ? _baseXPTable[level] : 0;
7955}
7956
7958{
7959 if (level < _playerXPperLevel.size())
7960 return _playerXPperLevel[level];
7961 return 0;
7962}
7963
7965{
7966 if (!areaEntry)
7967 return 0;
7968
7969 // Get level for the area
7970 FishingBaseSkillContainer::const_iterator itr = _fishingBaseForAreaStore.find(areaEntry->ID);
7971 if (itr != _fishingBaseForAreaStore.end())
7972 return itr->second;
7973
7974 // If there is no data for the current area and it has a parent area, get data from the last (recursive)
7975 if (AreaTableEntry const* parentAreaEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID))
7976 return GetFishingBaseSkillLevel(parentAreaEntry);
7977
7978 TC_LOG_ERROR("sql.sql", "Fishable areaId {} is not properly defined in `skill_fishing_base_level`.", areaEntry->ID);
7979
7980 return 0;
7981}
7982
7984{
7985 auto itr = _skillTiers.find(skillTierId);
7986 return itr != _skillTiers.end() ? &itr->second : nullptr;
7987}
7988
7990{
7991 if (tierIndex >= MAX_SKILL_STEP)
7992 tierIndex = MAX_SKILL_STEP - 1;
7993
7994 while (Value[tierIndex] == 0 && tierIndex > 0)
7995 --tierIndex;
7996
7997 return Value[tierIndex];
7998}
7999
8001{
8002 uint32 oldMSTime = getMSTime();
8003 // 0 1 2
8004 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
8005
8006 if (!result)
8007 {
8008 TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
8009 return;
8010 }
8011
8012 uint32 count = 0;
8013
8014 do
8015 {
8016 Field* fields = result->Fetch();
8017 std::string word = fields[0].GetString();
8018 uint32 entry = fields[1].GetUInt32();
8019 bool half = fields[2].GetBool();
8020 if (half)
8021 _petHalfName1[entry].push_back(word);
8022 else
8023 _petHalfName0[entry].push_back(word);
8024 ++count;
8025 }
8026 while (result->NextRow());
8027
8028 TC_LOG_INFO("server.loading", ">> Loaded {} pet name parts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8029}
8030
8032{
8033 uint32 oldMSTime = getMSTime();
8034
8035 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
8036 if (result)
8037 {
8038 Field* fields = result->Fetch();
8039 _hiPetNumber = fields[0].GetUInt32()+1;
8040 }
8041
8042 TC_LOG_INFO("server.loading", ">> Loaded the max pet number: {} in {} ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
8043}
8044
8046{
8047 std::vector<std::string>& list0 = _petHalfName0[entry];
8048 std::vector<std::string>& list1 = _petHalfName1[entry];
8049
8050 if (list0.empty() || list1.empty())
8051 {
8052 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
8053 if (!cinfo)
8054 return std::string();
8055
8056 char const* petname = DB2Manager::GetCreatureFamilyPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
8057 if (petname)
8058 return std::string(petname);
8059 else
8060 return cinfo->Name;
8061 }
8062
8063 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
8064}
8065
8067{
8068 uint32 oldMSTime = getMSTime();
8069
8070 _repRewardRateStore.clear(); // for reload case
8071
8072 uint32 count = 0; // 0 1 2 3 4 5 6 7
8073 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");
8074 if (!result)
8075 {
8076 TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
8077 return;
8078 }
8079
8080 do
8081 {
8082 Field* fields = result->Fetch();
8083
8084 uint32 factionId = fields[0].GetUInt32();
8085
8086 RepRewardRate repRate;
8087
8088 repRate.questRate = fields[1].GetFloat();
8089 repRate.questDailyRate = fields[2].GetFloat();
8090 repRate.questWeeklyRate = fields[3].GetFloat();
8091 repRate.questMonthlyRate = fields[4].GetFloat();
8092 repRate.questRepeatableRate = fields[5].GetFloat();
8093 repRate.creatureRate = fields[6].GetFloat();
8094 repRate.spellRate = fields[7].GetFloat();
8095
8096 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8097 if (!factionEntry)
8098 {
8099 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_reward_rate`", factionId);
8100 continue;
8101 }
8102
8103 if (repRate.questRate < 0.0f)
8104 {
8105 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate {}, skipping data for faction {}", repRate.questRate, factionId);
8106 continue;
8107 }
8108
8109 if (repRate.questDailyRate < 0.0f)
8110 {
8111 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate {}, skipping data for faction {}", repRate.questDailyRate, factionId);
8112 continue;
8113 }
8114
8115 if (repRate.questWeeklyRate < 0.0f)
8116 {
8117 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate {}, skipping data for faction {}", repRate.questWeeklyRate, factionId);
8118 continue;
8119 }
8120
8121 if (repRate.questMonthlyRate < 0.0f)
8122 {
8123 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate {}, skipping data for faction {}", repRate.questMonthlyRate, factionId);
8124 continue;
8125 }
8126
8127 if (repRate.questRepeatableRate < 0.0f)
8128 {
8129 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate {}, skipping data for faction {}", repRate.questRepeatableRate, factionId);
8130 continue;
8131 }
8132
8133 if (repRate.creatureRate < 0.0f)
8134 {
8135 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate {}, skipping data for faction {}", repRate.creatureRate, factionId);
8136 continue;
8137 }
8138
8139 if (repRate.spellRate < 0.0f)
8140 {
8141 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate {}, skipping data for faction {}", repRate.spellRate, factionId);
8142 continue;
8143 }
8144
8145 _repRewardRateStore[factionId] = repRate;
8146
8147 ++count;
8148 }
8149 while (result->NextRow());
8150
8151 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_reward_rate in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8152}
8153
8155{
8156 uint32 oldMSTime = getMSTime();
8157
8158 // For reload case
8159 _repOnKillStore.clear();
8160
8161 uint32 count = 0;
8162
8163 // 0 1 2
8164 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
8165 // 3 4 5 6 7 8 9
8166 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
8167 "FROM creature_onkill_reputation");
8168
8169 if (!result)
8170 {
8171 TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
8172 return;
8173 }
8174
8175 do
8176 {
8177 Field* fields = result->Fetch();
8178
8179 uint32 creature_id = fields[0].GetUInt32();
8180
8181 ReputationOnKillEntry repOnKill;
8182 repOnKill.RepFaction1 = fields[1].GetInt16();
8183 repOnKill.RepFaction2 = fields[2].GetInt16();
8184 repOnKill.IsTeamAward1 = fields[3].GetBool();
8185 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
8186 repOnKill.RepValue1 = fields[5].GetInt32();
8187 repOnKill.IsTeamAward2 = fields[6].GetBool();
8188 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
8189 repOnKill.RepValue2 = fields[8].GetInt32();
8190 repOnKill.TeamDependent = fields[9].GetBool();
8191
8192 if (!GetCreatureTemplate(creature_id))
8193 {
8194 TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry ({}), skipped", creature_id);
8195 continue;
8196 }
8197
8198 if (repOnKill.RepFaction1)
8199 {
8200 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
8201 if (!factionEntry1)
8202 {
8203 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
8204 continue;
8205 }
8206 }
8207
8208 if (repOnKill.RepFaction2)
8209 {
8210 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
8211 if (!factionEntry2)
8212 {
8213 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
8214 continue;
8215 }
8216 }
8217
8218 _repOnKillStore[creature_id] = repOnKill;
8219
8220 ++count;
8221 } while (result->NextRow());
8222
8223 TC_LOG_INFO("server.loading", ">> Loaded {} creature award reputation definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8224}
8225
8227{
8228 uint32 oldMSTime = getMSTime();
8229
8230 _repSpilloverTemplateStore.clear(); // for reload case
8231
8232 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8233 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");
8234
8235 if (!result)
8236 {
8237 TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
8238 return;
8239 }
8240
8241 do
8242 {
8243 Field* fields = result->Fetch();
8244
8245 uint32 factionId = fields[0].GetUInt16();
8246
8247 RepSpilloverTemplate repTemplate;
8248
8249 repTemplate.faction[0] = fields[1].GetUInt16();
8250 repTemplate.faction_rate[0] = fields[2].GetFloat();
8251 repTemplate.faction_rank[0] = fields[3].GetUInt8();
8252 repTemplate.faction[1] = fields[4].GetUInt16();
8253 repTemplate.faction_rate[1] = fields[5].GetFloat();
8254 repTemplate.faction_rank[1] = fields[6].GetUInt8();
8255 repTemplate.faction[2] = fields[7].GetUInt16();
8256 repTemplate.faction_rate[2] = fields[8].GetFloat();
8257 repTemplate.faction_rank[2] = fields[9].GetUInt8();
8258 repTemplate.faction[3] = fields[10].GetUInt16();
8259 repTemplate.faction_rate[3] = fields[11].GetFloat();
8260 repTemplate.faction_rank[3] = fields[12].GetUInt8();
8261 repTemplate.faction[4] = fields[13].GetUInt16();
8262 repTemplate.faction_rate[4] = fields[14].GetFloat();
8263 repTemplate.faction_rank[4] = fields[15].GetUInt8();
8264
8265 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8266
8267 if (!factionEntry)
8268 {
8269 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template`", factionId);
8270 continue;
8271 }
8272
8273 if (factionEntry->ParentFactionID == 0)
8274 {
8275 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} in `reputation_spillover_template` does not belong to any team, skipping", factionId);
8276 continue;
8277 }
8278
8279 bool invalidSpilloverFaction = false;
8280 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
8281 {
8282 if (repTemplate.faction[i])
8283 {
8284 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
8285
8286 if (!factionSpillover)
8287 {
8288 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);
8289 invalidSpilloverFaction = true;
8290 break;
8291 }
8292
8293 if (!factionSpillover->CanHaveReputation())
8294 {
8295 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);
8296 invalidSpilloverFaction = true;
8297 break;
8298 }
8299
8300 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
8301 {
8302 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]);
8303 invalidSpilloverFaction = true;
8304 break;
8305 }
8306 }
8307 }
8308
8309 if (invalidSpilloverFaction)
8310 continue;
8311
8312 _repSpilloverTemplateStore[factionId] = repTemplate;
8313
8314 ++count;
8315 }
8316 while (result->NextRow());
8317
8318 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_spillover_template in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8319}
8320
8322{
8323 uint32 oldMSTime = getMSTime();
8324
8325 _pointsOfInterestStore.clear(); // need for reload case
8326
8327 uint32 count = 0;
8328
8329 // 0 1 2 3 4 5 6 7 8
8330 QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, PositionZ, Icon, Flags, Importance, Name, WMOGroupID FROM points_of_interest");
8331
8332 if (!result)
8333 {
8334 TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
8335 return;
8336 }
8337
8338 do
8339 {
8340 Field* fields = result->Fetch();
8341
8342 uint32 id = fields[0].GetUInt32();
8343
8344 PointOfInterest pointOfInterest;
8345 pointOfInterest.ID = id;
8346 pointOfInterest.Pos.Relocate(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat());
8347 pointOfInterest.Icon = fields[4].GetUInt32();
8348 pointOfInterest.Flags = fields[5].GetUInt32();
8349 pointOfInterest.Importance = fields[6].GetUInt32();
8350 pointOfInterest.Name = fields[7].GetString();
8351 pointOfInterest.WMOGroupID = fields[8].GetInt32();
8352
8353 if (!Trinity::IsValidMapCoord(pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ()))
8354 {
8355 TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: {}) have invalid coordinates (PositionX: {} PositionY: {}, PositionZ: {}), ignored.",
8356 id, pointOfInterest.Pos.GetPositionX(), pointOfInterest.Pos.GetPositionY(), pointOfInterest.Pos.GetPositionZ());
8357 continue;
8358 }
8359
8360 _pointsOfInterestStore[id] = pointOfInterest;
8361
8362 ++count;
8363 } while (result->NextRow());
8364
8365 TC_LOG_INFO("server.loading", ">> Loaded {} Points of Interest definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8366}
8367
8369{
8370 uint32 oldMSTime = getMSTime();
8371
8372 _questPOIStore.clear(); // need for reload case
8373
8374 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
8375 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");
8376 if (!result)
8377 {
8378 TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
8379 return;
8380 }
8381
8382 // 0 1 2 3 4
8383 QueryResult pointsResult = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y, Z FROM quest_poi_points ORDER BY QuestID DESC, Idx1, Idx2");
8384
8385 std::unordered_map<int32, std::map<int32, std::vector<QuestPOIBlobPoint>>> allPoints;
8386
8387 if (pointsResult)
8388 {
8389 do
8390 {
8391 Field* fields = pointsResult->Fetch();
8392
8393 int32 QuestID = fields[0].GetInt32();
8394 int32 Idx1 = fields[1].GetInt32();
8395 int32 x = fields[2].GetInt32();
8396 int32 y = fields[3].GetInt32();
8397 int32 z = fields[4].GetInt32();
8398
8399 allPoints[QuestID][Idx1].emplace_back(x, y, z);
8400 } while (pointsResult->NextRow());
8401 }
8402
8403 do
8404 {
8405 Field* fields = result->Fetch();
8406
8407 int32 questID = fields[0].GetInt32();
8408 int32 blobIndex = fields[1].GetInt32();
8409 int32 idx1 = fields[2].GetInt32();
8410 int32 objectiveIndex = fields[3].GetInt32();
8411 int32 questObjectiveID = fields[4].GetInt32();
8412 int32 questObjectID = fields[5].GetInt32();
8413 int32 mapID = fields[6].GetInt32();
8414 int32 uiMapID = fields[7].GetInt32();
8415 int32 priority = fields[8].GetInt32();
8416 int32 flags = fields[9].GetInt32();
8417 int32 worldEffectID = fields[10].GetInt32();
8418 int32 playerConditionID = fields[11].GetInt32();
8419 int32 navigationPlayerConditionID = fields[12].GetInt32();
8420 int32 spawnTrackingID = fields[13].GetInt32();
8421 bool alwaysAllowMergingBlobs = fields[14].GetBool();
8422
8423 if (!GetQuestTemplate(questID))
8424 TC_LOG_ERROR("sql.sql", "`quest_poi` quest id ({}) Idx1 ({}) does not exist in `quest_template`", questID, idx1);
8425
8426 if (std::map<int32, std::vector<QuestPOIBlobPoint>>* blobs = Trinity::Containers::MapGetValuePtr(allPoints, questID))
8427 {
8428 if (std::vector<QuestPOIBlobPoint>* points = Trinity::Containers::MapGetValuePtr(*blobs, idx1))
8429 {
8430 QuestPOIData& poiData = _questPOIStore[questID];
8431 poiData.QuestID = questID;
8432 poiData.Blobs.emplace_back(blobIndex, objectiveIndex, questObjectiveID, questObjectID, mapID, uiMapID, priority, flags,
8433 worldEffectID, playerConditionID, navigationPlayerConditionID, spawnTrackingID, std::move(*points), alwaysAllowMergingBlobs);
8434 continue;
8435 }
8436 }
8437
8438 TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest {} POI id {}", questID, blobIndex);
8439
8440 } while (result->NextRow());
8441
8442 TC_LOG_INFO("server.loading", ">> Loaded {} quest POI definitions in {} ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
8443}
8444
8446{
8447 uint32 oldMSTime = getMSTime();
8448
8449 _spellClickInfoStore.clear();
8450 // 0 1 2 3
8451 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
8452
8453 if (!result)
8454 {
8455 TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
8456 return;
8457 }
8458
8459 uint32 count = 0;
8460
8461 do
8462 {
8463 Field* fields = result->Fetch();
8464
8465 uint32 npc_entry = fields[0].GetUInt32();
8466 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
8467 if (!cInfo)
8468 {
8469 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template {}. Skipping entry.", npc_entry);
8470 continue;
8471 }
8472
8473 uint32 spellid = fields[1].GetUInt32();
8474 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid, DIFFICULTY_NONE);
8475 if (!spellinfo)
8476 {
8477 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown spellid {}. Skipping entry.", npc_entry, spellid);
8478 continue;
8479 }
8480
8481 uint8 userType = fields[3].GetUInt16();
8482 if (userType >= SPELL_CLICK_USER_MAX)
8483 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown user type {}. Skipping entry.", npc_entry, uint32(userType));
8484
8485 uint8 castFlags = fields[2].GetUInt8();
8486 SpellClickInfo info;
8487 info.spellId = spellid;
8488 info.castFlags = castFlags;
8489 info.userType = SpellClickUserTypes(userType);
8490 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
8491
8492 ++count;
8493 }
8494 while (result->NextRow());
8495
8496 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
8497 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
8498 for (auto& creatureTemplatePair : _creatureTemplateStore)
8499 {
8500 if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
8501 {
8502 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);
8503 creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
8504 }
8505 }
8506
8507 TC_LOG_INFO("server.loading", ">> Loaded {} spellclick definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8508}
8509
8514
8516{
8517 // remove mapid*cellid -> guid_set map
8518 auto itr = _creatureDataStore.find(spawnId);
8519 if (itr == _creatureDataStore.end())
8520 return;
8521
8522 RemoveCreatureFromGrid(&itr->second);
8523 OnDeleteSpawnData(&itr->second);
8524
8525 _creatureDataStore.erase(itr);
8526}
8527
8532
8534{
8535 // remove mapid*cellid -> guid_set map
8536 auto itr = _gameObjectDataStore.find(spawnId);
8537 if (itr == _gameObjectDataStore.end())
8538 return;
8539
8540 RemoveGameobjectFromGrid(&itr->second);
8541 OnDeleteSpawnData(&itr->second);
8542
8543 _gameObjectDataStore.erase(itr);
8544}
8545
8546void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table)
8547{
8548 uint32 oldMSTime = getMSTime();
8549
8550 map.clear(); // need for reload case
8551
8552 uint32 count = 0;
8553
8554 QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM {}", table);
8555
8556 if (!result)
8557 {
8558 TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `{}`, table is empty.", table);
8559 return;
8560 }
8561
8562 do
8563 {
8564 uint32 id = result->Fetch()[0].GetUInt32();
8565 uint32 quest = result->Fetch()[1].GetUInt32();
8566
8567 if (!_questTemplates.count(quest))
8568 {
8569 TC_LOG_ERROR("sql.sql", "Table `{}`: Quest {} listed for entry {} does not exist.", table, quest, id);
8570 continue;
8571 }
8572
8573 map.insert(QuestRelations::value_type(id, quest));
8574 if (reverseMap)
8575 reverseMap->insert(QuestRelationsReverse::value_type(quest, id));
8576 ++count;
8577 } while (result->NextRow());
8578
8579 TC_LOG_INFO("server.loading", ">> Loaded {} quest relations from {} in {} ms", count, table, GetMSTimeDiffToNow(oldMSTime));
8580}
8581
8583{
8584 LoadQuestRelationsHelper(_goQuestRelations, nullptr, "gameobject_queststarter");
8585
8586 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
8587 {
8588 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8589 if (!goInfo)
8590 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8591 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8592 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);
8593 }
8594}
8595
8597{
8599
8600 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
8601 {
8602 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8603 if (!goInfo)
8604 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8605 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8606 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);
8607 }
8608}
8609
8611{
8612 LoadQuestRelationsHelper(_creatureQuestRelations, nullptr, "creature_queststarter");
8613
8614 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
8615 {
8616 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8617 if (!cInfo)
8618 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8619 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8620 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);
8621 }
8622}
8623
8625{
8627
8628 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
8629 {
8630 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8631 if (!cInfo)
8632 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8633 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8634 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);
8635 }
8636}
8637
8639{
8640 while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second))
8641 ++_it;
8642}
8643
8645{
8646 return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId));
8647}
8648
8650{
8651 uint32 oldMSTime = getMSTime();
8652
8653 _reservedNamesStore.clear(); // need for reload case
8654
8655 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
8656
8657 if (!result)
8658 {
8659 TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
8660 return;
8661 }
8662
8663 uint32 count = 0;
8664
8665 Field* fields;
8666 do
8667 {
8668 fields = result->Fetch();
8669 std::string name= fields[0].GetString();
8670
8671 std::wstring wstr;
8672 if (!Utf8toWStr (name, wstr))
8673 {
8674 TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: {}", name);
8675 continue;
8676 }
8677
8678 wstrToLower(wstr);
8679
8680 _reservedNamesStore.insert(wstr);
8681 ++count;
8682 }
8683 while (result->NextRow());
8684
8685 TC_LOG_INFO("server.loading", ">> Loaded {} reserved player names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8686}
8687
8688bool ObjectMgr::IsReservedName(std::string_view name) const
8689{
8690 std::wstring wstr;
8691 if (!Utf8toWStr (name, wstr))
8692 return false;
8693
8694 wstrToLower(wstr);
8695
8696 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
8697}
8698
8700{
8701 if (std::shared_ptr<Realm const> currentRealm = sRealmList->GetCurrentRealm())
8702 if (Cfg_CategoriesEntry const* category = sCfgCategoriesStore.LookupEntry(currentRealm->Timezone))
8703 return create ? category->GetCreateCharsetMask() : category->GetExistingCharsetMask();
8704
8705 return create ? CfgCategoriesCharsets::English : CfgCategoriesCharsets::Any; // basic-Latin at create, any at login
8706}
8707
8708bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
8709{
8710 if (strictMask == 0) // any language, ignore realm
8711 {
8712 if (isExtendedLatinString(wstr, numericOrSpace))
8713 return true;
8714 if (isCyrillicString(wstr, numericOrSpace))
8715 return true;
8716 if (isKoreanString(wstr, numericOrSpace))
8717 return true;
8718 if (isChineseString(wstr, numericOrSpace))
8719 return true;
8720 return false;
8721 }
8722
8723 if (strictMask & 0x2) // realm zone specific
8724 {
8727 return true;
8728 if (lt.HasFlag(CfgCategoriesCharsets::Latin1) && isExtendedLatinString(wstr, numericOrSpace))
8729 return true;
8730 if (lt.HasFlag(CfgCategoriesCharsets::English) && isBasicLatinString(wstr, numericOrSpace))
8731 return true;
8732 if (lt.HasFlag(CfgCategoriesCharsets::Russian) && isCyrillicString(wstr, numericOrSpace))
8733 return true;
8734 if (lt.HasFlag(CfgCategoriesCharsets::Korean) && isKoreanString(wstr, numericOrSpace))
8735 return true;
8736 if (lt.HasFlag(CfgCategoriesCharsets::Chinese) && isChineseString(wstr, numericOrSpace))
8737 return true;
8738 }
8739
8740 if (strictMask & 0x1) // basic Latin
8741 {
8742 if (isBasicLatinString(wstr, numericOrSpace))
8743 return true;
8744 }
8745
8746 return false;
8747}
8748
8749ResponseCodes ObjectMgr::CheckPlayerName(std::string_view name, LocaleConstant locale, bool create /*= false*/)
8750{
8751 std::wstring wname;
8752 if (!Utf8toWStr(name, wname))
8754
8755 if (wname.size() > MAX_PLAYER_NAME)
8756 return CHAR_NAME_TOO_LONG;
8757
8758 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
8759 if (wname.size() < minName)
8760 return CHAR_NAME_TOO_SHORT;
8761
8762 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
8763 if (!isValidString(wname, strictMask, false, create))
8765
8766 wstrToLower(wname);
8767 for (size_t i = 2; i < wname.size(); ++i)
8768 if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
8770
8771 return sDB2Manager.ValidateName(wname, locale);
8772}
8773
8774bool ObjectMgr::IsValidCharterName(std::string_view name)
8775{
8776 std::wstring wname;
8777 if (!Utf8toWStr(name, wname))
8778 return false;
8779
8780 if (wname.size() > MAX_CHARTER_NAME)
8781 return false;
8782
8783 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
8784 if (wname.size() < minName)
8785 return false;
8786
8787 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
8788
8789 return isValidString(wname, strictMask, true);
8790}
8791
8793{
8794 std::wstring wname;
8795 if (!Utf8toWStr(name, wname))
8796 return PET_NAME_INVALID;
8797
8798 if (wname.size() > MAX_PET_NAME)
8799 return PET_NAME_TOO_LONG;
8800
8801 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
8802 if (wname.size() < minName)
8803 return PET_NAME_TOO_SHORT;
8804
8805 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
8806 if (!isValidString(wname, strictMask, false))
8808
8809 return PET_NAME_SUCCESS;
8810}
8811
8813{
8814 uint32 oldMSTime = getMSTime();
8815
8816 _gameObjectForQuestStore.clear(); // need for reload case
8817
8818 if (_gameObjectTemplateStore.empty())
8819 {
8820 TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
8821 return;
8822 }
8823
8824 uint32 count = 0;
8825
8826 // collect GO entries for GO that must activated
8827 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
8828 {
8829 switch (gameObjectTemplatePair.second.type)
8830 {
8832 break;
8834 {
8835 // scan GO chest with loot including quest items
8836 // find quest loot for GO
8837 if (gameObjectTemplatePair.second.chest.questID
8838 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestLoot)
8839 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPersonalLoot)
8840 || LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.chest.chestPushLoot))
8841 break;
8842 continue;
8843 }
8845 {
8846 if (gameObjectTemplatePair.second.generic.questID > 0) //quests objects
8847 break;
8848 continue;
8849 }
8851 {
8852 if (gameObjectTemplatePair.second.spellFocus.questID > 0) //quests objects
8853 break;
8854 continue;
8855 }
8857 {
8858 if (gameObjectTemplatePair.second.goober.questID > 0) //quests objects
8859 break;
8860 continue;
8861 }
8863 {
8864 // scan GO chest with loot including quest items
8865 // find quest loot for GO
8866 if (LootTemplates_Gameobject.HaveQuestLootFor(gameObjectTemplatePair.second.gatheringNode.chestLoot))
8867 break;
8868 continue;
8869 }
8870 default:
8871 continue;
8872 }
8873
8874 _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
8875 ++count;
8876 }
8877
8878 for (auto [questObjectiveId, objective] : _questObjectives)
8879 {
8880 if (objective->Type != QUEST_OBJECTIVE_GAMEOBJECT)
8881 continue;
8882
8883 _gameObjectForQuestStore.insert(objective->ObjectID);
8884 ++count;
8885 }
8886
8887 TC_LOG_INFO("server.loading", ">> Loaded {} GameObjects for quests in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8888}
8889
8891{
8892 uint32 oldMSTime = getMSTime();
8893
8894 _trinityStringStore.clear(); // for reload case
8895
8896 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");
8897 if (!result)
8898 {
8899 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.");
8900 return false;
8901 }
8902
8903 do
8904 {
8905 Field* fields = result->Fetch();
8906
8907 uint32 entry = fields[0].GetUInt32();
8908
8909 TrinityString& data = _trinityStringStore[entry];
8910
8911 data.Content.resize(DEFAULT_LOCALE + 1);
8912
8913 for (int8 i = OLD_TOTAL_LOCALES - 1; i >= 0; --i)
8914 AddLocaleString(fields[i + 1].GetStringView(), LocaleConstant(i), data.Content);
8915 }
8916 while (result->NextRow());
8917
8918 TC_LOG_INFO("server.loading", ">> Loaded {} trinity strings in {} ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
8919 return true;
8920}
8921
8922char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
8923{
8924 if (TrinityString const* ts = GetTrinityString(entry))
8925 {
8926 if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
8927 return ts->Content[locale].c_str();
8928 return ts->Content[DEFAULT_LOCALE].c_str();
8929 }
8930
8931 TC_LOG_ERROR("sql.sql", "Trinity string entry {} not found in DB.", entry);
8932 return "<error>";
8933}
8934
8936{
8937 uint32 oldMSTime = getMSTime();
8938
8939 _fishingBaseForAreaStore.clear(); // for reload case
8940
8941 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
8942
8943 if (!result)
8944 {
8945 TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
8946 return;
8947 }
8948
8949 uint32 count = 0;
8950
8951 do
8952 {
8953 Field* fields = result->Fetch();
8954 uint32 entry = fields[0].GetUInt32();
8955 int32 skill = fields[1].GetInt16();
8956
8957 AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
8958 if (!fArea)
8959 {
8960 TC_LOG_ERROR("sql.sql", "AreaId {} defined in `skill_fishing_base_level` does not exist", entry);
8961 continue;
8962 }
8963
8964 _fishingBaseForAreaStore[entry] = skill;
8965 ++count;
8966 }
8967 while (result->NextRow());
8968
8969 TC_LOG_INFO("server.loading", ">> Loaded {} areas for fishing base skill level in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8970}
8971
8973{
8974 uint32 oldMSTime = getMSTime();
8975
8976 QueryResult result = WorldDatabase.Query("SELECT ID, Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9, Value10, "
8977 " Value11, Value12, Value13, Value14, Value15, Value16 FROM skill_tiers");
8978
8979 if (!result)
8980 {
8981 TC_LOG_ERROR("server.loading", ">> Loaded 0 skill max values. DB table `skill_tiers` is empty.");
8982 return;
8983 }
8984
8985 do
8986 {
8987 Field* fields = result->Fetch();
8988 uint32 id = fields[0].GetUInt32();
8989 SkillTiersEntry& tier = _skillTiers[id];
8990 for (uint32 i = 0; i < MAX_SKILL_STEP; ++i)
8991 tier.Value[i] = fields[1 + i].GetUInt32();
8992
8993 } while (result->NextRow());
8994
8995 TC_LOG_INFO("server.loading", ">> Loaded {} skill max values in {} ms", uint32(_skillTiers.size()), GetMSTimeDiffToNow(oldMSTime));
8996}
8997
8998bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
8999{
9000 // get main part of the name
9001 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
9002 // prepare flags
9003 bool x = true;
9004 bool y = true;
9005
9006 // check declined names
9007 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
9008 {
9009 std::wstring wname;
9010 if (!Utf8toWStr(names.name[i], wname))
9011 return false;
9012
9013 if (mainpart != GetMainPartOfName(wname, i+1))
9014 x = false;
9015
9016 if (w_ownname != wname)
9017 y = false;
9018 }
9019 return (x || y);
9020}
9021
9023{
9024 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
9025 if (i!= _areaTriggerScriptStore.end())
9026 return i->second;
9027 return 0;
9028}
9029
9031{
9032 return SpellScriptsBounds(_spellScriptsStore.equal_range(spellId));
9033}
9034
9036{
9037 EventScriptContainer::const_iterator i = _eventScriptStore.find(eventId);
9038 if (i != _eventScriptStore.end())
9039 return i->second;
9040 return 0;
9041}
9042
9043// this allows calculating base reputations to offline players, just by race and class
9044int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
9045{
9046 if (!factionEntry)
9047 return 0;
9048
9049 uint32 classMask = 1 << (playerClass - 1);
9050
9051 for (uint8 i = 0; i < 4; ++i)
9052 {
9053 if ((!factionEntry->ReputationClassMask[i] ||
9054 factionEntry->ReputationClassMask[i] & classMask) &&
9055 (factionEntry->ReputationRaceMask[i].IsEmpty() ||
9056 factionEntry->ReputationRaceMask[i].HasRace(race)))
9057 return factionEntry->ReputationBase[i];
9058 }
9059
9060 return 0;
9061}
9062
9064{
9065 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
9066 if (!skill)
9067 return SKILL_RANGE_NONE;
9068
9069 if (sObjectMgr->GetSkillTier(rcEntry->SkillTierID))
9070 return SKILL_RANGE_RANK;
9071
9072 if (rcEntry->SkillID == SKILL_RUNEFORGING)
9073 return SKILL_RANGE_MONO;
9074
9075 switch (skill->CategoryID)
9076 {
9078 return SKILL_RANGE_MONO;
9080 return SKILL_RANGE_LANGUAGE;
9081 }
9082
9083 return SKILL_RANGE_LEVEL;
9084}
9085
9087{
9088 uint32 oldMSTime = getMSTime();
9089
9090 _gameTeleStore.clear(); // for reload case
9091
9092 // 0 1 2 3 4 5 6
9093 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
9094
9095 if (!result)
9096 {
9097 TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
9098 return;
9099 }
9100
9101 uint32 count = 0;
9102
9103 do
9104 {
9105 Field* fields = result->Fetch();
9106
9107 uint32 id = fields[0].GetUInt32();
9108
9109 GameTele gt;
9110
9111 gt.position_x = fields[1].GetFloat();
9112 gt.position_y = fields[2].GetFloat();
9113 gt.position_z = fields[3].GetFloat();
9114 gt.orientation = fields[4].GetFloat();
9115 gt.mapId = fields[5].GetUInt16();
9116 gt.name = fields[6].GetString();
9117
9119 {
9120 TC_LOG_ERROR("sql.sql", "Wrong position for id {} (name: {}) in `game_tele` table, ignoring.", id, gt.name);
9121 continue;
9122 }
9123
9124 if (!Utf8toWStr(gt.name, gt.wnameLow))
9125 {
9126 TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_tele` table, ignoring.", id);
9127 continue;
9128 }
9129
9131
9132 _gameTeleStore[id] = gt;
9133
9134 ++count;
9135 }
9136 while (result->NextRow());
9137
9138 TC_LOG_INFO("server.loading", ">> Loaded {} GameTeleports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9139}
9140
9141GameTele const* ObjectMgr::GetGameTele(std::string_view name) const
9142{
9143 // explicit name case
9144 std::wstring wname;
9145 if (!Utf8toWStr(name, wname))
9146 return nullptr;
9147
9148 // converting string that we try to find to lower case
9149 wstrToLower(wname);
9150
9151 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
9152 GameTele const* alt = nullptr;
9153 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9154 {
9155 if (itr->second.wnameLow == wname)
9156 return &itr->second;
9157 else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
9158 alt = &itr->second;
9159 }
9160
9161 return alt;
9162}
9163
9164GameTele const* ObjectMgr::GetGameTeleExactName(std::string_view name) const
9165{
9166 // explicit name case
9167 std::wstring wname;
9168 if (!Utf8toWStr(name, wname))
9169 return nullptr;
9170
9171 // converting string that we try to find to lower case
9172 wstrToLower(wname);
9173
9174 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9175 {
9176 if (itr->second.wnameLow == wname)
9177 return &itr->second;
9178 }
9179
9180 return nullptr;
9181}
9182
9184{
9185 // find max id
9186 uint32 new_id = 0;
9187 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9188 if (itr->first > new_id)
9189 new_id = itr->first;
9190
9191 // use next
9192 ++new_id;
9193
9194 if (!Utf8toWStr(tele.name, tele.wnameLow))
9195 return false;
9196
9197 wstrToLower(tele.wnameLow);
9198
9199 _gameTeleStore[new_id] = tele;
9200
9202
9203 stmt->setUInt32(0, new_id);
9204 stmt->setFloat(1, tele.position_x);
9205 stmt->setFloat(2, tele.position_y);
9206 stmt->setFloat(3, tele.position_z);
9207 stmt->setFloat(4, tele.orientation);
9208 stmt->setUInt16(5, uint16(tele.mapId));
9209 stmt->setString(6, tele.name);
9210
9211 WorldDatabase.Execute(stmt);
9212
9213 return true;
9214}
9215
9216bool ObjectMgr::DeleteGameTele(std::string_view name)
9217{
9218 // explicit name case
9219 std::wstring wname;
9220 if (!Utf8toWStr(name, wname))
9221 return false;
9222
9223 // converting string that we try to find to lower case
9224 wstrToLower(wname);
9225
9226 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9227 {
9228 if (itr->second.wnameLow == wname)
9229 {
9231
9232 stmt->setString(0, itr->second.name);
9233
9234 WorldDatabase.Execute(stmt);
9235
9236 _gameTeleStore.erase(itr);
9237 return true;
9238 }
9239 }
9240
9241 return false;
9242}
9243
9245{
9246 uint32 oldMSTime = getMSTime();
9247
9248 _mailLevelRewardStore.clear(); // for reload case
9249
9250 // 0 1 2 3
9251 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
9252
9253 if (!result)
9254 {
9255 TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
9256 return;
9257 }
9258
9259 uint32 count = 0;
9260
9261 do
9262 {
9263 Field* fields = result->Fetch();
9264
9265 uint8 level = fields[0].GetUInt8();
9266 Trinity::RaceMask<uint64> raceMask = { fields[1].GetUInt64() };
9267 uint32 mailTemplateId = fields[2].GetUInt32();
9268 uint32 senderEntry = fields[3].GetUInt32();
9269
9270 if (level > MAX_LEVEL)
9271 {
9272 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level {} that more supported by client ({}), ignoring.", level, MAX_LEVEL);
9273 continue;
9274 }
9275
9276 if ((raceMask & RACEMASK_ALL_PLAYABLE).IsEmpty())
9277 {
9278 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask ({}) for level {} that not include any player races, ignoring.", raceMask.RawValue, level);
9279 continue;
9280 }
9281
9282 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
9283 {
9284 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId ({}) for level {} that invalid not include any player races, ignoring.", mailTemplateId, level);
9285 continue;
9286 }
9287
9288 if (!GetCreatureTemplate(senderEntry))
9289 {
9290 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);
9291 continue;
9292 }
9293
9294 _mailLevelRewardStore[level].emplace_back(raceMask, mailTemplateId, senderEntry);
9295
9296 ++count;
9297 }
9298 while (result->NextRow());
9299
9300 TC_LOG_INFO("server.loading", ">> Loaded {} level dependent mail rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9301}
9302
9304{
9305 uint32 oldMSTime = getMSTime();
9306
9307 // For reload case
9308 _trainers.clear();
9309
9310 std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
9311 if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
9312 {
9313 do
9314 {
9315 Field* fields = trainerSpellsResult->Fetch();
9316
9317 Trainer::Spell spell;
9318 uint32 trainerId = fields[0].GetUInt32();
9319 spell.SpellId = fields[1].GetUInt32();
9320 spell.MoneyCost = fields[2].GetUInt32();
9321 spell.ReqSkillLine = fields[3].GetUInt32();
9322 spell.ReqSkillRank = fields[4].GetUInt32();
9323 spell.ReqAbility[0] = fields[5].GetUInt32();
9324 spell.ReqAbility[1] = fields[6].GetUInt32();
9325 spell.ReqAbility[2] = fields[7].GetUInt32();
9326 spell.ReqLevel = fields[8].GetUInt8();
9327
9328 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId, DIFFICULTY_NONE);
9329 if (!spellInfo)
9330 {
9331 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) for TrainerId {}, ignoring", spell.SpellId, trainerId);
9332 continue;
9333 }
9334
9335 if (spell.ReqSkillLine && !sSkillLineStore.LookupEntry(spell.ReqSkillLine))
9336 {
9337 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing skill (ReqSkillLine: {}) for TrainerId {} and SpellId {}, ignoring",
9338 spell.ReqSkillLine, trainerId, spell.SpellId);
9339 continue;
9340 }
9341
9342 bool allReqValid = true;
9343 for (std::size_t i = 0; i < spell.ReqAbility.size(); ++i)
9344 {
9345 uint32 requiredSpell = spell.ReqAbility[i];
9346 if (requiredSpell && !sSpellMgr->GetSpellInfo(requiredSpell, DIFFICULTY_NONE))
9347 {
9348 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (ReqAbility{}: {}) for TrainerId {} and SpellId {}, ignoring",
9349 i + 1, requiredSpell, trainerId, spell.SpellId);
9350 allReqValid = false;
9351 }
9352 }
9353
9354 if (!allReqValid)
9355 continue;
9356
9357 spellsByTrainer[trainerId].push_back(spell);
9358
9359 } while (trainerSpellsResult->NextRow());
9360 }
9361
9362 if (QueryResult trainersResult = WorldDatabase.Query("SELECT Id, Type, Greeting FROM trainer"))
9363 {
9364 do
9365 {
9366 Field* fields = trainersResult->Fetch();
9367 uint32 trainerId = fields[0].GetUInt32();
9368 Trainer::Type trainerType = Trainer::Type(fields[1].GetUInt8());
9369 std::string_view greeting = fields[2].GetStringView();
9370 std::vector<Trainer::Spell> spells;
9371 auto spellsItr = spellsByTrainer.find(trainerId);
9372 if (spellsItr != spellsByTrainer.end())
9373 {
9374 spells = std::move(spellsItr->second);
9375 spellsByTrainer.erase(spellsItr);
9376 }
9377
9378 _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, greeting, std::move(spells)));
9379
9380 } while (trainersResult->NextRow());
9381 }
9382
9383 for (auto const& unusedSpells : spellsByTrainer)
9384 {
9385 for (Trainer::Spell const& unusedSpell : unusedSpells.second)
9386 {
9387 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing trainer (TrainerId: {}) for SpellId {}, ignoring", unusedSpells.first, unusedSpell.SpellId);
9388 }
9389 }
9390
9391 if (QueryResult trainerLocalesResult = WorldDatabase.Query("SELECT Id, locale, Greeting_lang FROM trainer_locale"))
9392 {
9393 do
9394 {
9395 Field* fields = trainerLocalesResult->Fetch();
9396 uint32 trainerId = fields[0].GetUInt32();
9397 std::string localeName = fields[1].GetString();
9398
9399 LocaleConstant locale = GetLocaleByName(localeName);
9400 if (!IsValidLocale(locale) || !sWorld->getBoolConfig(CONFIG_LOAD_LOCALES) || locale == LOCALE_enUS)
9401 continue;
9402
9403 if (Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId))
9404 trainer->AddGreetingLocale(locale, fields[2].GetString());
9405 else
9406 TC_LOG_ERROR("sql.sql", "Table `trainer_locale` references non-existing trainer (TrainerId: {}) for locale {}, ignoring",
9407 trainerId, localeName);
9408
9409 } while (trainerLocalesResult->NextRow());
9410 }
9411
9412 TC_LOG_INFO("server.loading", ">> Loaded {} Trainers in {} ms", _trainers.size(), GetMSTimeDiffToNow(oldMSTime));
9413}
9414
9416{
9417 uint32 oldMSTime = getMSTime();
9418
9419 _creatureDefaultTrainers.clear();
9420
9421 if (QueryResult result = WorldDatabase.Query("SELECT CreatureID, TrainerID, MenuID, OptionID FROM creature_trainer"))
9422 {
9423 do
9424 {
9425 Field* fields = result->Fetch();
9426 uint32 creatureId = fields[0].GetUInt32();
9427 uint32 trainerId = fields[1].GetUInt32();
9428 uint32 gossipMenuId = fields[2].GetUInt32();
9429 uint32 gossipOptionId = fields[3].GetUInt32();
9430
9431 if (!GetCreatureTemplate(creatureId))
9432 {
9433 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing creature template (CreatureID: {}), ignoring", creatureId);
9434 continue;
9435 }
9436
9437 if (!GetTrainer(trainerId))
9438 {
9439 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing trainer (TrainerID: {}) for CreatureID {} MenuID {} OptionID {}, ignoring",
9440 trainerId, creatureId, gossipMenuId, gossipOptionId);
9441 continue;
9442 }
9443
9444 if (gossipMenuId || gossipOptionId)
9445 {
9446 Trinity::IteratorPair<GossipMenuItemsContainer::const_iterator> gossipMenuItems = GetGossipMenuItemsMapBounds(gossipMenuId);
9447 auto gossipOptionItr = std::find_if(gossipMenuItems.begin(), gossipMenuItems.end(), [gossipOptionId](std::pair<uint32 const, GossipMenuItems> const& entry)
9448 {
9449 return entry.second.OrderIndex == gossipOptionId;
9450 });
9451 if (gossipOptionItr == gossipMenuItems.end())
9452 {
9453 TC_LOG_ERROR("sql.sql", "Table `creature_trainer` references non-existing gossip menu option (MenuID {} OptionID {}) for CreatureID {} and TrainerID {}, ignoring",
9454 gossipMenuId, gossipOptionId, creatureId, trainerId);
9455 continue;
9456 }
9457 }
9458
9459 _creatureDefaultTrainers[std::make_tuple(creatureId, gossipMenuId, gossipOptionId)] = trainerId;
9460 } while (result->NextRow());
9461 }
9462
9463 TC_LOG_INFO("server.loading", ">> Loaded {} default trainers in {} ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
9464}
9465
9466uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
9467{
9468 // find all items from the reference vendor
9470 stmt->setUInt32(0, uint32(item));
9471 PreparedQueryResult result = WorldDatabase.Query(stmt);
9472
9473 if (!result)
9474 return 0;
9475
9476 uint32 count = 0;
9477 do
9478 {
9479 Field* fields = result->Fetch();
9480
9481 int32 item_id = fields[0].GetInt32();
9482
9483 // if item is a negative, its a reference
9484 if (item_id < 0)
9485 count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
9486 else
9487 {
9488 VendorItem vItem;
9489 vItem.item = item_id;
9490 vItem.maxcount = fields[1].GetUInt32();
9491 vItem.incrtime = fields[2].GetUInt32();
9492 vItem.ExtendedCost = fields[3].GetUInt32();
9493 vItem.Type = fields[4].GetUInt8();
9494 vItem.PlayerConditionId = fields[6].GetUInt32();
9495 vItem.IgnoreFiltering = fields[7].GetBool();
9496
9497 for (std::string_view token : Trinity::Tokenize(fields[5].GetStringView(), ' ', false))
9498 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9499 vItem.BonusListIDs.push_back(*bonusListID);
9500
9501 if (!IsVendorItemValid(vendor, vItem, nullptr, skip_vendors))
9502 continue;
9503
9504 VendorItemData& vList = _cacheVendorItemStore[vendor];
9505 vList.AddItem(std::move(vItem));
9506 ++count;
9507 }
9508 } while (result->NextRow());
9509
9510 return count;
9511}
9512
9514{
9515 uint32 oldMSTime = getMSTime();
9516
9517 // For reload case
9518 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
9519 itr->second.Clear();
9520 _cacheVendorItemStore.clear();
9521
9522 std::set<uint32> skip_vendors;
9523
9524 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost, type, BonusListIDs, PlayerConditionID, IgnoreFiltering FROM npc_vendor ORDER BY entry, slot ASC");
9525 if (!result)
9526 {
9527 TC_LOG_ERROR("server.loading", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
9528 return;
9529 }
9530
9531 uint32 count = 0;
9532
9533 do
9534 {
9535 Field* fields = result->Fetch();
9536
9537 uint32 entry = fields[0].GetUInt32();
9538 int32 item_id = fields[1].GetInt32();
9539
9540 // if item is a negative, its a reference
9541 if (item_id < 0)
9542 count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
9543 else
9544 {
9545 VendorItem vItem;
9546 vItem.item = item_id;
9547 vItem.maxcount = fields[2].GetUInt32();
9548 vItem.incrtime = fields[3].GetUInt32();
9549 vItem.ExtendedCost = fields[4].GetUInt32();
9550 vItem.Type = fields[5].GetUInt8();
9551 vItem.PlayerConditionId = fields[7].GetUInt32();
9552 vItem.IgnoreFiltering = fields[8].GetBool();
9553
9554 for (std::string_view token : Trinity::Tokenize(fields[6].GetStringView(), ' ', false))
9555 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
9556 vItem.BonusListIDs.push_back(*bonusListID);
9557
9558 if (!IsVendorItemValid(entry, vItem, nullptr, &skip_vendors))
9559 continue;
9560
9561 VendorItemData& vList = _cacheVendorItemStore[entry];
9562 vList.AddItem(std::move(vItem));
9563 ++count;
9564 }
9565 }
9566 while (result->NextRow());
9567
9568 TC_LOG_INFO("server.loading", ">> Loaded {} Vendors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9569}
9570
9572{
9573 uint32 oldMSTime = getMSTime();
9574
9575 _gossipMenusStore.clear();
9576
9577 // 0 1
9578 QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
9579
9580 if (!result)
9581 {
9582 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
9583 return;
9584 }
9585
9586 do
9587 {
9588 Field* fields = result->Fetch();
9589
9590 GossipMenus gMenu;
9591
9592 gMenu.MenuID = fields[0].GetUInt32();
9593 gMenu.TextID = fields[1].GetUInt32();
9594
9595 if (!GetNpcText(gMenu.TextID))
9596 {
9597 TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID {} is using non-existing TextID {}", gMenu.MenuID, gMenu.TextID);
9598 continue;
9599 }
9600
9601 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
9602 } while (result->NextRow());
9603
9604 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu IDs in {} ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
9605}
9606
9608{
9609 uint32 oldMSTime = getMSTime();
9610
9611 _gossipMenuItemsStore.clear();
9612
9613 QueryResult result = WorldDatabase.Query(
9614 // 0 1 2 3 4 5 6 7 8 9 10
9615 "SELECT MenuID, GossipOptionID, OptionID, OptionNpc, OptionText, OptionBroadcastTextID, Language, Flags, ActionMenuID, ActionPoiID, GossipNpcOptionID, "
9616 //11 12 13 14 15 16
9617 "BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID, SpellID, OverrideIconID "
9618 "FROM gossip_menu_option ORDER BY MenuID, OptionID");
9619
9620 if (!result)
9621 {
9622 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
9623 return;
9624 }
9625
9626 std::unordered_map<int32, int32> optionToNpcOption;
9627 for (GossipNPCOptionEntry const* npcOption : sGossipNPCOptionStore)
9628 optionToNpcOption[npcOption->GossipOptionID] = npcOption->ID;
9629
9630 do
9631 {
9632 Field* fields = result->Fetch();
9633
9634 GossipMenuItems gMenuItem;
9635
9636 gMenuItem.MenuID = fields[0].GetUInt32();
9637 gMenuItem.GossipOptionID = fields[1].GetInt32();
9638 gMenuItem.OrderIndex = fields[2].GetUInt32();
9639 gMenuItem.OptionNpc = GossipOptionNpc(fields[3].GetUInt8());
9640 gMenuItem.OptionText = fields[4].GetString();
9641 gMenuItem.OptionBroadcastTextID = fields[5].GetUInt32();
9642 gMenuItem.Language = fields[6].GetUInt32();
9643 gMenuItem.Flags = GossipOptionFlags(fields[7].GetInt32());
9644 gMenuItem.ActionMenuID = fields[8].GetUInt32();
9645 gMenuItem.ActionPoiID = fields[9].GetUInt32();
9646 gMenuItem.GossipNpcOptionID = fields[10].GetInt32OrNull();
9647 gMenuItem.BoxCoded = fields[11].GetBool();
9648 gMenuItem.BoxMoney = fields[12].GetUInt64();
9649 gMenuItem.BoxText = fields[13].GetString();
9650 gMenuItem.BoxBroadcastTextID = fields[14].GetUInt32();
9651 gMenuItem.SpellID = fields[15].GetInt32OrNull();
9652 gMenuItem.OverrideIconID = fields[16].GetInt32OrNull();
9653
9654 if (gMenuItem.OptionNpc >= GossipOptionNpc::Count)
9655 {
9656 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));
9657 gMenuItem.OptionNpc = GossipOptionNpc::None;
9658 }
9659
9660 if (gMenuItem.OptionBroadcastTextID)
9661 {
9662 if (!sBroadcastTextStore.LookupEntry(gMenuItem.OptionBroadcastTextID))
9663 {
9664 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);
9665 gMenuItem.OptionBroadcastTextID = 0;
9666 }
9667 }
9668
9669 if (gMenuItem.Language && !sLanguagesStore.LookupEntry(gMenuItem.Language))
9670 {
9671 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Language {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.Language);
9672 gMenuItem.Language = 0;
9673 }
9674
9675 if (gMenuItem.ActionMenuID && gMenuItem.OptionNpc != GossipOptionNpc::None)
9676 {
9677 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);
9678 gMenuItem.ActionMenuID = 0;
9679 }
9680
9681 if (gMenuItem.ActionPoiID)
9682 {
9683 if (gMenuItem.OptionNpc != GossipOptionNpc::None)
9684 {
9685 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);
9686 gMenuItem.ActionPoiID = 0;
9687 }
9688 else if (!GetPointOfInterest(gMenuItem.ActionPoiID))
9689 {
9690 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing ActionPoiID {}, ignoring", gMenuItem.MenuID, gMenuItem.OrderIndex, gMenuItem.ActionPoiID);
9691 gMenuItem.ActionPoiID = 0;
9692 }
9693 }
9694
9695 if (gMenuItem.GossipNpcOptionID)
9696 {
9697 if (!sGossipNPCOptionStore.LookupEntry(*gMenuItem.GossipNpcOptionID))
9698 {
9699 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing GossipNPCOption {}, ignoring",
9700 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.GossipNpcOptionID);
9701 gMenuItem.GossipNpcOptionID.reset();
9702 }
9703 }
9704 else if (int32 const* npcOptionId = Trinity::Containers::MapGetValuePtr(optionToNpcOption, gMenuItem.GossipOptionID))
9705 gMenuItem.GossipNpcOptionID = *npcOptionId;
9706
9707 if (gMenuItem.BoxBroadcastTextID)
9708 {
9709 if (!sBroadcastTextStore.LookupEntry(gMenuItem.BoxBroadcastTextID))
9710 {
9711 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);
9712 gMenuItem.BoxBroadcastTextID = 0;
9713 }
9714 }
9715
9716 if (gMenuItem.SpellID)
9717 {
9718 if (!sSpellMgr->GetSpellInfo(*gMenuItem.SpellID, DIFFICULTY_NONE))
9719 {
9720 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing Spell {}, ignoring",
9721 gMenuItem.MenuID, gMenuItem.OrderIndex, *gMenuItem.SpellID);
9722 gMenuItem.SpellID.reset();
9723 }
9724 }
9725
9726 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
9727 } while (result->NextRow());
9728
9729 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option entries in {} ms", _gossipMenuItemsStore.size(), GetMSTimeDiffToNow(oldMSTime));
9730}
9731
9733{
9734 uint32 oldMSTime = getMSTime();
9735
9736 _gossipMenuAddonStore.clear();
9737
9738 // 0 1 2
9739 QueryResult result = WorldDatabase.Query("SELECT MenuID, FriendshipFactionID, LfgDungeonsID FROM gossip_menu_addon");
9740
9741 if (!result)
9742 {
9743 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_addon IDs. DB table `gossip_menu_addon` is empty!");
9744 return;
9745 }
9746
9747 do
9748 {
9749 Field* fields = result->Fetch();
9750
9751 uint32 menuID = fields[0].GetUInt32();
9752 GossipMenuAddon& addon = _gossipMenuAddonStore[menuID];
9753 addon.FriendshipFactionID = fields[1].GetInt32();
9754 addon.LfgDungeonsID = fields[2].GetInt32();
9755
9756 if (addon.FriendshipFactionID)
9757 {
9758 if (FactionEntry const* faction = sFactionStore.LookupEntry(addon.FriendshipFactionID))
9759 {
9760 if (!sFriendshipReputationStore.LookupEntry(faction->FriendshipRepID))
9761 {
9762 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using FriendshipFactionID {} referencing non-existing FriendshipRepID {}",
9763 menuID, addon.FriendshipFactionID, faction->FriendshipRepID);
9764 addon.FriendshipFactionID = 0;
9765 }
9766 }
9767 else
9768 {
9769 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using non-existing FriendshipFactionID {}", menuID, addon.FriendshipFactionID);
9770 addon.FriendshipFactionID = 0;
9771 }
9772 }
9773
9774 if (addon.LfgDungeonsID && sLFGDungeonsStore.LookupEntry(addon.LfgDungeonsID))
9775 {
9776 TC_LOG_ERROR("sql.sql", "Table gossip_menu_addon: ID {} is using non-existing LfgDungeonsID {}", menuID, addon.LfgDungeonsID);
9777 addon.LfgDungeonsID = 0;
9778 }
9779
9780 } while (result->NextRow());
9781
9782 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_addon IDs in {} ms", uint32(_gossipMenuAddonStore.size()), GetMSTimeDiffToNow(oldMSTime));
9783}
9784
9786{
9787 return Trinity::Containers::MapGetValuePtr(_trainers, trainerId);
9788}
9789
9790uint32 ObjectMgr::GetCreatureTrainerForGossipOption(uint32 creatureId, uint32 gossipMenuId, uint32 gossipOptionId) const
9791{
9792 auto itr = _creatureDefaultTrainers.find(std::make_tuple(creatureId, gossipMenuId, gossipOptionId));
9793 if (itr != _creatureDefaultTrainers.end())
9794 return itr->second;
9795
9796 return 0;
9797}
9798
9799void ObjectMgr::AddVendorItem(uint32 entry, VendorItem const& vItem, bool persist /*= true*/)
9800{
9801 VendorItemData& vList = _cacheVendorItemStore[entry];
9802 vList.AddItem(vItem);
9803
9804 if (persist)
9805 {
9807
9808 stmt->setUInt32(0, entry);
9809 stmt->setUInt32(1, vItem.item);
9810 stmt->setUInt8(2, vItem.maxcount);
9811 stmt->setUInt32(3, vItem.incrtime);
9812 stmt->setUInt32(4, vItem.ExtendedCost);
9813 stmt->setUInt8(5, vItem.Type);
9814
9815 WorldDatabase.Execute(stmt);
9816 }
9817}
9818
9819bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist /*= true*/)
9820{
9821 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
9822 if (iter == _cacheVendorItemStore.end())
9823 return false;
9824
9825 if (!iter->second.RemoveItem(item, type))
9826 return false;
9827
9828 if (persist)
9829 {
9831
9832 stmt->setUInt32(0, entry);
9833 stmt->setUInt32(1, item);
9834 stmt->setUInt8(2, type);
9835
9836 WorldDatabase.Execute(stmt);
9837 }
9838
9839 return true;
9840}
9841
9842bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
9843{
9844 CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry);
9845 if (!cInfo)
9846 {
9847 if (player)
9849 else
9850 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: {}), ignore", vendor_entry);
9851 return false;
9852 }
9853
9854 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
9855 {
9856 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9857 {
9858 if (player)
9860 else
9861 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: {}) without vendor flag, ignore", vendor_entry);
9862
9863 if (skip_vendors)
9864 skip_vendors->insert(vendor_entry);
9865 }
9866 return false;
9867 }
9868
9869 if ((vItem.Type == ITEM_VENDOR_TYPE_ITEM && !GetItemTemplate(vItem.item)) ||
9870 (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && !sCurrencyTypesStore.LookupEntry(vItem.item)))
9871 {
9872 if (player)
9874 else
9875 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);
9876 return false;
9877 }
9878
9879 if (vItem.PlayerConditionId && !sPlayerConditionStore.LookupEntry(vItem.PlayerConditionId))
9880 {
9881 if (!sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_PLAYER_CONDITION, vItem.PlayerConditionId))
9882 {
9883 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);
9884 return false;
9885 }
9886 }
9887
9888 if (vItem.ExtendedCost && !sItemExtendedCostStore.LookupEntry(vItem.ExtendedCost))
9889 {
9890 if (player)
9892 else
9893 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);
9894 return false;
9895 }
9896
9897 if (vItem.Type == ITEM_VENDOR_TYPE_ITEM) // not applicable to currencies
9898 {
9899 if (vItem.maxcount > 0 && vItem.incrtime == 0)
9900 {
9901 if (player)
9902 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", vItem.maxcount);
9903 else
9904 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);
9905 return false;
9906 }
9907 else if (vItem.maxcount == 0 && vItem.incrtime > 0)
9908 {
9909 if (player)
9910 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
9911 else
9912 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);
9913 return false;
9914 }
9915
9916 for (int32 bonusListId : vItem.BonusListIDs)
9917 {
9918 if (ItemBonusMgr::GetItemBonuses(bonusListId).empty())
9919 {
9920 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` have Item (Entry: {}) with invalid bonus {} for vendor ({}), ignore", vItem.item, bonusListId, vendor_entry);
9921 return false;
9922 }
9923 }
9924 }
9925
9926 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
9927 if (!vItems)
9928 return true; // later checks for non-empty lists
9929
9930 if (vItems->FindItemCostPair(vItem.item, vItem.ExtendedCost, vItem.Type))
9931 {
9932 if (player)
9934 else
9935 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);
9936 return false;
9937 }
9938
9939 if (vItem.Type == ITEM_VENDOR_TYPE_CURRENCY && vItem.maxcount == 0)
9940 {
9941 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);
9942 return false;
9943 }
9944
9945 return true;
9946}
9947
9949{
9950 // We insert an empty placeholder here so we can use the
9951 // script id 0 as dummy for "no script found".
9952 [[maybe_unused]] uint32 const id = insert("", false);
9953
9954 ASSERT(id == 0);
9955}
9956
9958{
9959 IndexToName.reserve(capacity);
9960}
9961
9962uint32 ObjectMgr::ScriptNameContainer::insert(std::string_view scriptName, bool isScriptNameBound)
9963{
9964 auto result = NameToIndex.lower_bound(scriptName);
9965 if (result == NameToIndex.end() || NameToIndex.key_comp()(scriptName, result->first))
9966 {
9967 ASSERT(NameToIndex.size() <= std::numeric_limits<uint32>::max());
9968 result = NameToIndex.emplace_hint(result, scriptName, Entry(static_cast<uint32>(NameToIndex.size()), isScriptNameBound));
9969 IndexToName.emplace_back(result);
9970 }
9971
9972 return result->second.Id;
9973}
9974
9976{
9977 return IndexToName.size();
9978}
9979
9980ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(size_t index) const
9981{
9982 return index < IndexToName.size() ? IndexToName[index] : end();
9983}
9984
9985ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(std::string_view name) const
9986{
9987 // assume "" is the first element
9988 if (name.empty())
9989 return end();
9990
9991 return NameToIndex.find(name);
9992}
9993
9994ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::end() const
9995{
9996 return NameToIndex.end();
9997}
9998
9999std::unordered_set<std::string> ObjectMgr::ScriptNameContainer::GetAllDBScriptNames() const
10000{
10001 std::unordered_set<std::string> scriptNames;
10002
10003 for (std::pair<std::string const, Entry> const& entry : NameToIndex)
10004 {
10005 if (entry.second.IsScriptDatabaseBound)
10006 {
10007 scriptNames.insert(entry.first);
10008 }
10009 }
10010
10011 return scriptNames;
10012}
10013
10014std::unordered_set<std::string> ObjectMgr::GetAllDBScriptNames() const
10015{
10016 return _scriptNamesStore.GetAllDBScriptNames();
10017}
10018
10019std::string const& ObjectMgr::GetScriptName(uint32 id) const
10020{
10021 auto const itr = _scriptNamesStore.find(id);
10022 if (itr != _scriptNamesStore.end())
10023 {
10024 return itr->first;
10025 }
10026 else
10027 {
10028 static std::string const empty;
10029 return empty;
10030 }
10031}
10032
10034{
10035 auto const itr = _scriptNamesStore.find(id);
10036 if (itr != _scriptNamesStore.end())
10037 {
10038 return itr->second.IsScriptDatabaseBound;
10039 }
10040 else
10041 {
10042 return false;
10043 }
10044}
10045
10046uint32 ObjectMgr::GetScriptId(std::string_view name, bool isDatabaseBound)
10047{
10048 return _scriptNamesStore.insert(name, isDatabaseBound);
10049}
10050
10052{
10053 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
10054
10055 if (it != _creatureBaseStatsStore.end())
10056 return &(it->second);
10057
10058 static constexpr CreatureBaseStats defStats
10059 {
10060 .BaseMana = 0,
10061 .AttackPower = 0,
10062 .RangedAttackPower = 0
10063 };
10064 return &defStats;
10065}
10066
10068{
10069 uint32 oldMSTime = getMSTime();
10070 // 0 1 2 3 4
10071 QueryResult result = WorldDatabase.Query("SELECT level, class, basemana, attackpower, rangedattackpower FROM creature_classlevelstats");
10072
10073 if (!result)
10074 {
10075 TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
10076 return;
10077 }
10078
10079 uint32 count = 0;
10080 do
10081 {
10082 Field* fields = result->Fetch();
10083
10084 uint8 Level = fields[0].GetUInt8();
10085 uint8 Class = fields[1].GetUInt8();
10086
10087 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
10088 TC_LOG_ERROR("sql.sql", "Creature base stats for level {} has invalid class {}", Level, Class);
10089
10090 CreatureBaseStats stats;
10091
10092 stats.BaseMana = fields[2].GetUInt32();
10093
10094 stats.AttackPower = fields[3].GetUInt16();
10095 stats.RangedAttackPower = fields[4].GetUInt16();
10096
10097 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
10098
10099 ++count;
10100 }
10101 while (result->NextRow());
10102
10104 for (uint8 unitLevel = 1; unitLevel <= maxLevel + 3; ++unitLevel)
10105 {
10106 for (uint8 unitClass = 1; unitClass <= MAX_UNIT_CLASSES; ++unitClass)
10107 {
10108 uint8 unitClassMask = 1 << (unitClass - 1);
10109 if (!_creatureBaseStatsStore.count(MAKE_PAIR16(unitLevel, unitClassMask)))
10110 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class {} level {}", unitClassMask, unitLevel);
10111 }
10112 }
10113
10114 TC_LOG_INFO("server.loading", ">> Loaded {} creature base stats in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10115}
10116
10118{
10119 uint32 oldMSTime = getMSTime();
10120
10121 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
10122
10123 if (!result)
10124 {
10125 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
10126 return;
10127 }
10128
10129 uint32 count = 0;
10130
10131 do
10132 {
10133 Field* fields = result->Fetch();
10134
10135 uint32 alliance = fields[0].GetUInt32();
10136 uint32 horde = fields[1].GetUInt32();
10137
10138 if (!sAchievementStore.LookupEntry(alliance))
10139 TC_LOG_ERROR("sql.sql", "Achievement {} (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
10140 else if (!sAchievementStore.LookupEntry(horde))
10141 TC_LOG_ERROR("sql.sql", "Achievement {} (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
10142 else
10143 FactionChangeAchievements[alliance] = horde;
10144
10145 ++count;
10146 }
10147 while (result->NextRow());
10148
10149 TC_LOG_INFO("server.loading", ">> Loaded {} faction change achievement pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10150}
10151
10153{
10154 uint32 oldMSTime = getMSTime();
10155 uint32 count = 0;
10156
10157 for (std::pair<uint32 const, ItemTemplate> const& itemPair : _itemTemplateStore)
10158 {
10159 if (!itemPair.second.GetOtherFactionItemId())
10160 continue;
10161
10162 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_HORDE))
10163 FactionChangeItemsHordeToAlliance[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10164
10165 if (itemPair.second.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
10166 FactionChangeItemsAllianceToHorde[itemPair.first] = itemPair.second.GetOtherFactionItemId();
10167
10168 ++count;
10169 }
10170
10171 TC_LOG_INFO("server.loading", ">> Loaded {} faction change item pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10172}
10173
10175{
10176 uint32 oldMSTime = getMSTime();
10177
10178 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
10179
10180 if (!result)
10181 {
10182 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
10183 return;
10184 }
10185
10186 uint32 count = 0;
10187
10188 do
10189 {
10190 Field* fields = result->Fetch();
10191
10192 uint32 alliance = fields[0].GetUInt32();
10193 uint32 horde = fields[1].GetUInt32();
10194
10195 if (!GetQuestTemplate(alliance))
10196 TC_LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
10197 else if (!GetQuestTemplate(horde))
10198 TC_LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
10199 else
10200 FactionChangeQuests[alliance] = horde;
10201
10202 ++count;
10203 }
10204 while (result->NextRow());
10205
10206 TC_LOG_INFO("server.loading", ">> Loaded {} faction change quest pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10207}
10208
10210{
10211 uint32 oldMSTime = getMSTime();
10212
10213 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
10214
10215 if (!result)
10216 {
10217 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
10218 return;
10219 }
10220
10221 uint32 count = 0;
10222
10223 do
10224 {
10225 Field* fields = result->Fetch();
10226
10227 uint32 alliance = fields[0].GetUInt32();
10228 uint32 horde = fields[1].GetUInt32();
10229
10230 if (!sFactionStore.LookupEntry(alliance))
10231 TC_LOG_ERROR("sql.sql", "Reputation {} (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
10232 else if (!sFactionStore.LookupEntry(horde))
10233 TC_LOG_ERROR("sql.sql", "Reputation {} (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
10234 else
10235 FactionChangeReputation[alliance] = horde;
10236
10237 ++count;
10238 }
10239 while (result->NextRow());
10240
10241 TC_LOG_INFO("server.loading", ">> Loaded {} faction change reputation pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10242}
10243
10245{
10246 uint32 oldMSTime = getMSTime();
10247
10248 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
10249
10250 if (!result)
10251 {
10252 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
10253 return;
10254 }
10255
10256 uint32 count = 0;
10257
10258 do
10259 {
10260 Field* fields = result->Fetch();
10261
10262 uint32 alliance = fields[0].GetUInt32();
10263 uint32 horde = fields[1].GetUInt32();
10264
10265 if (!sSpellMgr->GetSpellInfo(alliance, DIFFICULTY_NONE))
10266 TC_LOG_ERROR("sql.sql", "Spell {} (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
10267 else if (!sSpellMgr->GetSpellInfo(horde, DIFFICULTY_NONE))
10268 TC_LOG_ERROR("sql.sql", "Spell {} (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
10269 else
10270 FactionChangeSpells[alliance] = horde;
10271
10272 ++count;
10273 }
10274 while (result->NextRow());
10275
10276 TC_LOG_INFO("server.loading", ">> Loaded {} faction change spell pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10277}
10278
10280{
10281 uint32 oldMSTime = getMSTime();
10282
10283 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
10284
10285 if (!result)
10286 {
10287 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
10288 return;
10289 }
10290
10291 uint32 count = 0;
10292
10293 do
10294 {
10295 Field* fields = result->Fetch();
10296
10297 uint32 alliance = fields[0].GetUInt32();
10298 uint32 horde = fields[1].GetUInt32();
10299
10300 if (!sCharTitlesStore.LookupEntry(alliance))
10301 TC_LOG_ERROR("sql.sql", "Title {} (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
10302 else if (!sCharTitlesStore.LookupEntry(horde))
10303 TC_LOG_ERROR("sql.sql", "Title {} (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
10304 else
10305 FactionChangeTitles[alliance] = horde;
10306
10307 ++count;
10308 }
10309 while (result->NextRow());
10310
10311 TC_LOG_INFO("server.loading", ">> Loaded {} faction change title pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10312}
10313
10315{
10316 for (PhaseEntry const* phase : sPhaseStore)
10317 _phaseInfoById.emplace(std::make_pair(phase->ID, PhaseInfoStruct{ phase->ID, std::unordered_set<uint32>{} }));
10318
10319 for (MapEntry const* map : sMapStore)
10320 if (map->ParentMapID != -1)
10321 _terrainSwapInfoById.emplace(std::make_pair(map->ID, TerrainSwapInfo{ map->ID, std::vector<uint32>{} }));
10322
10323 TC_LOG_INFO("server.loading", "Loading Terrain World Map definitions...");
10324 LoadTerrainWorldMaps();
10325
10326 TC_LOG_INFO("server.loading", "Loading Terrain Swap Default definitions...");
10327 LoadTerrainSwapDefaults();
10328
10329 TC_LOG_INFO("server.loading", "Loading Phase Area definitions...");
10330 LoadAreaPhases();
10331}
10332
10334{
10335 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10336 for (PhaseAreaInfo& phase : itr->second)
10337 phase.Conditions.clear();
10338}
10339
10341{
10342 uint32 oldMSTime = getMSTime();
10343
10344 // 0 1
10345 QueryResult result = WorldDatabase.Query("SELECT TerrainSwapMap, UiMapPhaseId FROM `terrain_worldmap`");
10346
10347 if (!result)
10348 {
10349 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain world maps. DB table `terrain_worldmap` is empty.");
10350 return;
10351 }
10352
10353 uint32 count = 0;
10354 do
10355 {
10356 Field* fields = result->Fetch();
10357
10358 uint32 mapId = fields[0].GetUInt32();
10359 uint32 uiMapPhaseId = fields[1].GetUInt32();
10360
10361 if (!sMapStore.LookupEntry(mapId))
10362 {
10363 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_worldmap` does not exist, skipped.", mapId);
10364 continue;
10365 }
10366
10367 if (!sDB2Manager.IsUiMapPhase(uiMapPhaseId))
10368 {
10369 TC_LOG_ERROR("sql.sql", "Phase {} defined in `terrain_worldmap` is not a valid terrain swap phase, skipped.", uiMapPhaseId);
10370 continue;
10371 }
10372
10373 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[mapId];
10374 terrainSwapInfo->Id = mapId;
10375 terrainSwapInfo->UiMapPhaseIDs.push_back(uiMapPhaseId);
10376
10377 ++count;
10378 } while (result->NextRow());
10379
10380 TC_LOG_INFO("server.loading", ">> Loaded {} terrain world maps in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10381}
10382
10384{
10385 uint32 oldMSTime = getMSTime();
10386
10387 // 0 1
10388 QueryResult result = WorldDatabase.Query("SELECT MapId, TerrainSwapMap FROM `terrain_swap_defaults`");
10389
10390 if (!result)
10391 {
10392 TC_LOG_INFO("server.loading", ">> Loaded 0 terrain swap defaults. DB table `terrain_swap_defaults` is empty.");
10393 return;
10394 }
10395
10396 uint32 count = 0;
10397 do
10398 {
10399 Field* fields = result->Fetch();
10400
10401 uint32 mapId = fields[0].GetUInt32();
10402
10403 if (!sMapStore.LookupEntry(mapId))
10404 {
10405 TC_LOG_ERROR("sql.sql", "Map {} defined in `terrain_swap_defaults` does not exist, skipped.", mapId);
10406 continue;
10407 }
10408
10409 uint32 terrainSwap = fields[1].GetUInt32();
10410
10411 if (!sMapStore.LookupEntry(terrainSwap))
10412 {
10413 TC_LOG_ERROR("sql.sql", "TerrainSwapMap {} defined in `terrain_swap_defaults` does not exist, skipped.", terrainSwap);
10414 continue;
10415 }
10416
10417 TerrainSwapInfo* terrainSwapInfo = &_terrainSwapInfoById[terrainSwap];
10418 terrainSwapInfo->Id = terrainSwap;
10419 _terrainSwapInfoByMap[mapId].push_back(terrainSwapInfo);
10420
10421 ++count;
10422 } while (result->NextRow());
10423
10424 TC_LOG_INFO("server.loading", ">> Loaded {} terrain swap defaults in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10425}
10426
10428{
10429 uint32 oldMSTime = getMSTime();
10430
10431 // 0 1
10432 QueryResult result = WorldDatabase.Query("SELECT AreaId, PhaseId FROM `phase_area`");
10433
10434 if (!result)
10435 {
10436 TC_LOG_INFO("server.loading", ">> Loaded 0 phase areas. DB table `phase_area` is empty.");
10437 return;
10438 }
10439
10440 auto getOrCreatePhaseIfMissing = [this](uint32 phaseId)
10441 {
10442 PhaseInfoStruct* phaseInfo = &_phaseInfoById[phaseId];
10443 phaseInfo->Id = phaseId;
10444 return phaseInfo;
10445 };
10446
10447 uint32 count = 0;
10448 do
10449 {
10450 Field* fields = result->Fetch();
10451 uint32 area = fields[0].GetUInt32();
10452 uint32 phaseId = fields[1].GetUInt32();
10453 if (!sAreaTableStore.LookupEntry(area))
10454 {
10455 TC_LOG_ERROR("sql.sql", "Area {} defined in `phase_area` does not exist, skipped.", area);
10456 continue;
10457 }
10458
10459 if (!sPhaseStore.LookupEntry(phaseId))
10460 {
10461 TC_LOG_ERROR("sql.sql", "Phase {} defined in `phase_area` does not exist, skipped.", phaseId);
10462 continue;
10463 }
10464
10465 PhaseInfoStruct* phase = getOrCreatePhaseIfMissing(phaseId);
10466 phase->Areas.insert(area);
10467 _phaseInfoByArea[area].emplace_back(phase);
10468
10469 ++count;
10470 } while (result->NextRow());
10471
10472 for (auto itr = _phaseInfoByArea.begin(); itr != _phaseInfoByArea.end(); ++itr)
10473 {
10474 for (PhaseAreaInfo& phase : itr->second)
10475 {
10476 uint32 parentAreaId = itr->first;
10477 do
10478 {
10479 AreaTableEntry const* area = sAreaTableStore.LookupEntry(parentAreaId);
10480 if (!area)
10481 break;
10482
10483 parentAreaId = area->ParentAreaID;
10484 if (!parentAreaId)
10485 break;
10486
10487 if (std::vector<PhaseAreaInfo>* parentAreaPhases = Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, parentAreaId))
10488 for (PhaseAreaInfo& parentAreaPhase : *parentAreaPhases)
10489 if (parentAreaPhase.PhaseInfo->Id == phase.PhaseInfo->Id)
10490 parentAreaPhase.SubAreaExclusions.insert(itr->first);
10491
10492 } while (true);
10493 }
10494 }
10495
10496 TC_LOG_INFO("server.loading", ">> Loaded {} phase areas in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10497}
10498
10500{
10501 return std::any_of(Areas.begin(), Areas.end(), [areaId](uint32 areaToCheck)
10502 {
10503 return DB2Manager::IsInArea(areaId, areaToCheck);
10504 });
10505}
10506
10508{
10509 return Trinity::Containers::MapGetValuePtr(_phaseInfoById, phaseId);
10510}
10511
10512std::vector<PhaseAreaInfo> const* ObjectMgr::GetPhasesForArea(uint32 areaId) const
10513{
10514 return Trinity::Containers::MapGetValuePtr(_phaseInfoByArea, areaId);
10515}
10516
10518{
10519 return Trinity::Containers::MapGetValuePtr(_terrainSwapInfoById, terrainSwapId);
10520}
10521
10523{
10524 return Trinity::Containers::MapGetValuePtr(_destructibleHitpointStore, entry);
10525}
10526
10528{
10529 return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
10530}
10531
10533{
10534 auto itr = _gameObjectTemplateAddonStore.find(entry);
10535 if (itr != _gameObjectTemplateAddonStore.end())
10536 return &itr->second;
10537
10538 return nullptr;
10539}
10540
10542{
10543 return Trinity::Containers::MapGetValuePtr(_gameObjectOverrideStore, spawnId);
10544}
10545
10547{
10548 return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
10549}
10550
10552{
10553 return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
10554}
10555
10557{
10558 return Trinity::Containers::MapGetValuePtr(_vehicleTemplateStore, veh->GetCreatureEntry());
10559}
10560
10562{
10563 if (Creature* cre = veh->GetBase()->ToCreature())
10564 {
10565 // Give preference to GUID-based accessories
10566 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
10567 if (itr != _vehicleAccessoryStore.end())
10568 return &itr->second;
10569 }
10570
10571 // Otherwise return entry-based
10572 VehicleAccessoryTemplateContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
10573 if (itr != _vehicleTemplateAccessoryStore.end())
10574 return &itr->second;
10575 return nullptr;
10576}
10577
10579{
10580 return Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(class_) });
10581}
10582
10584{
10585 uint32 oldMSTime = getMSTime();
10586 _raceUnlockRequirementStore.clear();
10587
10588 // 0 1 2
10589 QueryResult result = WorldDatabase.Query("SELECT raceID, expansion, achievementId FROM `race_unlock_requirement`");
10590
10591 if (result)
10592 {
10593 do
10594 {
10595 Field* fields = result->Fetch();
10596
10597 uint8 raceID = fields[0].GetUInt8();
10598 uint8 expansion = fields[1].GetUInt8();
10599 uint32 achievementId = fields[2].GetUInt32();
10600
10601 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10602 if (!raceEntry)
10603 {
10604 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` does not exists, skipped.", raceID);
10605 continue;
10606 }
10607
10608 if (expansion >= MAX_ACCOUNT_EXPANSIONS)
10609 {
10610 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect expansion {}, skipped.", raceID, expansion);
10611 continue;
10612 }
10613
10614 if (achievementId && !sAchievementStore.LookupEntry(achievementId))
10615 {
10616 TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect achievement {}, skipped.", raceID, achievementId);
10617 continue;
10618 }
10619
10620 RaceUnlockRequirement& raceUnlockRequirement = _raceUnlockRequirementStore[raceID];
10621 raceUnlockRequirement.Expansion = expansion;
10622 raceUnlockRequirement.AchievementId = achievementId;
10623 }
10624 while (result->NextRow());
10625 TC_LOG_INFO("server.loading", ">> Loaded {} race expansion requirements in {} ms.", _raceUnlockRequirementStore.size(), GetMSTimeDiffToNow(oldMSTime));
10626 }
10627 else
10628 TC_LOG_INFO("server.loading", ">> Loaded 0 race expansion requirements. DB table `race_expansion_requirement` is empty.");
10629
10630 oldMSTime = getMSTime();
10631 _classExpansionRequirementStore.clear();
10632
10633 // 0 1 2 3
10634 result = WorldDatabase.Query("SELECT ClassID, RaceID, ActiveExpansionLevel, AccountExpansionLevel FROM `class_expansion_requirement`");
10635
10636 if (result)
10637 {
10638 std::map<uint8, std::map<uint8, std::pair<uint8, uint8>>> temp;
10639 std::array<uint8, MAX_CLASSES> minRequirementForClass = { };
10640 minRequirementForClass.fill(MAX_ACCOUNT_EXPANSIONS);
10641 uint32 count = 0;
10642 do
10643 {
10644 Field* fields = result->Fetch();
10645
10646 uint8 classID = fields[0].GetUInt8();
10647 uint8 raceID = fields[1].GetUInt8();
10648 uint8 activeExpansionLevel = fields[2].GetUInt8();
10649 uint8 accountExpansionLevel = fields[3].GetUInt8();
10650
10651 ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classID);
10652 if (!classEntry)
10653 {
10654 TC_LOG_ERROR("sql.sql", "Class {} (race {}) defined in `class_expansion_requirement` does not exists, skipped.",
10655 uint32(classID), uint32(raceID));
10656 continue;
10657 }
10658
10659 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
10660 if (!raceEntry)
10661 {
10662 TC_LOG_ERROR("sql.sql", "Race {} (class {}) defined in `class_expansion_requirement` does not exists, skipped.",
10663 uint32(raceID), uint32(classID));
10664 continue;
10665 }
10666
10667 if (activeExpansionLevel >= MAX_EXPANSIONS)
10668 {
10669 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect ActiveExpansionLevel {}, skipped.",
10670 uint32(classID), uint32(raceID), activeExpansionLevel);
10671 continue;
10672 }
10673
10674 if (accountExpansionLevel >= MAX_ACCOUNT_EXPANSIONS)
10675 {
10676 TC_LOG_ERROR("sql.sql", "Class {} Race {} defined in `class_expansion_requirement` has incorrect AccountExpansionLevel {}, skipped.",
10677 uint32(classID), uint32(raceID), accountExpansionLevel);
10678 continue;
10679 }
10680
10681 temp[raceID][classID] = { activeExpansionLevel, accountExpansionLevel };
10682 minRequirementForClass[classID] = std::min(minRequirementForClass[classID], activeExpansionLevel);
10683
10684 ++count;
10685 }
10686 while (result->NextRow());
10687
10688 for (auto&& race : temp)
10689 {
10690 RaceClassAvailability& raceClassAvailability = _classExpansionRequirementStore.emplace_back();
10691
10692 raceClassAvailability.RaceID = race.first;
10693
10694 for (auto&& class_ : race.second)
10695 {
10696 ClassAvailability& classAvailability = raceClassAvailability.Classes.emplace_back();
10697
10698 classAvailability.ClassID = class_.first;
10699 classAvailability.ActiveExpansionLevel = class_.second.first;
10700 classAvailability.AccountExpansionLevel = class_.second.second;
10701 classAvailability.MinActiveExpansionLevel = minRequirementForClass[class_.first];
10702 }
10703 }
10704
10705 TC_LOG_INFO("server.loading", ">> Loaded {} class expansion requirements in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
10706 }
10707 else
10708 TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
10709}
10710
10712{
10713 auto raceItr = std::find_if(_classExpansionRequirementStore.begin(), _classExpansionRequirementStore.end(), [raceId](RaceClassAvailability const& raceClass)
10714 {
10715 return raceClass.RaceID == raceId;
10716 });
10717 if (raceItr == _classExpansionRequirementStore.end())
10718 return nullptr;
10719
10720 auto classItr = std::find_if(raceItr->Classes.begin(), raceItr->Classes.end(), [classId](ClassAvailability const& classAvailability)
10721 {
10722 return classAvailability.ClassID == classId;
10723 });
10724 if (classItr == raceItr->Classes.end())
10725 return nullptr;
10726
10727 return &(*classItr);
10728}
10729
10731{
10732 for (RaceClassAvailability const& raceClassAvailability : _classExpansionRequirementStore)
10733 for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
10734 if (classAvailability.ClassID == classId)
10735 return &classAvailability;
10736
10737 return nullptr;
10738}
10739
10741{
10742 return Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
10743}
10744
10746{
10747 return Trinity::Containers::MapGetValuePtr(_jumpChargeParams, id);
10748}
10749
10751{
10752 CreatureStaticFlagsOverride const* staticFlagsOverride = Trinity::Containers::MapGetValuePtr(_creatureStaticFlagsOverrideStore, std::make_pair(spawnId, difficultyId));
10753 if (staticFlagsOverride)
10754 return staticFlagsOverride;
10755
10756 // If there is no data for the difficulty, try to get data for the fallback difficulty
10757 DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficultyId);
10758 if (difficultyEntry)
10759 return GetCreatureStaticFlagsOverride(spawnId, Difficulty(difficultyEntry->FallbackDifficultyID));
10760
10761 return nullptr;
10762}
10763
10765{
10766 uint32 oldMSTime = getMSTime();
10767
10768 // 0 1 2
10769 QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
10770
10771 if (!result)
10772 {
10773 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
10774 return;
10775 }
10776
10777 uint32 count = 0;
10778 do
10779 {
10780 Field* fields = result->Fetch();
10781
10782 uint32 entry = fields[0].GetUInt32();
10783 uint32 item = fields[1].GetUInt32();
10784 uint32 idx = fields[2].GetUInt32();
10785
10786 GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
10787 if (!goInfo)
10788 {
10789 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
10790 continue;
10791 };
10792
10793 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10794 if (!db2Data)
10795 {
10796 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
10797 continue;
10798 };
10799
10800 _gameObjectQuestItemStore[entry].push_back(item);
10801
10802 ++count;
10803 }
10804 while (result->NextRow());
10805
10806 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10807}
10808
10810{
10811 uint32 oldMSTime = getMSTime();
10812
10813 // 0 1 2 3
10814 QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
10815
10816 if (!result)
10817 {
10818 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
10819 return;
10820 }
10821
10822 uint32 count = 0;
10823 do
10824 {
10825 Field* fields = result->Fetch();
10826
10827 uint32 entry = fields[0].GetUInt32();
10828 Difficulty difficulty = Difficulty(fields[1].GetInt32());
10829 uint32 item = fields[2].GetUInt32();
10830 uint32 idx = fields[3].GetUInt32();
10831
10832 CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
10833 if (!creatureInfo)
10834 {
10835 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, difficulty: {}, idx: {}), skipped", entry, difficulty, idx);
10836 continue;
10837 }
10838
10839 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10840 if (!db2Data)
10841 {
10842 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, difficulty: {}, idx: {}), skipped", item, entry, difficulty, idx);
10843 continue;
10844 }
10845
10846 _creatureQuestItemStore[std::make_pair(entry, difficulty)].push_back(item);
10847
10848 ++count;
10849 }
10850 while (result->NextRow());
10851
10852 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10853}
10854
10856{
10857 uint32 oldMSTime = getMSTime();
10858
10859 // 0 1
10860 QueryResult result = WorldDatabase.Query("SELECT CreatureId, CurrencyId FROM creature_quest_currency ORDER BY CreatureId, CurrencyId ASC");
10861
10862 if (!result)
10863 {
10864 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest currencies. DB table `creature_quest_currency` is empty.");
10865 return;
10866 }
10867
10868 uint32 count = 0;
10869 do
10870 {
10871 Field* fields = result->Fetch();
10872
10873 uint32 entry = fields[0].GetUInt32();
10874 int32 currency = fields[1].GetInt32();
10875
10876 if (!GetCreatureTemplate(entry))
10877 {
10878 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has data for nonexistent creature (entry: {}, currency: {}), skipped", entry, currency);
10879 continue;
10880 }
10881
10882 if (!sCurrencyTypesStore.HasRecord(currency))
10883 {
10884 TC_LOG_ERROR("sql.sql", "Table `creature_quest_currency` has nonexistent currency (ID: {}) in creature (entry: {}, currency: {}), skipped", currency, entry, currency);
10885 continue;
10886 }
10887
10888 _creatureQuestCurrenciesStore[entry].push_back(currency);
10889
10890 ++count;
10891 }
10892 while (result->NextRow());
10893
10894 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest currencies in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10895}
10896
10898{
10899 // reload case
10900 _creatureStaticFlagsOverrideStore.clear();
10901
10902 uint32 oldMSTime = getMSTime();
10903
10904 // 0 1 2 3 4 5 6 7 8 9
10905 QueryResult result = WorldDatabase.Query("SELECT SpawnId, DifficultyId, StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, StaticFlags6, StaticFlags7, StaticFlags8 FROM creature_static_flags_override");
10906
10907 if (!result)
10908 {
10909 TC_LOG_INFO("server.loading", ">> Loaded 0 creature static flag overrides. DB table `creature_static_flags_override` is empty.");
10910 return;
10911 }
10912
10913 uint32 count = 0;
10914 do
10915 {
10916 Field* fields = result->Fetch();
10917
10918 ObjectGuid::LowType spawnId = fields[0].GetUInt64();
10919 Difficulty difficultyId = static_cast<Difficulty>(fields[1].GetInt32());
10920
10921 CreatureData const* creatureData = GetCreatureData(spawnId);
10922 if (!creatureData)
10923 {
10924 TC_LOG_ERROR("sql.sql", "Table `creature_static_flags_override` has data for nonexistent creature (SpawnId: {}), skipped", spawnId);
10925 continue;
10926 }
10927
10928 // DIFFICULTY_NONE is always a valid fallback
10929 if (difficultyId != DIFFICULTY_NONE)
10930 {
10931 if (!advstd::ranges::contains(creatureData->spawnDifficulties, difficultyId))
10932 {
10933 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);
10934 continue;
10935 }
10936 }
10937
10938 CreatureStaticFlagsOverride& staticFlagsOverride = _creatureStaticFlagsOverrideStore[std::make_pair(spawnId, difficultyId)];
10939 if (!fields[2].IsNull())
10940 staticFlagsOverride.StaticFlags1 = static_cast<CreatureStaticFlags>(fields[2].GetUInt32());
10941 if (!fields[3].IsNull())
10942 staticFlagsOverride.StaticFlags2 = static_cast<CreatureStaticFlags2>(fields[3].GetUInt32());
10943 if (!fields[4].IsNull())
10944 staticFlagsOverride.StaticFlags3 = static_cast<CreatureStaticFlags3>(fields[4].GetUInt32());
10945 if (!fields[5].IsNull())
10946 staticFlagsOverride.StaticFlags4 = static_cast<CreatureStaticFlags4>(fields[5].GetUInt32());
10947 if (!fields[6].IsNull())
10948 staticFlagsOverride.StaticFlags5 = static_cast<CreatureStaticFlags5>(fields[6].GetUInt32());
10949 if (!fields[7].IsNull())
10950 staticFlagsOverride.StaticFlags6 = static_cast<CreatureStaticFlags6>(fields[7].GetUInt32());
10951 if (!fields[8].IsNull())
10952 staticFlagsOverride.StaticFlags7 = static_cast<CreatureStaticFlags7>(fields[8].GetUInt32());
10953 if (!fields[9].IsNull())
10954 staticFlagsOverride.StaticFlags8 = static_cast<CreatureStaticFlags8>(fields[9].GetUInt32());
10955
10956 ++count;
10957 } while (result->NextRow());
10958
10959 TC_LOG_INFO("server.loading", ">> Loaded {} creature static flag overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10960}
10961
10963{
10964 uint32 oldMSTime = getMSTime();
10965
10966 // cache disabled
10967 if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
10968 {
10969 TC_LOG_INFO("server.loading", ">> Query data caching is disabled. Skipped initialization.");
10970 return;
10971 }
10972
10974
10975 // Initialize Query data for creatures
10976 if (mask & QUERY_DATA_CREATURES)
10977 for (auto& creatureTemplatePair : _creatureTemplateStore)
10978 pool.PostWork([creature = &creatureTemplatePair.second]() { creature->InitializeQueryData(); });
10979
10980 // Initialize Query Data for gameobjects
10981 if (mask & QUERY_DATA_GAMEOBJECTS)
10982 for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
10983 pool.PostWork([gobj = &gameObjectTemplatePair.second]() { gobj->InitializeQueryData(); });
10984
10985 // Initialize Query Data for quests
10986 if (mask & QUERY_DATA_QUESTS)
10987 for (auto& questTemplatePair : _questTemplates)
10988 pool.PostWork([quest = questTemplatePair.second.get()]() { quest->InitializeQueryData(); });
10989
10990 // Initialize Quest POI data
10991 if (mask & QUERY_DATA_POIS)
10992 for (auto& poiWrapperPair : _questPOIStore)
10993 pool.PostWork([poi = &poiWrapperPair.second]() { poi->InitializeQueryData(); });
10994
10995 pool.Join();
10996
10997 TC_LOG_INFO("server.loading", ">> Initialized query cache data in {} ms", GetMSTimeDiffToNow(oldMSTime));
10998}
10999
11001{
11002 ByteBuffer tempBuffer;
11003 tempBuffer << *this;
11004 tempBuffer.shrink_to_fit();
11005
11006 QueryDataBuffer = std::move(tempBuffer).Release();
11007}
11008
11010{
11011 uint32 oldMSTime = getMSTime();
11012 _sceneTemplateStore.clear();
11013
11014 QueryResult templates = WorldDatabase.Query("SELECT SceneId, Flags, ScriptPackageID, Encrypted, ScriptName FROM scene_template");
11015
11016 if (!templates)
11017 {
11018 TC_LOG_INFO("server.loading", ">> Loaded 0 scene templates. DB table `scene_template` is empty.");
11019 return;
11020 }
11021
11022 do
11023 {
11024 Field* fields = templates->Fetch();
11025
11026 uint32 sceneId = fields[0].GetUInt32();
11027 SceneTemplate& sceneTemplate = _sceneTemplateStore[sceneId];
11028 sceneTemplate.SceneId = sceneId;
11029 sceneTemplate.PlaybackFlags = static_cast<SceneFlag>(fields[1].GetUInt32());
11030 sceneTemplate.ScenePackageId = fields[2].GetUInt32();
11031 sceneTemplate.Encrypted = fields[3].GetUInt8() != 0;
11032 sceneTemplate.ScriptId = GetScriptId(fields[4].GetStringView());
11033
11034 } while (templates->NextRow());
11035
11036 TC_LOG_INFO("server.loading", ">> Loaded {} scene templates in {} ms.", _sceneTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
11037}
11038
11040{
11041 uint32 oldMSTime = getMSTime();
11042 _playerChoices.clear();
11043
11044 QueryResult choices = WorldDatabase.Query("SELECT ChoiceId, UiTextureKitId, SoundKitId, CloseSoundKitId, Duration, Question, PendingChoiceText, "
11045 "InfiniteRange, HideWarboardHeader, KeepOpenAfterChoice, ShowChoicesAsList, ForceDontShowChoicesAsList, RequiresSelection, MaxResponses, ScriptName FROM playerchoice");
11046 if (!choices)
11047 {
11048 TC_LOG_INFO("server.loading", ">> Loaded 0 player choices. DB table `playerchoice` is empty.");
11049 return;
11050 }
11051
11052 uint32 responseCount = 0;
11053 uint32 rewardCount = 0;
11054 uint32 itemRewardCount = 0;
11055 uint32 currencyRewardCount = 0;
11056 uint32 factionRewardCount = 0;
11057 uint32 itemChoiceRewardCount = 0;
11058 uint32 mawPowersCount = 0;
11059
11060 {
11061 do
11062 {
11063 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(UiTextureKitId)(SoundKitId)(CloseSoundKitId)(Duration)(Question)(PendingChoiceText)
11064 (InfiniteRange)(HideWarboardHeader)(KeepOpenAfterChoice)(ShowChoicesAsList)(ForceDontShowChoicesAsList)(RequiresSelection)(MaxResponses)(ScriptName)) fields { *choices };
11065
11066 int32 choiceId = fields.ChoiceId().GetInt32();
11067
11068 PlayerChoice& choice = _playerChoices[choiceId];
11069 choice.ChoiceId = choiceId;
11070 choice.UiTextureKitId = fields.UiTextureKitId().GetInt32();
11071 choice.SoundKitId = fields.SoundKitId().GetUInt32();
11072 choice.CloseSoundKitId = fields.CloseSoundKitId().GetUInt32();
11073 if (!fields.Duration().IsNull())
11074 choice.Duration = Seconds(fields.Duration().GetInt64());
11075 choice.Question = fields.Question().GetStringView();
11076 choice.PendingChoiceText = fields.PendingChoiceText().GetStringView();
11077 choice.InfiniteRange = fields.InfiniteRange().GetBool();
11078 choice.HideWarboardHeader = fields.HideWarboardHeader().GetBool();
11079 choice.KeepOpenAfterChoice = fields.KeepOpenAfterChoice().GetBool();
11080 choice.ShowChoicesAsList = fields.ShowChoicesAsList().GetBool();
11081 choice.ForceDontShowChoicesAsList = fields.ForceDontShowChoicesAsList().GetBool();
11082 choice.RequiresSelection = fields.RequiresSelection().GetBool();
11083 choice.MaxResponses = fields.MaxResponses().GetUInt32OrNull();
11084 choice.ScriptId = GetScriptId(fields.ScriptName().GetStringView());
11085
11086 } while (choices->NextRow());
11087 }
11088
11089 if (QueryResult responses = WorldDatabase.Query("SELECT ChoiceId, ResponseId, NULL, ChoiceArtFileId, Flags, WidgetSetID, "
11090 "UiTextureAtlasElementID, SoundKitID, GroupID, UiTextureKitID, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation, RewardQuestID "
11091 "FROM playerchoice_response ORDER BY `Index` ASC"))
11092 {
11093 do
11094 {
11095 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ChoiceArtFileId)(Flags)(WidgetSetID)(UiTextureAtlasElementID)(SoundKitID)
11096 (GroupID)(UiTextureKitID)(Answer)(Header)(SubHeader)(ButtonTooltip)(Description)(Confirmation)(RewardQuestID)) fields{ *responses };
11097
11098 int32 choiceId = fields.ChoiceId().GetInt32();
11099 int32 responseId = fields.ResponseId().GetInt32();
11100
11101 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11102 if (!choice)
11103 {
11104 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11105 continue;
11106 }
11107
11108 choice->Responses.emplace_back();
11109
11110 PlayerChoiceResponse& response = choice->Responses.back();
11111 response.ResponseId = responseId;
11112 response.ChoiceArtFileId = fields.ChoiceArtFileId().GetInt32();
11113 response.Flags = static_cast<PlayerChoiceResponseFlags>(fields.Flags().GetInt32());
11114 response.WidgetSetID = fields.WidgetSetID().GetUInt32();
11115 response.UiTextureAtlasElementID = fields.UiTextureAtlasElementID().GetUInt32();
11116 response.SoundKitID = fields.SoundKitID().GetUInt32();
11117 response.GroupID = fields.GroupID().GetUInt8();
11118 response.UiTextureKitID = fields.UiTextureKitID().GetInt32();
11119 response.Answer = fields.Answer().GetStringView();
11120 response.Header = fields.Header().GetStringView();
11121 response.SubHeader = fields.SubHeader().GetStringView();
11122 response.ButtonTooltip = fields.ButtonTooltip().GetStringView();
11123 response.Description = fields.Description().GetStringView();
11124 response.Confirmation = fields.Confirmation().GetStringView();
11125 response.RewardQuestID = fields.RewardQuestID().GetUInt32OrNull();
11126
11127 ++responseCount;
11128
11129 } while (responses->NextRow());
11130 }
11131
11132 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TitleId, PackageId, SkillLineId, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"))
11133 {
11134 do
11135 {
11136 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(TitleId)(PackageId)(SkillLineId)
11137 (SkillPointCount)(ArenaPointCount)(HonorPointCount)(Money)(Xp)) fields{ *rewards };
11138
11139 int32 choiceId = fields.ChoiceId().GetInt32();
11140 int32 responseId = fields.ResponseId().GetInt32();
11141
11142 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11143 if (!choice)
11144 {
11145 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11146 continue;
11147 }
11148
11149 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11150 if (responseItr == choice->Responses.end())
11151 {
11152 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11153 continue;
11154 }
11155
11156 PlayerChoiceResponseReward* reward = &responseItr->Reward.emplace();
11157 reward->TitleId = fields.TitleId().GetInt32();
11158 reward->PackageId = fields.PackageId().GetInt32();
11159 reward->SkillLineId = fields.SkillLineId().GetInt32();
11160 reward->SkillPointCount = fields.SkillPointCount().GetUInt32();
11161 reward->ArenaPointCount = fields.ArenaPointCount().GetUInt32();
11162 reward->HonorPointCount = fields.HonorPointCount().GetUInt32();
11163 reward->Money = fields.Money().GetUInt64();
11164 reward->Xp = fields.Xp().GetUInt32();
11165 ++rewardCount;
11166
11167 if (reward->TitleId && !sCharTitlesStore.LookupEntry(reward->TitleId))
11168 {
11169 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing Title {} for ChoiceId {}, ResponseId: {}, set to 0",
11170 reward->TitleId, choiceId, responseId);
11171 reward->TitleId = 0;
11172 }
11173
11174 if (reward->PackageId && !sDB2Manager.GetQuestPackageItems(reward->PackageId))
11175 {
11176 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing QuestPackage {} for ChoiceId {}, ResponseId: {}, set to 0",
11177 reward->TitleId, choiceId, responseId);
11178 reward->PackageId = 0;
11179 }
11180
11181 if (reward->SkillLineId && !sSkillLineStore.LookupEntry(reward->SkillLineId))
11182 {
11183 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing SkillLine {} for ChoiceId {}, ResponseId: {}, set to 0",
11184 reward->TitleId, choiceId, responseId);
11185 reward->SkillLineId = 0;
11186 reward->SkillPointCount = 0;
11187 }
11188
11189 } while (rewards->NextRow());
11190 }
11191
11192 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item ORDER BY `Index` ASC"))
11193 {
11194 do
11195 {
11196 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ItemId)(BonusListIDs)(Quantity)) fields { *rewards };
11197
11198 int32 choiceId = fields.ChoiceId().GetInt32();
11199 int32 responseId = fields.ResponseId().GetInt32();
11200 uint32 itemId = fields.ItemId().GetUInt32();
11201 std::vector<int32> bonusListIds;
11202 for (std::string_view token : Trinity::Tokenize(fields.BonusListIDs().GetStringView(), ' ', false))
11203 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11204 bonusListIds.push_back(*bonusListID);
11205 int32 quantity = fields.Quantity().GetInt32();
11206
11207 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11208 if (!choice)
11209 {
11210 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11211 continue;
11212 }
11213
11214 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11215 if (responseItr == choice->Responses.end())
11216 {
11217 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11218 continue;
11219 }
11220
11221 if (!responseItr->Reward)
11222 {
11223 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11224 choiceId, responseId);
11225 continue;
11226 }
11227
11228 if (!GetItemTemplate(itemId))
11229 {
11230 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11231 itemId, choiceId, responseId);
11232 continue;
11233 }
11234
11235 responseItr->Reward->Items.emplace_back(itemId, std::move(bonusListIds), quantity);
11236 ++itemRewardCount;
11237
11238 } while (rewards->NextRow());
11239 }
11240
11241 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, CurrencyId, Quantity FROM playerchoice_response_reward_currency ORDER BY `Index` ASC"))
11242 {
11243 do
11244 {
11245 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(CurrencyId)(Quantity)) fields { *rewards };
11246
11247 int32 choiceId = fields.ChoiceId().GetInt32();
11248 int32 responseId = fields.ResponseId().GetInt32();
11249 uint32 currencyId = fields.CurrencyId().GetUInt32();
11250 int32 quantity = fields.Quantity().GetInt32();
11251
11252 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11253 if (!choice)
11254 {
11255 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11256 continue;
11257 }
11258
11259 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11260 if (responseItr == choice->Responses.end())
11261 {
11262 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11263 continue;
11264 }
11265
11266 if (!responseItr->Reward)
11267 {
11268 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11269 choiceId, responseId);
11270 continue;
11271 }
11272
11273 if (!sCurrencyTypesStore.LookupEntry(currencyId))
11274 {
11275 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing currency {} for ChoiceId {}, ResponseId: {}, skipped",
11276 currencyId, choiceId, responseId);
11277 continue;
11278 }
11279
11280 responseItr->Reward->Currency.emplace_back(currencyId, quantity);
11281 ++currencyRewardCount;
11282
11283 } while (rewards->NextRow());
11284 }
11285
11286 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, FactionId, Quantity FROM playerchoice_response_reward_faction ORDER BY `Index` ASC"))
11287 {
11288 do
11289 {
11290 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(FactionId)(Quantity)) fields { *rewards };
11291
11292 int32 choiceId = fields.ChoiceId().GetInt32();
11293 int32 responseId = fields.ResponseId().GetInt32();
11294 uint32 factionId = fields.FactionId().GetUInt32();
11295 int32 quantity = fields.Quantity().GetInt32();
11296
11297 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11298 if (!choice)
11299 {
11300 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11301 continue;
11302 }
11303
11304 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11305 if (responseItr == choice->Responses.end())
11306 {
11307 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11308 continue;
11309 }
11310
11311 if (!responseItr->Reward)
11312 {
11313 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11314 choiceId, responseId);
11315 continue;
11316 }
11317
11318 if (!sFactionStore.LookupEntry(factionId))
11319 {
11320 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing faction {} for ChoiceId {}, ResponseId: {}, skipped",
11321 factionId, choiceId, responseId);
11322 continue;
11323 }
11324
11325 responseItr->Reward->Faction.emplace_back(factionId, quantity);
11326 ++factionRewardCount;
11327
11328 } while (rewards->NextRow());
11329 }
11330
11331 if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item_choice ORDER BY `Index` ASC"))
11332 {
11333 do
11334 {
11335 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(ItemId)(BonusListIDs)(Quantity)) fields { *rewards };
11336
11337 int32 choiceId = fields.ChoiceId().GetInt32();
11338 int32 responseId = fields.ResponseId().GetInt32();
11339 uint32 itemId = fields.ItemId().GetUInt32();
11340 std::vector<int32> bonusListIds;
11341 for (std::string_view token : Trinity::Tokenize(fields.BonusListIDs().GetStringView(), ' ', false))
11342 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
11343 bonusListIds.push_back(*bonusListID);
11344 int32 quantity = fields.Quantity().GetInt32();
11345
11346 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11347 if (!choice)
11348 {
11349 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11350 continue;
11351 }
11352
11353 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11354 if (responseItr == choice->Responses.end())
11355 {
11356 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11357 continue;
11358 }
11359
11360 if (!responseItr->Reward)
11361 {
11362 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing player choice reward for ChoiceId {}, ResponseId: {}, skipped",
11363 choiceId, responseId);
11364 continue;
11365 }
11366
11367 if (!GetItemTemplate(itemId))
11368 {
11369 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item_choice` references non-existing item {} for ChoiceId {}, ResponseId: {}, skipped",
11370 itemId, choiceId, responseId);
11371 continue;
11372 }
11373
11374 responseItr->Reward->ItemChoices.emplace_back(itemId, std::move(bonusListIds), quantity);
11375 ++itemChoiceRewardCount;
11376
11377 } while (rewards->NextRow());
11378 }
11379
11380 if (QueryResult mawPowersResult = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TypeArtFileID, Rarity, SpellID, MaxStacks FROM playerchoice_response_maw_power"))
11381 {
11382 do
11383 {
11384 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (ChoiceId)(ResponseId)(TypeArtFileID)(Rarity)(SpellID)(MaxStacks)) fields { *mawPowersResult };
11385
11386 int32 choiceId = fields.ChoiceId().GetInt32();
11387 int32 responseId = fields.ResponseId().GetInt32();
11388
11389 PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);
11390 if (!choice)
11391 {
11392 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ChoiceId: {} (ResponseId: {}), skipped", choiceId, responseId);
11393 continue;
11394 }
11395
11396 auto responseItr = std::ranges::find(choice->Responses, responseId, &PlayerChoiceResponse::ResponseId);
11397 if (responseItr == choice->Responses.end())
11398 {
11399 TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_maw_power` references non-existing ResponseId: {} for ChoiceId {}, skipped", responseId, choiceId);
11400 continue;
11401 }
11402
11403 PlayerChoiceResponseMawPower& mawPower = responseItr->MawPower.emplace();
11404 mawPower.TypeArtFileID = fields.TypeArtFileID().GetInt32();
11405 mawPower.Rarity = fields.Rarity().GetInt32OrNull();
11406 mawPower.SpellID = fields.SpellID().GetInt32();
11407 mawPower.MaxStacks = fields.MaxStacks().GetInt32();
11408
11409 ++mawPowersCount;
11410
11411 } while (mawPowersResult->NextRow());
11412 }
11413
11414 TC_LOG_INFO("server.loading", ">> Loaded {} player choices, {} responses, {} rewards, {} item rewards, {} currency rewards, {} faction rewards, {} item choice rewards and {} maw powers in {} ms.",
11415 _playerChoices.size(), responseCount, rewardCount, itemRewardCount, currencyRewardCount, factionRewardCount, itemChoiceRewardCount, mawPowersCount, GetMSTimeDiffToNow(oldMSTime));
11416}
11417
11419{
11420 uint32 oldMSTime = getMSTime();
11421
11422 // need for reload case
11423 _playerChoiceLocales.clear();
11424
11425 // 0 1 2
11426 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceId, locale, Question FROM playerchoice_locale"))
11427 {
11428 do
11429 {
11430 Field* fields = result->Fetch();
11431
11432 uint32 choiceId = fields[0].GetUInt32();
11433 std::string_view localeName = fields[1].GetStringView();
11434
11435 if (!GetPlayerChoice(choiceId))
11436 {
11437 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for locale {}, skipped", choiceId, localeName);
11438 continue;
11439 }
11440
11441 LocaleConstant locale = GetLocaleByName(localeName);
11442 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11443 continue;
11444
11445 PlayerChoiceLocale& data = _playerChoiceLocales[choiceId];
11446 AddLocaleString(fields[2].GetStringView(), locale, data.Question);
11447 } while (result->NextRow());
11448
11449 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _playerChoiceLocales.size(), GetMSTimeDiffToNow(oldMSTime));
11450 }
11451
11452 oldMSTime = getMSTime();
11453
11454 // 0 1 2 3 4 5 6 7 8
11455 if (QueryResult result = WorldDatabase.Query("SELECT ChoiceID, ResponseID, locale, Answer, Header, SubHeader, ButtonTooltip, Description, Confirmation FROM playerchoice_response_locale"))
11456 {
11457 std::size_t count = 0;
11458 do
11459 {
11460 Field* fields = result->Fetch();
11461
11462 int32 choiceId = fields[0].GetInt32();
11463 int32 responseId = fields[1].GetInt32();
11464 std::string_view localeName = fields[2].GetStringView();
11465
11466 auto itr = _playerChoiceLocales.find(choiceId);
11467 if (itr == _playerChoiceLocales.end())
11468 {
11469 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: {} for ResponseId {} locale {}, skipped",
11470 choiceId, responseId, localeName);
11471 continue;
11472 }
11473
11474 PlayerChoice const* playerChoice = ASSERT_NOTNULL(GetPlayerChoice(choiceId));
11475 if (!playerChoice->GetResponse(responseId))
11476 {
11477 TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ResponseId: {} for ChoiceId {} locale {}, skipped",
11478 responseId, choiceId, localeName);
11479 continue;
11480 }
11481
11482 LocaleConstant locale = GetLocaleByName(localeName);
11483 if (!IsValidLocale(locale) || locale == LOCALE_enUS)
11484 continue;
11485
11486 PlayerChoiceResponseLocale& data = itr->second.Responses[responseId];
11487 AddLocaleString(fields[3].GetStringView(), locale, data.Answer);
11488 AddLocaleString(fields[4].GetStringView(), locale, data.Header);
11489 AddLocaleString(fields[5].GetStringView(), locale, data.SubHeader);
11490 AddLocaleString(fields[6].GetStringView(), locale, data.ButtonTooltip);
11491 AddLocaleString(fields[7].GetStringView(), locale, data.Description);
11492 AddLocaleString(fields[8].GetStringView(), locale, data.Confirmation);
11493 ++count;
11494 } while (result->NextRow());
11495
11496 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice Response locale strings in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11497 }
11498}
11499
11501{
11502 uint32 oldMSTime = getMSTime();
11503
11504 // need for reload case
11505 _uiMapQuestLinesStore.clear();
11506
11507 // 0 1
11508 QueryResult result = WorldDatabase.Query("SELECT UiMapId, QuestLineId FROM ui_map_quest_line");
11509
11510 if (!result)
11511 {
11512 TC_LOG_INFO("server.loading", ">> Loaded 0 questlines for UIMaps. DB table `ui_map_quest_line` is empty!");
11513 return;
11514 }
11515
11516 uint32 count = 0;
11517
11518 do
11519 {
11520 Field* fields = result->Fetch();
11521
11522 uint32 uiMapId = fields[0].GetUInt32();
11523 uint32 questLineId = fields[1].GetUInt32();
11524
11525 if (!sUiMapStore.HasRecord(uiMapId))
11526 {
11527 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest_line` references non-existing UIMap {}, skipped", uiMapId);
11528 continue;
11529 }
11530
11531 if (QuestMgr::GetQuestsForQuestLine(questLineId).empty())
11532 {
11533 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest_line` references empty or non-existing questline {}, skipped", questLineId);
11534 continue;
11535 }
11536
11537 _uiMapQuestLinesStore[uiMapId].push_back(questLineId);
11538 ++count;
11539
11540 } while (result->NextRow());
11541
11542 TC_LOG_INFO("server.loading", ">> Loaded {} UiMap questlines definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11543}
11544
11545std::vector<uint32> const* ObjectMgr::GetUiMapQuestLinesList(uint32 uiMapId) const
11546{
11547 return Trinity::Containers::MapGetValuePtr(_uiMapQuestLinesStore, uiMapId);
11548}
11549
11551{
11552 uint32 oldMSTime = getMSTime();
11553
11554 // need for reload case
11555 _uiMapQuestsStore.clear();
11556
11557 // 0 1
11558 QueryResult result = WorldDatabase.Query("SELECT UiMapId, QuestId FROM ui_map_quest");
11559
11560 if (!result)
11561 {
11562 TC_LOG_INFO("server.loading", ">> Loaded 0 quests for UIMaps. DB table `ui_map_quest` is empty!");
11563 return;
11564 }
11565
11566 uint32 count = 0;
11567
11568 do
11569 {
11570 Field* fields = result->Fetch();
11571
11572 uint32 uiMapId = fields[0].GetUInt32();
11573 uint32 questId = fields[1].GetUInt32();
11574
11575 if (!sUiMapStore.HasRecord(uiMapId))
11576 {
11577 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest` references non-existing UIMap {}, skipped", uiMapId);
11578 continue;
11579 }
11580
11581 if (!GetQuestTemplate(questId))
11582 {
11583 TC_LOG_ERROR("sql.sql", "Table `ui_map_quest` references non-existing quest {}, skipped", questId);
11584 continue;
11585 }
11586
11587 _uiMapQuestsStore[uiMapId].push_back(questId);
11588 ++count;
11589
11590 } while (result->NextRow());
11591
11592 TC_LOG_INFO("server.loading", ">> Loaded {} UiMap quests definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11593}
11594
11595std::vector<uint32> const* ObjectMgr::GetUiMapQuestsList(uint32 uiMapId) const
11596{
11597 return Trinity::Containers::MapGetValuePtr(_uiMapQuestsStore, uiMapId);
11598}
11599
11601{
11602 uint32 oldMSTime = getMSTime();
11603
11604 // need for reload case
11605 _spawnTrackingDataStore.clear();
11606
11607 // 0 1 2 3 4
11608 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, MapId, PhaseId, PhaseGroup, PhaseUseFlags FROM spawn_tracking_template");
11609
11610 if (!result)
11611 {
11612 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking templates. DB table `spawn_tracking_template` is empty!");
11613 return;
11614 }
11615
11616 do
11617 {
11618 Field* fields = result->Fetch();
11619
11620 uint32 spawnTrackingId = fields[0].GetUInt32();
11621 uint32 mapId = fields[1].GetUInt32();
11622
11623 if (!sMapStore.HasRecord(mapId))
11624 {
11625 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` references non-existing map {}, skipped", mapId);
11626 continue;
11627 }
11628
11629 SpawnTrackingTemplateData& data = _spawnTrackingDataStore[spawnTrackingId];
11630 data.SpawnTrackingId = spawnTrackingId;
11631 data.MapId = mapId;
11632 data.PhaseId = fields[2].GetUInt32();
11633 data.PhaseGroup = fields[3].GetUInt32();
11634 data.PhaseUseFlags = fields[4].GetUInt8();
11635
11636 if (data.PhaseGroup && data.PhaseId)
11637 {
11638 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) with `PhaseId` and `PhaseGroup` set, `PhaseGroup` set to 0", data.SpawnTrackingId);
11639 data.PhaseGroup = 0;
11640 }
11641
11642 if (data.PhaseId)
11643 {
11644 if (!sPhaseStore.HasRecord(data.PhaseId))
11645 {
11646 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing non-existing `PhaseId` {}, set to 0", data.SpawnTrackingId, data.PhaseId);
11647 data.PhaseId = 0;
11648 }
11649 }
11650
11651 if (data.PhaseGroup)
11652 {
11653 if (!sDB2Manager.GetPhasesForGroup(data.PhaseGroup))
11654 {
11655 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing non-existing `PhaseGroup` {}, set to 0", data.SpawnTrackingId, data.PhaseGroup);
11656 data.PhaseGroup = 0;
11657 }
11658 }
11659
11661 {
11662 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_template` has spawn tracking (Id: {}) referencing unknown `PhaseUseFlags`, removed unknown value.", data.SpawnTrackingId);
11664 }
11665
11667 {
11668 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,"
11669 " removing PHASE_USE_FLAGS_INVERSE.", data.SpawnTrackingId);
11670 data.PhaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
11671 }
11672
11673 } while (result->NextRow());
11674
11675 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking templates in {} ms", _spawnTrackingDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
11676}
11677
11679{
11680 return Trinity::Containers::MapGetValuePtr(_spawnTrackingDataStore, spawnTrackingId);
11681}
11682
11683bool ObjectMgr::IsQuestObjectiveForSpawnTracking(uint32 spawnTrackingId, uint32 questObjectiveId) const
11684{
11685 if (std::vector<QuestObjective const*> const* questObjectiveList = Trinity::Containers::MapGetValuePtr(_spawnTrackingQuestObjectiveStore, spawnTrackingId))
11686 return advstd::ranges::contains(*questObjectiveList, questObjectiveId, &QuestObjective::ID);
11687
11688 return false;
11689}
11690
11692{
11693 uint32 oldMSTime = getMSTime();
11694
11695 // need for reload case
11696 _spawnTrackingQuestObjectiveStore.clear();
11697
11698 // 0 1
11699 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, QuestObjectiveId FROM spawn_tracking_quest_objective");
11700
11701 if (!result)
11702 {
11703 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking quest objectives. DB table `spawn_tracking_quest_objective` is empty!");
11704 return;
11705 }
11706
11707 uint32 count = 0;
11708
11709 do
11710 {
11711 Field* fields = result->Fetch();
11712
11713 uint32 spawnTrackingId = fields[0].GetUInt32();
11714 uint32 objectiveId = fields[1].GetUInt32();
11715
11716 if (!GetSpawnTrackingData(spawnTrackingId))
11717 {
11718 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);
11719 continue;
11720 }
11721
11722 QuestObjective const* questObjective = GetQuestObjective(objectiveId);
11723 if (!questObjective)
11724 {
11725 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);
11726 continue;
11727 }
11728
11729 _spawnTrackingQuestObjectiveStore[spawnTrackingId].push_back(questObjective);
11730
11731 ++count;
11732 } while (result->NextRow());
11733
11734 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking quest objectives in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11735}
11736
11738{
11739 uint32 oldMSTime = getMSTime();
11740
11741 // need for reload case
11742 _spawnTrackingMapStore.clear();
11743
11744 // 0 1 2 3
11745 QueryResult result = WorldDatabase.Query("SELECT SpawnTrackingId, SpawnType, SpawnId, QuestObjectiveIds FROM spawn_tracking");
11746
11747 if (!result)
11748 {
11749 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking members. DB table `spawn_tracking` is empty!");
11750 return;
11751 }
11752
11753 uint32 count = 0;
11754
11755 do
11756 {
11757 Field* fields = result->Fetch();
11758 uint32 spawnTrackingId = fields[0].GetUInt32();
11759 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
11760 ObjectGuid::LowType spawnId = fields[2].GetUInt64();
11761
11762 if (!SpawnData::TypeIsValid(spawnType))
11763 {
11764 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn data with invalid type {} listed for spawn tracking {}. Skipped.", uint32(spawnType), spawnTrackingId);
11765 continue;
11766 }
11767 else if (spawnType == SPAWN_TYPE_AREATRIGGER)
11768 {
11769 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has areatrigger spawn ({}) listed for spawn tracking {}. Skipped.", uint32(spawnType), spawnTrackingId);
11770 continue;
11771 }
11772
11773 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
11774 if (!data)
11775 {
11776 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);
11777 continue;
11778 }
11779 else if (data->spawnTrackingData)
11780 {
11781 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.",
11782 uint32(spawnType), spawnId, spawnTrackingId, data->spawnTrackingData->SpawnTrackingId);
11783 continue;
11784 }
11785
11786 SpawnTrackingTemplateData const* spawnTrackingTemplateData = GetSpawnTrackingData(spawnTrackingId);
11787 if (!spawnTrackingTemplateData)
11788 {
11789 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} assigned to spawn ({},{}), but spawn tracking does not exist!", spawnTrackingId, uint32(spawnType), spawnId);
11790 continue;
11791 }
11792
11793 if (spawnTrackingTemplateData->MapId != data->mapId)
11794 {
11795 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!",
11796 spawnTrackingId, spawnTrackingTemplateData->MapId, uint32(spawnType), spawnId, data->mapId);
11797 continue;
11798 }
11799
11800 SpawnData const* spawnData = data->ToSpawnData();
11801 if (spawnTrackingTemplateData->PhaseId != spawnData->phaseId || spawnTrackingTemplateData->PhaseGroup != spawnData->phaseGroup || spawnTrackingTemplateData->PhaseUseFlags != spawnData->phaseUseFlags)
11802 {
11803 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking` has spawn tracking {} with phase info (PhaseId: {}, PhaseGroup: {}, PhaseUseFlags: {}), ",
11804 "but spawn ({},{}) has different phase info (PhaseId: {}, PhaseGroup: {}, PhaseUseFlags: {}) - spawn NOT added to spawn tracking!",
11805 spawnTrackingId, spawnTrackingTemplateData->PhaseId, spawnTrackingTemplateData->PhaseGroup, spawnTrackingTemplateData->PhaseUseFlags,
11806 uint32(spawnType), spawnId, spawnData->phaseId, spawnData->phaseGroup, spawnData->phaseUseFlags);
11807 continue;
11808 }
11809
11810 std::vector<uint32> objectiveList;
11811 if (Optional<std::string_view> objectivesStr = fields[3].GetStringViewOrNull())
11812 {
11813 for (std::string_view objectiveStr : Trinity::Tokenize(*objectivesStr, ',', false))
11814 {
11815 Optional<uint32> objectiveId = Trinity::StringTo<uint32>(objectiveStr);
11816 if (!objectiveId)
11817 continue;
11818
11819 if (!IsQuestObjectiveForSpawnTracking(spawnTrackingId, *objectiveId))
11820 {
11821 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);
11822 continue;
11823 }
11824
11825 objectiveList.push_back(*objectiveId);
11826 }
11827
11828 if (objectiveList.empty())
11829 {
11830 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);
11831 continue;
11832 }
11833 }
11834
11835 const_cast<SpawnMetadata*>(data)->spawnTrackingData = spawnTrackingTemplateData;
11836 const_cast<SpawnMetadata*>(data)->spawnTrackingQuestObjectives = std::move(objectiveList);
11837 _spawnTrackingMapStore.emplace(spawnTrackingId, data);
11838
11839 ++count;
11840 } while (result->NextRow());
11841
11842 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking members in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11843}
11844
11846{
11847 uint32 oldMSTime = getMSTime();
11848
11849 // 0 1 2 3 4 5 6 7
11850 QueryResult result = WorldDatabase.Query("SELECT SpawnType, SpawnId, State, Visible, StateSpellVisualId, StateAnimId, StateAnimKitId, StateWorldEffects FROM spawn_tracking_state");
11851
11852 if (!result)
11853 {
11854 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn tracking states. DB table `spawn_tracking_state` is empty!");
11855 return;
11856 }
11857
11858 uint32 count = 0;
11859
11860 do
11861 {
11862 Field* fields = result->Fetch();
11863 SpawnObjectType spawnType = SpawnObjectType(fields[0].GetUInt8());
11864 ObjectGuid::LowType spawnId = fields[1].GetUInt64();
11865 SpawnTrackingState state = SpawnTrackingState(fields[2].GetUInt8());
11866
11867 if (!SpawnData::TypeIsValid(spawnType))
11868 {
11869 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn data with invalid type {}. Skipped.", uint32(spawnType));
11870 continue;
11871 }
11872 else if (spawnType == SPAWN_TYPE_AREATRIGGER)
11873 {
11874 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has areatrigger spawn ({}). Skipped.", uint32(spawnType));
11875 continue;
11876 }
11877
11878 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
11879 if (!data)
11880 {
11881 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn ({},{}) not found!", uint32(spawnType), spawnId);
11882 continue;
11883 }
11884 else if (!data->spawnTrackingData)
11885 {
11886 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);
11887 continue;
11888 }
11889
11890 if (state >= SpawnTrackingState::Max)
11891 {
11892 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` has spawn ({},{}) with invalid state type {}. Skipped.", uint32(spawnType), spawnId, uint8(state));
11893 continue;
11894 }
11895
11896 SpawnTrackingStateData& spawnTrackingStateData = const_cast<SpawnMetadata*>(data)->spawnTrackingStates[AsUnderlyingType(state)];
11897 spawnTrackingStateData.Visible = fields[3].GetBool();
11898 spawnTrackingStateData.StateSpellVisualId = fields[4].GetUInt32OrNull();
11899 spawnTrackingStateData.StateAnimId = fields[5].GetUInt16OrNull();
11900 spawnTrackingStateData.StateAnimKitId = fields[6].GetUInt16OrNull();
11901
11902 if (spawnTrackingStateData.StateSpellVisualId && !sSpellVisualStore.HasRecord(*spawnTrackingStateData.StateSpellVisualId))
11903 {
11904 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateSpellVisualId {} for spawn ({},{}), set to none.",
11905 *spawnTrackingStateData.StateSpellVisualId, uint32(spawnType), spawnId);
11906 spawnTrackingStateData.StateSpellVisualId.reset();
11907 }
11908
11909 if (spawnTrackingStateData.StateAnimId && *spawnTrackingStateData.StateAnimId != sDB2Manager.GetEmptyAnimStateID() && !sAnimationDataStore.HasRecord(*spawnTrackingStateData.StateAnimId))
11910 {
11911 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateAnimId {} for spawn ({},{}), set to none.",
11912 *spawnTrackingStateData.StateAnimId, uint32(spawnType), spawnId);
11913 spawnTrackingStateData.StateAnimId.reset();
11914 }
11915
11916 if (spawnTrackingStateData.StateAnimKitId && !sAnimKitStore.HasRecord(*spawnTrackingStateData.StateAnimKitId))
11917 {
11918 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid StateAnimKitId {} for spawn ({},{}), set to none.",
11919 *spawnTrackingStateData.StateAnimKitId, uint32(spawnType), spawnId);
11920 spawnTrackingStateData.StateAnimKitId.reset();
11921 }
11922
11923 if (Optional<std::string_view> worldEffectsStr = fields[7].GetStringViewOrNull())
11924 {
11925 for (std::string_view worldEffectStr : Trinity::Tokenize(*worldEffectsStr, ',', false))
11926 {
11927 Optional<uint32> worldEffectId = Trinity::StringTo<uint32>(worldEffectStr);
11928 if (!worldEffectId)
11929 continue;
11930
11931 if (!sWorldEffectStore.HasRecord(*worldEffectId))
11932 {
11933 TC_LOG_ERROR("sql.sql", "Table `spawn_tracking_state` references invalid WorldEffectId {} for spawn ({},{}). Skipped.",
11934 *worldEffectId, uint32(spawnType), spawnId);
11935 continue;
11936 }
11937
11938 spawnTrackingStateData.StateWorldEffects.push_back(*worldEffectId);
11939 }
11940 }
11941
11942 ++count;
11943 } while (result->NextRow());
11944
11945 TC_LOG_INFO("server.loading", ">> Loaded {} spawn tracking states in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
11946}
11947
11949{
11950 uint32 oldMSTime = getMSTime();
11951
11952 // need for reload case
11953 _jumpChargeParams.clear();
11954
11955 QueryResult result = WorldDatabase.Query("SELECT id, speed, treatSpeedAsMoveTimeSeconds, minHeight, maxHeight, unlimitedSpeed, spellVisualId, progressCurveId, parabolicCurveId, triggerSpellId FROM jump_charge_params");
11956 if (!result)
11957 {
11958 return;
11959 }
11960
11961 DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(ResultSet, (id)(speed)(treatSpeedAsMoveTimeSeconds)(minHeight)(maxHeight)(unlimitedSpeed)
11962 (spellVisualId)(progressCurveId)(parabolicCurveId)(triggerSpellId)) fields { *result };
11963
11964 do
11965 {
11966 int32 id = fields.id().GetInt32();
11967 JumpChargeParams& params = _jumpChargeParams[id];
11968 params.Speed = fields.speed().GetFloat();
11969 params.TreatSpeedAsMoveTimeSeconds = fields.treatSpeedAsMoveTimeSeconds().GetBool();
11970 params.UnlimitedSpeed = fields.unlimitedSpeed().GetBool();
11971 params.MinHeight = fields.minHeight().GetFloatOrNull();
11972 params.MaxHeight = fields.maxHeight().GetFloatOrNull();
11973 params.SpellVisualId = fields.spellVisualId().GetInt32OrNull();
11974 params.ProgressCurveId = fields.progressCurveId().GetInt32OrNull();
11975 params.ParabolicCurveId = fields.parabolicCurveId().GetInt32OrNull();
11976 params.TriggerSpellId = fields.triggerSpellId().GetInt32OrNull();
11977
11978 if (params.Speed <= 0.0f)
11979 {
11980 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid speed {} for id {}, set to default charge speed {}.",
11981 params.Speed, id, SPEED_CHARGE);
11982 params.Speed = SPEED_CHARGE;
11983 }
11984
11985 if (params.MinHeight && *params.MinHeight <= 0.0f)
11986 {
11987 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid min height {} for id {}, set to none.",
11988 params.MinHeight, id);
11989 params.MinHeight.reset();
11990 }
11991
11992 if (params.MaxHeight && *params.MaxHeight <= 0.0f)
11993 {
11994 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid max height {} for id {}, set to none.",
11995 params.MaxHeight, id);
11996 params.MaxHeight.reset();
11997 }
11998
11999 if (params.MinHeight && params.MaxHeight && *params.MinHeight >= *params.MaxHeight)
12000 {
12001 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` uses invalid max height {} (must be greated than min height {}) for id {}, set to none.",
12002 params.MaxHeight, params.MinHeight, id);
12003 params.MaxHeight.reset();
12004 }
12005
12006 if (params.SpellVisualId && !sSpellVisualStore.LookupEntry(*params.SpellVisualId))
12007 {
12008 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing SpellVisual: {} for id {}, ignored.",
12009 *params.SpellVisualId, id);
12010 params.SpellVisualId.reset();
12011 }
12012
12013 if (params.ProgressCurveId && !sCurveStore.LookupEntry(*params.ProgressCurveId))
12014 {
12015 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing progress Curve: {} for id {}, ignored.",
12016 *params.ProgressCurveId, id);
12017 params.ProgressCurveId.reset();
12018 }
12019
12020 if (params.ParabolicCurveId && !sCurveStore.LookupEntry(*params.ParabolicCurveId))
12021 {
12022 TC_LOG_ERROR("sql.sql", "Table `jump_charge_params` references non-existing parabolic Curve: {} for id {}, ignored.",
12023 *params.ParabolicCurveId, id);
12024 params.ParabolicCurveId.reset();
12025 }
12026
12027 if (params.TriggerSpellId && !sSpellMgr->GetSpellInfo(*params.TriggerSpellId, DIFFICULTY_NONE))
12028 {
12029 TC_LOG_DEBUG("sql.sql", "Table `jump_charge_params` references non-existing trigger spell id: {} for id {}, ignored.",
12030 *params.TriggerSpellId, id);
12031 params.TriggerSpellId.reset();
12032 }
12033
12034 } while (result->NextRow());
12035
12036 TC_LOG_INFO("server.loading", ">> Loaded {} Player Choice locale strings in {} ms", _jumpChargeParams.size(), GetMSTimeDiffToNow(oldMSTime));
12037}
12038
12040{
12041 uint32 oldMSTime = getMSTime();
12042 _phaseNameStore.clear();
12043
12044 // 0 1
12045 QueryResult result = WorldDatabase.Query("SELECT `ID`, `Name` FROM `phase_name`");
12046
12047 if (!result)
12048 {
12049 TC_LOG_INFO("server.loading", ">> Loaded 0 phase names. DB table `phase_name` is empty.");
12050 return;
12051 }
12052
12053 uint32 count = 0;
12054 do
12055 {
12056 Field* fields = result->Fetch();
12057
12058 uint32 phaseId = fields[0].GetUInt32();
12059 std::string name = fields[1].GetString();
12060
12061 _phaseNameStore[phaseId] = name;
12062
12063 ++count;
12064 } while (result->NextRow());
12065 TC_LOG_INFO("server.loading", ">> Loaded {} phase names in {} ms.", count, GetMSTimeDiffToNow(oldMSTime));
12066}
12067
12068std::string ObjectMgr::GetPhaseName(uint32 phaseId) const
12069{
12070 PhaseNameContainer::const_iterator iter = _phaseNameStore.find(phaseId);
12071 return iter != _phaseNameStore.end() ? iter->second : "Unknown Name";
12072}
#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
#define sCriteriaMgr
std::vector< Criteria const * > CriteriaList
DB2Storage< PhaseEntry > sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance)
DB2Storage< SkillRaceClassInfoEntry > sSkillRaceClassInfoStore("SkillRaceClassInfo.db2", &SkillRaceClassInfoLoadInfo::Instance)
DB2Storage< DifficultyEntry > sDifficultyStore("Difficulty.db2", &DifficultyLoadInfo::Instance)
DB2Storage< GameObjectDisplayInfoEntry > sGameObjectDisplayInfoStore("GameObjectDisplayInfo.db2", &GameobjectDisplayInfoLoadInfo::Instance)
DB2Storage< CharacterLoadoutEntry > sCharacterLoadoutStore("CharacterLoadout.db2", &CharacterLoadoutLoadInfo::Instance)
DB2Storage< SpellVisualEntry > sSpellVisualStore("SpellVisual.db2", &SpellVisualLoadInfo::Instance)
DB2Storage< ItemEntry > sItemStore("Item.db2", &ItemLoadInfo::Instance)
DB2Storage< SkillLineEntry > sSkillLineStore("SkillLine.db2", &SkillLineLoadInfo::Instance)
DB2Storage< 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)
TaxiPathNodesByPath sTaxiPathNodesByPath
DB2Storage< ItemXItemEffectEntry > sItemXItemEffectStore("ItemXItemEffect.db2", &ItemXItemEffectLoadInfo::Instance)
DB2Storage< ScenarioEntry > sScenarioStore("Scenario.db2", &ScenarioLoadInfo::Instance)
DB2Storage< ChrClassesEntry > sChrClassesStore("ChrClasses.db2", &ChrClassesLoadInfo::Instance)
DB2Storage< VehicleSeatEntry > sVehicleSeatStore("VehicleSeat.db2", &VehicleSeatLoadInfo::Instance)
DB2Storage< LockEntry > sLockStore("Lock.db2", &LockLoadInfo::Instance)
DB2Storage< CharTitlesEntry > sCharTitlesStore("CharTitles.db2", &CharTitlesLoadInfo::Instance)
DB2Storage< ChrSpecializationEntry > sChrSpecializationStore("ChrSpecialization.db2", &ChrSpecializationLoadInfo::Instance)
DB2Storage< Cfg_CategoriesEntry > sCfgCategoriesStore("Cfg_Categories.db2", &CfgCategoriesLoadInfo::Instance)
DB2Storage< GossipNPCOptionEntry > sGossipNPCOptionStore("GossipNPCOption.db2", &GossipNpcOptionLoadInfo::Instance)
DB2Storage< QuestSortEntry > sQuestSortStore("QuestSort.db2", &QuestSortLoadInfo::Instance)
DB2Storage< ItemEffectEntry > sItemEffectStore("ItemEffect.db2", &ItemEffectLoadInfo::Instance)
DB2Storage< CriteriaTreeEntry > sCriteriaTreeStore("CriteriaTree.db2", &CriteriaTreeLoadInfo::Instance)
DB2Storage< BattlePetSpeciesEntry > sBattlePetSpeciesStore("BattlePetSpecies.db2", &BattlePetSpeciesLoadInfo::Instance)
DB2Storage< AreaTriggerEntry > sAreaTriggerStore("AreaTrigger.db2", &AreaTriggerLoadInfo::Instance)
DB2Storage< MailTemplateEntry > sMailTemplateStore("MailTemplate.db2", &MailTemplateLoadInfo::Instance)
DB2Storage< EmotesEntry > sEmotesStore("Emotes.db2", &EmotesLoadInfo::Instance)
DB2Storage< 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:569
@ MAX_LEVEL
Definition DBCEnums.h:45
@ STRONG_MAX_LEVEL
Definition DBCEnums.h:49
ItemContext
Definition DBCEnums.h:1315
Difficulty
Definition DBCEnums.h:932
@ DIFFICULTY_NONE
Definition DBCEnums.h:933
ItemSpecStat
Definition DBCEnums.h:1537
@ ITEM_SPEC_STAT_TWO_HANDED_MACE
Definition DBCEnums.h:1550
@ ITEM_SPEC_STAT_GUN
Definition DBCEnums.h:1553
@ ITEM_SPEC_STAT_HASTE
Definition DBCEnums.h:1563
@ ITEM_SPEC_STAT_CRIT
Definition DBCEnums.h:1562
@ ITEM_SPEC_STAT_CLOAK
Definition DBCEnums.h:1565
@ ITEM_SPEC_STAT_DODGE
Definition DBCEnums.h:1543
@ ITEM_SPEC_STAT_INTELLECT
Definition DBCEnums.h:1538
@ ITEM_SPEC_STAT_RELIC_FROST
Definition DBCEnums.h:1572
@ ITEM_SPEC_STAT_RELIC_BLOOD
Definition DBCEnums.h:1568
@ ITEM_SPEC_STAT_STRENGTH
Definition DBCEnums.h:1540
@ ITEM_SPEC_STAT_SHIELD
Definition DBCEnums.h:1560
@ ITEM_SPEC_STAT_NONE
Definition DBCEnums.h:1579
@ ITEM_SPEC_STAT_ONE_HANDED_AXE
Definition DBCEnums.h:1545
@ ITEM_SPEC_STAT_BONUS_ARMOR
Definition DBCEnums.h:1564
@ ITEM_SPEC_STAT_RELIC
Definition DBCEnums.h:1561
@ ITEM_SPEC_STAT_STAFF
Definition DBCEnums.h:1556
@ ITEM_SPEC_STAT_DAGGER
Definition DBCEnums.h:1551
@ ITEM_SPEC_STAT_AGILITY
Definition DBCEnums.h:1539
@ ITEM_SPEC_STAT_RELIC_HOLY
Definition DBCEnums.h:1577
@ ITEM_SPEC_STAT_RELIC_WIND
Definition DBCEnums.h:1576
@ ITEM_SPEC_STAT_THROWN
Definition DBCEnums.h:1558
@ ITEM_SPEC_STAT_WAND
Definition DBCEnums.h:1559
@ ITEM_SPEC_STAT_BOW
Definition DBCEnums.h:1554
@ ITEM_SPEC_STAT_RELIC_FIRE
Definition DBCEnums.h:1573
@ ITEM_SPEC_STAT_FIST_WEAPON
Definition DBCEnums.h:1552
@ ITEM_SPEC_STAT_TWO_HANDED_SWORD
Definition DBCEnums.h:1548
@ ITEM_SPEC_STAT_PARRY
Definition DBCEnums.h:1544
@ ITEM_SPEC_STAT_POLEARM
Definition DBCEnums.h:1557
@ ITEM_SPEC_STAT_RELIC_FEL
Definition DBCEnums.h:1570
@ ITEM_SPEC_STAT_RELIC_WATER
Definition DBCEnums.h:1574
@ ITEM_SPEC_STAT_ONE_HANDED_MACE
Definition DBCEnums.h:1549
@ ITEM_SPEC_STAT_RELIC_SHADOW
Definition DBCEnums.h:1569
@ ITEM_SPEC_STAT_WARGLAIVES
Definition DBCEnums.h:1566
@ ITEM_SPEC_STAT_RELIC_LIFE
Definition DBCEnums.h:1575
@ ITEM_SPEC_STAT_RELIC_ARCANE
Definition DBCEnums.h:1571
@ ITEM_SPEC_STAT_CROSSBOW
Definition DBCEnums.h:1555
@ ITEM_SPEC_STAT_TWO_HANDED_AXE
Definition DBCEnums.h:1546
@ ITEM_SPEC_STAT_RELIC_IRON
Definition DBCEnums.h:1567
@ ITEM_SPEC_STAT_ONE_HANDED_SWORD
Definition DBCEnums.h:1547
@ ITEM_SPEC_STAT_HIT
Definition DBCEnums.h:1542
@ PHASE_USE_FLAGS_ALWAYS_VISIBLE
Definition DBCEnums.h:2167
@ PHASE_USE_FLAGS_ALL
Definition DBCEnums.h:2170
@ PHASE_USE_FLAGS_INVERSE
Definition DBCEnums.h:2168
#define MAX_ITEM_PROTO_STATS
Definition DBCEnums.h:1211
CriteriaType
Definition DBCEnums.h:546
@ 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:88
#define ABORT
Definition Errors.h:87
#define ASSERT_NOTNULL(pointer)
Definition Errors.h:82
#define ASSERT
Definition Errors.h:80
#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:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
@ MAIL_CHECK_MASK_COD_PAYMENT
This mail was copied. Do not allow making a copy of items in mail.
Definition Mail.h:56
@ MAIL_CHECK_MASK_RETURNED
Definition Mail.h:54
std::vector< MailItemInfo > MailItemInfoVec
Definition Mail.h:172
@ MAIL_NORMAL
Definition Mail.h:39
#define sMapMgr
Definition MapManager.h: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:99
ScriptMapMap sSpellScripts
Definition ObjectMgr.cpp:84
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:85
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:87
ExtendedPlayerName ExtractExtendedPlayerName(std::string const &name)
bool normalizePlayerName(std::string &name)
std::vector< PlayerCreateInfoItem > PlayerCreateInfoItems
Definition ObjectMgr.h:628
#define MAX_PLAYER_NAME
Definition ObjectMgr.h:888
std::pair< GraveyardContainer::const_iterator, GraveyardContainer::const_iterator > GraveyardMapBounds
Definition ObjectMgr.h:847
std::multimap< uint32, ScriptInfo > ScriptMap
Definition ObjectMgr.h:412
#define MAX_CHARTER_NAME
Definition ObjectMgr.h:891
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const *rcEntry)
std::pair< SpellScriptsContainer::iterator, SpellScriptsContainer::iterator > SpellScriptsBounds
Definition ObjectMgr.h:415
ScriptMapMap * GetScriptsMapByType(ScriptsType type)
Definition ObjectMgr.cpp:99
ScriptCommands
Definition ObjectMgr.h:98
@ SCRIPT_COMMAND_EMOTE
Definition ObjectMgr.h:100
@ SCRIPT_COMMAND_CREATE_ITEM
Definition ObjectMgr.h:116
@ SCRIPT_COMMAND_DESPAWN_SELF
Definition ObjectMgr.h:117
@ SCRIPT_COMMAND_CLOSE_DOOR
Definition ObjectMgr.h:111
@ SCRIPT_COMMAND_CAST_SPELL
Definition ObjectMgr.h:114
@ SCRIPT_COMMAND_RESPAWN_GAMEOBJECT
Definition ObjectMgr.h:108
@ SCRIPT_COMMAND_QUEST_EXPLORED
Definition ObjectMgr.h:106
@ SCRIPT_COMMAND_ACTIVATE_OBJECT
Definition ObjectMgr.h:112
@ SCRIPT_COMMAND_TALK
Definition ObjectMgr.h:99
@ SCRIPT_COMMAND_OPEN_DOOR
Definition ObjectMgr.h:110
@ SCRIPT_COMMAND_EQUIP
Definition ObjectMgr.h:125
@ SCRIPT_COMMAND_PLAYMOVIE
Definition ObjectMgr.h:128
@ SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT
Definition ObjectMgr.h:120
@ SCRIPT_COMMAND_PLAY_ANIMKIT
Definition ObjectMgr.h:130
@ SCRIPT_COMMAND_TELEPORT_TO
Definition ObjectMgr.h:105
@ SCRIPT_COMMAND_FIELD_SET_DEPRECATED
Definition ObjectMgr.h:101
@ SCRIPT_COMMAND_MOVE_TO
Definition ObjectMgr.h:102
@ SCRIPT_COMMAND_FLAG_REMOVE_DEPRECATED
Definition ObjectMgr.h:104
@ SCRIPT_COMMAND_TEMP_SUMMON_CREATURE
Definition ObjectMgr.h:109
@ SCRIPT_COMMAND_MOVEMENT
Definition ObjectMgr.h:129
@ SCRIPT_COMMAND_KILL_CREDIT
Definition ObjectMgr.h:107
@ SCRIPT_COMMAND_KILL
Definition ObjectMgr.h:121
@ SCRIPT_COMMAND_LOAD_PATH
Definition ObjectMgr.h:119
@ SCRIPT_COMMAND_ORIENTATION
Definition ObjectMgr.h:124
@ SCRIPT_COMMAND_PLAY_SOUND
Definition ObjectMgr.h:115
@ SCRIPT_COMMAND_MODEL
Definition ObjectMgr.h:126
@ SCRIPT_COMMAND_CLOSE_GOSSIP
Definition ObjectMgr.h:127
@ SCRIPT_COMMAND_REMOVE_AURA
Definition ObjectMgr.h:113
@ SCRIPT_COMMAND_FLAG_SET_DEPRECATED
Definition ObjectMgr.h:103
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, CellObjectGuids > CellObjectGuidsMap
Definition ObjectMgr.h:478
#define SPAWNGROUP_MAP_UNSET
Definition ObjectMgr.h:894
TC_GAME_API ScriptMapMap sEventScripts
Definition ObjectMgr.cpp:85
#define sObjectMgr
Definition ObjectMgr.h:1885
std::pair< GossipMenusContainer::const_iterator, GossipMenusContainer::const_iterator > GossipMenusMapBounds
Definition ObjectMgr.h:777
TC_GAME_API ScriptMapMap sSpellScripts
Definition ObjectMgr.cpp:84
@ CHAT_TYPE_WHISPER
Definition ObjectMgr.h:139
std::map< uint32, ScriptMap > ScriptMapMap
Definition ObjectMgr.h:413
SummonerType
Definition ObjectMgr.h:71
@ SUMMONER_TYPE_MAP
Definition ObjectMgr.h:74
@ SUMMONER_TYPE_CREATURE
Definition ObjectMgr.h:72
@ SUMMONER_TYPE_GAMEOBJECT
Definition ObjectMgr.h:73
std::string GetScriptsTableNameByType(ScriptsType type)
Definition ObjectMgr.cpp:87
ScriptsType
Definition ObjectMgr.h:169
@ SCRIPTS_EVENT
Definition ObjectMgr.h:173
@ SCRIPTS_SPELL
Definition ObjectMgr.h:172
#define MAX_SKILL_STEP
Definition ObjectMgr.h:876
std::multimap< uint32, uint32 > QuestRelationsReverse
Definition ObjectMgr.h:560
std::multimap< uint32, uint32 > QuestRelations
Definition ObjectMgr.h:559
#define MAX_PET_NAME
Definition ObjectMgr.h:890
SkillRangeType
Definition ObjectMgr.h:868
@ SKILL_RANGE_MONO
Definition ObjectMgr.h:871
@ SKILL_RANGE_NONE
Definition ObjectMgr.h:873
@ SKILL_RANGE_LANGUAGE
Definition ObjectMgr.h:869
@ SKILL_RANGE_RANK
Definition ObjectMgr.h:872
@ SKILL_RANGE_LEVEL
Definition ObjectMgr.h:870
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:48
#define QUEST_REWARD_ITEM_COUNT
Definition QuestDef.h:50
#define QUEST_REWARD_REPUTATIONS_COUNT
Definition QuestDef.h:52
@ QUEST_OBJECTIVE_DEFEATBATTLEPET
Definition QuestDef.h:370
@ QUEST_OBJECTIVE_WINPVPPETBATTLES
Definition QuestDef.h:371
@ QUEST_OBJECTIVE_INCREASE_REPUTATION
Definition QuestDef.h:376
@ QUEST_OBJECTIVE_HAVE_CURRENCY
Definition QuestDef.h:374
@ QUEST_OBJECTIVE_WINPETBATTLEAGAINSTNPC
Definition QuestDef.h:369
@ QUEST_OBJECTIVE_MONSTER
Definition QuestDef.h:358
@ QUEST_OBJECTIVE_TALKTO
Definition QuestDef.h:361
@ QUEST_OBJECTIVE_KILL_WITH_LABEL
Definition QuestDef.h:379
@ QUEST_OBJECTIVE_CRITERIA_TREE
Definition QuestDef.h:372
@ QUEST_OBJECTIVE_OBTAIN_CURRENCY
Definition QuestDef.h:375
@ QUEST_OBJECTIVE_PROGRESS_BAR
Definition QuestDef.h:373
@ QUEST_OBJECTIVE_PLAYERKILLS
Definition QuestDef.h:367
@ QUEST_OBJECTIVE_ITEM
Definition QuestDef.h:359
@ QUEST_OBJECTIVE_MONEY
Definition QuestDef.h:366
@ QUEST_OBJECTIVE_MAX_REPUTATION
Definition QuestDef.h:365
@ QUEST_OBJECTIVE_AREA_TRIGGER_EXIT
Definition QuestDef.h:378
@ QUEST_OBJECTIVE_AREATRIGGER
Definition QuestDef.h:368
@ QUEST_OBJECTIVE_CURRENCY
Definition QuestDef.h:362
@ QUEST_OBJECTIVE_LEARNSPELL
Definition QuestDef.h:363
@ QUEST_OBJECTIVE_AREA_TRIGGER_ENTER
Definition QuestDef.h:377
@ QUEST_OBJECTIVE_GAMEOBJECT
Definition QuestDef.h:360
@ QUEST_OBJECTIVE_MIN_REPUTATION
Definition QuestDef.h:364
#define QUEST_REWARD_CURRENCY_COUNT
Definition QuestDef.h:54
@ QUEST_OBJECTIVE_FLAG_SEQUENCED
Definition QuestDef.h:388
#define QUEST_REWARD_CHOICES_COUNT
Definition QuestDef.h:49
@ QUEST_FLAGS_DAILY
Definition QuestDef.h:230
@ QUEST_FLAGS_WEEKLY
Definition QuestDef.h:233
@ QUEST_FLAGS_COMPLETION_AREA_TRIGGER
Definition QuestDef.h:220
@ QUEST_FLAGS_COMPLETION_EVENT
Definition QuestDef.h:219
@ QUEST_FLAGS_TRACKING_EVENT
Definition QuestDef.h:228
@ QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES
Definition QuestDef.h:333
@ QUEST_SPECIAL_FLAGS_REPEATABLE
Definition QuestDef.h:324
@ QUEST_SPECIAL_FLAGS_DB_ALLOWED
Definition QuestDef.h:331
@ QUEST_SPECIAL_FLAGS_MONTHLY
Definition QuestDef.h:328
constexpr Trinity::RaceMask< uint64 > RACEMASK_ALL_PLAYABLE
Definition RaceMask.h:289
constexpr Trinity::RaceMask< T > RACEMASK_ALL_PLAYABLE_v
Definition RaceMask.h:231
constexpr Trinity::RaceMask< T > RACEMASK_ALL_v
Definition RaceMask.h:228
Races
Definition RaceMask.h:27
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
#define sRealmList
Definition RealmList.h:93
SceneFlag
#define sScriptMgr
Definition ScriptMgr.h:1449
SpellEffIndex
#define MAX_STATS
#define MAX_REPUTATION_RANK
#define CURRENT_EXPANSION
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
@ SPELL_EFFECT_DUMMY
@ SPELL_EFFECT_SCRIPT_EFFECT
@ SPELL_EFFECT_SEND_EVENT
constexpr uint32 SkillByQuestSort(int32 QuestSort)
SpellClickUserTypes
@ SPELL_CLICK_USER_PARTY
@ SPELL_CLICK_USER_RAID
@ SPELL_CLICK_USER_MAX
@ SPELL_CLICK_USER_FRIEND
#define MAX_GAMEOBJECT_DATA
@ ALLIANCE
@ HORDE
#define MAX_CLASSES
@ STAT_INTELLECT
@ STAT_AGILITY
@ STAT_STRENGTH
@ STAT_STAMINA
#define CLASSMASK_ALL_PLAYABLE
#define MAX_GO_STATE
CreatureClassifications
@ SPELL_CATEGORY_DRINK
@ SPELL_CATEGORY_FOOD
@ SPELL_SCHOOL_NORMAL
@ SPELL_SCHOOL_HOLY
@ MAX_SPELL_SCHOOL
PetNameInvalidReason
@ PET_NAME_INVALID
@ PET_NAME_SUCCESS
@ PET_NAME_MIXED_LANGUAGES
@ PET_NAME_TOO_SHORT
@ PET_NAME_TOO_LONG
#define CLASSMASK_ALL_CREATURES
@ EXPANSION_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
uint32 const Entry[5]
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
void shrink_to_fit()
Definition ByteBuffer.h:584
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:225
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:1718
std::set< uint32 > EventContainer
Definition ObjectMgr.h:988
CreatureMovementData const * GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
QuestOfferRewardLocaleContainer _questOfferRewardLocaleStore
Definition ObjectMgr.h:1854
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:1848
QuestRelations _creatureQuestInvolvedRelations
Definition ObjectMgr.h:1740
PetLevelInfo const * GetPetLevelInfo(uint32 creature_id, uint8 level) const
void LoadCreatureTemplateResistances()
VehicleAccessoryList const * GetVehicleAccessoryList(Vehicle *veh) const
VehicleAccessoryContainer _vehicleAccessoryStore
Definition ObjectMgr.h:1759
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:1838
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:1510
QuestObjectivesByIdContainer _questObjectives
Definition ObjectMgr.h:1703
MapPersonalObjectGuids _mapPersonalObjectGuidsStore
Definition ObjectMgr.h:1817
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:1800
CreatureQuestCurrenciesMap _creatureQuestCurrenciesStore
Definition ObjectMgr.h:1829
void LoadPageTextLocales()
AreaTriggerTeleport const * GetGoBackTrigger(uint32 Map) const
HalfNameContainer _petHalfName1
Definition ObjectMgr.h:1814
void LoadVehicleTemplate()
void LoadInstanceTemplate()
CreatureTemplateSparringContainer _creatureTemplateSparringStore
Definition ObjectMgr.h:1824
void LoadItemScriptNames()
uint32 GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team=false)
void LoadSpellScripts()
SpellScriptsContainer _spellScriptsStore
Definition ObjectMgr.h:1755
SpawnGroupDataContainer _spawnGroupDataStore
Definition ObjectMgr.h:1840
QuestPOIData const * GetQuestPOIData(int32 questId)
WorldSafeLocsEntry const * GetDefaultGraveyard(uint32 team) const
void RemoveSpawnDataFromGrid(SpawnData const *data)
void LoadQuestGreetingLocales()
CreatureDataContainer _creatureDataStore
Definition ObjectMgr.h:1818
void LoadPageTexts()
void ValidateSpellScripts()
void LoadCreatureQuestEnders()
QuestRelations _goQuestRelations
Definition ObjectMgr.h:1736
void LoadSceneTemplates()
void LoadGameobjectQuestEnders()
void LoadCreatureMovementOverrides()
void LoadCreatureLocales()
void LoadDestructibleHitpoints()
TavernAreaTriggerContainer _tavernAreaTriggerStore
Definition ObjectMgr.h:1711
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:1856
EquipmentInfoContainer _equipmentInfoStore
Definition ObjectMgr.h:1831
ObjectGuid::LowType _gameObjectSpawnId
Definition ObjectMgr.h:1695
void GetPlayerClassLevelInfo(uint32 class_, uint8 level, uint32 &baseMana) const
void LoadTrainers()
AccessRequirementContainer _accessRequirementStore
Definition ObjectMgr.h:1719
JumpChargeParams const * GetJumpChargeParams(int32 id) const
void LoadQuestAreaTriggers()
QuestGreetingLocaleContainer _questGreetingLocaleStore
Definition ObjectMgr.h:1715
SpawnData const * GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const
std::unordered_map< uint32, SkillTiersEntry > _skillTiers
Definition ObjectMgr.h:1810
CreatureTemplateContainer _creatureTemplateStore
Definition ObjectMgr.h:1819
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:1845
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:1855
std::map< HighGuid, ObjectGuidGenerator > _guidGenerators
Definition ObjectMgr.h:1700
PlayerChoice const * GetPlayerChoice(int32 choiceId) const
void LoadPhases()
QuestGreetingContainer _questGreetingStore
Definition ObjectMgr.h:1714
SpawnGroupTemplateData const * GetLegacySpawnGroup() const
Definition ObjectMgr.h:1368
void LoadFactionChangeReputations()
void RemoveCreatureFromGrid(CreatureData const *data)
std::set< uint32 > _transportMaps
Definition ObjectMgr.h:1881
void LoadPetNumber()
std::unordered_map< ObjectGuid::LowType, CreatureMovementData > _creatureMovementOverrides
Definition ObjectMgr.h:1825
GraveyardContainer GraveyardStore
Definition ObjectMgr.h:1608
std::string const & GetScriptName(uint32 id) const
GameObjectTemplateContainer _gameObjectTemplateStore
Definition ObjectMgr.h:1837
void LoadReputationSpilloverTemplate()
void LoadExplorationBaseXP()
void LoadFishingBaseSkillLevel()
InstanceSpawnGroupContainer _instanceSpawnGroupStore
Definition ObjectMgr.h:1843
uint32 GetScriptId(std::string_view name, bool isDatabaseBound=true)
void LoadQuestRequestItemsLocale()
EventScriptContainer _eventScriptStore
Definition ObjectMgr.h:1723
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:1448
bool IsValidEvent(uint32 eventId) const
Definition ObjectMgr.h:1117
void LoadSpawnGroups()
NpcText const * GetNpcText(uint32 textID) const
void LoadGossipMenu()
void LoadCreatureStaticFlagsOverride()
QuestRelationsReverse _creatureQuestInvolvedRelationsReverse
Definition ObjectMgr.h:1741
ItemTemplateContainer _itemTemplateStore
Definition ObjectMgr.h:1851
void LoadQuestStartersAndEnders()
GameObjectOverride const * GetGameObjectOverride(ObjectGuid::LowType spawnId) const
static PetNameInvalidReason CheckPetName(std::string_view name)
GameObjectDataContainer _gameObjectDataStore
Definition ObjectMgr.h:1834
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:1833
void CheckCreatureTemplate(CreatureTemplate const *cInfo)
std::atomic< uint32 > _hiPetNumber
Definition ObjectMgr.h:1693
bool IsReservedName(std::string_view name) const
void AddCreatureToGrid(CreatureData const *data)
std::unordered_map< uint32, std::vector< uint32 > > _spawnGroupsByMap
Definition ObjectMgr.h:1841
std::atomic< uint64 > _mailId
Definition ObjectMgr.h:1692
void RemoveGameobjectFromGrid(GameObjectData const *data)
GameTele const * GetGameTeleExactName(std::string_view name) const
void LoadGameTele()
int32 GetBaseReputationOf(FactionEntry const *factionEntry, uint8 race, uint8 playerClass) const
void LoadCreatureSummonedData()
PointOfInterestLocaleContainer _pointOfInterestLocaleStore
Definition ObjectMgr.h:1858
InstanceTemplateContainer _instanceTemplateStore
Definition ObjectMgr.h:1764
void LoadTerrainSwapDefaults()
bool DeleteGameTele(std::string_view name)
ObjectGuid::LowType GenerateCreatureSpawnId()
QuestRelations _goQuestInvolvedRelations
Definition ObjectMgr.h:1737
void LoadCreatureQuestItems()
void LoadNPCSpellClickSpells()
QuestAreaTriggerContainer _questAreaTriggerStore
Definition ObjectMgr.h:1710
FishingBaseSkillContainer _fishingBaseForAreaStore
Definition ObjectMgr.h:1809
bool IsTransportMap(uint32 mapId) const
Definition ObjectMgr.h:1645
ObjectGuid::LowType _creatureSpawnId
Definition ObjectMgr.h:1694
AreaTriggerContainer _areaTriggerStore
Definition ObjectMgr.h:1716
void LoadUiMapQuests()
void LoadAccessRequirements()
MapObjectGuids _mapObjectGuidsStore
Definition ObjectMgr.h:1816
CreatureAddonContainer _creatureAddonStore
Definition ObjectMgr.h:1822
void LoadSpawnTrackingStates()
PetLevelInfoContainer _petInfoStore
Definition ObjectMgr.h:1796
static CreatureModel const * ChooseDisplayId(CreatureTemplate const *cinfo, CreatureData const *data=nullptr)
VehicleAccessoryTemplateContainer _vehicleTemplateAccessoryStore
Definition ObjectMgr.h:1758
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:1813
void LoadSpawnTrackings()
void LoadGameObjectLocales()
bool IsQuestObjectiveForSpawnTracking(uint32 spawnTrackingId, uint32 questObjectiveId) const
void LoadAreaPhases()
void LoadTempSummons()
void LoadQuestGreetings()
NpcTextContainer _npcTextStore
Definition ObjectMgr.h:1713
QuestContainer _questTemplates
Definition ObjectMgr.h:1701
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:1702
ObjectGuidGenerator & GetGuidSequenceGenerator(HighGuid high)
uint32 GetBaseXP(uint8 level)
CreatureTemplateAddonContainer _creatureTemplateAddonStore
Definition ObjectMgr.h:1823
static bool IsValidCharterName(std::string_view name)
std::unordered_map< uint32, VehicleTemplate > _vehicleTemplateStore
Definition ObjectMgr.h:1757
void LoadPetNames()
GameObjectTemplate const * GetGameObjectTemplate(uint32 entry) const
GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore
Definition ObjectMgr.h:1857
BaseXPContainer _baseXPTable
Definition ObjectMgr.h:1806
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:1717
void CheckCreatureMovement(char const *table, uint64 id, CreatureMovementData &creatureMovement)
QuestRelations _creatureQuestRelations
Definition ObjectMgr.h:1739
ClassAvailability const * GetClassExpansionRequirementFallback(uint8 classId) const
void AddSpawnDataToGrid(SpawnData const *data)
SceneTemplate const * GetSceneTemplate(uint32 sceneId) const
Definition ObjectMgr.h:1673
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:1726
void LoadGameObjectForQuests()
void LoadFactionChangeTitles()
uint32 GetAreaTriggerScriptId(uint32 trigger_id) const
void LoadPlayerChoices()
SpawnGroupLinkContainer _spawnGroupMapStore
Definition ObjectMgr.h:1842
std::string GetPhaseName(uint32 phaseId) const
uint64 _equipmentSetGuid
Definition ObjectMgr.h:1691
RepSpilloverTemplateContainer _repSpilloverTemplateStore
Definition ObjectMgr.h:1727
void LoadSpawnTrackingTemplates()
void LoadAreaTriggerScripts()
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid)
SpawnMetadata const * GetSpawnMetadata(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1416
void AddVendorItem(uint32 entry, VendorItem const &vItem, bool persist=true)
void ReturnOrDeleteOldMails(bool serverUp)
void LoadGameObjectAddons()
SpawnGroupTemplateData const * GetDefaultSpawnGroup() const
Definition ObjectMgr.h:1367
GameObjectData & NewOrExistGameObjectData(ObjectGuid::LowType spawnId)
CreatureModelInfo const * GetCreatureModelInfo(uint32 modelId) const
CreatureModelContainer _creatureModelStore
Definition ObjectMgr.h:1820
LinkedRespawnContainer _linkedRespawnStore
Definition ObjectMgr.h:1832
uint64 GenerateEquipmentSetGuid()
void LoadSkillTiers()
uint32 _auctionId
Definition ObjectMgr.h:1690
void GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
void DeleteCreatureData(ObjectGuid::LowType spawnId)
void LoadCreatureTemplateSparring()
CellObjectGuids const * GetCellPersonalObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 phaseId, uint32 cell_id) const
CellObjectGuidsMap const * GetMapObjectGuids(uint32 mapid, Difficulty spawnMode)
GameObjectLocaleContainer _gameObjectLocaleStore
Definition ObjectMgr.h:1835
CellObjectGuids const * GetCellObjectGuids(uint32 mapid, Difficulty spawnMode, uint32 cell_id)
GameObjectAddonContainer _gameObjectAddonStore
Definition ObjectMgr.h:1826
QuestObjectivesLocaleContainer _questObjectivesLocaleStore
Definition ObjectMgr.h:1853
void LoadCreatureQuestStarters()
bool IsScriptDatabaseBound(uint32 id) const
GameTele const * GetGameTele(uint32 id) const
Definition ObjectMgr.h:1539
void LoadInstanceSpawnGroups()
void LoadItemTemplateAddon()
PlayerXPperLevel _playerXPperLevel
Definition ObjectMgr.h:1803
QuestRelationsReverse _goQuestInvolvedRelationsReverse
Definition ObjectMgr.h:1738
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:1427
void AddGameobjectToGrid(GameObjectData const *data)
PointOfInterestContainer _pointsOfInterestStore
Definition ObjectMgr.h:1732
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:1725
EventContainer _eventStore
Definition ObjectMgr.h:1722
uint32 GenerateAuctionID()
std::string GeneratePetName(uint32 entry)
void LoadCreatureTemplateGossip()
void LoadPointOfInterestLocales()
VehicleSeatAddonContainer _vehicleSeatAddonStore
Definition ObjectMgr.h:1882
QuestTemplateLocaleContainer _questTemplateLocaleStore
Definition ObjectMgr.h:1852
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:1763
void LoadGossipMenuItems()
void LoadCreatureTrainers()
ExclusiveQuestGroups _exclusiveQuestGroups
Definition ObjectMgr.h:1743
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:1828
void LoadRaceAndClassExpansionRequirements()
AreaTriggerTeleport const * GetMapEntranceTrigger(uint32 Map) const
QuestPOIContainer _questPOIStore
Definition ObjectMgr.h:1734
void OnDeleteSpawnData(SpawnData const *data)
DestructibleHitpointContainer _destructibleHitpointStore
Definition ObjectMgr.h:1836
void LoadGameObjects()
void LoadSpawnTrackingQuestObjectives()
SpellClickInfoContainer _spellClickInfoStore
Definition ObjectMgr.h:1753
void LoadPlayerChoicesLocale()
QuestGreeting const * GetQuestGreeting(TypeID type, uint32 id) const
void LoadPhaseNames()
void LoadWorldSafeLocs()
std::unordered_map< uint32, WorldSafeLocsEntry > _worldSafeLocs
Definition ObjectMgr.h:1720
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:1839
void LoadCreatures()
std::unordered_map< uint32, CreatureSummonedData > _creatureSummonedDataStore
Definition ObjectMgr.h:1821
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:2272
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:827
int32 _prevQuestID
Definition QuestDef.h:875
int32 _questSortID
Definition QuestDef.h:793
uint32 _contentTuningID
Definition QuestDef.h:792
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionValue
Definition QuestDef.h:752
uint32 _requiredSkillPoints
Definition QuestDef.h:882
std::array< LootItemType, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemType
Definition QuestDef.h:747
std::vector< uint32 > DependentBreadcrumbQuests
Definition QuestDef.h:780
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyId
Definition QuestDef.h:755
int32 _breadcrumbForQuestId
Definition QuestDef.h:878
void LoadQuestDetails(Field *fields)
Definition QuestDef.cpp:190
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemCount
Definition QuestDef.h:749
bool HasQuestObjectiveType(QuestObjectiveType type) const
Definition QuestDef.h:625
void LoadRewardDisplaySpell(Field *fields)
Definition QuestDef.cpp:154
uint32 _sourceItemIdCount
Definition QuestDef.h:887
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:748
uint32 _rewardSkillPoints
Definition QuestDef.h:821
void LoadQuestRequestItems(Field *fields)
Definition QuestDef.cpp:207
uint32 _requiredMinRepFaction
Definition QuestDef.h:883
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemId
Definition QuestDef.h:743
uint32 _requiredSkillId
Definition QuestDef.h:881
int32 _requiredMinRepValue
Definition QuestDef.h:884
uint32 _flags
Definition QuestDef.h:810
void LoadConditionalConditionalQuestDescription(Field *fields)
Definition QuestDef.cpp:321
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemCount
Definition QuestDef.h:744
void LoadQuestMailSender(Field *fields)
Definition QuestDef.cpp:267
uint32 _nextQuestInChain
Definition QuestDef.h:796
void LoadQuestObjective(Field *fields)
Definition QuestDef.cpp:272
int32 _exclusiveGroup
Definition QuestDef.h:877
Trinity::RaceMask< std::array< int32, 2 > > _allowableRaces
Definition QuestDef.h:831
uint32 _sourceItemId
Definition QuestDef.h:809
int32 GetPrevQuestId() const
Definition QuestDef.h:654
void LoadConditionalConditionalOfferRewardText(Field *fields)
Definition QuestDef.cpp:367
int32 _requiredMaxRepValue
Definition QuestDef.h:886
uint32 GetQuestType() const
Definition QuestDef.h:638
uint32 _requiredMaxRepFaction
Definition QuestDef.h:885
std::vector< uint32 > DependentPreviousQuests
Definition QuestDef.h:779
uint32 GetQuestId() const
Definition QuestDef.h:637
void LoadQuestOfferReward(Field *fields)
Definition QuestDef.cpp:223
uint32 _rewardMailDelay
Definition QuestDef.h:880
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDropQuantity
Definition QuestDef.h:746
uint32 _sourceSpellID
Definition QuestDef.h:874
uint32 _rewardSkillId
Definition QuestDef.h:820
uint32 _soundTurnIn
Definition QuestDef.h:828
int32 GetBreadcrumbForQuestId() const
Definition QuestDef.h:657
void LoadRewardHouseDecor(Field *fields)
Definition QuestDef.cpp:423
uint32 _specialFlags
Definition QuestDef.h:889
void SetSpecialFlag(QuestSpecialFlags flag)
Definition QuestDef.h:624
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionOverride
Definition QuestDef.h:753
std::array< uint32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionId
Definition QuestDef.h:751
uint32 _rewardSpell
Definition QuestDef.h:802
uint32 _allowableClasses
Definition QuestDef.h:873
QuestObjectives const & GetObjectives() const
Definition QuestDef.h:681
std::array< uint32, QUEST_REWARD_CURRENCY_COUNT > RewardCurrencyCount
Definition QuestDef.h:756
uint32 _id
Definition QuestDef.h:789
uint32 _rewardMailSenderEntry
Definition QuestDef.h:888
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDrop
Definition QuestDef.h:745
void LoadRewardHouseRoom(Field *fields)
Definition QuestDef.cpp:418
void LoadConditionalConditionalQuestCompletionLog(Field *fields)
Definition QuestDef.cpp:390
uint32 _rewardTitleId
Definition QuestDef.h:818
bool HasFlag(QuestFlags flag) const
Definition QuestDef.h:619
uint32 _rewardMailTemplateId
Definition QuestDef.h:879
void LoadRewardChoiceItems(Field *fields)
Definition QuestDef.cpp:184
void LoadTreasurePickers(Field *fields)
Definition QuestDef.cpp:413
uint32 _nextQuestID
Definition QuestDef.h:876
void LoadQuestTemplateAddon(Field *fields)
Definition QuestDef.cpp:242
static const int32 Reputation_Cap
SpellEffectName Effect
Definition SpellInfo.h:215
SpellInfo const * GetFirstRankSpell() const
uint32 const Id
Definition SpellInfo.h:328
bool IsRanked() const
int32 GetDuration() const
SpellEffectInfo const & GetEffect(SpellEffIndex index) const
Definition SpellInfo.h:588
std::vector< SpellEffectInfo > const & GetEffects() const
Definition SpellInfo.h:587
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:2506
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:635
TempSummon * ToTempSummon()
Definition Unit.h:1828
bool IsSummon() const
Definition Unit.h:749
bool IsInRaidWith(Unit const *unit) const
Definition Unit.cpp:12177
bool IsInPartyWith(Unit const *unit) const
Definition Unit.cpp:12158
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
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2186
static void StopNow(uint8 exitcode)
Definition World.h:667
#define sWorld
Definition World.h:916
@ CONFIG_STRICT_PET_NAMES
Definition World.h:250
@ CONFIG_MIN_PET_NAME
Definition World.h:253
@ CONFIG_MAX_PLAYER_LEVEL
Definition World.h:262
@ CONFIG_MIN_CHARTER_NAME
Definition World.h:252
@ CONFIG_STRICT_CHARTER_NAMES
Definition World.h:249
@ CONFIG_STRICT_PLAYER_NAMES
Definition World.h:248
@ CONFIG_MIN_PLAYER_NAME
Definition World.h:251
@ 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:200
@ ERROR_EXIT_CODE
Definition World.h:76
static constexpr ObjectData creatureData[]
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.
GridCoord ComputeGridCoord(float x, float y)
Optional< Result > StringTo(std::string_view str, Params &&... params)
CellCoord ComputeCellCoord(float x, float y)
struct advstd::ranges::Contains contains
STL namespace.
std::string questFailedText
Definition ObjectMgr.h:469
std::vector< Position > Vertices
Definition ObjectMgr.h:456
Optional< float > Height
Definition ObjectMgr.h:457
uint8 MinActiveExpansionLevel
Definition ObjectMgr.h:933
uint8 AccountExpansionLevel
Definition ObjectMgr.h:932
uint8 ActiveExpansionLevel
Definition ObjectMgr.h:931
std::weak_ptr< ConditionContainer > Conditions
bool Meets(WorldObject const *object) const
uint32 x_coord
uint32 y_coord
uint32 GetId() const
std::vector< uint32 > auras
uint16 meleeAnimKit
uint16 movementAnimKit
VisibilityDistanceType visibilityDistanceType
float wander_distance
Optional< uint64 > npcflag
uint32 curHealthPct
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
std::array< int32, 4 > ReputationBase
uint16 ParentFactionID
std::array< int16, 4 > ReputationClassMask
bool CanHaveReputation() const
std::array< Trinity::RaceMask< int64 >, 4 > ReputationRaceMask
QuaternionData ParentRotation
InvisibilityType invisibilityType
QuaternionData rotation
std::vector< std::string > Name
std::vector< std::string > Unk1
std::vector< std::string > CastBarCaption
std::array< uint32, 5 > ArtKits
struct GameObjectTemplate::@197::@201 questgiver
struct GameObjectTemplate::@197::@231 barberChair
uint32 data[MAX_GAMEOBJECT_DATA]
struct GameObjectTemplate::@197::@211 areaDamage
struct GameObjectTemplate::@197::@264 raw
struct GameObjectTemplate::@197::@207 spellFocus
struct GameObjectTemplate::@197::@232 destructibleBuilding
struct GameObjectTemplate::@197::@223 flagStand
struct GameObjectTemplate::@197::@206 chair
struct GameObjectTemplate::@197::@209 goober
bool IsDespawnAtAction() const
struct GameObjectTemplate::@197::@224 fishingHole
struct GameObjectTemplate::@197::@199 door
struct GameObjectTemplate::@197::@249 gatheringNode
struct GameObjectTemplate::@197::@205 trap
struct GameObjectTemplate::@197::@200 button
struct GameObjectTemplate::@197::@225 flagDrop
struct GameObjectTemplate::@197::@214 moTransport
struct GameObjectTemplate::@197::@221 spellCaster
std::string castBarCaption
struct GameObjectTemplate::@197::@202 chest
struct GameObjectTemplate::@197::@237 garrisonBuilding
float position_y
Definition ObjectMgr.h:158
float orientation
Definition ObjectMgr.h:160
float position_x
Definition ObjectMgr.h:157
uint32 mapId
Definition ObjectMgr.h:161
std::string name
Definition ObjectMgr.h:162
float position_z
Definition ObjectMgr.h:159
std::wstring wnameLow
Definition ObjectMgr.h:163
int32 FriendshipFactionID
Definition ObjectMgr.h:772
int32 LfgDungeonsID
Definition ObjectMgr.h:773
std::vector< std::string > BoxText
Definition ObjectMgr.h:529
std::vector< std::string > OptionText
Definition ObjectMgr.h:528
Optional< int32 > SpellID
Definition ObjectMgr.h:758
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:757
std::string OptionText
Definition ObjectMgr.h:747
int32 GossipOptionID
Definition ObjectMgr.h:744
uint32 ActionMenuID
Definition ObjectMgr.h:751
uint32 OrderIndex
Definition ObjectMgr.h:745
Optional< int32 > GossipNpcOptionID
Definition ObjectMgr.h:753
Optional< int32 > OverrideIconID
Definition ObjectMgr.h:759
uint32 ActionPoiID
Definition ObjectMgr.h:752
GossipOptionNpc OptionNpc
Definition ObjectMgr.h:746
std::string BoxText
Definition ObjectMgr.h:756
GossipOptionFlags Flags
Definition ObjectMgr.h:750
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:748
uint32 TextID
Definition ObjectMgr.h:766
uint32 MenuID
Definition ObjectMgr.h:765
uint32 safeLocId
Definition ObjectMgr.h:842
ConditionsReference Conditions
Definition ObjectMgr.h:843
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:169
uint32 item_template
Definition Mail.h:170
Definition Mail.h:175
uint64 messageID
Definition Mail.h:176
ObjectGuid::LowType receiver
Definition Mail.h:181
uint8 messageType
Definition Mail.h:177
uint64 COD
Definition Mail.h:189
time_t expire_time
Definition Mail.h:186
ObjectGuid::LowType sender
Definition Mail.h:180
std::vector< MailItemInfo > items
Definition Mail.h:184
time_t deliver_time
Definition Mail.h:187
uint32 checked
Definition Mail.h:190
uint16 mailTemplateId
Definition Mail.h:179
DBCPosition2D Corpse
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:67
std::string Text
Definition ObjectMgr.h:64
uint32 NextPageID
Definition ObjectMgr.h:65
int32 PlayerConditionID
Definition ObjectMgr.h:66
std::vector< PathPropertyEntry const * > Properties
Definition DB2Stores.h:346
std::vector< DBCPosition3D > Locations
Definition DB2Stores.h:345
uint16 health
Definition ObjectMgr.h:680
uint16 armor
Definition ObjectMgr.h:682
uint16 stats[MAX_STATS]
Definition ObjectMgr.h:679
uint16 mana
Definition ObjectMgr.h:681
std::unordered_set< uint32 > Areas
Definition ObjectMgr.h:914
bool IsAllowedInArea(uint32 areaId) const
std::vector< std::string > Question
Definition ObjectMgr.h:553
std::vector< std::string > Confirmation
Definition ObjectMgr.h:548
std::vector< std::string > SubHeader
Definition ObjectMgr.h:545
std::vector< std::string > Header
Definition ObjectMgr.h:544
std::vector< std::string > Description
Definition ObjectMgr.h:547
std::vector< std::string > Answer
Definition ObjectMgr.h:543
std::vector< std::string > ButtonTooltip
Definition ObjectMgr.h:546
std::string Confirmation
std::string ButtonTooltip
Optional< uint32 > RewardQuestID
std::string Description
std::string SubHeader
EnumFlag< PlayerChoiceResponseFlags > Flags
bool ForceDontShowChoicesAsList
uint32 SoundKitId
bool HideWarboardHeader
uint32 CloseSoundKitId
bool KeepOpenAfterChoice
std::vector< PlayerChoiceResponse > Responses
std::string Question
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:674
PlayerCreateInfoItems item
Definition ObjectMgr.h:663
int32 stats[MAX_STATS]
Definition ObjectMgr.h:632
std::vector< std::string > Name
Definition ObjectMgr.h:536
std::string Name
Definition ObjectMgr.h:738
uint32 Importance
Definition ObjectMgr.h:736
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
bool isUnit() const
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
std::vector< std::string > Greeting
Definition QuestDef.h:440
uint32 QuestID
Definition QuestDef.h:483
std::vector< std::string > Description
Definition QuestDef.h:463
std::vector< std::string > RewardText
Definition QuestDef.h:468
int32 QuestID
Definition ObjectMgr.h:821
void InitializeQueryData()
std::vector< QuestPOIBlobData > Blobs
Definition ObjectMgr.h:822
QuestRelations::const_iterator _it
Definition ObjectMgr.h:591
QuestRelations::const_iterator _end
Definition ObjectMgr.h:591
bool HasQuest(uint32 questId) const
QuestRelations::const_iterator _end
Definition ObjectMgr.h:605
QuestRelations::const_iterator _begin
Definition ObjectMgr.h:605
Iterator end() const
Definition ObjectMgr.h:600
std::vector< std::string > CompletionText
Definition QuestDef.h:458
std::vector< std::string > PortraitTurnInText
Definition QuestDef.h:451
std::vector< std::string > QuestCompletionLog
Definition QuestDef.h:453
std::vector< std::string > LogTitle
Definition QuestDef.h:445
std::vector< std::string > PortraitGiverName
Definition QuestDef.h:450
std::vector< std::string > PortraitTurnInName
Definition QuestDef.h:452
std::vector< std::string > QuestDescription
Definition QuestDef.h:447
std::vector< std::string > LogDescription
Definition QuestDef.h:446
std::vector< std::string > PortraitGiverText
Definition QuestDef.h:449
std::vector< std::string > AreaDescription
Definition QuestDef.h:448
std::vector< ClassAvailability > Classes
Definition ObjectMgr.h:939
float questMonthlyRate
Definition ObjectMgr.h:704
float questWeeklyRate
Definition ObjectMgr.h:703
float questDailyRate
Definition ObjectMgr.h:702
float questRepeatableRate
Definition ObjectMgr.h:705
float spellRate
Definition ObjectMgr.h:707
float questRate
Definition ObjectMgr.h:701
float creatureRate
Definition ObjectMgr.h:706
uint32 faction_rank[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:727
uint32 faction[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:725
float faction_rate[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:726
uint32 SceneId
Definition ObjectMgr.h:856
EnumFlag< SceneFlag > PlaybackFlags
Definition ObjectMgr.h:857
uint32 ScriptId
Definition ObjectMgr.h:860
uint32 ScenePackageId
Definition ObjectMgr.h:858
float Orientation
Definition ObjectMgr.h:273
uint32 Flags
Definition ObjectMgr.h:230
struct ScriptInfo::@270::@273 Talk
ScriptsType type
Definition ObjectMgr.h:214
uint32 QuestID
Definition ObjectMgr.h:278
struct ScriptInfo::@270::@280 KillCredit
struct ScriptInfo::@270::@287 CreateItem
int32 TextID
Definition ObjectMgr.h:231
struct ScriptInfo::@270::@278 TeleportTo
float DestX
Definition ObjectMgr.h:252
uint32 ItemEntry
Definition ObjectMgr.h:338
struct ScriptInfo::@270::@285 CastSpell
uint32 ChatType
Definition ObjectMgr.h:229
uint32 id
Definition ObjectMgr.h:215
struct ScriptInfo::@270::@272 Raw
uint32 delay
Definition ObjectMgr.h:216
float fData[4]
Definition ObjectMgr.h:224
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
float PosZ
Definition ObjectMgr.h:302
struct ScriptInfo::@270::@284 RemoveAura
struct ScriptInfo::@270::@282 TempSummonCreature
struct ScriptInfo::@270::@297 PlayAnimKit
float PosX
Definition ObjectMgr.h:300
uint32 Distance
Definition ObjectMgr.h:279
uint32 SpellID
Definition ObjectMgr.h:317
std::string GetDebugInfo() const
float DestZ
Definition ObjectMgr.h:254
struct ScriptInfo::@270::@279 QuestExplored
struct ScriptInfo::@270::@283 ToggleDoor
struct ScriptInfo::@270::@281 RespawnGameobject
uint32 GOGuid
Definition ObjectMgr.h:290
uint32 CreatureEntry
Definition ObjectMgr.h:284
uint32 Amount
Definition ObjectMgr.h:339
struct ScriptInfo::@270::@274 Emote
uint32 EmoteID
Definition ObjectMgr.h:236
uint32 GetValueForTierIndex(uint32 tierIndex) const
uint32 Value[MAX_SKILL_STEP]
Definition ObjectMgr.h:881
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:444
std::array< int32, 2 > MountCreatureID
Stores data for temp summons.
Definition ObjectMgr.h:89
Milliseconds time
Despawn time, usable only with certain temp summon types.
Definition ObjectMgr.h:93
TempSummonType type
Summon type, see TempSummonType for available types.
Definition ObjectMgr.h:92
uint32 entry
Entry of summoned creature.
Definition ObjectMgr.h:90
Position pos
Position, where should be creature spawned.
Definition ObjectMgr.h:91
Key for storing temp summon data in TempSummonDataContainer.
Definition ObjectMgr.h:79
uint32 SummonerEntry
Summoner's entry.
Definition ObjectMgr.h:82
std::vector< uint32 > UiMapPhaseIDs
Definition ObjectMgr.h:908
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:484
constexpr bool IsEmpty() const
Definition RaceMask.h:161
constexpr bool HasRace(uint32 raceId) const
Definition RaceMask.h:96
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:837
WorldLocation Loc
Definition ObjectMgr.h:836